Server Requirements
Technical requirements for building MCP servers compatible with mctx. Learn the mctx.json schema, directory structure, build configuration, and what the platform expects from your server.
Required: mctx.json File
Every MCP server deployed on mctx requires a mctx.json configuration file in the repository root (or subdirectory for monorepos).
This file follows the official MCP server manifest specification with one mctx-specific addition: the entrypoint field.
Minimal Example
{
"version": "1.0.0",
"description": "Get real-time weather data for any location",
"entrypoint": "dist/index.js"
}Full Example
{
"name": "Weather API",
"version": "1.0.0",
"description": "Get real-time weather data for any location worldwide",
"entrypoint": "dist/index.js",
"instructions": "Use 'get_weather' for current conditions. Provide city name or coordinates.",
"capabilities": {
"tools": {
"listChanged": false
}
}
}Field Reference
Required Fields
| Field | Type | Description |
|---|---|---|
version | string | Semantic version (e.g., 1.0.0, 2.1.3-beta.1) |
description | string | What your server does (1-500 characters) |
entrypoint | string | Path to built JavaScript file (.js or .mjs) |
Optional Fields
| Field | Type | Description |
|---|---|---|
name | string | Display name suggestion (can be overridden in dashboard) |
instructions | string | Instructions for AI models using your server (max 2000 chars) |
capabilities | object | MCP capability declarations |
Version Format
The version field must follow semantic versioning:
MAJOR.MINOR.PATCH[-PRERELEASE]Valid examples:
1.0.0- Initial release1.1.0- New feature added1.1.1- Bug fix2.0.0-beta.1- Pre-release version
Important: Each deployment requires a unique version. Bump the version before pushing updates to trigger a new deployment.
Description Guidelines
Good descriptions are concise, action-oriented, and specific:
Good:
"Query financial data from SEC filings with natural language"
Bad:
"A server that does things with data"
Best practices:
- Keep under 200 characters for optimal display
- Start with a verb
- Mention what makes your server unique
- Avoid generic phrases
Entrypoint
The entrypoint is the path to your compiled JavaScript file, relative to mctx.json.
Common patterns:
| Build Tool | Typical Entrypoint |
|---|---|
| tsc | dist/index.js |
| esbuild | dist/index.js |
| Rollup | build/index.js |
| unbuild | .output/index.mjs |
Requirements:
- Must be a
.jsor.mjsfile - Must export a default HTTP handler (Cloudflare Worker format)
- Must be committed to your repository (or built during CI)
Example handler:
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Handle MCP JSON-RPC requests
},
};Instructions Field
The instructions field provides context to AI models about how to use your server effectively.
Example:
{
"instructions": "Use 'search_docs' for finding documentation. Use 'get_page' with a URL to retrieve specific content. Results are markdown-formatted."
}Tips:
- Explain tool purposes briefly
- Mention expected input formats
- Note any limitations or special requirements
Capabilities Field
The capabilities field declares what MCP protocol features your server supports.
Important: Capabilities are protocol-level flags only. They do NOT contain tool/resource/prompt definitions. Your actual tools, resources, and prompts are discovered via your server's tools/list, resources/list, and prompts/list handlers.
{
"capabilities": {
"tools": {
"listChanged": false
},
"resources": {
"subscribe": true,
"listChanged": true
},
"prompts": {
"listChanged": true
}
}
}Capability flags:
| Capability | Description |
|---|---|
tools.listChanged | Server may send notifications/tools/list_changed |
resources.subscribe | Server supports resource subscriptions |
resources.listChanged | Server may send notifications/resources/list_changed |
prompts.listChanged | Server may send notifications/prompts/list_changed |
Common configurations:
| Server Type | Capabilities |
|---|---|
| Tools-only | { "tools": {} } or omit entirely |
| Static resources | { "resources": {} } |
| Dynamic resources | { "resources": { "subscribe": true, "listChanged": true } } |
If omitted, defaults to { "tools": {} }.
Directory Structure
Recommended project structure:
my-mcp-server/
├── src/
│ └── index.ts # Main entry point
├── dist/
│ └── index.js # Built output (committed or CI-built)
├── mctx.json # mctx configuration
├── package.json
└── tsconfig.jsonFor monorepos:
my-monorepo/
├── packages/
│ └── my-mcp-server/
│ ├── mctx.json # Your config here
│ ├── src/
│ └── dist/
│ └── index.js
└── package.jsonWhen deploying, enter packages/my-mcp-server as the path in the mctx dashboard.
Build Configuration
Your build output must be a single JavaScript file committed to your repository. This is the same pattern GitHub uses for shared Actions - bundle your dependencies, commit the output, and it just works.
esbuild Example
// build.js
import * as esbuild from "esbuild";
await esbuild.build({
entryPoints: ["src/index.ts"],
bundle: true,
outfile: "dist/index.js",
format: "esm",
platform: "browser",
});Or via package.json script:
{
"scripts": {
"build": "esbuild src/index.ts --bundle --format=esm --platform=browser --outfile=dist/index.js"
}
}TypeScript Configuration
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"outDir": "dist",
"strict": true
}
}Handler Interface
Your entrypoint must export a default object with a fetch method:
interface Env {
// Environment variables (set in mctx dashboard)
API_KEY?: string;
DATABASE_URL?: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
if (request.method !== "POST") {
return new Response("Method not allowed", { status: 405 });
}
const body = await request.json();
const { method, params, id } = body;
// Handle MCP JSON-RPC methods
// Return JSON-RPC response with proper headers
},
};Required Methods
Your server must implement these JSON-RPC methods:
| Method | Required? | Description |
|---|---|---|
initialize | ❌ No | Platform handles this automatically |
notifications/initialized | ❌ No | Platform handles this |
tools/list | ✅ Yes (if you have tools) | Return available tools |
tools/call | ✅ Yes (if you have tools) | Execute tool logic |
resources/list | ✅ Yes (if you have resources) | Return available resources |
resources/read | ✅ Yes (if you have resources) | Return resource content |
prompts/list | ✅ Yes (if you have prompts) | Return available prompts |
prompts/get | ✅ Yes (if you have prompts) | Return prompt messages |
Platform-managed initialization: mctx intercepts MCP initialize requests and responds directly using your mctx.json configuration. Your server code does NOT need to handle initialize.
JSON-RPC Response Format
All responses must follow JSON-RPC 2.0 specification:
Success response:
{
"jsonrpc": "2.0",
"result": { ... },
"id": 1
}Error response:
{
"jsonrpc": "2.0",
"error": {
"code": -32601,
"message": "Method not found"
},
"id": 1
}Required headers:
Content-Type: application/json
MCP-Protocol-Version: 2025-11-25Standard Error Codes
| Code | Constant | Description |
|---|---|---|
| -32700 | PARSE_ERROR | Invalid JSON |
| -32600 | INVALID_REQUEST | Invalid JSON-RPC |
| -32601 | METHOD_NOT_FOUND | Method does not exist |
| -32602 | INVALID_PARAMS | Invalid method parameters |
| -32603 | INTERNAL_ERROR | Server error |
Resource Limits
Your MCP server runs as a Cloudflare Worker with these limits per request:
| Resource | Limit | Notes |
|---|---|---|
| CPU time | 10,000ms | Total compute time (not wall clock) |
| Subrequests | 50 | Maximum outbound fetch() calls |
Tips for staying within limits:
- Cache expensive computations when possible
- Batch multiple API calls into fewer requests
- Keep tool implementations focused and efficient
Environment Variables
Access environment variables via the env parameter passed to your handler:
interface Env {
API_KEY: string;
DATABASE_URL: string;
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const apiKey = env.API_KEY;
// Use the API key in your tool implementations
},
};Set these in the mctx dashboard under Environment Variables.
Security: All environment variables are:
- Encrypted at rest - AES-256-GCM encryption via WebCrypto
- Decrypted only at deploy time - Injected into your worker, never stored in plaintext
- Never exposed - Not visible in logs, API responses, or dashboard after creation
Logging
Use console.* methods for debugging. Your output is streamed in real-time to your dashboard when you open the logs modal:
console.log(`[INFO] Processing request for ${toolName}`);
console.warn(`[WARN] Rate limit approaching`);
console.error(`[ERROR] API call failed: ${error.message}`);Best practices:
- Use structured prefixes (
[INFO],[WARN],[ERROR]) - Log important events (tool calls, external API calls)
- Never log secrets or API keys
- Keep log messages concise
Validation
mctx validates your mctx.json when you:
- Select a repository in the deployment form
- Push a new version to your branch
Common validation errors:
| Error | Solution |
|---|---|
| "mctx.json not found" | Add the file to your repository root or specified path |
| "Invalid version format" | Use semver format (e.g., 1.0.0) |
| "Entrypoint file not found" | Ensure the built file exists and is committed |
| "Description too long" | Keep under 500 characters |
Next Steps
- MCP Basics - Understanding the MCP protocol
- Tools & Resources - MCP SDK, examples, debugging guides
See something wrong? Report it or suggest an improvement — your feedback helps make these docs better.