typescript-mcp by jezweb/claude-skills
npx skills add https://github.com/jezweb/claude-skills --skill typescript-mcp最后更新 : 2026-01-21 版本 : @modelcontextprotocol/sdk@1.25.3, hono@4.11.3, zod@4.3.5 规范版本 : 2025-11-25
npm install @modelcontextprotocol/sdk@latest hono zod
npm install -D @cloudflare/workers-types wrangler typescript
传输层推荐 : 生产环境使用 StreamableHTTPServerTransport。SSE 传输方式已弃用,仅为向后兼容而保留。Streamable HTTP 提供更好的错误恢复、双向通信和简化的部署。
基础 MCP 服务器 :
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { Hono } from 'hono';
import { z } from 'zod';
const server = new McpServer({ name: 'my-mcp-server', version: '1.0.0' });
server.registerTool(
'echo',
{
description: '回显输入内容',
inputSchema: z.object({ text: z.string() })
},
async ({ text }) => ({ content: [{ type: 'text', text }] })
);
const app = new Hono();
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
// 关键:设置错误处理器以捕获传输层错误
transport.onerror = (error) => {
console.error('MCP 传输层错误:', error);
};
// 关键:关闭传输层以防止内存泄漏
c.res.raw.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(c.req.raw, c.res.raw, await c.req.json());
return c.body(null);
});
export default app; // 关键:直接导出,而不是 { fetch: app.fetch }
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
部署 : wrangler deploy
API 密钥 (基于 KV):
app.use('/mcp', async (c, next) => {
const apiKey = c.req.header('Authorization')?.replace('Bearer ', '');
const isValid = await c.env.MCP_API_KEYS.get(`key:${apiKey}`);
if (!isValid) return c.json({ error: '未经授权' }, 403);
await next();
});
Cloudflare Zero Trust :
const jwt = c.req.header('Cf-Access-Jwt-Assertion');
const payload = await verifyJWT(jwt, c.env.CF_ACCESS_TEAM_DOMAIN);
任务支持长时间运行的操作,返回一个句柄供后续轮询结果。适用于计算密集型任务、批处理或可能需要输入的操作。
任务状态 : working → input_required → completed / failed / cancelled
服务器能力声明 :
const server = new McpServer({
name: 'my-server',
version: '1.0.0',
capabilities: {
tasks: {
list: {},
cancel: {},
requests: {
tools: { call: {} }
}
}
}
});
支持任务的工具 :
server.registerTool(
'long-running-analysis',
{
description: '分析大型数据集',
inputSchema: z.object({ datasetId: z.string() }),
execution: { taskSupport: 'optional' } // 'forbidden' | 'optional' | 'required'
},
async ({ datasetId }, extra) => {
// 如果作为任务调用,extra.task 包含 taskId
const result = await performAnalysis(datasetId);
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
}
);
客户端任务请求 :
{
"method": "tools/call",
"params": {
"name": "long-running-analysis",
"arguments": { "datasetId": "abc123" },
"task": { "ttl": 60000 }
}
}
任务生命周期 :
task 参数的请求 → 接收 taskIdtasks/get 轮询 taskIdcompleted 时,客户端调用 tasks/result 获取输出tasks/cancel 来中止任务服务器现在可以在采样请求中包含工具定义,实现服务器端的智能体循环。
使用场景 : 服务器需要使用 LLM + 工具编排多步推理,而无需自定义框架。
// 服务器发起包含可用工具的采样
const result = await server.requestSampling({
messages: [{ role: 'user', content: '分析此数据并在需要时获取更多' }],
maxTokens: 4096,
tools: [
{
name: 'fetch_data',
description: '从 API 获取额外数据',
inputSchema: { type: 'object', properties: { query: { type: 'string' } } }
}
]
});
// 处理响应中的工具调用
if (result.content[0].type === 'tool_use') {
const toolResult = await executeLocalTool(result.content[0]);
// 使用工具结果继续对话...
}
关键点 :
tools/list 相同的模式📚 规范 : SEP-1577
D1 数据库 :
server.registerTool('query-db', {
inputSchema: z.object({ query: z.string(), params: z.array(z.union([z.string(), z.number()])).optional() })
}, async ({ query, params }, env) => {
const result = await env.DB.prepare(query).bind(...(params || [])).all();
return { content: [{ type: 'text', text: JSON.stringify(result.results) }] };
});
KV, R2, Vectorize : 参见 references/cloudflare-integration.md
此技能预防了官方 MCP SDK 和 Cloudflare 仓库中记录的 20 个生产问题:
错误 : "Cannot read properties of undefined (reading 'map')" 来源 : honojs/hono#3955, honojs/vite-plugins#237 原因 : 使用 Vite 构建时错误的导出格式导致隐晦错误 预防 :
// ❌ 错误 - 导致隐晦的构建错误
export default { fetch: app.fetch };
// ✅ 正确 - 直接导出
export default app;
错误 : 内存泄漏、连接挂起 来源 : SDK 维护者的最佳实践 原因 : 请求结束时未关闭 StreamableHTTPServerTransport 预防 :
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport(/*...*/);
// 关键:始终在响应结束时关闭
c.res.raw.on('close', () => transport.close());
// ... 处理请求
});
错误 : ListTools 请求处理器无法生成 inputSchema 来源 : GitHub modelcontextprotocol/typescript-sdk#1028 原因 : Zod 模式未正确转换为 JSON Schema 预防 :
// ✅ 正确 - SDK 自动处理 Zod 模式转换
server.registerTool(
'tool-name',
{
inputSchema: z.object({ a: z.number() })
},
handler
);
// 除非需要自定义验证,否则无需手动使用 zodToJsonSchema()
错误 : 处理器收到 undefined 参数 来源 : GitHub modelcontextprotocol/typescript-sdk#1026 原因 : 注册和调用之间的模式类型不匹配 预防 :
const schema = z.object({ a: z.number(), b: z.number() });
type Input = z.infer<typeof schema>;
server.registerTool(
'add',
{ inputSchema: schema },
async (args: Input) => {
// args.a 和 args.b 类型正确且已传递
return { content: [{ type: 'text', text: String(args.a + args.b) }] };
}
);
错误 : 浏览器客户端无法连接到 MCP 服务器 来源 : 常见生产问题 原因 : HTTP 传输缺少 CORS 头 预防 :
import { cors } from 'hono/cors';
app.use('/mcp', cors({
origin: ['http://localhost:3000', 'https://your-app.com'],
allowMethods: ['POST', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization']
}));
错误 : API 滥用、DDoS 漏洞 来源 : 生产安全最佳实践 原因 : MCP 端点没有速率限制 预防 :
app.post('/mcp', async (c) => {
const ip = c.req.header('CF-Connecting-IP');
const rateLimitKey = `ratelimit:${ip}`;
const count = await c.env.CACHE.get(rateLimitKey);
if (count && parseInt(count) > 100) {
return c.json({ error: '超出速率限制' }, 429);
}
await c.env.CACHE.put(
rateLimitKey,
String((parseInt(count || '0') + 1)),
{ expirationTtl: 60 }
);
// 继续...
});
错误 : tsc 构建期间 Out of memory 来源 : GitHub modelcontextprotocol/typescript-sdk#985 原因 : MCP SDK 中的大型依赖树 预防 :
# 添加到 package.json scripts
"build": "NODE_OPTIONS='--max-old-space-size=4096' tsc && vite build"
错误 : 服务器在恶意 URI 模式上挂起 来源 : GitHub modelcontextprotocol/typescript-sdk#965 (安全) 原因 : URI 模板解析中的正则表达式拒绝服务 预防 : 更新到 SDK v1.20.2 或更高版本(包含修复)
错误 : 未经身份验证访问 MCP 工具 来源 : 生产安全最佳实践 原因 : 缺少或未正确实现身份验证 预防 : 生产服务器始终实现身份验证(参见身份验证模式部分)
错误 : 错误消息或日志中暴露密钥 来源 : Cloudflare Workers 安全最佳实践 原因 : 环境变量被记录或返回在响应中 预防 :
// ❌ 错误 - 暴露密钥
console.log('Env:', JSON.stringify(env));
// ✅ 正确 - 永不记录 env 对象
try {
// ... 使用 env.SECRET_KEY
} catch (error) {
// 不要在错误上下文中包含 env
console.error('操作失败:', error.message);
}
错误 : AbortError: This operation was aborted 来源 : GitHub Issue #1405 原因 : 调用 Server.connect(transport) 会静默覆盖之前的传输层而不发出警告,破坏所有早期连接 预防 :
// ✅ 正确 - 为每个 HTTP 会话创建新的 McpServer
app.post('/mcp', async (c) => {
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// 每个请求注册工具
server.registerTool('echo', { inputSchema: z.object({ text: z.string() }) },
async ({ text }) => ({ content: [{ type: 'text', text }] })
);
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
transport.onerror = (error) => console.error('传输层错误:', error);
c.res.raw.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(c.req.raw, c.res.raw, await c.req.json());
return c.body(null);
});
// ❌ 错误 - 跨会话重用服务器实例
const sharedServer = new McpServer({ name: 'my-server', version: '1.0.0' });
app.post('/mcp', async (c) => {
await sharedServer.connect(transport); // 破坏之前的会话!
});
错误 : Type 'undefined' is not assignable to type '() => string' 来源 : GitHub Issue #1397 原因 : SDK 1.25.2 类型破坏了在 tsconfig.json 中使用 exactOptionalPropertyTypes: true 的项目 预防 :
// 使用 exactOptionalPropertyTypes: true
// ✅ 正确 - 省略属性而不是设置为 undefined
const transport = new StreamableHTTPServerTransport({
enableJsonResponse: true
// sessionIdGenerator 完全省略
});
// ❌ 错误 - 设置为 undefined 在 SDK 1.25.2 中导致类型错误
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 类型错误!
enableJsonResponse: true
});
// 替代方案:提供生成器函数
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
enableJsonResponse: true
});
错误 : 导入 SDK 后原生 Node.js fetch 行为被破坏 来源 : GitHub Issue #1376 原因 : Hono 的服务器代码全局覆盖了 global.fetch,破坏了期望原生行为的库 预防 :
// 在 SDK v1.25.3 中已修复 - 更新到最新版本
npm install @modelcontextprotocol/sdk@1.25.3
// 旧版本 (1.25.0-1.25.2) 的解决方法:
const nativeFetch = global.fetch;
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
global.fetch = nativeFetch; // 如果需要则恢复
错误 : 令人困惑的错误信息隐藏了实际的验证失败 来源 : GitHub Issue #1385 原因 : 当任务增强的工具调用在任务创建之前验证失败时,SDK 错误地包装了错误 预防 :
// 无效输入的预期错误:
// "Invalid arguments: Too small: expected number to be >=500"
// 实际错误(令人困惑):
// "Invalid task creation result: expected object, received undefined"
// 解决方法:在任务逻辑之前添加显式验证
server.experimental.tasks.registerToolTask(
'batch_process',
{
inputSchema: z.object({
itemCount: z.number().min(1).max(10),
processingTimeMs: z.number().min(500).max(5000).optional()
})
},
{
createTask: async (args, extra) => {
// SDK 应该修复此问题 - 目前没有解决方法
// 验证错误被任务包装掩盖
}
}
);
错误 : "expected": "object", "received": "undefined" 来源 : GitHub Issue #400 原因 : 当所有模式属性都是可选时,某些 LLM 客户端会省略 arguments 字段 预防 :
// ❌ 错误 - 所有可选字段可能导致问题
server.registerTool('fetch-records', {
inputSchema: z.object({
limit: z.number().optional()
})
}, handler);
// ✅ 正确 - 始终至少包含一个必填字段
server.registerTool('fetch-records', {
inputSchema: z.object({
action: z.literal('fetch').default('fetch'), // 必填
limit: z.number().optional()
})
}, handler);
// 替代方案:使用空对象模式
server.registerTool('fetch-records', {
inputSchema: z.object({}).passthrough()
}, handler);
错误 : MaxListenersExceededWarning: Possible EventEmitter memory leak detected 来源 : GitHub Issue #842 原因 : 在循环中注册 80+ 个工具会因快速的 sendToolListChanged() 通知而压倒 stdout 缓冲区 预防 :
// 解决方法:在批量注册之前增加 maxListeners
process.stdout.setMaxListeners(100);
const tools = [...]; // 80+ 个工具定义的数组
for (const tool of tools) {
server.registerTool(tool.name, tool.schema, tool.handler);
}
// 未来的 SDK 可能提供批量注册 API
错误 : 传输层错误消失,没有日志或异常 来源 : GitHub Issue #1395 原因 : 如果未设置 onerror 回调,SDK 会静默吞掉传输层错误 预防 :
// ✅ 正确 - 始终设置 onerror 处理器
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
transport.onerror = (error) => {
console.error('传输层错误:', error);
// 适当处理错误
};
await server.connect(transport);
错误 : 恶意查询参数导致内存耗尽 来源 : GitHub Issue #1368 原因 : qs 库的 arrayLimit 可以通过括号表示法如 ?foo[99999999]=bar 绕过 预防 :
// 验证查询参数以防止 DoS
app.post('/mcp', async (c) => {
const queryParams = c.req.query();
// 拒绝恶意模式
if (Object.keys(queryParams).some(key => /\[\d{6,}\]/.test(key))) {
return c.json({ error: '无效的查询参数' }, 400);
}
// ... 处理请求
});
错误 : 长时间运行的处理器在客户端断开连接后继续执行,浪费资源 来源 : GitHub Issue #611 原因 : 当传输层连接关闭时,SDK 不会自动取消请求处理器 预防 :
// 解决方法:手动使用 AbortController 模式
server.registerTool(
'long-running-task',
{ inputSchema: z.object({ duration: z.number() }) },
async ({ duration }, extra) => {
const abortController = new AbortController();
// 监听传输层关闭
const transport = extra.transport;
if (transport) {
const originalOnClose = transport.onclose;
transport.onclose = () => {
abortController.abort();
if (originalOnClose) originalOnClose();
};
}
try {
await longRunningTask(duration, abortController.signal);
return { content: [{ type: 'text', text: '完成' }] };
} catch (error) {
if (error.name === 'AbortError') {
return { content: [{ type: 'text', text: '已取消' }], isError: true };
}
throw error;
}
}
);
错误 : can't resolve reference #/$defs/... 来源 : GitHub Issue #1175 原因 : SDK 1.22.0 中 cacheToolOutputSchemas 的回归破坏了具有复杂 JSON Schema 的 listTools() 预防 : 更新到 SDK v1.23.0 或更高版本(已修复)。如果在 1.22.x 版本,请立即升级。
# 本地
wrangler dev # http://localhost:8787/mcp
# 生产环境
wrangler deploy
测试 : npx @modelcontextprotocol/inspector (连接到 http://localhost:8787/mcp)
模板 : basic-mcp-server.ts, tool-server.ts, resource-server.ts, authenticated-server.ts, tasks-server.ts, wrangler.jsonc
参考 : tool-patterns.md, authentication-guide.md, testing-guide.md, cloudflare-integration.md, common-errors.md
始终 :
McpServer 实例(切勿跨会话重用)transport.onerror 处理器以捕获静默错误c.res.raw.on('close', () => transport.close()))export default app, 而不是 { fetch: app.fetch })StreamableHTTPServerTransport(SSE 已弃用)切勿 :
McpServer 实例transport.onerror 处理器每周安装次数
335
仓库
GitHub 星标数
643
首次出现
2026年1月20日
安全审计
安装于
claude-code278
opencode223
gemini-cli219
cursor212
antigravity199
codex195
Last Updated : 2026-01-21 Versions : @modelcontextprotocol/sdk@1.25.3, hono@4.11.3, zod@4.3.5 Spec Version : 2025-11-25
npm install @modelcontextprotocol/sdk@latest hono zod
npm install -D @cloudflare/workers-types wrangler typescript
Transport Recommendation : Use StreamableHTTPServerTransport for production. SSE transport is deprecated and maintained for backwards compatibility only. Streamable HTTP provides better error recovery, bidirectional communication, and simplified deployment.
Basic MCP Server :
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { Hono } from 'hono';
import { z } from 'zod';
const server = new McpServer({ name: 'my-mcp-server', version: '1.0.0' });
server.registerTool(
'echo',
{
description: 'Echoes back input',
inputSchema: z.object({ text: z.string() })
},
async ({ text }) => ({ content: [{ type: 'text', text }] })
);
const app = new Hono();
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
// CRITICAL: Set error handler to catch transport errors
transport.onerror = (error) => {
console.error('MCP transport error:', error);
};
// CRITICAL: Close transport to prevent memory leaks
c.res.raw.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(c.req.raw, c.res.raw, await c.req.json());
return c.body(null);
});
export default app; // CRITICAL: Direct export, not { fetch: app.fetch }
Deploy : wrangler deploy
API Key (KV-based):
app.use('/mcp', async (c, next) => {
const apiKey = c.req.header('Authorization')?.replace('Bearer ', '');
const isValid = await c.env.MCP_API_KEYS.get(`key:${apiKey}`);
if (!isValid) return c.json({ error: 'Unauthorized' }, 403);
await next();
});
Cloudflare Zero Trust :
const jwt = c.req.header('Cf-Access-Jwt-Assertion');
const payload = await verifyJWT(jwt, c.env.CF_ACCESS_TEAM_DOMAIN);
Tasks enable long-running operations that return a handle for polling results later. Useful for expensive computations, batch processing, or operations that may need input.
Task States : working → input_required → completed / failed / cancelled
Server Capability Declaration :
const server = new McpServer({
name: 'my-server',
version: '1.0.0',
capabilities: {
tasks: {
list: {},
cancel: {},
requests: {
tools: { call: {} }
}
}
}
});
Tool with Task Support :
server.registerTool(
'long-running-analysis',
{
description: 'Analyze large dataset',
inputSchema: z.object({ datasetId: z.string() }),
execution: { taskSupport: 'optional' } // 'forbidden' | 'optional' | 'required'
},
async ({ datasetId }, extra) => {
// If invoked as task, extra.task contains taskId
const result = await performAnalysis(datasetId);
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
}
);
Client Task Request :
{
"method": "tools/call",
"params": {
"name": "long-running-analysis",
"arguments": { "datasetId": "abc123" },
"task": { "ttl": 60000 }
}
}
Task Lifecycle :
task param → receives taskIdtasks/get with taskIdcompleted, client calls tasks/result to get outputtasks/cancel to abort📚 Spec : https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
Servers can now include tool definitions in sampling requests , enabling server-side agent loops.
Use Case : Server needs to orchestrate multi-step reasoning using LLM + tools without custom frameworks.
// Server initiates sampling with tools available
const result = await server.requestSampling({
messages: [{ role: 'user', content: 'Analyze this data and fetch more if needed' }],
maxTokens: 4096,
tools: [
{
name: 'fetch_data',
description: 'Fetch additional data from API',
inputSchema: { type: 'object', properties: { query: { type: 'string' } } }
}
]
});
// Handle tool calls in response
if (result.content[0].type === 'tool_use') {
const toolResult = await executeLocalTool(result.content[0]);
// Continue conversation with tool result...
}
Key Points :
tools/list📚 Spec : SEP-1577
D1 Database :
server.registerTool('query-db', {
inputSchema: z.object({ query: z.string(), params: z.array(z.union([z.string(), z.number()])).optional() })
}, async ({ query, params }, env) => {
const result = await env.DB.prepare(query).bind(...(params || [])).all();
return { content: [{ type: 'text', text: JSON.stringify(result.results) }] };
});
KV, R2, Vectorize : See references/cloudflare-integration.md
This skill prevents 20 production issues documented in official MCP SDK and Cloudflare repos:
Error : "Cannot read properties of undefined (reading 'map')" Source : honojs/hono#3955, honojs/vite-plugins#237 Why It Happens : Incorrect export format with Vite build causes cryptic errors Prevention :
// ❌ WRONG - Causes cryptic build errors
export default { fetch: app.fetch };
// ✅ CORRECT - Direct export
export default app;
Error : Memory leaks, hanging connections Source : Best practice from SDK maintainers Why It Happens : Not closing StreamableHTTPServerTransport on request end Prevention :
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport(/*...*/);
// CRITICAL: Always close on response end
c.res.raw.on('close', () => transport.close());
// ... handle request
});
Error : ListTools request handler fails to generate inputSchema Source : GitHub modelcontextprotocol/typescript-sdk#1028 Why It Happens : Zod schemas not properly converted to JSON Schema Prevention :
// ✅ CORRECT - SDK handles Zod schema conversion automatically
server.registerTool(
'tool-name',
{
inputSchema: z.object({ a: z.number() })
},
handler
);
// No need for manual zodToJsonSchema() unless custom validation
Error : Handler receives undefined arguments Source : GitHub modelcontextprotocol/typescript-sdk#1026 Why It Happens : Schema type mismatch between registration and invocation Prevention :
const schema = z.object({ a: z.number(), b: z.number() });
type Input = z.infer<typeof schema>;
server.registerTool(
'add',
{ inputSchema: schema },
async (args: Input) => {
// args.a and args.b properly typed and passed
return { content: [{ type: 'text', text: String(args.a + args.b) }] };
}
);
Error : Browser clients can't connect to MCP server Source : Common production issue Why It Happens : Missing CORS headers for HTTP transport Prevention :
import { cors } from 'hono/cors';
app.use('/mcp', cors({
origin: ['http://localhost:3000', 'https://your-app.com'],
allowMethods: ['POST', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization']
}));
Error : API abuse, DDoS vulnerability Source : Production security best practice Why It Happens : No rate limiting on MCP endpoints Prevention :
app.post('/mcp', async (c) => {
const ip = c.req.header('CF-Connecting-IP');
const rateLimitKey = `ratelimit:${ip}`;
const count = await c.env.CACHE.get(rateLimitKey);
if (count && parseInt(count) > 100) {
return c.json({ error: 'Rate limit exceeded' }, 429);
}
await c.env.CACHE.put(
rateLimitKey,
String((parseInt(count || '0') + 1)),
{ expirationTtl: 60 }
);
// Continue...
});
Error : Out of memory during tsc build Source : GitHub modelcontextprotocol/typescript-sdk#985 Why It Happens : Large dependency tree in MCP SDK Prevention :
# Add to package.json scripts
"build": "NODE_OPTIONS='--max-old-space-size=4096' tsc && vite build"
Error : Server hangs on malicious URI patterns Source : GitHub modelcontextprotocol/typescript-sdk#965 (Security) Why It Happens : Regex denial-of-service in URI template parsing Prevention : Update to SDK v1.20.2 or later (includes fix)
Error : Unauthenticated access to MCP tools Source : Production security best practice Why It Happens : Missing or improperly implemented authentication Prevention : Always implement authentication for production servers (see Authentication Patterns section)
Error : Secrets exposed in error messages or logs Source : Cloudflare Workers security best practice Why It Happens : Environment variables logged or returned in responses Prevention :
// ❌ WRONG - Exposes secrets
console.log('Env:', JSON.stringify(env));
// ✅ CORRECT - Never log env objects
try {
// ... use env.SECRET_KEY
} catch (error) {
// Don't include env in error context
console.error('Operation failed:', error.message);
}
Error : AbortError: This operation was aborted Source : GitHub Issue #1405 Why It Happens : Calling Server.connect(transport) silently overwrites the previous transport without warning, breaking all earlier connections Prevention :
// ✅ CORRECT - Create fresh McpServer per HTTP session
app.post('/mcp', async (c) => {
const server = new McpServer({ name: 'my-server', version: '1.0.0' });
// Register tools per request
server.registerTool('echo', { inputSchema: z.object({ text: z.string() }) },
async ({ text }) => ({ content: [{ type: 'text', text }] })
);
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
transport.onerror = (error) => console.error('Transport error:', error);
c.res.raw.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(c.req.raw, c.res.raw, await c.req.json());
return c.body(null);
});
// ❌ WRONG - Reusing server instance across sessions
const sharedServer = new McpServer({ name: 'my-server', version: '1.0.0' });
app.post('/mcp', async (c) => {
await sharedServer.connect(transport); // Breaks previous sessions!
});
Error : Type 'undefined' is not assignable to type '() => string' Source : GitHub Issue #1397 Why It Happens : SDK 1.25.2 types break projects using exactOptionalPropertyTypes: true in tsconfig.json Prevention :
// With exactOptionalPropertyTypes: true
// ✅ CORRECT - Omit the property instead of setting to undefined
const transport = new StreamableHTTPServerTransport({
enableJsonResponse: true
// sessionIdGenerator omitted entirely
});
// ❌ WRONG - Setting to undefined causes type error in SDK 1.25.2
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // Type error!
enableJsonResponse: true
});
// Alternative: Provide a generator function
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
enableJsonResponse: true
});
Error : Native Node.js fetch behavior breaks after importing SDK Source : GitHub Issue #1376 Why It Happens : Hono's server code globally overwrites global.fetch, breaking libraries expecting native behavior Prevention :
// FIXED in SDK v1.25.3 - Update to latest version
npm install @modelcontextprotocol/sdk@1.25.3
// Workaround for older versions (1.25.0-1.25.2):
const nativeFetch = global.fetch;
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
global.fetch = nativeFetch; // Restore if needed
Error : Confusing error message hides actual validation failure Source : GitHub Issue #1385 Why It Happens : When task-augmented tool call fails validation before task creation, SDK wraps error incorrectly Prevention :
// Expected error for invalid input:
// "Invalid arguments: Too small: expected number to be >=500"
// Actual error (confusing):
// "Invalid task creation result: expected object, received undefined"
// WORKAROUND: Add explicit validation before task logic
server.experimental.tasks.registerToolTask(
'batch_process',
{
inputSchema: z.object({
itemCount: z.number().min(1).max(10),
processingTimeMs: z.number().min(500).max(5000).optional()
})
},
{
createTask: async (args, extra) => {
// SDK should fix this - currently no workaround
// Validation errors are masked by task wrapping
}
}
);
Error : "expected": "object", "received": "undefined" Source : GitHub Issue #400 Why It Happens : Some LLM clients omit arguments field when all schema properties are optional Prevention :
// ❌ WRONG - All optional fields may cause issues
server.registerTool('fetch-records', {
inputSchema: z.object({
limit: z.number().optional()
})
}, handler);
// ✅ CORRECT - Always include at least one required field
server.registerTool('fetch-records', {
inputSchema: z.object({
action: z.literal('fetch').default('fetch'), // Required
limit: z.number().optional()
})
}, handler);
// Alternative: Use empty object schema
server.registerTool('fetch-records', {
inputSchema: z.object({}).passthrough()
}, handler);
Error : MaxListenersExceededWarning: Possible EventEmitter memory leak detected Source : GitHub Issue #842 Why It Happens : Registering 80+ tools in a loop overwhelms stdout buffer with rapid sendToolListChanged() notifications Prevention :
// Workaround: Increase maxListeners before bulk registration
process.stdout.setMaxListeners(100);
const tools = [...]; // Array of 80+ tool definitions
for (const tool of tools) {
server.registerTool(tool.name, tool.schema, tool.handler);
}
// Future SDK may provide batch registration API
Error : Transport errors vanish without logs or exceptions Source : GitHub Issue #1395 Why It Happens : SDK silently swallows transport errors if onerror callback is not set Prevention :
// ✅ CORRECT - Always set onerror handler
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
transport.onerror = (error) => {
console.error('Transport error:', error);
// Handle error appropriately
};
await server.connect(transport);
Error : Memory exhaustion from malicious query parameters Source : GitHub Issue #1368 Why It Happens : The qs library's arrayLimit can be bypassed using bracket notation like ?foo[99999999]=bar Prevention :
// Validate query parameters to prevent DoS
app.post('/mcp', async (c) => {
const queryParams = c.req.query();
// Reject malicious patterns
if (Object.keys(queryParams).some(key => /\[\d{6,}\]/.test(key))) {
return c.json({ error: 'Invalid query parameters' }, 400);
}
// ... handle request
});
Error : Long-running handlers continue executing after client disconnect, wasting resources Source : GitHub Issue #611 Why It Happens : SDK doesn't automatically cancel request handlers when transport connection closes Prevention :
// Workaround: Use AbortController pattern manually
server.registerTool(
'long-running-task',
{ inputSchema: z.object({ duration: z.number() }) },
async ({ duration }, extra) => {
const abortController = new AbortController();
// Listen for transport close
const transport = extra.transport;
if (transport) {
const originalOnClose = transport.onclose;
transport.onclose = () => {
abortController.abort();
if (originalOnClose) originalOnClose();
};
}
try {
await longRunningTask(duration, abortController.signal);
return { content: [{ type: 'text', text: 'Done' }] };
} catch (error) {
if (error.name === 'AbortError') {
return { content: [{ type: 'text', text: 'Cancelled' }], isError: true };
}
throw error;
}
}
);
Error : can't resolve reference #/$defs/... Source : GitHub Issue #1175 Why It Happens : SDK 1.22.0 regression in cacheToolOutputSchemas broke listTools() with complex JSON Schema Prevention : Update to SDK v1.23.0 or later (fixed). If on 1.22.x, upgrade immediately.
# Local
wrangler dev # http://localhost:8787/mcp
# Production
wrangler deploy
Testing : npx @modelcontextprotocol/inspector (connect to http://localhost:8787/mcp)
Templates : basic-mcp-server.ts, tool-server.ts, resource-server.ts, authenticated-server.ts, tasks-server.ts, wrangler.jsonc
References : tool-patterns.md, authentication-guide.md, testing-guide.md, cloudflare-integration.md, common-errors.md
Always :
McpServer instance per HTTP request (never reuse across sessions)transport.onerror handler to catch silent errorsc.res.raw.on('close', () => transport.close()))export default app, NOT { fetch: app.fetch })StreamableHTTPServerTransport for production (SSE is deprecated)Never :
McpServer instance across concurrent HTTP sessionstransport.onerror handlerWeekly Installs
335
Repository
GitHub Stars
643
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubWarnSocketPassSnykWarn
Installed on
claude-code278
opencode223
gemini-cli219
cursor212
antigravity199
codex195
Azure 配额管理指南:服务限制、容量验证与配额增加方法
79,700 周安装