使用说明
MCP代理包装器
一个轻量级、功能强大的模型上下文协议(MCP)服务器包装器,提供了一个全面的钩子系统,用于拦截、监视和修改工具调用,而无需更改现有的服务器代码。
🚀 特性
- 🔧 零修改包装:包装现有的MCP服务器,而不更改其代码
- 🪝 强大的挂钩系统:在工具调用前后执行自定义逻辑
- 🔌 插件架构:用于可重用功能的可扩展插件系统
- 🔄 参数和结果修改:动态转换输入和输出
- ⚡ 短路能力:使用自定义响应跳过工具执行
- 🧠 包括智能插件:LLM摘要和聊天记忆插件
- 📊 综合录井:内置监控和调试支持
- 🧪 经过全面测试:通过真实的MCP客户端-服务器验证实现100%的测试覆盖率
- 📘 TypeScript优先:完全支持TypeScript,具有完全的类型安全性
- 🌐 通用兼容性:适用于任何MCP SDK v1.6.0+服务器
📦 安装
npm install mcp-proxy-wrapper
🎯 快速开始
基本用法
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { wrapWithProxy } from 'mcp-proxy-wrapper';
import { z } from 'zod';
// Create your existing MCP server
const server = new McpServer({
name: 'My Server',
version: '1.0.0'
});
// Wrap it with proxy functionality
const proxiedServer = await wrapWithProxy(server, {
hooks: {
// Monitor all tool calls
beforeToolCall: async (context) => {
console.log(`🔧 Calling tool: ${context.toolName}`);
console.log(`📝 Arguments:`, context.args);
},
// Process results
afterToolCall: async (context, result) => {
console.log(`✅ Tool completed: ${context.toolName}`);
return result; // Pass through unchanged
}
},
debug: true // Enable detailed logging
});
// Register tools normally
proxiedServer.tool('greet', { name: z.string() }, async (args) => {
return {
content: [{ type: 'text', text: `Hello, ${args.name}!` }]
};
});
🔌 插件系统
MCP代理包装器包含一个强大的插件架构,允许您创建可重用、可组合的功能。
使用内置插件
import { LLMSummarizationPlugin, ChatMemoryPlugin } from 'mcp-proxy-wrapper';
const summarizationPlugin = new LLMSummarizationPlugin();
const memoryPlugin = new ChatMemoryPlugin();
const proxiedServer = await wrapWithProxy(server, {
plugins: [
summarizationPlugin,
memoryPlugin
]
});
LLM摘要插件
使用AI自动总结长工具响应:
import { LLMSummarizationPlugin } from 'mcp-proxy-wrapper';
const plugin = new LLMSummarizationPlugin();
plugin.updateConfig({
options: {
provider: 'openai', // or 'mock' for testing
openaiApiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o-mini',
minContentLength: 500,
summarizeTools: ['research', 'analyze', 'fetch-data'],
saveOriginal: true // Store original responses for retrieval
}
});
const proxiedServer = await wrapWithProxy(server, {
plugins: [plugin]
});
// Tool responses are automatically summarized
const result = await client.callTool({
name: 'research',
arguments: { topic: 'artificial intelligence' }
});
console.log(result._meta.summarized); // true
console.log(result._meta.originalLength); // 2000
console.log(result._meta.summaryLength); // 200
console.log(result.content[0].text); // "Summary: ..."
聊天记忆插件
为保存的工具响应提供对话界面:
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]
});
// Tool responses are automatically saved
await client.callTool({
name: 'research',
arguments: { topic: 'climate change', userId: 'user123' }
});
// Chat with your saved data
const sessionId = await memoryPlugin.startChatSession('user123');
const response = await memoryPlugin.chatWithMemory(
sessionId,
"What did I research about climate change?",
'user123'
);
console.log(response); // AI response based on saved research
创建自定义插件
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> {
// Add custom metadata
return {
...result,
result: {
...result.result,
_meta: {
...result.result._meta,
processedBy: this.name,
customField: 'custom value'
}
}
};
}
}
const proxiedServer = await wrapWithProxy(server, {
plugins: [new MyCustomPlugin()]
});
插件配置
const plugin = new LLMSummarizationPlugin();
// Runtime configuration updates
plugin.updateConfig({
enabled: true,
priority: 10,
options: {
minContentLength: 200,
provider: 'openai'
},
includeTools: ['research', 'analyze'], // Only these tools
excludeTools: ['chat'], // Skip these tools
debug: true
});
高级钩子示例
1.参数修改
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// Add timestamp to all tool calls
context.args.timestamp = new Date().toISOString();
// Sanitize user input
if (context.args.message) {
context.args.message = context.args.message.trim();
}
}
}
});
2.成果提升
const proxiedServer = wrapWithProxy(server, {
hooks: {
afterToolCall: async (context, result) => {
// Add metadata to all responses
if (result.result.content) {
result.result._meta = {
toolName: context.toolName,
processedAt: new Date().toISOString(),
version: '1.0.0'
};
}
return result;
}
}
});
3.访问控制和短路
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// Block certain tools
if (context.toolName === 'delete' && !context.args.adminKey) {
return {
result: {
content: [{ type: 'text', text: 'Access denied: Admin key required' }],
isError: true
}
};
}
// Rate limiting
if (await isRateLimited(context.args.userId)) {
return {
result: {
content: [{ type: 'text', text: 'Rate limit exceeded. Try again later.' }],
isError: true
}
};
}
}
}
});
4.错误处理和监控
const proxiedServer = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
// Log to monitoring service
await analytics.track('tool_call_started', {
tool: context.toolName,
userId: context.args.userId,
timestamp: Date.now()
});
},
afterToolCall: async (context, result) => {
// Handle errors
if (result.result.isError) {
await errorLogger.log({
tool: context.toolName,
error: result.result.content[0].text,
context: context.args
});
}
return result;
}
}
});
📚 核心概念
钉钩系统
代理包装器提供了两个主要钩子:
-
beforeToolCall:在原始工具功能之前执行- 可以修改参数
- 可以通过返回结果来缩短执行时间
- 非常适合验证、身份验证和日志记录
-
afterToolCall:在原始工具功能之后执行- 可以修改结果
- 必须返回a
ToolCallResult - 非常适合后处理、缓存和分析
上下文对象
每个钩子都有一个 ToolCallContext 与:
interface ToolCallContext {
toolName: string; // Name of the tool being called
args: Record<string, any>; // Tool arguments (mutable)
metadata?: Record<string, any>; // Additional context data
}
结果对象
这 afterToolCall 钩子与 ToolCallResult:
interface ToolCallResult {
result: any; // The tool's return value
metadata?: Record<string, any>; // Additional result metadata
}
🔧 api参考
wrapWithProxy(server, options)
使用代理功能包装MCP服务器实例。
参数:
server(McpServer):要包装的MCP服务器options(ProxyWrapperOptions):配置选项
退货:
Promise<McpServer> -具有代理功能的新MCP服务器实例
ProxyWrapper选项
interface ProxyWrapperOptions {
hooks?: ProxyHooks; // Hook functions
plugins?: ProxyPlugin[]; // Plugin instances
pluginConfig?: Record<string, any>; // Global plugin configuration
metadata?: Record<string, any>; // Global metadata
debug?: boolean; // Enable debug logging
}
ProxyHooks
interface ProxyHooks {
beforeToolCall?: (context: ToolCallContext) => Promise<void | ToolCallResult>;
afterToolCall?: (context: ToolCallContext, result: ToolCallResult) => Promise<ToolCallResult>;
}
🧪 测试
MCP代理包装器包括使用真实MCP客户端-服务器通信的全面测试:
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Run specific test suites
npm test -- --testNamePattern="Comprehensive Tests"
npm test -- --testNamePattern="Edge Cases"
npm test -- --testNamePattern="Protocol Compliance"
测试覆盖率
- ✅ 65+综合测试 涵盖所有功能
- ✅ 真正的MCP客户端-服务器通信 使用内存传输
- ✅ 插件系统验证 集成测试
- ✅ 边缘案例 包括并发性、大数据、Unicode处理
- ✅ 协议遵从 验证
- ✅ 错误场景 压力测试
- ✅ TypeScript和JavaScript 兼容性
🔄 迁移和兼容性
MCP SDK兼容性
- 支持:MCP SDK v1.6.0及更高版本
- 已测试:完全通过MCP SDK v1.12.1验证
- 备注:需要Zod模式才能正确传递参数
升级您的服务器
代理包装器被设计为一个即插即用的替代品:
// Before
const server = new McpServer(config);
server.tool('myTool', schema, handler);
// After
const server = new McpServer(config);
const proxiedServer = await wrapWithProxy(server, {
hooks: myHooks,
plugins: [new LLMSummarizationPlugin()]
});
proxiedServer.tool('myTool', schema, handler); // Same API!
🛠 用例
1.身份验证和授权
const authProxy = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
if (!await validateApiKey(context.args.apiKey)) {
return { result: { content: [{ type: 'text', text: 'Invalid API key' }], isError: true }};
}
}
}
});
2.费率限制
const rateLimitedProxy = wrapWithProxy(server, {
hooks: {
beforeToolCall: async (context) => {
const userId = context.args.userId;
if (await rateLimiter.isExceeded(userId)) {
return { result: { content: [{ type: 'text', text: 'Rate limit exceeded' }], isError: true }};
}
await rateLimiter.increment(userId);
}
}
});
3.缓存
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;
}
}
});
4.分析和监控
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;
}
}
});
5.人工智能增强
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
}
})
]
});
// Long research responses are automatically summarized
// All responses are saved for conversational querying
🤝 贡献
我们欢迎捐款!请查看我们的 贡献指南 了解详情。
开发设置
git clone https://github.com/crazyrabbitLTC/mcp-proxy-wrapper.git
cd mcp-proxy-wrapper
npm install
npm run build
npm test
📄 许可证
MIT许可证-请参阅 许可证 文件以获取详细信息。
🔗 链接
Built with ❤️ for the MCP ecosystem
Created by Dennison Bertram
