MCP Starter
A production-grade MCP server for order management, featuring tools for looking up and refunding orders with safety measures like confirmation tokens, rate limiting, and error handling.
README
MCP Starter
A production-grade Model Context Protocol server you can fork for client work. Both transports are implemented and tested: stdio (local) and Streamable HTTP (remote, stateless).
54 tests, clean typecheck, no any in the source.
npm install
npm test # 54 passing
npm run build
npm start # stdio
npm run start:http
npm run inspect # official MCP Inspector UI
What MCP is
MCP is a standard way to give an AI assistant access to somebody else's system. Before it, every integration was one-off: custom code for Shopify, more for HubSpot, more for the client's internal database. MCP replaces that with one protocol, so any assistant that speaks it — Claude Desktop, Claude Code, Cursor, ChatGPT — can use any MCP server.
A server exposes three things:
| Concept | What it is | In this repo |
|---|---|---|
| Tools | Actions the model can take | lookup_order, refund_order |
| Resources | Read-only data pulled into context | the return policy, an order record |
| Prompts | Reusable templates the user picks | draft_reply |
Tools are what clients pay for. Resources and prompts are polish.
Transport is how the assistant reaches the server. stdio: the host launches the server as a local process and talks over stdin/stdout — used for anything touching a local machine, zero hosting cost. Streamable HTTP: the server lives at a URL — used for multi-user or SaaS-backed work, costs hosting but the customer installs nothing. Same server code, different entrypoint.
What makes this production grade
Most MCP examples online are a server.tool() call and a console.log. Here is the difference.
Destructive actions are gated by a signed, single-use token
refund_order moves money. Calling it without a confirmToken does not refund — it returns a summary plus a token. The model must show the summary to the user and call again with the token.
The token is an HMAC bound to the exact arguments, so the model cannot get approval for a $1 refund and then execute a $189 one. It is single-use, so a retry loop can't fire twice, and it expires in five minutes. All four properties are tested, including the escalation attempt.
Errors never leak internals to the model
Every handler is wrapped. Domain errors become a clean isError tool result the model can recover from; unknown throws become a generic message while the real one goes to the logs. A test asserts a Postgres connection string in an exception never reaches the model-visible text.
Errors are also classified retryable vs not, and the retry logic respects it — a 404 fails once instead of burning the full retry budget.
Upstream calls have deadlines, retries, and jitter
Hard per-attempt timeout (a hung vendor API would otherwise hang the whole assistant turn), exponential backoff with full jitter, Retry-After honored, caller cancellation respected without retrying. Tested with an injected fetch and clock, so the suite is fast and deterministic.
Auth is constant-time; the server refuses to run open in production
Tokens are SHA-256 hashed before comparison, so timingSafeEqual gets equal-length buffers and comparison time leaks nothing. With NODE_ENV=production and no tokens configured, the server returns 500 rather than serving an open endpoint.
DNS-rebinding protection that actually works
A local MCP server is reachable from any web page the user visits: the page resolves an attacker domain to 127.0.0.1 and POSTs to it. Checking the Host header blocks this.
The SDK ships this feature, but it does an exact string compare against the raw header — which includes the port — so an allowlist of 127.0.0.1 silently 403s every request on any non-default port. Our integration test caught it. host-guard.ts compares hostname and port separately: an entry without a port matches any port, an entry with one must match both. IPv6 literals and case-insensitivity are handled and tested.
Everything else
- Structured tool output (
outputSchema+structuredContent) so hosts get typed data, not a string to re-parse - Tool annotations (
readOnlyHint,destructiveHint,idempotentHint) so hosts can prompt appropriately - Per-credential rate limiting (token bucket, lazy refill, swept to bound memory)
- Single-flight TTL cache — a model firing the same tool three times in a turn makes one upstream call
- Structured NDJSON logging to stderr with request IDs, child loggers, and automatic secret redaction
- Config validated at boot — bad config kills the process with a readable message
- Graceful shutdown — SIGTERM drains in-flight requests with a hard-stop backstop
- Multi-stage Dockerfile, non-root, with a healthcheck
- CI across Node 20/22/24 plus
npm audit
Layout
src/
config.ts zod-validated env
logger.ts NDJSON to stderr, redaction
errors.ts error taxonomy, public vs internal messages
http-client.ts timeouts, retries, jitter
cache.ts TTL + single-flight
rate-limit.ts token bucket
confirm.ts HMAC confirmation tokens
factory.ts dependency graph + server assembly
domain/orders.ts upstream adapter (HTTP + in-memory)
tools/
wrap.ts timing, logging, error normalization
orders.ts the tools themselves
transports/
stdio.ts local entrypoint
http.ts remote entrypoint
host-guard.ts DNS-rebinding protection
test/
unit.test.ts 37 tests
integration.test.ts 17 tests, real MCP client + real HTTP server
Wire it into Claude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"starter": {
"command": "node",
"args": ["/absolute/path/to/mcp-starter/dist/transports/stdio.js"]
}
}
}
Restart Claude Desktop.
Deploy the HTTP version
docker build -t mcp-starter .
docker run -p 3000:3000 \
-e NODE_ENV=production \
-e MCP_AUTH_TOKENS="$(openssl rand -hex 32)" \
-e ALLOWED_HOSTS=your-domain.com \
mcp-starter
Or push to GitHub and point Railway at it with start command npm run start:http. Set MCP_AUTH_TOKENS and ALLOWED_HOSTS. See .env.example for every setting.
Building a client's server
- Replace
HttpOrderServiceinsrc/domain/orders.tswith their API. Parse responses through a zod schema so an upstream change surfaces as one validation error instead of malformed data reaching the model. - Rewrite the tools around what their staff does by hand every day. One tool per job to be done, not per API endpoint. Five well-named tools beat twenty.
- Write descriptions that say when to call the tool. That single line drives most of the accuracy.
- Mark every destructive tool
destructiveHint: trueand route it throughConfirmationGate. - Keep
InMemoryOrderServiceworking. It's how the tests stay fast and how you demo without credentials.
Gotchas that bite people
- Never write to stdout in a stdio server. One
console.logcorrupts the protocol stream. Everything here logs to stderr. - Don't return raw API dumps. Every token of tool output is context the model must read.
- Stateless HTTP scales; sessions don't. This creates a fresh server per request, so it runs on any number of instances with no sticky routing. If you need session state, switch
sessionIdGeneratortorandomUUID— but then you own session eviction.
Scoping this as paid work
- Wrap one internal API for Claude Desktop — stdio, no hosting, clean one-time deliverable.
- Hosted multi-tenant server with auth — HTTP, needs hosting, key rotation, and upkeep when the upstream API changes. Never sell this as a fixed one-time build; attach a retainer.
- Fix or finish a half-built MCP server — fits the Fix & Finish line.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。