MCP Starter

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.

Category
访问服务器

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

  1. Replace HttpOrderService in src/domain/orders.ts with their API. Parse responses through a zod schema so an upstream change surfaces as one validation error instead of malformed data reaching the model.
  2. 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.
  3. Write descriptions that say when to call the tool. That single line drives most of the accuracy.
  4. Mark every destructive tool destructiveHint: true and route it through ConfirmationGate.
  5. Keep InMemoryOrderService working. 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.log corrupts 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 sessionIdGenerator to randomUUID — 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

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选