| name | mcp-development |
| description | Build, iterate, and ship MCP servers. Triggers on: 'build an MCP server', 'create MCP tools for API', 'design MCP server surface', 'MCP server architecture', 'add tools to MCP server', 'MCP server best practices'. |
| license | Apache-2.0 |
| compatibility | {"clients":["openai-codex","gemini-cli","opencode"]} |
| metadata | {"owner":"codex","domain":"mcp-development","maturity":"stable","risk":"low","tags":["mcp","development","server","lifecycle"]} |
Purpose
Guide the full development lifecycle of an MCP server — from understanding the target API, through designing the tool/resource/prompt surface, to implementation, testing, and deployment. This is the hub skill for MCP server projects that span multiple phases.
When to use this skill
- Building a new MCP server that wraps an external API or service
- Planning which tools, resources, and prompts an MCP server should expose
- Iterating on an existing MCP server (adding tools, improving schemas, fixing patterns)
- Converting API endpoints into MCP tool/resource/prompt primitives
- Designing the surface area (which operations become tools vs resources)
- Need a structured development workflow rather than ad hoc implementation
When NOT to use this skill
- Scaffolding a first-ever MCP server from a tutorial template → use framework getting-started guides first
- The task is narrowly about one tool's schema design in isolation
- The task is only about testing strategies without implementation
- Following a specific SDK's patterns without broader server design questions
Operating procedure
Phase 1 — Research and plan
- Study the target API. Read its documentation. Identify key endpoints, auth model, data shapes, rate limits, and pagination patterns.
- Map endpoints to MCP primitives:
- Tools — for actions the LLM should invoke (queries, mutations, computations)
- Resources — for read-only context data the host can present (schemas, configs, documentation)
- Prompts — for reusable interaction templates (e.g., "analyze this data using the available tools")
- Decide coverage strategy. Prioritize comprehensive API coverage over bespoke workflow tools. Comprehensive coverage lets agents compose operations flexibly.
- Choose stack. TypeScript (recommended for broadest compatibility) or Python (FastMCP for rapid development). See
mcp-typescript-sdk or mcp-python-fastmcp.
Phase 2 — Implement core infrastructure
- API client wrapper — centralized auth, error handling, retry logic
- Response formatting — consistent JSON or Markdown output across tools
- Pagination helper — many APIs and MCP
*/list methods use cursor-based pagination
- Error mapping — translate API errors to MCP error format:
Phase 3 — Implement primitives
For each tool:
- Define
inputSchema using Zod (TS) or Pydantic/type hints (Python)
- Write a clear
description — this is what the LLM reads to decide when to use the tool
- Set
annotations: readOnlyHint, destructiveHint, idempotentHint, openWorldHint
- Implement the handler with async I/O, proper error handling, and pagination support
- Optionally define
outputSchema for structured responses
For each resource:
- Choose a URI scheme (e.g.,
file://, https://, or custom)
- Implement
resources/list and resources/read
- Support
resources/subscribe if the data changes over time
For each prompt:
- Define the prompt name, description, and arguments
- Return structured
messages array with role and content
Phase 4 — Declare capabilities
In the initialize response, declare what the server supports:
{
"capabilities": {
"tools": { "listChanged": true },
"resources": { "subscribe": true, "listChanged": true },
"prompts": { "listChanged": true },
"logging": {}
}
}
Only declare capabilities you actually implement.
Phase 5 — Test and validate
- Build:
npm run build (TS) or python -m py_compile (Python)
- Inspector:
npx @modelcontextprotocol/inspector — verify all tools list, call correctly, return expected schemas
- Host test: Connect to a real host (Claude Desktop, VS Code, Codex) and verify end-to-end
- Edge cases: Test with invalid inputs, missing auth, rate-limited APIs, empty results
Phase 6 — Ship
- Choose transport (stdio for local, Streamable HTTP for remote)
- Add auth if remote (see
mcp-auth-transports)
- Write README with installation and configuration instructions
- Publish to npm/PyPI or register in an MCP registry
Decision rules
- Tool naming: Use
<domain>_<action> prefix pattern (e.g., github_create_issue, github_list_repos) for discoverability
- Tool count: Start with the most common 10-15 operations. Expand based on usage, not speculation.
- Resource vs Tool: If the LLM needs to take action → tool. If the host needs context → resource.
- Error messages: Include what went wrong AND what to try next. "API returned 403: check that the API key has write permissions for this repository."
- DRY: Extract shared API client, pagination, and formatting into utility modules
Output requirements
- Working MCP server with declared capabilities
- All tools have
inputSchema, description, and annotations
- Passes MCP Inspector verification for all primitives
- README with host configuration examples
Related skills
community-skill-harvester — find existing MCP-related skills or patterns
skill-evaluation — test and validate MCP server behavior
References
Failure handling
- No API documentation available: Use the API's OpenAPI/Swagger spec if available; otherwise explore endpoints manually with curl/httpie and document behavior as you discover it
- Tool too complex for single call: Split into a read step and a write step rather than one tool that does both; give each step a descriptive name like
github_get_issue and github_update_issue
- Tool count exceeds 30: Consider splitting into multiple focused MCP servers (e.g.,
github-issues-server, github-repos-server) rather than one monolith
- Inspector tests failing:
- Check that all tools return valid JSON with
isError flag set correctly
- Verify
inputSchema uses proper JSON Schema types (not TypeScript types)
- Ensure
listChanged capability matches actual implementation
- Schema validation errors:
- Zod schemas must use
.describe() on every field — this becomes the LLM's context
- Avoid nested objects deeper than 3 levels; flatten to top-level params with descriptive names
- Host connection failures:
- Verify transport matches host expectations (stdio for Claude Desktop, HTTP for custom hosts)
- Check that server responds to
initialize request within timeout (default 30s)
- Confirm no extra stdout output before or after JSON-RPC messages