一个轻量级、强大的包装器,用于Model Context Protocol (MCP)服务器,提供全面的钩子系统,可以在不更改现有服务器代码的情况下拦截、监控和修改工具调用。
npm install mcp-proxy-wrapper
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { wrapWithProxy } from 'mcp-proxy-wrapper';
import { z } from 'zod';
// 创建你的现有MCP服务器
const server = new McpServer({
name: '我的服务器',
version: '1.0.0'
});
// 使用代理功能包装它
const proxiedServer = await wrapWithProxy(server, {
hooks: {
// 监控所有工具调用
beforeToolCall: async (context) => {
console.log(`🔧 调用工具: ${context.toolName}`);
console.log(`📝 参数:`, context.args);
},
// 处理结果
afterToolCall: async (context, result) => {
console.log(`✅ 工具完成: ${context.toolName}`);
return result; // 不变地传递
}
},
debug: true // 启用详细日志
});
// 正常注册工具
proxiedServer.tool('greet', { name: z.string() }, async (args) => {
return {
content: [{ type: 'text', text: `你好,${args.name}!` }]
};
});
MCP Proxy Wrapper 包含一个强大的插件架构,允许您创建可复用、组合的功能。
import { LLMSummarizationPlugin, ChatMemoryPlugin } from 'mcp-proxy-wrapper';
const summarizationPlugin = new LLMSummarizationPlugin();
const memoryPlugin = new ChatMemoryPlugin();
const proxiedServer = await wrapWithProxy(server, {
plugins: [
summarizationPlugin,
memoryPlugin
]
});
自动使用AI对长工具响应进行摘要:
import { LLMSummarizationPlugin } from 'mcp-proxy-wrapper';
const plugin = new LLMSummarizationPlugin();
plugin.updateConfig({
options: {
provider: 'openai', // 或者'mock'用于测试
openaiApiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o-mini',
minContentLength: 500,
summarizeTools: ['research', 'analyze', 'fetch-data'],
saveOriginal: true // 存储原始响应以供检索
}
});
const proxiedServer = await wrapWithProxy(server, {
plugins: [plugin]
});
// 工具响应会自动被摘要
const result = await client.callTool({
name: 'research',
arguments: { topic: '人工智能' }
});
console.log(result._meta.summarized); // true
console.log(result._meta.originalLength); // 2000
console.log(result._meta.summaryLength); // 200
console.log(result.content[0].text); // "摘要:..."
为保存的工具响应提供对话接口:
import { ChatMemoryPlugin } from 'mcp-proxy-wrapper';
const memoryPlugin = new ChatMemoryPlugin();
memoryPlugin.updateConfig({
options: {
provider: 'openai',
openaiApiKey: process.env.OPENAI_API_KEY,
saveResponses: true,
enableChat: true,
maxEntries: 1000
}
});
const proxiedServer = await wrapWithProxy(server, {
plugins: [memoryPlugin]
});
// 工具响应会自动保存
await client.callTool({
name: 'research',
arguments: { topic: '气候变化', userId: 'user123' }
});
// 与保存的数据对话
const sessionId = await memoryPlugin.startChatSession('user123');
const response = await memoryPlugin.chatWithMemory(
sessionId,
"我关于气候变化研究了什么?",
'user123'
);
console.log(response); // 基于保存的研究的AI响应
import { BasePlugin, PluginContext, ToolCallResult } from 'mcp-proxy-wrapper';
class MyCustomPlugin extends BasePlugin {
name = 'my-custom-plugin';
version = '1.0.0';
async afterToolCall(context: PluginContext, result: ToolCallResult): Promise<ToolCallResult> {
// 添加自定义元数据
return {
...result,
result: {
...result.result,
_meta: {
...result.result._meta,
processedBy: this.name,
customField: '自定义值'
}
}
};
}
}
const proxiedServer = await wrapWithProxy(server, {
plugins: [new MyCustomPlugin()]
});
const plugin = new LLMSummarizationPlugin();
// 运行时配置更新
plugin.updateConfig({
enabled: true,
priority: 10,
options: {
minContentLength: 200,
provider: 'openai'
},
includeTools: ['research', 'analyze'], // 只有这些工具
excludeTools: ['chat'], // 跳过这些工具
debug: true
});
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// 为所有工具调用添加时间戳
context.args.timestamp = new Date().toISOString();
// 清理用户输入
if (context.args.message) {
context.args.message = context.args.message.trim();
}
}
}
});
const proxiedServer = wrapWithProxy(server, {
hooks: {
afterToolCall: async (context, result) => {
// 为所有响应添加元数据
if (result.result.content) {
result.result._meta = {
toolName: context.toolName,
processedAt: new Date().toISOString(),
version: '1.0.0'
};
}
return result;
}
}
});
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// 阻止某些工具
if (context.toolName === 'delete' && !context.args.adminKey) {
return {
result: {
content: [{ type: 'text', text: '访问被拒绝:需要管理员密钥' }],
isError: true
}
};
}
// 速率限制
if (await isRateLimited(context.args.userId)) {
return {
result: {
content: [{ type: 'text', text: '超过速率限制。稍后再试。' }],
isError: true
}
};
}
}
}
});
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// 日志到监控服务
await analytics.track('tool_call_started', {
tool: context.toolName,
userId: context.args.userId,
timestamp: Date.now()
});
},
afterToolCall: async (context, result) => {
// 处理错误
if (result.result.isError) {
await errorLogger.log({
tool: context.toolName,
error: result.result.content[0].text,
context: context.args
});
}
return result;
}
}
});
代理包装器提供了两个主要钩子:
beforeToolCall:在原始工具函数之前执行
afterToolCall:在原始工具函数之后执行
ToolCallResult每个钩子都会收到一个 ToolCallContext:
interface ToolCallContext {
toolName: string; // 正在调用的工具名称
args: Record<string, any>; // 工具参数(可变)
metadata?: Record<string, any>; // 额外的上下文数据
}
afterToolCall 钩子使用 ToolCallResult:
interface ToolCallResult {
result: any; // 工具的返回值
metadata?: Record<string, any>; // 额外的结果元数据
}
wrapWithProxy(server, options)用代理功能包装一个MCP服务器实例。
参数:
server (McpServer):要包装的MCP服务器options (ProxyWrapperOptions):配置选项返回:
Promise<McpServer> - 一个新的具有代理功能的MCP服务器实例
interface ProxyWrapperOptions {
hooks?: ProxyHooks; // 钩子函数
plugins?: ProxyPlugin[]; // 插件实例
pluginConfig?: Record<string, any>; // 全局插件配置
metadata?: Record<string, any>; // 全局元数据
debug?: boolean; // 启用调试日志
}
interface ProxyHooks {
beforeToolCall?: (context: ToolCallContext) => Promise<void | ToolCallResult>;
afterToolCall?: (context: ToolCallContext, result: ToolCallResult) => Promise<ToolCallResult>;
}
MCP Proxy Wrapper 包含全面的测试,使用真实的MCP客户端-服务器通信:
# 运行所有测试
npm test
# 运行带有覆盖率的测试
npm run test:coverage
# 运行特定的测试套件
npm test -- --testNamePattern="综合测试"
npm test -- --testNamePattern="边界情况"
npm test -- --testNamePattern="协议合规性"
代理包装器设计为即插即用替换:
// 之前
const server = new McpServer(config);
server.tool('myTool', schema, handler);
// 之后
const server = new McpServer(config);
const proxiedServer = await wrapWithProxy(server, {
hooks: myHooks,
plugins: [new LLMSummarizationPlugin()]
});
proxiedServer.tool('myTool', schema, handler); // 同样的API!
const authProxy = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
if (!await validateApiKey(context.args.apiKey)) {
return { result: { content: [{ type: 'text', text: '无效的API密钥' }], isError: true }};
}
}
}
});
const rateLimitedProxy = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
const userId = context.args.userId;
if (await rateLimiter.isExceeded(userId)) {
return { result: { content: [{ type: 'text', text: '超过速率限制' }], isError: true }};
}
await rateLimiter.increment(userId);
}
}
});
const cachedProxy = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
const cacheKey = `${context.toolName}:${JSON.stringify(context.args)}`;
const cached = await cache.get(cacheKey);
if (cached) {
return { result: cached };
}
},
afterToolCall: async (context, result) => {
const cacheKey = `${context.toolName}:${JSON.stringify(context.args)}`;
await cache.set(cacheKey, result.result, { ttl: 300 });
return result;
}
}
});
const monitoredProxy = await wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
await metrics.increment('tool_calls_total', { tool: context.toolName });
context.startTime = Date.now();
},
afterToolCall: async (context, result) => {
const duration = Date.now() - context.startTime;
await metrics.histogram('tool_call_duration', duration, { tool: context.toolName });
return result;
}
}
});
import { LLMSummarizationPlugin, ChatMemoryPlugin } from 'mcp-proxy-wrapper';
const aiEnhancedProxy = await wrapWithProxy(server, {
plugins: [
new LLMSummarizationPlugin({
options: {
provider: 'openai',
openaiApiKey: process.env.OPENAI_API_KEY,
summarizeTools: ['research', 'analyze', 'fetch-data'],
minContentLength: 500
}
}),
new ChatMemoryPlugin({
options: {
provider: 'openai',
openaiApiKey: process.env.OPENAI_API_KEY,
saveResponses: true,
enableChat: true
}
})
]
});
// 长研究响应会被自动摘要
// 所有响应都会被保存以便对话查询
我们欢迎贡献!请参阅我们的贡献指南了解详情。
git clone https://github.com/crazyrabbitLTC/mcp-proxy-wrapper.git
cd mcp-proxy-wrapper
npm install
npm run build
npm test
MIT 许可证 - 详情见LICENSE文件。