| name | mcp-builder |
| description | Build, test, and deploy production-quality MCP (Model Context Protocol) servers. Full lifecycle -- build locally, test interactively with MCP Inspector, iterate until working, then optionally deploy to Runlayer. Triggers include "build an MCP server", "create MCP tools", "implement MCP", "add tools for [service]", "connect [service] via MCP", or any MCP server development task. |
MCP Server Builder
Build, test, and deploy MCP servers that enable LLMs to accomplish real-world tasks through well-designed tools. Server quality is measured by how effectively agents can use the tools, not by API coverage alone.
Before You Start
Ask the user which programming language they want to use before doing anything else. Use AskQuestion to prompt for their preferred language. Do not proceed until they answer. Then load the corresponding language-specific reference from references/:
- TypeScript -> read
references/typescript.md
- Python -> read
references/python.md
- Other -> use the language-agnostic patterns in
references/guidelines.md and adapt to the chosen language
Apply the language-specific conventions (naming, SDK, validation library, project structure, idioms) from the loaded reference throughout the entire build.
Development Process
Follow six phases in order. Phases 1-5 are mandatory. Do NOT skip any phase. Do NOT declare the server complete until Phase 5 passes.
- Research & Plan -- Study the target API (endpoints, auth, data models, rate limits, pagination). Plan tool coverage. Prioritize comprehensive API coverage over workflow shortcuts when uncertain.
- Implement -- Build infrastructure first (API client, auth, errors, formatters), then tools incrementally.
- Review & Build -- Build, lint, fix all errors. Review for duplication, consistent errors, type coverage.
- Evaluate -- Create 10 complex realistic questions requiring multiple tool calls with stable verifiable answers.
- Verify (MANDATORY) -- Test the server by sending real MCP protocol messages. At minimum: initialize, tools/list, and one tool call. Iterate on failures. The server is NOT done until this passes. See
references/testing.md.
- Deploy (Optional) -- When tests pass, offer to deploy to Runlayer via
uvx runlayer deploy. See references/deploy.md.
Architecture Decisions
Determine these upfront:
Transport:
stdio -- local/CLI tools, single user, simple setup
Streamable HTTP -- remote/cloud, multi-client, serverless. Avoid SSE (deprecated).
State:
- Stateless (recommended for remote) -- fresh server per request, horizontal scaling, no session leaks
- Stateful -- when session continuity is required
Auth:
- OAuth 2.1 -- remote servers with user identity
- API key via env vars -- simpler integrations
- None -- local-only tools
Language conventions (applied after user selects language):
| Language | Server name | Validation | SDK |
|---|
| TypeScript | {service}-mcp-server | Zod | @modelcontextprotocol/sdk |
| Python | {service}_mcp | Pydantic | mcp (FastMCP) |
| Other | {service}-mcp | JSON Schema | Implement protocol directly |
Tool Design Rules
- Name:
{service}_{action}_{resource} in snake_case. Always prefix with service name.
- Required fields:
name, title, description, inputSchema, outputSchema, annotations
- Annotations: Set
readOnlyHint, destructiveHint, idempotentHint, openWorldHint on every tool.
- Descriptions: Must precisely match functionality. Include parameter docs, return schema, usage examples (when to use AND when NOT to use), and error conditions.
- Schemas: Use strict mode. Add constraints (min/max/pattern/enum) and
.describe() on every field.
- Responses: Return both
content (text) and structuredContent (typed data).
For detailed patterns, validation examples, response formatting, error handling, pagination, auth, testing, security, logging, and deployment guidance, read references/guidelines.md.
Project Structure
{service}-mcp-server/
├── src/
│ ├── index.ts # Entry point (dual-mode: local + serverless)
│ ├── tools/ # Tool implementations + registry
│ ├── resources/ # MCP resource handlers (if needed)
│ ├── services/ # API clients, external service wrappers
│ ├── schemas/ # Validation schemas
│ ├── utils/ # Shared helpers (retry, formatting, errors)
│ ├── auth/ # Auth, token storage, middleware
│ └── types/ # Type definitions
├── tests/
│ ├── unit/
│ └── integration/
└── README.md
Implementation Workflow
- Scaffold the project structure above
- Build shared utilities first:
makeApiRequest -- centralized HTTP client with auth, timeout, retries
handleApiError -- maps HTTP status to MCP error codes with context
createToolResponse / createErrorResponse -- consistent response envelopes
withRetry -- exponential backoff (retry 429, 5xx, network errors; skip other 4xx)
- Implement tools one at a time, each with:
- Schema-validated input (strict mode, constraints, descriptions)
- Handler wrapped in
withRetry
- Both text + structured content in response
- Pagination for any list operation (default 20-50 items, return
has_more + next_offset)
- Character limit enforcement (truncate at ~25K chars with message)
- Implement resources if the service has URI-addressable data
- Set up transport (stdio or Streamable HTTP based on deployment target)
- Add dual-mode entry point (serverless handler export + local server start)
- Test -- unit tests for handlers/validators, integration tests gated on env vars
- Evaluate -- 10 QA pairs per the evaluation format
Quality Checklist
Before declaring the server complete:
Phase 5: Verify (MANDATORY -- DO NOT SKIP)
This phase is required. The server is NOT complete until these tests pass. Read references/testing.md for detailed patterns.
Minimum Viable Test (always run, no credentials needed)
Even without API credentials, you MUST verify the server works at the MCP protocol level by piping JSON-RPC messages to the server process:
- Initialize -- Send
initialize request, confirm server responds with protocolVersion, capabilities, and serverInfo.
- List tools -- Send
tools/list, confirm all expected tools appear with correct names, schemas, and annotations.
- Error handling -- Call a tool (e.g. a read tool) and verify it returns a graceful
isError: true response (not a crash) when credentials are missing or the resource doesn't exist.
Example for stdio servers:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' | node dist/index.js 2>/dev/null
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}\n{"jsonrpc":"2.0","method":"notifications/initialized"}\n{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n' | node dist/index.js 2>/dev/null
printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}\n{"jsonrpc":"2.0","method":"notifications/initialized"}\n{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"SERVICE_TOOL_NAME","arguments":{}}}\n' | node dist/index.js 2>/dev/null
Adapt node dist/index.js for Python (uv run server.py) as needed.
If any test fails, fix the code and re-run. Do not proceed to Phase 6.
Full Interactive Testing (when credentials are available)
-
Start MCP Inspector:
- TypeScript:
npx @anthropic-ai/mcp-inspector npx tsx src/index.ts
- Python:
npx @anthropic-ai/mcp-inspector uv run server.py
-
Test each tool systematically:
- Start with read-only tools (lowest risk)
- Test edge cases: empty inputs, pagination boundaries, max lengths
- Verify error handling with invalid inputs
- Test destructive operations last (with user confirmation)
-
On failure:
- Capture the error message from Inspector output
- Fix the issue in code
- Re-run the failing test
- Continue until all tools pass
-
Exit criteria: All tools respond correctly to valid inputs and return proper MCP errors for invalid inputs.
Phase 6: Deploy (Optional)
When all interactive tests pass, ask the user:
"Your MCP server is working correctly. Would you like to deploy it to Runlayer?"
If yes, read references/deploy.md for full details, then:
Step 1: Login
uvx runlayer login
This stores credentials locally. No --secret/--host flags needed on subsequent commands.
Step 2: Ensure the server can listen on a port
See references/deploy.md Transport Considerations. Options:
- Native HTTP (recommended): add
/health + /mcp endpoints
- stdio with bridge: wrap with
supergateway in the Dockerfile
- Dual-mode: use
PORT env var to switch between stdio and HTTP
Step 3: Create deployment files
- Dockerfile — Use template from
references/deploy.md. Test locally with docker build .
.dockerignore — Exclude node_modules, dist, .git, .env, tests
- Initialize deployment:
uvx runlayer deploy init
This generates runlayer.yaml with the deployment ID. Edit it to set service.port.
Step 4: Deploy
uvx runlayer deploy
Step 5: Report success
Show:
- Deployment ID
- Instructions to register as MCP server in the Runlayer UI
- Any env vars the user still needs to configure in Runlayer (API keys, credentials for the target service)