mcp工具路由器
将来自多个MCP服务器的工具聚合到一个统一的命名空间中。
](https://www.npmjs.com/package/mcp-tool-router) ](https://www.npmjs.com/package/mcp-tool-router)  ](https://nodejs.org) 
______________________________________________________________________
描述
mcp-tool-router 是MCP(模型上下文协议)服务器的程序化路由器和聚合器。它连接到多个上游MCP服务器,将它们的工具合并到一个统一的命名空间中,并根据命名空间前缀将工具调用路由到正确的上游。下游客户端从一个路由器看到一个工具列表——它不知道存在多个后端。
当LLM代理或MCP主机同时连接到许多MCP服务器时,会出现两个问题。首先,每个服务器的工具列表都被注入到上下文窗口中,在用户键入单个消息之前消耗数万个令牌。其次,工具名称冲突——两个服务器都公开了一个 search 工具强制临时消歧。
mcp-tool-router 在路由级别解决了这两个问题。来自每个上游的工具都使用可配置的前缀和分隔符进行命名空间(例如。, github/create_issue, jira/search),通过施工消除碰撞。选择性转发允许路由器仅公开每个上游工具的子集,从而减少上下文膨胀。中间件拦截用于日志记录、访问控制或参数注入的工具调用。
该架构反映了在相邻域中证明的模式:GraphQL联邦在网关后面合并子图模式,Envoy在单个入口后面反向代理HTTP微服务,以及 mcp-tool-router 使用基于前缀的路由在单个虚拟服务器后面组成MCP服务器。
关键设计决策
- 零运行时依赖关系 --仅使用
node:events来自Node.js - ES2022目标,CommonJS模块格式
- TypeScript严格模式 全类型出口
- 仅路由层 --提供与任何MCP服务器框架集成的工具调度逻辑
______________________________________________________________________
安装
npm install mcp-tool-router需要Node.js>=18。
______________________________________________________________________
快速开始
import { ToolRouter } from 'mcp-tool-router';
const router = new ToolRouter({
name: 'my-router',
version: '1.0.0',
separator: '/',
conflictResolution: 'prefix',
});
// Register upstream servers with their tools and handlers
router.addServer('github', {
tools: [
{ name: 'create_issue', description: 'Create a GitHub issue', inputSchema: { type: 'object', properties: { title: { type: 'string' } }, required: ['title'] } },
{ name: 'search', description: 'Search repositories' },
],
handler: async (toolName, args) => {
// Forward to actual MCP server or implement directly
return { content: [{ type: 'text', text: `GitHub ${toolName}: ${JSON.stringify(args)}` }] };
},
});
router.addServer('jira', {
tools: [
{ name: 'create_ticket', description: 'Create a Jira ticket' },
{ name: 'search', description: 'Search Jira issues' },
],
handler: async (toolName, args) => {
return { content: [{ type: 'text', text: `Jira ${toolName}: ${JSON.stringify(args)}` }] };
},
});
// Tools are namespaced automatically:
// github/create_issue, github/search, jira/create_ticket, jira/search
const tools = router.listTools();
console.log(tools.map(t => t.namespacedName));
// Route calls to the correct upstream server
const result = await router.callTool('github/create_issue', { title: 'Bug report' });______________________________________________________________________
特性
命名空间管理
每个上游服务器的工具都以服务器名称(或自定义前缀)和可配置的分隔符作为前缀,以防止名称冲突。
// Default: server name is used as prefix
router.addServer('github', { tools, handler });
// Tool exposed as: github/create_issue
// Custom prefix
router.addServer('postgres', { tools, handler }).namespace('pg');
// Tool exposed as: pg/query
// Disable namespacing (use with caution -- collisions possible)
router.addServer('local', { tools, handler }).namespace(null);
// Tool exposed as: my_tool (original name, no prefix)自定义分隔符
const dotRouter = new ToolRouter({ separator: '.' });
// Tools: github.create_issue, github.search
const dunderRouter = new ToolRouter({ separator: '__' });
// Tools: github__create_issue, github__search
const colonRouter = new ToolRouter({ separator: '::' });
// Tools: github::create_issue, github::search工具筛选
控制每个上游的哪些工具暴露在外。过滤器支持精确的名称和glob模式(* 匹配任何字符, ? 匹配单个字符)。首先应用包含模式,然后应用排除模式,最后应用谓词函数。
// Include only specific tools (glob patterns supported)
router.addServer('github', { tools, handler })
.include(['create_*', 'search']);
// Exclude dangerous tools
router.addServer('postgres', { tools, handler })
.exclude(['drop_*', 'truncate_*']);
// Full filter config with predicate function
router.addServer('db', { tools, handler })
.filter({
include: ['*'],
exclude: ['internal_*'],
predicate: (tool) => !tool.annotations?.destructiveHint,
});工具别名
重命名工具以获得更短或更清晰的名称。当工具被别名化时,原始的命名空间名称将从工具列表中删除,只显示别名。
// Router-level alias: replaces the namespaced name entirely
router.alias('search', 'github/search_repositories');
// "github/search_repositories" is removed, "search" is exposed
// Server-level alias via the builder
router.addServer('github', { tools, handler })
.alias('find', 'search_repositories');
// "github/search_repositories" is removed, "find" is exposed中间件
拦截工具调用日志记录、访问控制、参数注入或响应修改。中间件遵循 (context, next) => response 图案。特定于服务器的中间件先于全局中间件运行。
// Global middleware: applies to all tool calls
router.use(async (ctx, next) => {
console.log(`Calling ${ctx.namespacedName} on ${ctx.upstreamName}`);
const result = await next();
console.log(`Completed in context of ${ctx.upstreamName}`);
return result;
});
// Server-specific middleware via the builder
router.addServer('db', { tools, handler })
.use(async (ctx, next) => {
if (ctx.toolDefinition.annotations?.destructiveHint) {
return {
content: [{ type: 'text', text: 'Denied: destructive operations are blocked' }],
isError: true,
};
}
return next();
});
// Short-circuit: return without calling next() to skip the upstream
router.use(async (ctx, next) => {
if (ctx.namespacedName === 'cached/tool') {
return { content: [{ type: 'text', text: 'cached result' }] };
}
return next();
});冲突解决
当来自不同服务器的工具共享相同的限定名时(通常在使用 null 前缀)。
// 'prefix' (default): tools are namespaced; collision on identical qualified names throws
const router = new ToolRouter({ conflictResolution: 'prefix' });
// 'first-wins': the first registered tool keeps the name, duplicates are silently dropped
const router2 = new ToolRouter({ conflictResolution: 'first-wins' });
// 'error': throw CollisionError immediately on any collision
const router3 = new ToolRouter({ conflictResolution: 'error' });指标
跟踪每台服务器的呼叫计数、延迟和错误率。
const metrics = router.metrics;
console.log(metrics.totalCalls); // Total calls across all servers
console.log(metrics.totalErrors); // Total errors across all servers
console.log(metrics.totalTools); // Number of tools in the route table
console.log(metrics.uptimeMs); // Router uptime in milliseconds
// Per-upstream metrics
console.log(metrics.upstreams.github.callCount);
console.log(metrics.upstreams.github.errorCount);
console.log(metrics.upstreams.github.avgLatencyMs);
console.log(metrics.upstreams.github.lastCallAt);事件
订阅路由器生命周期和工具调用事件。 ToolRouter 延伸 EventEmitter.
router.on('serverConnected', (e) => console.log(`Connected: ${e.name}`));
router.on('serverDisconnected', (e) => console.log(`Disconnected: ${e.name}`));
router.on('toolCall', (e) => {
console.log(`Tool: ${e.tool}, Upstream: ${e.upstream}, Duration: ${e.durationMs}ms, Error: ${e.isError}`);
});动态服务器管理
在运行时添加、删除和更新服务器。每次更改后,路由表都会自动重建。
// Add servers dynamically
router.addServer('new-server', { tools, handler });
// Remove a server (its tools are removed from the route table)
router.removeServer('old-server');
// Update a server's tool list without removing it
router.updateServerTools('github', [
{ name: 'search' },
{ name: 'create_issue' },
{ name: 'close_issue' }, // newly added
]);______________________________________________________________________
API 参考
createRouter(options?)
创建并返回新的工厂函数 ToolRouter 例子
import { createRouter } from 'mcp-tool-router';
const router = createRouter({ name: 'my-router', version: '1.0.0' });参数:
options(RouterOptions,可选)--请参阅RouterOptions在......下面
退货: ToolRouter
______________________________________________________________________
ToolRouter
聚合来自多个上游服务器的工具的主类。扩展 EventEmitter.
构造函数
new ToolRouter(options?: RouterOptions)RouterOptions
| 属性 | 类型 | 默认值 | 描述 | |
|---|---|---|---|---|
name | string | 'mcp-tool-router' | 虚拟服务器的名称。 | |
version | string | '1.0.0' | 虚拟服务器的版本。 | |
separator | string | '/' | 位于命名空间前缀和工具名称之间的字符。 | |
conflictResolution | ConflictResolution | 'prefix' | 处理名称冲突的策略: 'prefix', 'first-wins',或 'error'. | |
healthCheck | boolean | false | 是否启用健康检查。 | |
connectionStrategy | `'eager' \ | 'lazy'` | 'eager' | 何时连接到上游服务器。 |
aggregateResources | boolean | true | 是否从上游聚集资源。 | |
aggregatePrompts | boolean | true | 是否聚合来自上游的提示。 | |
pageSize | number | 0 | 列表响应中每页的最大工具数。 0 禁用分页。 |
方法
| 方法 | 签名 | 描述 | |
|---|---|---|---|
addServer | (name: string, config: { tools?: ToolDefinition[]; handler?: ToolCallHandler; ... }) => UpstreamBuilder | 注册上游服务器。返回一个 UpstreamBuilder 为了实现流畅的配置。 | |
removeServer | (name: string) => boolean | 注销服务器并删除其工具。退货 true 如果服务器存在。 | |
callTool | (name: string, args?: Record) => Promise | 按其命名空间名称将工具调用路由到正确的上游。 | |
listTools | () => Array | 列出所有可用工具及其命名空间名称和上游来源。 | |
listServers | () => UpstreamInfo[] | 列出所有已注册的服务器及其状态和指标。 | |
updateServerTools | (name: string, tools: ToolDefinition[]) => void | 替换服务器的工具列表并重建路由表。 | |
use | (middleware: MiddlewareFn) => ToolRouter | 注册一个全局中间件函数。退货 this 用于链式。 | |
alias | (from: string, to: string) => ToolRouter | 注册路由器级工具别名。 to 是完全命名空间的名称。退货 this. | |
start | () => Promise | 启动路由器。 | |
stop | () => Promise | 停止路由器并清除所有状态。 | |
lookupRoute | `(qualifiedName: string) => RouteEntry \ | undefined` | 按限定名称查找路线条目。 |
属性
| 属性 | 类型 | 描述 |
|---|---|---|
tools | ReadonlyArray | 当前聚合工具列表。 |
upstreams | ReadonlyArray | 当前上游服务器信息。 |
metrics | RouterMetrics | 当前路由器指标快照。 |
routeCount | number | 路由表中的条目数。 |
separator | string | 已配置的命名空间分隔符。 |
事件
| 事件 | 有效载荷 | 描述 |
|---|---|---|
serverConnected | { name: string } | 添加服务器时触发。 |
serverDisconnected | { name: string } | 当服务器被移除时触发。 |
toolCall | ToolCallEvent | 在每次工具调用后发出,带有计时和错误信息。 |
______________________________________________________________________
UpstreamBuilder
流利的建设者返回 ToolRouter.addServer().所有方法返回 this 用于链式。
| 方法 | 签名 | 描述 | |
|---|---|---|---|
namespace | `(prefix: string \ | null) => UpstreamBuilder` | 设置命名空间前缀。通过 null 禁用命名空间。 |
filter | (config: FilterConfig) => UpstreamBuilder | 设置包含/排除/谓词筛选器。 | |
include | (patterns: string[]) => UpstreamBuilder | 速记:只包括与这些glob模式匹配的工具。 | |
exclude | (toolNames: string[]) => UpstreamBuilder | 速记:排除与这些glob模式匹配的工具。 | |
alias | (from: string, to: string) => UpstreamBuilder | 注册服务器级别名。 to 是原始工具名称(在命名空间之前)。 | |
use | (middleware: MiddlewareFn) => UpstreamBuilder | 注册特定于此上游的中间件。 |
______________________________________________________________________
NamespaceManager
管理命名空间前缀应用程序和工具名称的剥离。
import { NamespaceManager } from 'mcp-tool-router';
const ns = new NamespaceManager('/', 'prefix');构造函数
new NamespaceManager(separator?: string, conflictResolution?: ConflictResolution)| 方法 | 签名 | 描述 | |
|---|---|---|---|
qualify | `(prefix: string \ | null, toolName: string) => string` | 根据前缀和工具名称构建限定名。如果前缀为,则返回原始名称 null. |
dequalify | `(qualifiedName: string) => { serverName: string; originalName: string } \ | null` | 去掉前缀,在出现第一个分隔符时拆分。 |
addTool | `(serverName: string, tool: ToolDefinition, prefix?: string \ | null) => void` | 在服务器的命名空间下注册工具。 |
resolveTool | `(qualifiedName: string) => NamespaceEntry \ | undefined` | 按合格名称查找已注册的工具。 |
listTools | () => NamespaceEntry[] | 列出所有已注册的工具。 | |
listToolsForServer | (serverName: string) => NamespaceEntry[] | 列出特定服务器的工具。 | |
removeServer | (serverName: string) => void | 删除属于服务器的所有工具。 | |
has | (qualifiedName: string) => boolean | 检查是否注册了限定名称。 | |
clear | () => void | 删除所有条目。 | |
getSeparator | () => string | 获取已配置的分隔符。 | |
getConflictResolution | () => ConflictResolution | 获取配置的冲突解决策略。 | |
size | number (getter) | 已注册的工具总数。 |
______________________________________________________________________
ServerRegistry
管理服务器注册、工具/资源/提示列表、状态和调用指标。
import { ServerRegistry } from 'mcp-tool-router';
const registry = new ServerRegistry();| 方法 | 签名 | 描述 | |
|---|---|---|---|
registerServer | (config, tools, handler, resources?, prompts?) => void | 使用服务器的工具和处理程序注册服务器。 | |
unregisterServer | (name: string) => boolean | 删除服务器注册。 | |
getServer | `(name: string) => ServerEntry \ | undefined` | 按名称获取服务器条目。 |
hasServer | (name: string) => boolean | 检查服务器是否已注册。 | |
listServers | () => ServerEntry[] | 列出所有已注册的服务器。 | |
listServerNames | () => string[] | 列出所有服务器名称。 | |
updateTools | (name: string, tools: ToolDefinition[]) => void | 更新服务器的工具列表。 | |
updateResources | (name: string, resources: ResourceDefinition[]) => void | 更新服务器的资源列表。 | |
updatePrompts | (name: string, prompts: PromptDefinition[]) => void | 更新服务器的提示列表。 | |
updateStatus | (name: string, status: UpstreamStatus) => void | 更新服务器的连接状态。 | |
recordCall | (name: string, durationMs: number, isError: boolean) => void | 记录工具调用以进行指标跟踪。 | |
getUpstreamInfo | `(name: string) => UpstreamInfo \ | undefined` | 使用指标获取聚合的上游信息。 |
clear | () => void | 删除所有服务器注册。 | |
size | number (getter) | 已注册的服务器数量。 |
______________________________________________________________________
RequestRouter
处理对正确上游服务器的路由工具调用。构建和维护路由表,执行中间件链,并记录调用指标。
import { RequestRouter } from 'mcp-tool-router';
const router = new RequestRouter(namespaceManager, serverRegistry);| 方法 | 签名 | 描述 | |
|---|---|---|---|
buildRouteTable | () => void | 从当前命名空间和注册表状态重建路由表。 | |
route | (request: ToolCallRequest) => Promise | 将工具调用请求路由到正确的上游。 | |
lookup | `(qualifiedName: string) => RouteEntry \ | undefined` | 按限定名称查找路线条目。 |
listRoutes | () => RouteEntry[] | 获取所有路线条目。 | |
listTools | () => ToolDefinition[] | 获取所有具有命名空间名称的工具定义。 | |
addMiddleware | (middleware: MiddlewareFn) => void | 注册一个全局中间件。 | |
addServerMiddleware | (serverName: string, middleware: MiddlewareFn) => void | 为特定服务器注册中间件。 | |
addAlias | (from: string, to: string) => void | 注册一个全局别名。 | |
addServerAlias | (serverName: string, from: string, to: string) => void | 注册每个服务器的别名。 | |
size | number (getter) | 表中的路由数。 |
______________________________________________________________________
applyFilter(tools, filter?)
独立功能,应用 FilterConfig 到工具定义列表。
import { applyFilter } from 'mcp-tool-router';
const filtered = applyFilter(tools, {
include: ['get_*'],
exclude: ['get_internal_*'],
predicate: (tool) => !!tool.description,
});参数:
tools(ToolDefinition[])--要筛选的工具列表。filter(FilterConfig,可选)--过滤器配置。如果省略,则返回所有工具。
退货: ToolDefinition[]
______________________________________________________________________
CollisionError
当来自不同上游服务器的两个工具解析为相同的限定名时抛出。
import { CollisionError } from 'mcp-tool-router';
try {
ns.addTool('server2', { name: 'search' }, null);
} catch (err) {
if (err instanceof CollisionError) {
console.log(err.conflicts);
// [{ name: 'search', upstreams: ['server1', 'server2'] }]
}
}属性:
conflicts(Array)--冲突名称列表以及产生这些名称的上游。
______________________________________________________________________
ConfigError
因配置无效而抛出(例如,分隔符无效、缺少必填字段)。
import { ConfigError } from 'mcp-tool-router';______________________________________________________________________
类型导出
所有类型都从包入口点导出:
import type {
ToolDefinition,
ToolAnnotations,
ResourceDefinition,
PromptDefinition,
PromptArgument,
ServerConfig,
RouterOptions,
ConflictResolution,
ToolCallRequest,
ToolCallResponse,
ToolCallHandler,
ToolCallContext,
ToolContent,
MiddlewareFn,
FilterConfig,
AliasConfig,
UpstreamStatus,
UpstreamInfo,
RouterMetrics,
ToolCallEvent,
UpstreamEvent,
RouterEvents,
RouteEntry,
UpstreamTransportConfig,
ReconnectConfig,
ServerRegistration,
} from 'mcp-tool-router';______________________________________________________________________
配置
ServerConfig
上游服务器注册的配置。
| 属性 | 类型 | 默认值 | 描述 | |
|---|---|---|---|---|
name | string | 必需 | 上游服务器的唯一标识符。 | |
transport | UpstreamTransportConfig | -- | 传输配置(stdio, http,或 sse). | |
prefix | `string \ | null` | 服务器名称 | 命名空间前缀。 null 禁用命名空间。 |
separator | string | '/' | 覆盖此服务器的路由器级别分隔符。 | |
filter | FilterConfig | -- | 此服务器工具的包含/排除/谓词筛选器。 | |
aliases | AliasConfig[] | -- | 此服务器的工具别名。 | |
connectTimeout | number | 30000 | 连接超时(毫秒)。 | |
requestTimeout | number | 60000 | 每个请求超时(毫秒)。 | |
reconnect | ReconnectConfig | -- | 重新连接配置。 | |
env | Record | -- | 环境变量(stdio传输)。 | |
cwd | string | -- | 工作目录(stdio传输)。 | |
headers | Record | -- | HTTP标头(HTTP/sse传输)。 |
FilterConfig
| 属性 | 类型 | 描述 |
|---|---|---|
include | string[] | 球状图案。仅包括与至少一种模式匹配的工具。 |
exclude | string[] | 球状图案。不包括与任何模式匹配的工具。 |
predicate | (tool: ToolDefinition) => boolean | 功能过滤器。返回 true 包括, false 排除。 |
ReconnectConfig
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
enabled | boolean | true | 断开连接时是否自动重新连接。 |
maxAttempts | number | 10 | 放弃前的最大重新连接尝试次数。 |
initialDelayMs | number | 1000 | 首次重新连接尝试前的初始延迟。 |
maxDelayMs | number | 30000 | 重新连接尝试之间的最大延迟。 |
backoffMultiplier | number | 2 | 每次尝试失败后,应用乘数计算延迟。 |
UpstreamTransportConfig
type UpstreamTransportConfig =
| { type: 'stdio'; command: string; args?: string[] }
| { type: 'http'; url: string }
| { type: 'sse'; url: string };______________________________________________________________________
错误处理
未知工具
当 callTool 使用路由表中不存在的工具名称调用,响应具有 isError: true 内容包含 Unknown tool: "".
const result = await router.callTool('nonexistent/tool', {});
if (result.isError) {
console.error(result.content[0]); // { type: 'text', text: 'Unknown tool: "nonexistent/tool"' }
}上游不可用
当目标上游服务器断开连接或处于非连接状态时,响应具有 isError: true 内容描述了服务器状态。
const result = await router.callTool('github/search', {});
if (result.isError) {
console.error(result.content[0]); // { type: 'text', text: 'Server "github" is disconnected' }
}处理程序错误
如果上游处理程序抛出异常,则会捕获错误并将其作为错误响应返回。错误也记录在服务器的指标中。
const result = await router.callTool('flaky/operation', {});
if (result.isError) {
// { type: 'text', text: 'Error calling tool "operation" on server "flaky": Connection timeout' }
}名称冲突
CollisionError 当两台服务器生成相同的限定工具名称并且冲突解决策略为 'error' 或 'prefix'.
import { CollisionError } from 'mcp-tool-router';
try {
router.addServer('server2', { tools: [{ name: 'search' }], handler }).namespace(null);
} catch (err) {
if (err instanceof CollisionError) {
console.error(err.message);
// Tool name collision detected: "search" is exposed by both upstream "server1" and upstream "server2"
console.error(err.conflicts);
}
}重复的服务器名称
尝试使用已注册的名称注册服务器会抛出 Error.
router.addServer('github', { tools: [], handler });
router.addServer('github', { tools: [], handler }); // throws: Server "github" is already registered______________________________________________________________________
高级用法
具有过滤和别名的多服务器聚合
import { ToolRouter } from 'mcp-tool-router';
const router = new ToolRouter({
name: 'enterprise-router',
version: '2.0.0',
separator: '/',
});
// GitHub: expose only read operations
router.addServer('github', {
tools: [
{ name: 'create_issue', description: 'Create issue', annotations: { readOnlyHint: false } },
{ name: 'search', description: 'Search repos', annotations: { readOnlyHint: true } },
{ name: 'get_repo', description: 'Get repo info', annotations: { readOnlyHint: true } },
{ name: 'delete_repo', description: 'Delete repo', annotations: { destructiveHint: true } },
],
handler: githubHandler,
}).filter({
predicate: (tool) => !tool.annotations?.destructiveHint,
}).namespace('gh');
// Postgres: hide dangerous DDL operations
router.addServer('postgres', {
tools: [
{ name: 'query', description: 'Run SQL query' },
{ name: 'list_tables', description: 'List tables' },
{ name: 'drop_table', description: 'Drop table' },
{ name: 'truncate_table', description: 'Truncate table' },
],
handler: pgHandler,
}).exclude(['drop_*', 'truncate_*']).namespace('pg');
// Slack: expose everything, add a short alias
router.addServer('slack', {
tools: [
{ name: 'send_message', description: 'Send a message' },
{ name: 'list_channels', description: 'List channels' },
],
handler: slackHandler,
});
// Router-level alias for convenience
router.alias('send', 'slack/send_message');
// Final tool list:
// gh/create_issue, gh/search, gh/get_repo, pg/query, pg/list_tables,
// slack/list_channels, send访问控制中间件
router.use(async (ctx, next) => {
if (ctx.toolDefinition.annotations?.destructiveHint) {
return {
content: [{ type: 'text', text: 'Access denied: destructive operations are not allowed' }],
isError: true,
};
}
return next();
});日志和审计中间件
router.use(async (ctx, next) => {
const start = Date.now();
console.log(`[AUDIT] Calling ${ctx.namespacedName} on ${ctx.upstreamName}`);
const result = await next();
console.log(`[AUDIT] ${ctx.namespacedName} completed in ${Date.now() - start}ms, error=${!!result.isError}`);
return result;
});服务器特定中间件
// Add input validation middleware only to the database server
router.addServer('db', { tools, handler })
.use(async (ctx, next) => {
// Inject a read-only flag for safety
if (!ctx.arguments.readOnly) {
ctx.arguments.readOnly = true;
}
return next();
});响应修改中间件
router.use(async (ctx, next) => {
const result = await next();
// Redact sensitive data from all responses
return {
...result,
content: result.content.map(c =>
c.type === 'text'
? { ...c, text: c.text.replace(/\b\d{3}-\d{2}-\d{4}\b/g, '***-**-****') }
: c
),
};
});使用事件和指标进行监控
const router = new ToolRouter({ name: 'monitored-router' });
router.on('serverConnected', ({ name }) => {
console.log(`[EVENT] Server connected: ${name}`);
});
router.on('serverDisconnected', ({ name }) => {
console.log(`[EVENT] Server disconnected: ${name}`);
});
router.on('toolCall', (event) => {
if (event.isError) {
console.error(`[ERROR] ${event.tool} on ${event.upstream}: ${event.errorMessage}`);
}
});
// Periodic metrics reporting
setInterval(() => {
const m = router.metrics;
console.log(`[METRICS] Tools: ${m.totalTools}, Calls: ${m.totalCalls}, Errors: ${m.totalErrors}, Uptime: ${m.uptimeMs}ms`);
for (const [name, info] of Object.entries(m.upstreams)) {
console.log(` ${name}: calls=${info.callCount}, errors=${info.errorCount}, avg=${info.avgLatencyMs.toFixed(1)}ms`);
}
}, 60_000);生命周期管理
const router = new ToolRouter({ name: 'managed-router' });
// Register servers
router.addServer('github', { tools: githubTools, handler: githubHandler });
router.addServer('slack', { tools: slackTools, handler: slackHandler });
// Start the router
await router.start();
// ... use the router ...
// Dynamically add a new server
router.addServer('jira', { tools: jiraTools, handler: jiraHandler });
// Dynamically update tools when upstream changes
router.updateServerTools('github', updatedGithubTools);
// Remove a server
router.removeServer('slack');
// Stop and clean up
await router.stop();______________________________________________________________________
TypeScript
此包是用TypeScript编写的,启用了严格模式。所有公共类型都从包入口点导出。
import { ToolRouter, createRouter, NamespaceManager, ServerRegistry, RequestRouter, applyFilter, CollisionError, ConfigError } from 'mcp-tool-router';
import type {
ToolDefinition,
ToolAnnotations,
ResourceDefinition,
PromptDefinition,
PromptArgument,
ServerConfig,
RouterOptions,
ConflictResolution,
ToolCallRequest,
ToolCallResponse,
ToolCallHandler,
ToolCallContext,
ToolContent,
MiddlewareFn,
FilterConfig,
AliasConfig,
UpstreamStatus,
UpstreamInfo,
RouterMetrics,
ToolCallEvent,
UpstreamEvent,
RouterEvents,
RouteEntry,
UpstreamTransportConfig,
ReconnectConfig,
ServerRegistration,
} from 'mcp-tool-router';编译输出包括 .d.ts 申报文件和 .d.ts.map IDE导航的声明映射。该包以ES2022为目标,并发出CommonJS模块。
______________________________________________________________________
许可证
麻省理工学院
