使用说明
导体
AI代理的工具网关——一个MCP端点、多个上游、真实身份验证、真实审计。
亮点 • 快速启动 • 配置 • 建筑 • 编写提供者 • 发展
conductor 说 模型上下文协议 并从承载令牌身份验证、基于组的访问控制和完整的逐个调用审计日志背后的任意数量的后端聚合工具。 代理人不再关心工具是如何实施的;停止将API密钥粘贴到代理配置中。
后端实现了一个通用 ToolProvider 界面。今天: MCP上游服务器 超过stdio。计划:OpenAPI、GraphQL、HTTP工具和 沙盒代码和CLI执行 通过OpenShell——在沙盒映像中安装CLI并调用它 sandbox_exec 而不是编写一次性的MCP包装器。
为什么存在
将许多MCP服务器直接连接到代理是一个上下文税陷阱。在用户输入单词之前,每个上游的工具列表和模式都会被预先加载到LLM的上下文窗口中——仅官方的GitHub MCP就有大约50万个代币,模型精度开始下降到大约100万(“迷失在中间”)。指挥坐在N上游的前面 单MCP端点 并给予代理人 懒惰,被统治 发现:只查看呼叫者的角色允许的内容,按需列出提供者和工具,而不是一次全部列出,审核每次呼叫并限制费率,以及(计划) 按意图预过滤工具 所以模型永远看不到其他95%。CLI与MCP的争论没有抓住重点——当你想要廉价和个性化的上下文时,使用本地CLI;当您需要跨团队的治理、审计、RBAC和一个端点时,请使用conductor。
┌──────────────┐ MCP/HTTP ┌───────────────────────────┐ stdio/… ┌─────────────┐
│ MCP client │ ──────────▶ │ conductor │ ──────────▶ │ upstream │
│ (agent/IDE) │ │ auth · groups · audit │ │ MCP server │
└──────────────┘ │ tool namespacing (__) │ └─────────────┘
└───────────────────────────┘ also: OpenAPI · GraphQL · …
亮点
- 一个端点,许多工具。 上游被抽象为
ToolProvider合同;工具以命名空间显示为<provider>__<tool>. - 懒惰的工具发现。 内建
conductor__list_providers和conductor__list_tools元工具允许代理根据需要发现功能,而不是将每个工具的模式预加载到上下文中。供应商级别的描述和说明来自每个上游serverInfo/initialize有效载荷。 - 真实身份。 SHA-256散列API密钥,定时安全比较。明文键从不出现在配置中。
- 基于组的访问控制。 用户属于组;团体资助提供者(或
"*"为所有人)。 - 审计一切。 每个工具调用都记录用户、提供者、工具、已编辑的参数、持续时间、状态和请求id。
- 内置可观察性。 结构化JSON日志;OpenTetry在以下情况下每次呼叫的跨度
OTEL_EXPORTER_OTLP_ENDPOINT已设置。 - 优美的生命周期。 清理LIFO关闭:HTTP→ 会话→ 提供商→ 审计→ 遥测。
- 零仪式开始。 提供一个带有虚拟密钥的工作配置——克隆、安装、运行、连接。
快速启动
[!注意] 需要 Node.js≥20.11 和 pnpm≥9.
pnpm install
pnpm build
CONDUCTOR_CONFIG=examples/conductor.json pnpm dev
捆绑 examples/conductor.json 带用户的靴子 alice (API密钥 changeme,哈希烘焙)和一个上游提供商(everything 通过 npx @modelcontextprotocol/server-everything).
连接任何MCP客户端:
URL: http://127.0.0.1:18080/mcp
Header: Authorization: Bearer changeme
出现13个工具,其名称间隔为 everything__*.卫生检查:
curl http://127.0.0.1:18080/health
# {"ok":true,"sessions":0,"providers":["everything"]}
使用您自己的API密钥
pnpm hash-key my-secret-key
# sha256:7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
复制 examples/conductor.json,将哈希粘贴到 users[].apiKeyHash,点 CONDUCTOR_CONFIG 在你的副本。
配置
一个JSON文件,在加载时由Zod验证。Schema位于 packages/server/src/conductor-config.ts。未知密钥被拒绝。
{
"server": { "host": "127.0.0.1", "port": 18080, "maxSessions": 100 },
"users": [
{ "name": "alice", "apiKeyHash": "sha256:…", "groups": ["admins"] }
],
"groups": [{ "name": "admins", "providers": ["*"] }],
"providers": [
{
"type": "mcp",
"name": "everything",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-everything"],
"env": {}
}
],
"audit": { "type": "console" },
"telemetry": { "serviceName": "conductor", "otlpEndpoint": "" }
}
顶级字段
| 字段 | 目的 |
|---|---|
server |
host, port, maxSessions --绑定地址和会话LRU上限。 |
users[] |
name, apiKeyHash (sha256:<64 hex>), groups[]. |
groups[] |
name, providers[] (提供商名称,或 ["*"] 为所有人)。 |
providers[] |
歧视 type.今天: "mcp" (stdio)。更多类型传入。 |
audit |
目前 { "type": "console" } 可选 bufferSize. |
telemetry |
serviceName + otlpEndpoint。如果端点未设置,则跟踪为不可操作。 |
MCP提供商选项
| 字段 | 默认值 | 注释 |
|---|---|---|
command, args, env |
—— | 如何在上游产卵。 |
initialListTimeoutMs |
15000 |
第一次的截止日期 tools/list 连接后。 |
callTimeoutMs |
60000 |
每次呼叫超时被转发为 AbortSignal. |
reconnect.maxAttempts |
10 |
之间的指数回退 initialDelayMs 和 maxDelayMs. |
reconnect.initialDelayMs |
1000 |
|
reconnect.maxDelayMs |
30000 |
环境变量
| Var | 含义 | 默认值 |
|---|---|---|
CONDUCTOR_CONFIG |
通往 conductor.json. |
./conductor.json |
PORT |
覆盖范围 server.port 加载配置后。 |
— |
LOG_LEVEL |
debug, info, warn, error. |
info |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP HTTP基本URL。如果未设置,则禁用跟踪。 | — |
[!重要] 切勿提交纯文本API密钥。跑
pnpm hash-key <plaintext>并仅存储结果sha256:…消化。
建筑
conductor 是一个具有严格依赖分层的pnpm工作区-- core 没有兄弟deps,提供者和网关仅依赖 core,以及 server 将它们连接在一起。
| 包 | 角色 |
|---|---|
@mcp-conductor/core |
ToolProvider 接口, ProviderRegistry,审计和配置存储,记录器,OTel引导,关闭注册表。没有兄弟姐妹。 |
@mcp-conductor/provider-mcp |
ToolProvider 通过stdio生成上游MCP服务器。处理超时和重新连接。 |
@mcp-conductor/provider-openshell |
ToolProvider 为了 NVIDIA OpenShell (gRPC)。购买原型;再生与 pnpm update-openshell-protos. 目前是一个存根。 |
@mcp-conductor/gateway |
HTTP MCP服务器。授权、组、审核包装、命名空间、会话管理器。接受任何 ToolProvider[]. |
@mcp-conductor/server |
CLI(conductor).负载 conductor.json,将提供商连接到网关,拥有信号。 |
请求的生命周期
- 验证。 持有者令牌→ 定时安全SHA-256与每个用户的
apiKeyHash.不良令牌→401. - 解决访问问题。 用户组→ 可访问的提供者集(
"*"扩展到所有配置)。 - 会议。 新会话=MCP
initialize请求。网关组装了一个每个会话的MCP服务器,该服务器只通告调用者可能看到的工具。 - 命名空间。 工具注册为
<provider>__<tool>.分离器__保留;提供程序名称不得包含它。 - 打电话。 处理程序解码
<provider>__<tool>,通过以下方式转发给审核包裹的提供程序{ user, requestId, signal }.供应商必须遵守signal取消。 - 审计+跟踪。 一
AuditStore.insertCall每次调用都有经过编辑的参数、状态、持续时间、请求id。每次调用一个OTel跨度。 - 关机。 LIFO:HTTP服务器→ 会话→ 提供商→ 审计→ 遥测。
存储库布局
packages/
core/ foundation: ToolProvider, stores, logger, OTel, lifecycle
provider-mcp/ stdio MCP upstream adapter
provider-openshell/ OpenShell gRPC provider (stub; protos vendored)
gateway/ HTTP MCP server, auth, groups, audit, namespacing
server/ CLI binary: config + wiring
examples/
conductor.json working example config (alice / changeme)
scripts/
hash-api-key.ts prints sha256:<hex> for an API key
docs/
plans/ roadmap and implementation plans
编写提供者
实施 ToolProvider 从 @mcp-conductor/core:
import type {
ToolProvider,
ToolSpec,
ToolCallContext,
ToolCallResult,
} from "@mcp-conductor/core";
export class MyProvider implements ToolProvider {
readonly name = "my-provider";
async connect(): Promise<void> { /* … */ }
async close(): Promise<void> { /* … */ }
async listTools(): Promise<ToolSpec[]> { /* … */ }
async callTool(
name: string,
args: unknown,
ctx: ToolCallContext,
): Promise<ToolCallResult> {
// honour ctx.signal for cancellation
}
}
向注册 ProviderRegistry --或者,对于配置驱动的加载,添加一个分支到 packages/server/src/provider-factory.ts 并延伸 ProviderEntrySchema.
发展
pnpm build # tsc -p tsconfig.build.json per package
pnpm typecheck # tsc --noEmit (includes tests)
pnpm test # vitest — 255 tests across all packages
pnpm test:watch
pnpm clean # rm -rf dist/ .tsbuildinfo
运行单个测试文件或测试名称:
pnpm test -- packages/gateway/tests/auth.test.ts
pnpm test -- packages/gateway/tests/auth.test.ts -t "rejects invalid bearer"
[!重要] 这只是ESM(
"type": "module",NodeNext分辨率)。本地进口 必须 使用.js扩展——TypeScript编译为ESM.jsNodeNext不会解析无扩展的导入。
协议说明
- 运输。 流式HTTP(
@modelcontextprotocol/sdk).一initialize请求打开一个会话;后续请求必须携带mcp-session-id.没有会话的非初始化流量将获得400. - 工具架构。 网关通过以下方式向上游传递JSON模式
z.unknown()每个财产。上游仍然是验证的真相来源——网关从不重写工具输入。 - 相关性。 每个请求都会得到一个
X-Request-Id(回应并贯穿始终ToolCallContext以及审计日志)。
[!提示] 看
docs/plans/对于路线图,接下来是OpenAPI、GraphQL、HTTP工具和OpenShell沙盒执行。
