MCP Authoring Guide¶
重要性:⭐⭐ 目标读者:MCP server 作者 关联:MCP_PROTOCOL.md、topics/deep-dive-mcp-client.md、tutorials/build-mcp-server.md
1. 概览¶
写一个生产级 MCP server 的指南。
5 大主题: - 设计 - 实现 - 测试 - 发布 - 维护
2. 5 个设计原则¶
2.1 单一职责¶
单一。
2.2 描述清晰¶
// ✅ 描述让 LLM 知道何时调
{
name: 'get_weather',
description: 'Get the current weather for a city. Returns temperature and conditions.',
// 不是:'weather tool'
}
清晰。
2.3 输入严格¶
// ✅ 用 zod
const schema = z.object({
city: z.string().min(1).max(100),
units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
})
严格。
2.4 错误友好¶
友好。
2.5 超时控制¶
// ✅ 30s 默认
const TIMEOUT = 30_000
const timeoutPromise = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), TIMEOUT)
)
const result = await Promise.race([fetchData(), timeoutPromise])
超时。
3. 5 步实现¶
3.1 Step 1: 项目初始化¶
mkdir my-mcp-server
cd my-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
npm。
3.2 Step 2: tsconfig¶
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"strict": true,
"esModuleInterop": true,
"outDir": "dist"
}
}
TS。
3.3 Step 3: server 实现¶
// src/index.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js'
import { z } from 'zod'
const server = new Server(
{ name: 'my-mcp', version: '1.0.0' },
{ capabilities: { tools: {} } }
)
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'add',
description: 'Add two numbers and return the sum',
inputSchema: {
type: 'object',
properties: {
a: { type: 'number', description: 'First number' },
b: { type: 'number', description: 'Second number' },
},
required: ['a', 'b'],
},
},
],
}))
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params
if (name === 'add') {
const { a, b } = z.object({
a: z.number(),
b: z.number(),
}).parse(args)
return {
content: [{ type: 'text', text: String(a + b) }],
}
}
throw new Error(`Unknown tool: ${name}`)
})
const transport = new StdioServerTransport()
await server.connect(transport)
完整。
3.4 Step 4: package.json¶
{
"name": "@my-org/my-mcp",
"version": "1.0.0",
"bin": { "my-mcp": "dist/index.js" },
"scripts": { "build": "tsc" },
"files": ["dist"]
}
npm。
3.5 Step 5: build & test¶
npm run build
chmod +x dist/index.js
# 接到 Claude Code
claude mcp add --transport stdio -- npx @my-org/my-mcp
build + test。
4. 5 个核心 API¶
4.1 ListToolsRequestSchema¶
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{ name, description, inputSchema }
],
}))
list tools。
4.2 CallToolRequestSchema¶
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params
// 处理
return { content: [...] }
})
call tool。
4.3 ListResourcesRequestSchema¶
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
resources: [
{ uri, name, mimeType }
],
}))
list resources。
4.4 ReadResourceRequestSchema¶
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params
// 读
return { contents: [{ uri, mimeType, text }] }
})
read resource。
4.5 ListPromptsRequestSchema¶
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
prompts: [
{ name, description, arguments }
],
}))
list prompts。
5. 4 种 transport 实现¶
5.1 stdio(最常见)¶
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const transport = new StdioServerTransport()
await server.connect(transport)
stdio。
5.2 SSE¶
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
const transport = new SSEServerTransport('/endpoint', response)
SSE。
5.3 Streamable HTTP¶
Streamable。
5.4 WebSocket¶
// 自实现
const ws = new WebSocketServer({ port: 8080 })
ws.on('connection', (socket) => { /* ... */ })
WS。
6. 5 个 OAuth 步骤¶
6.1 Step 1: 暴露 OAuth endpoint¶
app.get('/.well-known/oauth-authorization-server', ...)
app.post('/oauth/register', ...) // Dynamic client registration
app.get('/oauth/authorize', ...)
app.post('/oauth/token', ...)
endpoint。
6.2 Step 2: Dynamic Client Registration¶
DCR。
6.3 Step 3: Authorize¶
authorize。
6.4 Step 4: Token¶
token。
6.5 Step 5: Refresh¶
refresh。
7. 5 种 Tool 设计¶
7.1 文件读取¶
{
name: 'read_file',
description: 'Read a file and return its contents',
inputSchema: {
type: 'object',
properties: {
path: { type: 'string' },
encoding: { type: 'string', enum: ['utf-8', 'base64'] }
},
required: ['path']
}
}
read。
7.2 数据查询¶
{
name: 'query_database',
description: 'Run a SQL query against the database',
inputSchema: {
type: 'object',
properties: {
sql: { type: 'string' },
params: { type: 'array' }
},
required: ['sql']
}
}
query。
7.3 API 调用¶
{
name: 'fetch_url',
description: 'Fetch a URL and return its content',
inputSchema: {
type: 'object',
properties: { url: { type: 'string' } },
required: ['url']
}
}
API。
7.4 计算¶
{
name: 'calculate',
description: 'Perform a calculation',
inputSchema: {
type: 'object',
properties: {
expression: { type: 'string' }
},
required: ['expression']
}
}
compute。
7.5 写操作¶
{
name: 'create_issue',
description: 'Create a GitHub issue',
inputSchema: {
type: 'object',
properties: {
repo: { type: 'string' },
title: { type: 'string' },
body: { type: 'string' }
},
required: ['repo', 'title']
}
}
write。
8. 3 种 Resource 设计¶
8.1 文件型¶
file。
8.2 API 型¶
API。
8.3 动态型¶
dynamic。
9. 5 个测试技巧¶
9.1 单元测试¶
import { describe, it, expect } from 'vitest'
describe('add tool', () => {
it('returns sum', async () => {
const result = await handleCallTool('add', { a: 1, b: 2 })
expect(result.content[0].text).toBe('3')
})
})
unit。
9.2 集成测试¶
integration。
9.3 真实 client¶
real。
9.4 错误路径¶
error。
9.5 性能¶
// 测超时
const start = Date.now()
await handleCallTool('slow_tool', {})
const elapsed = Date.now() - start
expect(elapsed).toBeLessThan(30000)
perf。
10. 5 个发布步骤¶
10.1 Step 1: 测试¶
test。
10.2 Step 2: 文档¶
docs。
10.3 Step 3: 打包¶
build。
10.4 Step 4: npm publish¶
publish。
10.5 Step 5: 注册市场¶
marketplace。
11. 5 个维护任务¶
11.1 监控¶
monitor。
11.2 错误追踪¶
track。
11.3 性能¶
perf。
11.4 升级 SDK¶
upgrade。
11.5 用户支持¶
support。
12. 5 个常见错误¶
12.1 没描述¶
无 description。
12.2 描述太长¶
过长。
12.3 错误信息无 hint¶
// ❌
throw new Error('Invalid input')
// ✅
throw new Error('City not found. Provide a real city name.')
无 hint。
12.4 长操作无进度¶
// ❌ 30s 静默
await longOperation()
// ✅
sendProgress(0, 100)
await step1()
sendProgress(50, 100)
await step2()
无 progress。
12.5 资源泄漏¶
leak。
13. 完整示例:GitHub MCP Server¶
import { Octokit } from '@octokit/rest'
import { z } from 'zod'
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params
if (name === 'create_issue') {
const { repo, title, body } = z.object({
repo: z.string().regex(/^[\w-]+\/[\w-]+$/),
title: z.string().min(1).max(256),
body: z.string().optional(),
}).parse(args)
const [owner, repoName] = repo.split('/')
const issue = await octokit.issues.create({
owner, repo: repoName, title, body: body || '',
})
return {
content: [{
type: 'text',
text: `Issue created: ${issue.data.html_url}`,
}],
}
}
// ... 其他工具
})
完整。
14. 总结¶
MCP Authoring = 设计 + 实现 + 测试 + 发布 + 维护。
核心: - 5 设计原则 - 5 实现步骤 - 5 核心 API - 4 transport - 5 OAuth 步骤 - 5 测试技巧 - 5 发布步骤
下一步: - 看 MCP_PROTOCOL.md - 看 deep-dive-mcp-client.md - 看 tutorials/build-mcp-server.md - 写第一个 server