agentleFS
VelimirMuellerVelimirMueller/claude-skills/skills/ai/build-mcp-server/SKILL.md

build-mcp-server

Skill7 starsChanged 17 days agoMITView on GitHub

Use when exposing a service or dataset to AI clients through the Model Context Protocol — few task-shaped tools with typed inputs, read-only by default, server-enforced confirmation for writes, stdio or Streamable HTTP, OAuth for remote, size limits, tests with the Inspector.

  • Installs packages
  • Sends data out

12 other projects use a version of this.

Adopted by

Most recently taken up by

The file

We split this skill into the rules it gives an agent

2 rules. 0 are common, 1 is distinctive, and 1 is very specific to this project.

  • 0

    Common

    Most of the 11 edited versions we read keep these.

  • 1

    Distinctive

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Read .claude/stack-profile.md (languages, backend.track, ai).

    line 12
  2. Distinctive:

    No server → full install (steps 4–6).

    line 26

“Common” is measured: kept by at least 6 in 10 of the 11 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Build an MCP Server

An MCP server is an API whose caller is a model: it reads your tool descriptions on every turn and recovers from errors by reading your messages. Design it for that reader. Spec revision 2026-07-28 (stateless, no sessions) and the v2 TypeScript SDK (@modelcontextprotocol/server) are the targets; the older single-package @modelcontextprotocol/sdk 1.x is the previous line.

1. Audit current state

Read .claude/stack-profile.md (languages, backend.track, ai). If absent, detect per stack-profile.md. Change nothing yet:

grep -n "modelcontextprotocol" package.json pyproject.toml 2>/dev/null           # which SDK and line
grep -rn "Mcp-Session-Id\|StreamableHTTPServerTransport\|SSEServerTransport\|initialize" src | head   # 2025-era code
ls .mcp.json mcp.json src/server.ts src/server.py 2>/dev/null
grep -rn "registerTool\|server.tool(\|@mcp.tool" src | wc -l                      # tool count
npm view @modelcontextprotocol/server version 2>/dev/null                          # current v2 line

Report: SDK line (v1 or v2), transport, tool count and names, whether any tool writes, how callers are authenticated, whether tool outputs are bounded.

2. Decide what to do

  • No server → full install (steps 4–6).
  • v1 server (@modelcontextprotocol/sdk, sessions, SSE) → migrate to v2 with the SDK's upgrade guide (ts.sdk.modelcontextprotocol.io/v2/migration/upgrade-to-v2); v1 still gets fixes for at least six months after 2026-07-27, so plan it, do not panic.
  • Server exists → check each tool against mcp-patterns.md and apply the delta.
  • A thin wrapper over one HTTP endpoint with one user → ask whether a plain CLI, a script or a skill is simpler. MCP earns its cost with several clients, several tools, or shared auth.
  • Everything present and Verify passes → "already in place".

3. Detect the track

SignalBranch
Local, one user, launched by the host as a child processstdio. No OAuth: credentials come from the environment the host passes
Remote or multi-userStreamable HTTP behind OAuth 2.1 (the server is a resource server). Stateless: no session
TypeScript@modelcontextprotocol/server + @modelcontextprotocol/hono (Hono) or @modelcontextprotocol/node / express
Pythonmcp 2.x: from mcp.server import MCPServer
Go, RustUse the official SDK for that language; apply mcp-patterns.md unchanged. No canonical code here

4. Install only what's missing

pnpm add @modelcontextprotocol/server @modelcontextprotocol/hono hono @hono/node-server zod    # remote, TypeScript
pnpm add -D @modelcontextprotocol/client vitest tsx typescript @types/node                      # tests
uv add mcp                                                                                      # Python

TypeScript 6 or later needs "types": ["node"] in tsconfig.json. Zod must be 4.2 or later (peer of the server package).

5. Generate the seams

Code: mcp-code.md.

src/server.ts     # createServer(auth): tools, resources; no transport code
src/http.ts       # Streamable HTTP: Hono app, bearer auth, protected-resource metadata
src/stdio.ts      # stdio entry for local use
tests/server.test.ts

Design the tools before writing code: list the 3 to 7 things a user asks, make one tool per intent, and write each description as "what it does, when to use it, what it returns, limits". Rules and examples: mcp-patterns.md. Identity (tenant, user) comes from auth (the verified token) passed to createServer, never from a tool argument.

6. Wire it up

  1. Local: claude mcp add my-server -- npx tsx src/stdio.ts (Claude Code). Remote: claude mcp add --transport http my-server https://host/mcp; the client discovers the authorization server from the 401 challenge.
  2. Remote only: point the protected-resource metadata at your authorization server (AUTH_ISSUER) and set PUBLIC_URL to the exact public URL of the endpoint; tokens must be issued for that audience. Use an existing authorization server (Auth0, Keycloak, Entra ID, a cloud IdP); do not write one.
  3. Behind a reverse proxy, set the allowed hosts and origins explicitly; disable response buffering for the MCP route.
  4. Log to stderr (stdio) or your logger (HTTP), never to stdout on stdio: stdout is the protocol channel.

7. Verify

pnpm typecheck && pnpm vitest run tests/server.test.ts           # in-memory client: tools, structured output, scoping, confirmation
npx @modelcontextprotocol/inspector --cli npx tsx src/stdio.ts --method tools/list
npx @modelcontextprotocol/inspector --cli npx tsx src/stdio.ts --method tools/call --tool-name search_tickets --tool-arg status=open
curl -si -X POST http://127.0.0.1:3000/mcp -H 'Content-Type: application/json' -d '{}' | grep -i www-authenticate

Expected: tests green (6 in the example); tools/list returns your tools in a stable order with annotations; the tools/call result carries structuredContent; the unauthenticated curl returns 401 with www-authenticate: Bearer … resource_metadata="…/.well-known/oauth-protected-resource/mcp". For a visual check run the Inspector without --cli and click through each tool, including a bad input and a request with the wrong token.

References

  • mcp-patterns.md: rules and why. mcp-code.md: TypeScript and Python code.
  • llm-security.md: tool outputs are untrusted text for the client; least privilege by token.
  • stack-versions.md: spec revision, SDK lines, Inspector.
  • security-baseline.md, config.md.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.