MCP Server Design
Overview
The Model Context Protocol (MCP) is the 2026 standard for exposing tools, resources, and prompts to any agent runtime (Claude, Codex, Gemini, OpenCode, custom). This skill covers transport choice, capability schema, Server Cards, and registry publishing.
When to use
- Need to expose internal APIs/databases as agent-callable tools
- Want one integration that works across Claude / Codex / OpenCode / etc.
- Building reusable, shareable capability packs
- Replacing brittle plugin systems with a standard protocol
Capability primitives
| Primitive |
Use for |
| Tools |
Side-effecting actions (write, call API, run code) |
| Resources |
Read-only data (files, DB rows, search results) |
| Prompts |
Reusable templates with parameters |
| Sampling |
Server requests completion from client's LLM |
| Roots |
Filesystem boundaries the server may touch |
Don't make every read a Tool — use Resources. Reserve Tools for things that change state or have cost.
Transport selection
| Transport |
Pick when |
| stdio |
Local single-user (Claude Desktop, IDE plugin) |
| Streamable HTTP (2026 default) |
Multi-user, remote, stateless, web-friendly |
| SSE (legacy) |
Migrate to Streamable HTTP |
Stateless Streamable HTTP is the production default — works behind load balancers, no sticky sessions.
Minimum server (Python, FastMCP)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("acme-billing")
@mcp.tool()
def create_invoice(customer_id: str, amount_cents: int) -> dict:
"""Create a draft invoice. Returns {invoice_id, status}."""
return billing_api.create(customer_id, amount_cents)
@mcp.resource("invoice://{invoice_id}")
def get_invoice(invoice_id: str) -> str:
return billing_api.fetch(invoice_id).json()
if __name__ == "__main__":
mcp.run(transport="streamable-http", port=8080)
Server Card (required for registry)
{
"name": "acme-billing",
"version": "1.2.0",
"description": "Acme billing operations",
"publisher": "acme",
"homepage": "https://github.com/acme/mcp-billing",
"transports": ["streamable-http", "stdio"],
"capabilities": {
"tools": ["create_invoice", "void_invoice"],
"resources": ["invoice://{id}", "customer://{id}"]
},
"auth": {"type": "oauth2", "scopes": ["billing:write"]},
"license": "MIT"
}
Design rules
- Tool descriptions are the API contract — write them for an LLM reader: action verb, when to call, what comes back, what side-effects occur
- Type strictly — JSON Schema for every argument, no
Any
- Idempotency — accept an
idempotency_key for state-changing tools
- Pagination — every list resource returns
cursor, not offsets
- Errors — structured
{error: {code, message, retriable}}, never raw stack traces
- Auth at the transport — OAuth2/OIDC at HTTP, never in tool args
- Rate limit headers — return
X-RateLimit-* so agents can back off
Publishing
mcp registry publish ./server-card.json
mcp registry verify acme/billing
Public registry: registry.modelcontextprotocol.io (2026).
Further reading
- MCP 2026 spec — protocol, transports, lifecycle
- Server Card schema —
registry.modelcontextprotocol.io
- FastMCP / TypeScript SDK — server patterns
- MCP security checklist — auth, sandboxing, audit
1---2name: mcp-server-design3description: Build and publish production MCP servers — capability primitives, Streamable HTTP transport, Server Cards, registry compliance. Use when you're exposing tools/resources/prompts to multiple agent runtimes via Model Context Protocol.4---56# MCP Server Design78## Overview910The Model Context Protocol (MCP) is the 2026 standard for exposing tools, resources, and prompts to any agent runtime (Claude, Codex, Gemini, OpenCode, custom). This skill covers transport choice, capability schema, Server Cards, and registry publishing.1112## When to use1314- Need to expose internal APIs/databases as agent-callable tools15- Want one integration that works across Claude / Codex / OpenCode / etc.16- Building reusable, shareable capability packs17- Replacing brittle plugin systems with a standard protocol1819## Capability primitives2021| Primitive | Use for |22|-----------|---------|23| **Tools** | Side-effecting actions (write, call API, run code) |24| **Resources** | Read-only data (files, DB rows, search results) |25| **Prompts** | Reusable templates with parameters |26| **Sampling** | Server requests completion from client's LLM |27| **Roots** | Filesystem boundaries the server may touch |2829Don't make every read a Tool — use Resources. Reserve Tools for things that change state or have cost.3031## Transport selection3233| Transport | Pick when |34|-----------|-----------|35| **stdio** | Local single-user (Claude Desktop, IDE plugin) |36| **Streamable HTTP** (2026 default) | Multi-user, remote, stateless, web-friendly |37| **SSE** (legacy) | Migrate to Streamable HTTP |3839Stateless Streamable HTTP is the production default — works behind load balancers, no sticky sessions.4041## Minimum server (Python, FastMCP)4243```python44from mcp.server.fastmcp import FastMCP45mcp = FastMCP("acme-billing")4647@mcp.tool()48def create_invoice(customer_id: str, amount_cents: int) -> dict:49 """Create a draft invoice. Returns {invoice_id, status}."""50 return billing_api.create(customer_id, amount_cents)5152@mcp.resource("invoice://{invoice_id}")53def get_invoice(invoice_id: str) -> str:54 return billing_api.fetch(invoice_id).json()5556if __name__ == "__main__":57 mcp.run(transport="streamable-http", port=8080)58```5960## Server Card (required for registry)6162```json63{64 "name": "acme-billing",65 "version": "1.2.0",66 "description": "Acme billing operations",67 "publisher": "acme",68 "homepage": "https://github.com/acme/mcp-billing",69 "transports": ["streamable-http", "stdio"],70 "capabilities": {71 "tools": ["create_invoice", "void_invoice"],72 "resources": ["invoice://{id}", "customer://{id}"]73 },74 "auth": {"type": "oauth2", "scopes": ["billing:write"]},75 "license": "MIT"76}77```7879## Design rules80811. **Tool descriptions are the API contract** — write them for an LLM reader: action verb, when to call, what comes back, what side-effects occur822. **Type strictly** — JSON Schema for every argument, no `Any`833. **Idempotency** — accept an `idempotency_key` for state-changing tools844. **Pagination** — every list resource returns `cursor`, not offsets855. **Errors** — structured `{error: {code, message, retriable}}`, never raw stack traces866. **Auth at the transport** — OAuth2/OIDC at HTTP, never in tool args877. **Rate limit headers** — return `X-RateLimit-*` so agents can back off8889## Publishing9091```bash92mcp registry publish ./server-card.json93mcp registry verify acme/billing94```9596Public registry: `registry.modelcontextprotocol.io` (2026).9798## Further reading99100- MCP 2026 spec — protocol, transports, lifecycle101- Server Card schema — `registry.modelcontextprotocol.io`102- FastMCP / TypeScript SDK — server patterns103- MCP security checklist — auth, sandboxing, audit