@vouchid/mcp
MCP服务器的身份验证中间件。在处理程序运行之前验证每个传入的工具调用,并可选择自动执行后端策略(速率限制、金额限制、人工批准)。
npm install @vouchid/mcp需要Node.js 18+。仅ESM("type": "module" 在你的 package.json).
______________________________________________________________________
运作原理
每个工具调用在到达处理程序之前都会经过中间件:
- 从中提取代理令牌
arguments._agentid_token或x-agent-token头球 - 根据您的VouchID后端进行验证
- 检查代理是否具备该特定工具所需的功能
- _(如果
enforcePolicy: true)_ 呼叫POST /v1/agents/check-permission在您的后端 - _(如果后端需要人工批准)_ 民意调查
GET /v1/approvals/:id/status直到人类行动 - 调用你的处理程序——或抛出
AgentIDError如果任何检查失败
你的处理器只有在一切顺利的情况下才会运行。服务器代码中没有样板。
______________________________________________________________________
快速开始
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { AgentIDMiddleware, getAgentIdentity } from "@vouchid/mcp";
const middleware = new AgentIDMiddleware({
apiUrl: process.env.VOUCHID_API_URL,
apiKey: process.env.VOUCHID_API_KEY,
toolCapabilities: {
read_file: "read:filesystem",
write_file: "write:filesystem",
},
});
// Use the low-level Server class — it exposes setRequestHandler
const server = new Server(
{ name: "my-server", version: "1.0.0" },
{ capabilities: { tools: {} } },
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: "read_file",
description: "Read a file from disk",
inputSchema: {
type: "object",
properties: { path: { type: "string" } },
required: ["path"],
},
},
],
}));
server.setRequestHandler(
CallToolRequestSchema,
middleware.wrap(async (request) => {
const agent = getAgentIdentity(request);
console.log(`Called by: ${agent.name} (trust score: ${agent.trustScore})`);
const { name, arguments: args } = request.params;
if (name === "read_file") {
const { readFile } = await import("fs/promises");
const text = await readFile(args.path, "utf8");
return { content: [{ type: "text", text }] };
}
throw new Error(`Unknown tool: ${name}`);
}),
);
const transport = new StdioServerTransport();
await server.connect(transport);注: 使用Server从@modelcontextprotocol/sdk/server/index.js,不McpServer.McpServer不暴露setRequestHandler.
______________________________________________________________________
政策执行和人工批准
集 enforcePolicy: true 让中间件自动调用后端 check-permission 端点在每次工具调用之前。这将强制执行VouchID仪表板中配置的费率限制、金额限制和人工审批规则,服务器中无需额外代码。
const middleware = new AgentIDMiddleware({
apiUrl: process.env.VOUCHID_API_URL,
apiKey: process.env.VOUCHID_API_KEY,
toolCapabilities: {
read_file: "read:filesystem",
write_file: "write:filesystem",
},
enforcePolicy: true, // enable policy enforcement
approvalTimeoutMs: 300_000, // wait up to 5 min for a human (default)
approvalPollMs: 3_000, // poll every 3 seconds (default)
});
server.setRequestHandler(
CallToolRequestSchema,
middleware.wrap(async (request) => {
// Only reaches here if:
// ✓ token is valid
// ✓ agent has the required capability
// ✓ backend policy allows the action (or a human approved it)
const agent = getAgentIdentity(request);
// ... your tool logic
}),
);当工具调用需要人工批准时,中间件会自动阻止和轮询。呼叫代理保持暂停状态,直到有人在VouchID仪表板中批准或拒绝该操作,或者达到超时时间。
______________________________________________________________________
附加令牌(客户端)
调用代理使用以下命令将其令牌附加到工具参数中 @vouchid/sdk:
import { AgentID } from "@vouchid/sdk";
const vouchid = new AgentID({
apiUrl: process.env.VOUCHID_API_URL,
apiKey: process.env.VOUCHID_API_KEY,
});
const agent = await vouchid.register({
name: "my-bot",
capabilities: ["read:filesystem"],
});
const token = await agent.getToken(); // auto-refreshes near expiry
await mcpClient.callTool({
name: "read_file",
arguments: {
path: "/data/report.csv",
_agentid_token: token, // middleware picks this up and strips it automatically
},
});对于HTTP传输,您也可以将令牌作为 x-agent-token 请求标头。
______________________________________________________________________
API 参考
new AgentIDMiddleware(options)
| 选项 | 类型 | 默认值 | 描述 | |
|---|---|---|---|---|
apiUrl | string | — | 必修的。 您的VouchID后端URL | |
apiKey | string | — | 必修的。 您的组织API密钥。 | |
toolCapabilities | object | {} | 工具名称图→ 所需的功能字符串。未列出的工具只需要有效的令牌。 | |
strict | boolean | true | 拒绝没有令牌的请求。集 false 允许未经身份验证的请求通过。 | |
enforcePolicy | boolean | false | 呼叫 check-permission 自动并处理批准轮询。 | |
approvalTimeoutMs | number | 300000 | 在投掷之前等待人类批准需要多长时间 APPROVAL_TIMEOUT. | |
approvalPollMs | number | 3000 | 轮询审批状态端点的频率。 | |
timeoutMs | number | 8000 | 验证/策略API调用的请求超时。 | |
maxRetries | number | 2 | 重试429/5xx和网络错误。 | |
logger | `object\ | null` | console | 自定义记录器 .warn() 和 .error().通行证 null 沉默。 |
______________________________________________________________________
middleware.wrap(handler)
包装您的MCP请求处理程序。首先运行身份验证(以及可选的策略执行),然后仅在所有检查都通过时调用您的处理程序。
server.setRequestHandler(
CallToolRequestSchema,
middleware.wrap(async (request) => {
// only reached if agent is verified and all policies pass
}),
);______________________________________________________________________
getAgentIdentity(request)
返回请求中附带的已验证代理信息。将其称为包装处理程序。
const agent = getAgentIdentity(request);
agent.id; // "agent_01jk2m3n4p5q6r7s"
agent.name; // "my-data-bot"
agent.org; // "acmecorp"
agent.capabilities; // ["read:filesystem"]
agent.trustLevel; // "verified"
agent.trustScore; // 91退货 null 仅在未提供令牌的非严格模式下。
______________________________________________________________________
错误处理
中间件抛出 AgentIDError 所有的失败。每个错误都有一个 code 用于程序化处理的属性。
| 代码 | 原因 |
|---|---|
MISSING_TOKEN | 请求时没有令牌 strict: true. |
INVALID_TOKEN | 令牌已过期、吊销或格式错误。 |
MISSING_CAPABILITY | 代理缺少此工具所需的功能。 |
POLICY_DENIED | 后端策略拒绝了该操作(速率限制、超出金额等)。 |
APPROVAL_DENIED | 一名人工审查员拒绝了批准请求。 |
APPROVAL_TIMEOUT | 内部无人类反应 approvalTimeoutMs. |
API_ERROR | VouchID后端返回错误。 |
API_UNREACHABLE | 所有重试后都无法访问后端。 |
import { AgentIDError } from "@vouchid/mcp";
server.onerror = (err) => {
if (err instanceof AgentIDError) {
console.error(`[${err.code}] ${err.message}`);
}
};______________________________________________________________________
许可证
麻省理工学院
