从零构建LLM上下文服务:MCP TypeScript SDK实战指南
在LLM开发过程中,你是否曾遇到以下挑战:如何标准化管理模型上下文数据?如何构建灵活的工具调用系统?如何实现跨平台的服务器客户端通信?MCP TypeScript SDK(模型上下文协议的官方TypeScript实现)为这些问题提供了完整解决方案。本文将通过"问题-方案-实践"三段式结构,帮助你掌握使用TypeScript开发LLM上下文服务的核心技能,从基础架构到实战进阶,全面提升你的模型上下文管理能力。
一、LLM上下文管理的核心痛点与MCP解决方案
在构建基于大语言模型的应用时,开发者常面临三个核心挑战:
痛点一:上下文数据管理混乱
不同LLM接口对上下文的处理方式各异,导致数据格式不统一,难以维护和迁移。
痛点二:工具集成复杂
为LLM添加工具能力时,需重复开发认证、参数验证和结果处理逻辑,代码复用率低。
痛点三:通信协议不兼容
客户端与服务器之间的通信方式多样,从HTTP到WebSocket,缺乏标准化的交互模式。
MCP协议(Model Context Protocol - 模型上下文协议)通过以下创新解决这些问题:
- 标准化的上下文数据格式
- 统一的工具、资源和提示注册机制
- 多传输方式支持(Streamable HTTP、Stdio等)
二、基础架构篇:MCP核心组件与实现
如何实现MCP服务器的基础架构
MCP服务器是整个上下文服务的核心,负责管理工具、资源和提示,处理客户端请求。
概念解析:
McpServer类是MCP协议的核心实现,提供了工具、资源和提示的注册机制,以及请求处理逻辑。
场景案例:
构建一个天气查询服务器,提供实时天气查询工具和地理位置资源。
代码实现:[examples/server/simpleServer.ts]
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express';
import { z } from 'zod';
// 创建MCP服务器实例
const server = new McpServer({
name: 'weather-server',
version: '1.0.0',
description: '提供天气查询服务的MCP服务器'
});
// 注册天气查询工具
server.registerTool(
'get-weather',
{
title: '天气查询工具',
description: '根据经纬度获取实时天气信息',
inputSchema: z.object({
latitude: z.number().min(-90).max(90),
longitude: z.number().min(-180).max(180)
}),
outputSchema: z.object({
temperature: z.number(),
condition: z.string(),
humidity: z.number().min(0).max(100)
})
},
async ({ latitude, longitude }) => {
// 实际应用中这里会调用天气API
const mockWeatherData = {
temperature: 22.5,
condition: '晴朗',
humidity: 65
};
return {
content: [{
type: 'text',
text: `当前天气:${mockWeatherData.condition},温度:${mockWeatherData.temperature}°C`
}],
structuredContent: mockWeatherData
};
}
);
// 设置Express服务器
const app = express();
app.use(express.json());
// 配置Streamable HTTP传输
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 禁用会话管理,适合无状态服务
enableJsonResponse: true
});
// 客户端断开连接时清理资源
res.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
// 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`MCP天气服务器运行在 http://localhost:${port}/mcp`);
});
适用场景:
快速搭建需要对外提供API服务的LLM上下文服务器,如企业内部AI助手、客服机器人后端等。
MCP工具、资源与提示的核心原理
MCP提供三种核心功能组件,它们在LLM应用中扮演不同角色:
| 组件类型 | 核心作用 | 设计理念 | 数据流向 | 典型应用场景 |
|---|---|---|---|---|
| 工具(Tools) | 执行操作或计算 | 模型控制,AI决定调用时机和参数 | 客户端→服务器→外部服务→客户端 | 天气查询、数据计算、API调用 |
| 资源(Resources) | 提供上下文数据 | 应用驱动,由应用决定暴露内容 | 服务器→客户端 | 用户资料、配置信息、文档内容 |
| 提示(Prompts) | 提供交互模板 | 用户驱动,由用户选择使用方式 | 服务器→客户端→LLM | 代码审查、邮件生成、翻译助手 |
工具实现:股票价格查询
代码实现:[examples/server/tools/stockTool.ts]
// 注册股票价格查询工具
server.registerTool(
'get-stock-price',
{
title: '股票价格查询',
description: '获取指定股票代码的当前价格',
inputSchema: z.object({
symbol: z.string().regex(/^[A-Za-z0-9]{1,10}$/, '无效股票代码')
}),
outputSchema: z.object({
symbol: z.string(),
price: z.number(),
timestamp: z.string()
})
},
async ({ symbol }) => {
// 实际应用中这里会调用股票API
const mockPrice = 156.75 + Math.random() * 10;
return {
content: [{
type: 'text',
text: `股票 ${symbol.toUpperCase()} 当前价格: $${mockPrice.toFixed(2)}`
}],
structuredContent: {
symbol: symbol.toUpperCase(),
price: mockPrice,
timestamp: new Date().toISOString()
}
};
}
);
资源实现:用户配置信息
代码实现:[examples/server/resources/userConfigResource.ts]
import { ResourceTemplate } from '@modelcontextprotocol/sdk/core/types.js';
// 注册用户配置资源
server.registerResource(
'user-config',
new ResourceTemplate('config://users/{userId}', { list: undefined }),
{
title: '用户配置信息',
description: '获取指定用户的应用配置'
},
async (uri, { userId }) => {
// 实际应用中这里会从数据库获取
const mockConfig = {
theme: 'dark',
notifications: true,
timezone: 'Asia/Shanghai',
displayDensity: 'comfortable'
};
return {
contents: [{
uri: uri.href,
text: JSON.stringify(mockConfig, null, 2),
mimeType: 'application/json'
}]
};
}
);
提示实现:邮件生成助手
代码实现:[examples/server/prompts/emailPrompt.ts]
// 注册邮件生成提示
server.registerPrompt(
'generate-email',
{
title: '邮件生成助手',
description: '根据主题和收件人生成专业邮件',
argsSchema: z.object({
recipient: z.string().email(),
subject: z.string(),
purpose: z.enum(['introduction', 'follow-up', 'complaint', 'thank-you']),
details: z.string().optional()
})
},
({ recipient, subject, purpose, details }) => {
const purposeDescriptions = {
introduction: '介绍自己或公司业务',
'follow-up': '跟进之前的沟通或会议',
complaint: '表达不满或投诉问题',
'thank-you': '表达感谢'
};
return {
messages: [
{
role: 'system',
content: {
type: 'text',
text: `你是一位专业邮件撰写助手。请根据以下信息生成一封${purposeDescriptions[purpose]}的邮件。`
}
},
{
role: 'user',
content: {
type: 'text',
text: `收件人: ${recipient}\n主题: ${subject}\n详细信息: ${details || '无'}`
}
}
]
};
}
);
💡 提示技巧:在设计工具和资源时,尽量使用详细的描述和明确的输入输出模式,这将帮助LLM更好地理解如何使用它们。
三、实战进阶篇:传输方式与部署策略
如何选择适合的MCP传输方式
MCP支持多种传输方式,每种方式都有其适用场景:
1. Streamable HTTP传输
概念解析:
Streamable HTTP是一种支持双向通信的HTTP传输方式,允许在单个HTTP连接上发送多个消息,适合远程服务器通信。
场景案例:
构建一个需要处理大量并发请求的公共API服务。
代码实现:[examples/server/streamableHttpServer.ts]
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express';
import { createServer } from 'http';
import { randomUUID } from 'crypto';
const app = express();
app.use(express.json());
// 创建带有会话管理的Streamable HTTP传输
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(), // 为每个请求生成唯一会话ID
enableJsonResponse: true
});
// 处理连接关闭
res.on('close', () => {
console.log(`会话 ${transport.sessionId} 已关闭`);
transport.close();
});
// 创建并配置服务器
const server = new McpServer({
name: 'streamable-server',
version: '1.0.0'
});
// 注册基础工具...
await server.connect(transport);
console.log(`新会话已建立: ${transport.sessionId}`);
try {
await transport.handleRequest(req, res, req.body);
} catch (error) {
console.error(`处理请求时出错: ${error}`);
res.status(500).json({ error: '服务器内部错误' });
}
});
const httpServer = createServer(app);
const port = 3000;
httpServer.listen(port, () => {
console.log(`Streamable HTTP服务器运行在 http://localhost:${port}/mcp`);
});
适用场景:生产环境中的API服务,需要处理多个客户端的并发请求。
2. Stdio传输
概念解析:
Stdio传输通过标准输入输出流进行进程间通信,适合本地应用集成。
场景案例:
作为独立进程运行的LLM助手,与主应用通过标准流通信。
代码实现:[examples/server/stdioServer.ts]
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
async function runServer() {
const transport = new StdioServerTransport({
// 配置输入输出流,默认为process.stdin和process.stdout
input: process.stdin,
output: process.stdout
});
const server = new McpServer({
name: 'stdio-server',
version: '1.0.0',
description: '通过Stdio通信的MCP服务器'
});
// 注册工具和资源...
console.log('Stdio MCP服务器已启动,等待连接...');
// 处理连接关闭
transport.on('close', () => {
console.log('连接已关闭');
process.exit(0);
});
await server.connect(transport);
console.log('服务器已准备就绪');
}
runServer().catch(error => {
console.error('服务器启动失败:', error);
process.exit(1);
});
适用场景:桌面应用中的本地LLM集成,或需要与其他进程通信的场景。
⚠️ 注意事项:Stdio传输不适合网络通信,仅用于本地进程间通信。在生产环境中,建议使用Streamable HTTP传输。
MCP服务器的部署策略与性能优化
单节点部署
概念解析:
单节点部署是最简单的部署方式,所有组件运行在单个服务器实例上。
代码实现:[examples/deployment/singleNode.ts]
// 这是一个完整的单节点部署示例,包含服务器配置和启动脚本
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express';
import { createServer } from 'http';
import { config } from 'dotenv';
// 加载环境变量
config();
// 创建Express应用
const app = express();
app.use(express.json());
// 健康检查端点
app.get('/health', (req, res) => {
res.status(200).json({ status: 'ok', timestamp: new Date().toISOString() });
});
// MCP端点
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // 无状态模式
enableJsonResponse: true
});
res.on('close', () => transport.close());
const server = new McpServer({
name: 'production-server',
version: process.env.VERSION || '1.0.0'
});
// 注册工具、资源和提示...
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
// 创建HTTP服务器并启动
const port = process.env.PORT || 3000;
const httpServer = createServer(app);
httpServer.listen(port, () => {
console.log(`MCP服务器运行在 http://localhost:${port}`);
console.log(`环境: ${process.env.NODE_ENV || 'development'}`);
});
// 优雅关闭
process.on('SIGTERM', () => {
console.log('收到关闭信号,正在优雅关闭服务器...');
httpServer.close(() => {
console.log('服务器已关闭');
process.exit(0);
});
});
适用场景:开发环境、小型应用或流量适中的服务。
多节点部署
概念解析:
多节点部署通过负载均衡将请求分发到多个服务器实例,提高系统的可用性和扩展性。
场景案例:
构建支持高并发的MCP服务,确保服务稳定性和可扩展性。
架构说明:
客户端 → 负载均衡器 → MCP服务器集群 → 共享数据库/缓存
性能优化建议:
- 使用连接池:对数据库和外部API调用使用连接池,减少连接建立开销
- 实现请求缓存:对频繁访问的资源和工具响应进行缓存
- 启用压缩:对传输的数据进行gzip压缩,减少网络传输量
- 限流保护:实现请求限流,防止服务过载
- 异步处理:对耗时操作采用异步处理,避免阻塞主线程
四、MCP客户端实现与应用集成
如何构建MCP客户端应用
概念解析:
MCP客户端负责与MCP服务器通信,发送请求并处理响应,是连接LLM与MCP服务器的桥梁。
场景案例:
创建一个天气查询客户端,连接到前面实现的MCP服务器。
代码实现:[examples/client/weatherClient.ts]
import { McpClient } from '@modelcontextprotocol/sdk/client/client.js';
import { StreamableHttpClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
async function main() {
// 创建HTTP传输
const transport = new StreamableHttpClientTransport({
url: 'http://localhost:3000/mcp',
timeout: 30000 // 30秒超时
});
// 创建MCP客户端
const client = new McpClient(transport);
try {
// 连接到服务器
console.log('连接到MCP服务器...');
await client.connect();
console.log('连接成功');
// 获取服务器元数据
const serverInfo = await client.getServerInfo();
console.log(`服务器信息: ${serverInfo.name} v${serverInfo.version}`);
// 调用天气查询工具
console.log('\n调用天气查询工具...');
const weatherResult = await client.callTool('get-weather', {
latitude: 39.9042,
longitude: 116.4074
});
console.log('天气查询结果:');
console.log(weatherResult.content[0].text);
console.log('结构化数据:', weatherResult.structuredContent);
// 获取用户配置资源
console.log('\n获取用户配置资源...');
const configResource = await client.getResource('config://users/123');
console.log('用户配置:');
console.log(configResource.contents[0].text);
// 使用邮件生成提示
console.log('\n生成邮件提示...');
const emailPrompt = await client.getPrompt('generate-email', {
recipient: 'contact@example.com',
subject: 'MCP服务使用咨询',
purpose: 'introduction',
details: '我想了解如何将MCP集成到我们的AI助手产品中'
});
console.log('邮件提示:');
emailPrompt.messages.forEach((msg, index) => {
console.log(`[${index}] ${msg.role}: ${msg.content.text}`);
});
} catch (error) {
console.error('操作出错:', error);
} finally {
// 断开连接
await client.disconnect();
console.log('\n已断开与服务器的连接');
}
}
main();
适用场景:任何需要与MCP服务器交互的应用,如AI助手、自动化工具、开发环境插件等。
五、测试调试与常见错误排查
MCP应用的测试策略
概念解析:
测试是确保MCP服务质量的关键环节,包括单元测试、集成测试和端到端测试。
代码实现:[test/integration/mcpServer.test.ts]
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { McpClient } from '@modelcontextprotocol/sdk/client/client.js';
import { StreamableHttpClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import express from 'express';
import { createServer } from 'http';
describe('MCP服务器集成测试', () => {
let server: McpServer;
let httpServer: any;
let port: number;
beforeAll(async () => {
// 创建测试服务器
const app = express();
app.use(express.json());
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
res.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
// 随机端口
port = 30000 + Math.floor(Math.random() * 1000);
httpServer = createServer(app).listen(port);
// 创建MCP服务器并注册测试工具
server = new McpServer({
name: 'test-server',
version: '1.0.0'
});
server.registerTool(
'test-add',
{
title: '测试加法工具',
description: '用于测试的简单加法工具',
inputSchema: { a: { type: 'number' }, b: { type: 'number' } },
outputSchema: { result: { type: 'number' } }
},
async ({ a, b }) => ({
content: [{ type: 'text', text: `结果: ${a + b}` }],
structuredContent: { result: a + b }
})
);
});
afterAll(async () => {
httpServer.close();
});
it('应该成功连接到服务器并调用工具', async () => {
const transport = new StreamableHttpClientTransport({
url: `http://localhost:${port}/mcp`
});
const client = new McpClient(transport);
await client.connect();
const result = await client.callTool('test-add', { a: 2, b: 3 });
expect(result.structuredContent.result).toBe(5);
expect(result.content[0].text).toContain('结果: 5');
await client.disconnect();
});
it('应该处理不存在的工具调用', async () => {
const transport = new StreamableHttpClientTransport({
url: `http://localhost:${port}/mcp`
});
const client = new McpClient(transport);
await client.connect();
await expect(
client.callTool('non-existent-tool', {})
).rejects.toThrow(/工具不存在/);
await client.disconnect();
});
});
常见错误排查
1. 连接超时
问题:客户端无法连接到服务器,出现超时错误。
排查步骤:
- 检查服务器是否正在运行
- 验证网络连接和防火墙设置
- 确认服务器地址和端口是否正确
- 检查服务器日志是否有错误信息
解决方案:
// 增加超时时间并添加重试逻辑
const transport = new StreamableHttpClientTransport({
url: 'http://localhost:3000/mcp',
timeout: 30000 // 增加超时时间到30秒
});
// 实现简单的重试机制
async function withRetry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {
try {
return await fn();
} catch (error) {
if (retries > 0) {
console.log(`重试中... (剩余: ${retries})`);
await new Promise(resolve => setTimeout(resolve, 1000));
return withRetry(fn, retries - 1);
}
throw error;
}
}
// 使用重试机制连接服务器
await withRetry(() => client.connect());
2. 工具调用参数错误
问题:调用工具时出现参数验证错误。
排查步骤:
- 检查工具定义的输入模式
- 验证客户端提供的参数类型和格式
- 查看服务器返回的详细错误信息
解决方案:
// 客户端参数验证
const AddInputSchema = z.object({
a: z.number(),
b: z.number()
});
try {
// 调用前验证参数
const validatedInput = AddInputSchema.parse(inputData);
const result = await client.callTool('add', validatedInput);
} catch (error) {
if (error instanceof z.ZodError) {
console.error('参数验证失败:', error.errors);
// 向用户显示友好的错误提示
} else {
console.error('工具调用失败:', error);
}
}
3. 会话管理问题
问题:状态信息丢失或会话无法维持。
排查步骤:
- 确认服务器是否启用了会话管理
- 检查会话ID是否正确传递
- 验证会话存储是否正常工作
解决方案:
// 服务器端启用会话管理
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
// 使用持久化存储
eventStore: new DatabaseEventStore()
});
// 客户端保存会话ID
let sessionId: string | undefined;
transport.on('session', (id) => {
sessionId = id;
// 保存会话ID到本地存储
localStorage.setItem('mcp-session-id', id);
});
// 重连时恢复会话
const savedSessionId = localStorage.getItem('mcp-session-id');
if (savedSessionId) {
transport.sessionId = savedSessionId;
}
六、进阶学习路径
掌握MCP TypeScript SDK后,你可以通过以下路径继续深入学习:
路径一:高级协议特性
深入了解MCP协议的高级特性,如:
- 增量更新机制
- 权限控制与认证
- 批量操作优化
- 协议扩展与自定义消息类型
相关资源:
- 协议规范文档:docs/protocol.md
- 高级示例:examples/advanced/
路径二:性能优化与扩展
学习如何优化MCP服务性能和扩展性:
- 分布式会话管理
- 负载均衡策略
- 缓存机制实现
- 水平扩展架构设计
相关资源:
- 性能测试工具:tools/performance/
- 扩展指南:docs/scaling.md
路径三:实际应用场景
探索MCP在不同领域的应用:
- AI助手集成
- 自动化工作流
- 多模型协作
- 实时数据处理
相关资源:
- 应用案例:examples/applications/
- 集成指南:docs/integrations.md
总结
MCP TypeScript SDK为LLM上下文管理提供了标准化解决方案,通过本文的学习,你已经掌握了从基础架构到实战进阶的核心技能。无论是构建简单的工具服务还是复杂的分布式系统,MCP都能提供灵活而强大的支持。随着LLM技术的不断发展,MCP协议将继续进化,为开发者提供更完善的上下文管理工具。
开始你的MCP开发之旅吧!通过实践和探索,你将能够构建出更智能、更高效的LLM应用。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust098- DDeepSeek-V4-ProDeepSeek-V4-Pro(总参数 1.6 万亿,激活 49B)面向复杂推理和高级编程任务,在代码竞赛、数学推理、Agent 工作流等场景表现优异,性能接近国际前沿闭源模型。Python00
MiMo-V2.5-ProMiMo-V2.5-Pro作为旗舰模型,擅⻓处理复杂Agent任务,单次任务可完成近千次⼯具调⽤与⼗余轮上 下⽂压缩。Python00
GLM-5.1GLM-5.1是智谱迄今最智能的旗舰模型,也是目前全球最强的开源模型。GLM-5.1大大提高了代码能力,在完成长程任务方面提升尤为显著。和此前分钟级交互的模型不同,它能够在一次任务中独立、持续工作超过8小时,期间自主规划、执行、自我进化,最终交付完整的工程级成果。Jinja00
Kimi-K2.6Kimi K2.6 是一款开源的原生多模态智能体模型,在长程编码、编码驱动设计、主动自主执行以及群体任务编排等实用能力方面实现了显著提升。Python00
MiniMax-M2.7MiniMax-M2.7 是我们首个深度参与自身进化过程的模型。M2.7 具备构建复杂智能体应用框架的能力,能够借助智能体团队、复杂技能以及动态工具搜索,完成高度精细的生产力任务。Python00