Skip to content
Back to skills

Build Mcp Server

ASecurity

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.

  • 5 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 10, 2026
developmenttypescriptpythonrustgobashnodeexpressapibackendsecurity

Works with

  • claude code
  • cli
  • api
  • mcp

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 3 files and shows the line behind each finding

Scanned October 10, 2026

npx -y skills add VelimirMueller/lab-claude-skills --skill build-mcp-server --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Build Mcp Server?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Build Mcp Server
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/velimirmueller-build-mcp-server/badge)](https://www.skillsdirectory.com/skills/velimirmueller-build-mcp-server)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: build-mcp-server
description: 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.
---

# 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](../../core/_shared/stack-profile.md). Change nothing yet:

```bash
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](./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

| Signal | Branch |
|---|---|
| Local, one user, launched by the host as a child process | **stdio**. No OAuth: credentials come from the environment the host passes |
| Remote or multi-user | **Streamable HTTP** behind OAuth 2.1 (the server is a resource server). Stateless: no session |
| TypeScript | `@modelcontextprotocol/server` + `@modelcontextprotocol/hono` (Hono) or `@modelcontextprotocol/node` / `express` |
| Python | `mcp` 2.x: `from mcp.server import MCPServer` |
| Go, Rust | Use the official SDK for that language; apply [mcp-patterns.md](./mcp-patterns.md) unchanged. No canonical code here |

## 4. Install only what's missing

```bash
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](./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](./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

```bash
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](./mcp-patterns.md): rules and why. [mcp-code.md](./mcp-code.md): TypeScript and Python code.
- [llm-security.md](../_shared/llm-security.md): tool outputs are untrusted text for the client; least privilege by token.
- [stack-versions.md](../_shared/stack-versions.md): spec revision, SDK lines, Inspector.
- [security-baseline.md](../../core/_shared/security-baseline.md), [config.md](../../backend/_shared/config.md).

Files in this skill

  • SKILL.md6.1 KB
  • mcp-code.md12.5 KB
  • mcp-patterns.md13.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.