AI代理工具网关

conductor是一个AI代理工具网关,通过单一MCP端点聚合多个上游工具,提供实时认证、审计和访问控制。

  • by niradler
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 niradler/mcp-conductor

使用说明

导体

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,将提供商连接到网关,拥有信号。

请求的生命周期

  1. 验证。 持有者令牌→ 定时安全SHA-256与每个用户的 apiKeyHash.不良令牌→ 401.
  2. 解决访问问题。 用户组→ 可访问的提供者集("*" 扩展到所有配置)。
  3. 会议。 新会话=MCP initialize 请求。网关组装了一个每个会话的MCP服务器,该服务器只通告调用者可能看到的工具。
  4. 命名空间。 工具注册为 <provider>__<tool>.分离器 __ 保留;提供程序名称不得包含它。
  5. 打电话。 处理程序解码 <provider>__<tool>,通过以下方式转发给审核包裹的提供程序 { user, requestId, signal }.供应商必须遵守 signal 取消。
  6. 审计+跟踪。 一 AuditStore.insertCall 每次调用都有经过编辑的参数、状态、持续时间、请求id。每次调用一个OTel跨度。
  7. 关机。 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 .js NodeNext不会解析无扩展的导入。

协议说明

  • 运输。 流式HTTP(@modelcontextprotocol/sdk).一 initialize 请求打开一个会话;后续请求必须携带 mcp-session-id.没有会话的非初始化流量将获得 400.
  • 工具架构。 网关通过以下方式向上游传递JSON模式 z.unknown() 每个财产。上游仍然是验证的真相来源——网关从不重写工具输入。
  • 相关性。 每个请求都会得到一个 X-Request-Id (回应并贯穿始终 ToolCallContext 以及审计日志)。

[!提示] 看 docs/plans/ 对于路线图,接下来是OpenAPI、GraphQL、HTTP工具和OpenShell沙盒执行。