内容
# 连接器

在所有连接器前面只有一个MCP端点。代理看到九个工具而不是数百个,每个客户端只需配置一次,而不是每次集成都配置一次。
## 问题
连接到N个MCP服务器的代理需要为所有N个服务器付费,无论它是否使用任何工具。每个服务器的工具列表都会在连接时注入到上下文窗口中——数百个定义及其模式,几乎与实际任务无关。该预算无论模型调用一个还是不调用任何工具都会被花费,而且它会从同一个窗口中扣除工作所需的费用。
第二个成本是配置。每个客户端——Claude、Cursor,还是其他——都必须单独指向每个服务器,并为每个服务器配置自己的身份验证。添加集成意味着需要触及每个客户端;旋转一个令牌意味着需要找到它曾经被粘贴到的每个位置。
## 什么是连接器
您部署一个MCP端点——Cloudflare Worker、Node或Docker——它在固定的九个元工具后面聚合下游连接器。连接器要么是远程MCP服务器,由连接器代理,要么是具有手写工具定义和获取处理程序的普通HTTP API。
```
┌── remoteMcp("notion") → mcp.notion.com
Claude / Cursor ── MCP ──▶ 连接器 ───┼── remoteMcp("linear") → mcp.linear.app
看见 9 个工具 /mcp ├── api("resend") → fetch(...)
└── api("internal") → fetch(...)
```
代理不是一次性收到所有工具,而是根据需要发现它们。`search_tools` 返回查询的排名匹配结果,`describe_tools` 返回仅针对即将调用的地址的模式——默认为紧凑型,请求时返回原始JSON模式。 `call_tool`、`batch_call` 和 `call_destructive_tool` 通过地址(`<connectorId>.<toolName>`)调用它们。 `list_connectors` 报告存在哪些工具以及是否可达,`authorize_connector` 启动下游OAuth流程,`get_result` 分页显示太大而无法内联返回的结果,`skills` 向模型提供一个简短的指南,指导其从其余工具中选择。
这就是整个界面。代理的上下文中包含九个工具定义,无论其后面有十个还是上千个工具;客户端持有一個URL和一个凭证,无论该URL扩展到多少个服务。
一个可选的第十个元工具 `execute_code` 在一个没有网络、文件系统或环境访问的沙盒中运行模型编写的异步JavaScript——只有显式只读工具作为可调用的全局变量——将循环、跨连接器的连接或大型响应上的筛选器变成一个往返而不是十几个。配置 `executor` 为空,则连接器恰好是九工具服务器。
## 为什么是这种设计
**代码配置,每个租户一个部署。** 连接器在TypeScript中声明。添加一个连接器是一个代码更改和部署——没有集成数据库,没有注册API,没有运行时管理员。部署是一个小型配置文件,您可以一目读完,并在拉取请求中审查,而不是一个需要管理的平台。
**凭证保留在服务器端。** 下游令牌存储在部署自己的存储上的AES-GCM加密保管库中,密钥存储在保管库之外。连接器通过 `ctx.credential` 访问其自己的凭证; `/ui`、元工具和代码沙盒只会看到掩码元数据。旋转令牌是操作员操作,而不是重新部署——尽管写入保管库故意比其他操作更窄,需要Clerk身份验证的操作员进行同源请求,因此带有持有者令牌的部署无法从浏览器管理凭证。
**只读是关闭的。** 只有显式注解为 `readOnlyHint: true` 的工具才能通过 `call_tool`、`batch_call` 和沙盒访问。缺少、错误或矛盾的注解不会被视为例外:它们需要 `call_destructive_tool`,它本身被注解,以便MCP主机可以将问题提交给人类。连接器使边界可见;审批是主机的任务。
**工具包限定团队看到的内容。** 部署属于一个组织;工具包是其注册表对组织内一个组的命名视图——支持团队看到Zendesk和Notion,高管还看到Gmail。客户端通过在MCP URL上附加 `?toolkit=support` 选择一个,并可以绑定一个凭证,使其打开该视图而非其他。 在作用域会话内,作用域外的地址失败,就像不存在的地址一样。两个团队,一个部署。谁能进入是首要问题:静态持有者令牌或Clerk——其中 `allowedDomains: ["acme.com"]` 允许任何已验证的主邮箱在您的域上的用户,无需枚举用户,并且 `gate` 钩子处理域名规则无法表达的内容。两者都关闭, 每个配置的都要经过。
**活动记录事实,而不是有效载荷。** 可选的活动存储记录哪些已解析的工具为谁运行以及运行结果如何——从不记录参数、结果、生成的代码、搜索文本或原始错误消息。排除是结构性的,而不是通过编辑器实现的:事件类型没有地方放置有效载荷,这就是为什么操作日志不会变成值得窃取的东西。
**一个只能查看的操作员仪表盘。** `GET /ui` 是一个只读状态页面,没有构建步骤:连接器健康状况、工具计数、下游授权链接、凭证控件和活动选项卡(当配置了存储时)。它显示状态并管理凭证;它不能更改代理可以调用的内容。连接器的凭证存储也会主动探测其存活状态——使用每个连接器的测试或状态钩子,而不是下游工具调用——因此死亡令牌会以 `auth_required` 显示,并带有 `/ui` 上的 URL 和 `list_connectors` 中的 URL, 在代理的实际调用失败之前。
## 何时不使用
连接器故意保持简单,并拒绝几种诱人的形状。 它是 **非多租户** 的——一个部署就是一个租户,具有一个注册表和一个凭证存储,工具包是作用域视图而不是租户。 没有 **策略引擎** ,没有审批,也没有暂停——访问决策是连接器已经知道如何回答的固定决策(此工具是只读的,此凭证是否可以打开此工具包),而不是您编写的规则。 没有 **运行时管理** ——您不能从浏览器或API添加连接器。 它只聚合 **工具** ,不聚合MCP资源或提示,并且它不会摄取 **GraphQL** 模式,因为生成数百个低质量的工具定义是存在九个元工具的原因。 OpenAPI是一个更柔和的“否”:今天没有内置,但也不拒绝, 并在 [问题 #26](https://github.com/zackbart/connecta/issues/26) 中跟踪。 如果您想要一个具有审批工作流的托管多租户集成平台,那么它不是合适的; [非目标](https://github.com/zackbart/connecta/blob/main/docs/decisions.md#non-goals) 更详细地说明了这一点。
## 入门
Node部署需要Node.js 20.9或更高版本。
```sh
npm install @zackbart/connecta
```
一个带有手写连接器的最小服务器:
```ts
import { api, bearerToken, createConnecta } from "@zackbart/connecta";
import { fileStorage, listen } from "@zackbart/connecta/node";
const connecta = createConnecta({
storage: fileStorage("./.connecta-state.json"), // 或 memoryStorage()
auth: bearerToken(process.env.CONNECTA_TOKEN ?? "dev-token"),
connectors: [
api("time", {
description: "时间 - 当前时间戳",
tools: [
{
name: "get_now",
description: "返回当前时间作为ISO 8601时间戳。",
inputSchema: { type: "object", properties: {} },
annotations: { readOnlyHint: true },
handler: async () => ({ now: new Date().toISOString() }),
},
],
}),
],
});
listen(connecta, 8787); // MCP 在 http://localhost:8787/mcp,状态在 /ui
```
将MCP客户端指向 `http://localhost:8787/mcp` 并带有 `Authorization: Bearer` 头,它将看到九个元工具,其中 `time.get_now` 可以通过 `search_tools` 发现。
可运行的部署位于 [`examples/`](https://github.com/zackbart/connecta/tree/main/examples): [`worker/`](https://github.com/zackbart/connecta/tree/main/examples/worker) 是一个可部署的Cloudflare Worker,带有KV和D1适配器,以及一个复制的真实部署模板; [`node/`](https://github.com/zackbart/connecta/tree/main/examples/node) 在上面的服务器上添加了工具包和代码模式; [`docker/`](https://github.com/zackbart/connecta/tree/main/examples/docker) 是一个单一服务的组合堆栈。 核心以外的内容仅由使用它的部署安装:`@clerk/backend` 和 `quickjs-emscripten` 是可选的对等依赖项,通过 `/auth/clerk` 和 `/quickjs` 子路径访问, 使用代码模式的Worker会带来自己的 `@cloudflare/codemode`。 连接器不附带服务特定的连接器: 端点、凭证和工具选择保留在您的项目中, 使用 `remoteMcp()` 和 `api()` 声明。
## 了解更多
- **[文档](https://github.com/zackbart/connecta/blob/main/docs/documentation.md)** — 参考手册:架构、连接器、入站身份验证和下游OAuth、存储、Clerk设置、测试和故障排除。从 [元工具参考](https://github.com/zackbart/connecta/blob/main/docs/documentation.md#3-meta-tools-reference)、 [配置选项](https://github.com/zackbart/connecta/blob/main/docs/documentation.md#8-running-it)、 [代码模式](https://github.com/zackbart/connecta/blob/main/docs/documentation.md#13-code-mode-execute_code)、 [工具包](https://github.com/zackbart/connecta/blob/main/docs/documentation.md#16-toolkits-scoped-views) 或 [凭证健康状况](https://github.com/zackbart/connecta/blob/main/docs/documentation.md#17-credential-health-proactive-liveness-checks) 开始。
- **[决策](https://github.com/zackbart/connecta/blob/main/docs/decisions.md)** — 连接器拒绝的内容,哪些替代方案丢失以及为什么,以及更改必须保留的不变量。
- **[CHANGELOG](https://github.com/zackbart/connecta/blob/main/CHANGELOG.md)** — 每个版本中更改的内容。
- **[安全](https://github.com/zackbart/connecta/blob/main/SECURITY.md)** — 支持的版本以及如何报告漏洞。