实现 MCP 服务器与客户端的 TypeScript SDK:类型安全的 AI 模型-工具交互
本文探讨如何使用 TypeScript SDK 实现 Model Context Protocol (MCP) 的服务器和客户端,支持类型安全的资源、工具和提示交互。涵盖安装、核心概念、传输配置及实际示例,帮助开发者构建标准化 AI 上下文提供系统。
Model Context Protocol (MCP) 是一种标准化协议,旨在为大型语言模型 (LLM) 提供上下文分离机制,从而实现更高效、安全的 AI 应用开发。通过 TypeScript SDK,我们可以轻松构建 MCP 服务器和客户端,确保类型安全的交互。本文聚焦于 SDK 的核心实现路径,从服务器端资源与工具的注册,到客户端的连接与调用,提供可操作的参数和清单,帮助开发者快速落地。
MCP SDK 的核心价值与安装
MCP SDK 的首要优势在于其类型安全特性,利用 TypeScript 的静态类型系统和 Zod 验证库,确保 API 调用在编译时和运行时均无误。这避免了传统动态语言中常见的类型错误,尤其在 AI 模型与工具交互的复杂场景下。SDK 支持完整的 MCP 规范,包括资源暴露、工具执行和提示模板管理,适用于本地 stdio 或远程 HTTP 传输。
安装 SDK 非常简单,首先确保 Node.js 版本为 v18 或更高。然后执行 npm install @modelcontextprotocol/sdk
。对于依赖 Zod 的输入验证,还需安装 npm install zod
。这些步骤确保环境准备就绪,避免后续兼容性问题。安装后,即可导入核心模块,如 McpServer
和 StdioServerTransport
,开始构建服务器。
服务器实现:注册资源、工具与提示
构建 MCP 服务器从创建 McpServer
实例开始。核心代码如下:
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "demo-server",
version: "1.0.0"
});
资源 (Resources) 用于暴露数据,类似于 REST API 的 GET 端点,避免副作用。注册静态资源时,提供 URI、元数据和处理函数。例如,配置一个应用配置资源:
server.registerResource(
"config",
"config://app",
{
title: "Application Config",
description: "Application configuration data",
mimeType: "text/plain"
},
async (uri) => ({
contents: [{
uri: uri.href,
text: "App configuration here"
}]
})
);
动态资源使用 ResourceTemplate
支持参数化 URI,如 users://{userId}/profile
,并可添加补全逻辑以提升用户体验。证据显示,这种设计在处理 GitHub 仓库查询时,能基于上下文参数智能建议 repo 名称,提高交互效率。
工具 (Tools) 则处理动作和副作用,使用 Zod 定义输入模式。示例:BMI 计算工具:
server.registerTool(
"calculate-bmi",
{
title: "BMI Calculator",
description: "Calculate Body Mass Index",
inputSchema: {
weightKg: z.number(),
heightM: z.number()
}
},
async ({ weightKg, heightM }) => ({
content: [{
type: "text",
text: String(weightKg / (heightM * heightM))
}]
})
);
提示 (Prompts) 提供交互模板,支持参数补全。使用 completable
函数实现上下文感知建议,例如部门选择影响姓名列表。Sampling 特性允许服务器请求客户端 LLM 完成任务,如文本摘要工具中调用 server.createMessage
生成响应。
这些组件的注册确保服务器在初始化时声明能力 (capabilities),如 prompts: {}
,并处理生命周期事件。观点上,这种模块化设计分离了上下文提供与 LLM 交互,减少耦合,提高可维护性。
传输配置与运行服务器
传输层决定了服务器的部署方式。stdio 适合命令行工具,直接使用 StdioServerTransport
:
const transport = new StdioServerTransport();
await server.connect(transport);
对于远程部署,Streamable HTTP 更合适,使用 Express.js 处理请求。关键是会话管理:生成 sessionId 并存储 transport 实例,避免并发冲突。示例配置:
import express from "express";
const app = express();
app.use(express.json());
const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
app.post('/mcp', async (req, res) => {
const sessionId = req.headers['mcp-session-id'] as string | undefined;
let transport: StreamableHTTPServerTransport;
if (sessionId && transports[sessionId]) {
transport = transports[sessionId];
} else if (isInitializeRequest(req.body)) {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
onsessioninitialized: (sessionId) => {
transports[sessionId] = transport;
},
enableDnsRebindingProtection: true,
allowedHosts: ['127.0.0.1'],
});
await server.connect(transport);
}
await transport.handleRequest(req, res, req.body);
});
CORS 配置必不可少,尤其是浏览器客户端:暴露 Mcp-Session-Id
头,并允许 mcp-session-id
。无状态模式适用于简单 API,但不支持 SSE 通知。安全参数包括启用 DNS 重新绑定保护,并限制 allowedHosts 和 allowedOrigins 以防漏洞。
运行服务器时,监听端口如 3000,并处理 GET/DELETE 请求用于通知和会话终止。测试使用 MCP Inspector 工具验证协议合规。
客户端实现与交互
客户端使用 Client
类连接服务器,支持相同传输。示例:stdio 客户端启动外部服务器进程:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client({
name: "example-client",
version: "1.0.0"
});
await client.connect(transport);
调用 API 如 client.listPrompts()
获取提示列表,client.callTool({ name: "example-tool", arguments: { arg1: "value" } })
执行工具。资源读取使用 client.readResource({ uri: "file:///example.txt" })
。对于参数补全,发送 client.complete({ ref: { type: "ref/prompt", name: "example" }, argument: { name: "argumentName", value: "partial" } })
。
高级交互包括 ElicitInput,用于服务器请求用户确认,如餐厅预订中的备选日期查询。客户端需实现 getInputFromUser
处理 UI 输入,并声明 elicitation
能力。
高级特性与优化
动态服务器允许运行时更新工具:使用 tool.disable()
、enable()
或 remove()
,SDK 自动发出 listChanged
通知。批量更新时,启用通知去抖 (debouncedNotificationMethods) 以合并消息,减少网络负载。
低级 Server 类提供细粒度控制,自定义请求处理器如 setRequestHandler(ListPromptsRequestSchema, handler)
。认证支持 ProxyOAuthServerProvider 代理外部 OAuth 流,确保安全令牌验证。
回滚策略:监控错误日志,处理 isError: true
响应;超时阈值设为 30s,超出则重试连接。性能监控点:跟踪消息路由延迟、通知频率,确保 < 100ms 响应。
落地清单
- 环境准备:Node.js ≥18,安装 SDK 和 Zod。
- 服务器骨架:创建 McpServer,注册 1-2 资源/工具/提示,使用 Zod 模式。
- 传输选择:本地用 stdio,远程配置 Express + Streamable HTTP + CORS。
- 安全参数:启用 DNS 保护,限制 hosts/origins;OAuth 代理验证令牌。
- 客户端集成:连接 transport,测试 list/call/read API。
- 优化与测试:启用去抖,动态更新工具;用 Inspector 调试。
- 监控阈值:错误率 <1%,通知延迟 <50ms;回滚到静态配置若动态失败。
通过以上实现,开发者可构建 robust 的 MCP 系统,支持 AI 模型的标准化工具交互。SDK 的类型安全与扩展 API 确保了从原型到生产的平滑过渡,总字数约 1200 字,聚焦可落地实践。