使用说明
@credat/mcp
Trust & authentication layer for MCP servers.
Verify agent identity, delegated permissions, and scopes — before any tool executes.
您的MCP服务器信任任何调用它的代理。应该吗?
现在,MCP服务器无法知道 谁 正在呼叫, 谁授权的,或 _他们被允许做什么_任何代理都可以调用任何工具。
@credat/mcp 修复了3行:
import { CredatAuth } from '@credat/mcp'
const auth = new CredatAuth({
serverDid: 'did:web:api.example.com',
ownerPublicKey: base64urlToUint8Array(process.env.OWNER_PUBLIC_KEY!),
})
auth.install(server) // registers auth tools
然后保护任何工具:
server.registerTool('read-emails', { ... },
auth.protect({ scopes: ['email:read'] }, (args, extra) => {
console.log(`Authorized agent: ${extra.auth.agentDid}`)
// ... your tool logic
})
)
零依赖 · 仅限同行 · 适用于Stdio+HTTP · 全型安全
安装
npm install @credat/mcp @credat/sdk @modelcontextprotocol/sdk
运作原理
@credat/mcp 通过MCP的工具调用接口实现挑战-响应握手:
Agent MCP Server
│ │
│ 1. Call "credat:challenge" │
│ ─────────────────────────────────> │ Generate nonce
│ ← { nonce, timestamp } │
│ │
│ [Sign nonce with delegation] │
│ │
│ 2. Call "credat:authenticate" │
│ ─────────────────────────────────> │ Verify signature + delegation
│ ← { authenticated, scopes } │ Store session
│ │
│ 3. Call "read-emails" │
│ ─────────────────────────────────> │ Check session auth + scopes
│ ← Tool result │ ✓ Execute tool
在幕后,Credat SDK验证:
- 代理使用其私钥(身份证明)对nonce进行签名
- 委托凭据由预期所有者(信任链)颁发
- 凭据尚未过期或被吊销
- 代理具有工具所需的作用域
所有这些都使用标准密码学(ES256/EdDSA)、DID和SD-JWT可验证凭据。
完整示例
服务器
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { base64urlToUint8Array } from '@credat/sdk'
import { z } from 'zod'
import { CredatAuth } from '@credat/mcp'
const server = new McpServer({ name: 'email-service', version: '1.0.0' })
const auth = new CredatAuth({
serverDid: 'did:web:email-service.example.com',
ownerPublicKey: base64urlToUint8Array(process.env.OWNER_PUBLIC_KEY!),
agentPublicKey: base64urlToUint8Array(process.env.AGENT_PUBLIC_KEY!),
})
auth.install(server)
// Public tool — no auth
server.registerTool('health', { description: 'Health check' }, () => ({
content: [{ type: 'text', text: '{"status":"ok"}' }],
}))
// Protected tool — requires scope
server.registerTool('read-emails', {
description: 'Read emails',
inputSchema: z.object({ query: z.string() }),
}, auth.protect({ scopes: ['email:read'] }, (args, extra) => ({
content: [{ type: 'text', text: JSON.stringify({
agent: extra.auth.agentDid,
results: [{ subject: 'Hello', from: 'alice@example.com' }],
})}],
})))
const transport = new StdioServerTransport()
await server.connect(transport)
客户(代理人)
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { presentCredentials } from '@credat/sdk'
import type { ChallengeMessage } from '@credat/sdk'
// After connecting to the MCP server...
// Step 1: Request challenge
const challengeResult = await client.callTool({
name: 'credat:challenge',
arguments: {},
})
const challenge: ChallengeMessage = JSON.parse(challengeResult.content[0].text)
// Step 2: Sign nonce with your delegation
const presentation = await presentCredentials({
challenge,
delegation: myDelegation.token,
agent: myAgentIdentity,
})
// Step 3: Authenticate
const authResult = await client.callTool({
name: 'credat:authenticate',
arguments: { presentation },
})
// Step 4: Call protected tools
const emails = await client.callTool({
name: 'read-emails',
arguments: { query: 'meeting notes' },
})
API 参考
CredatAuth
const auth = new CredatAuth(options: CredatAuthOptions)
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
serverDid |
string |
必需的 | 服务器的DID(质询发出者) |
ownerPublicKey |
Uint8Array |
必需的 | 授权发行人的公钥 |
agentPublicKey |
Uint8Array |
-- | 静态代理密钥(单个代理) |
resolveAgentKey |
(did: string) => Promise<Uint8Array> |
-- | 动态密钥解析(多代理) |
challengeMaxAgeMs |
number |
300000 |
挑战到期(5分钟) |
sessionMaxAgeMs |
number |
3600000 |
会话到期(1小时) |
toolPrefix |
string |
"credat" |
工具名称前缀 |
方法:
auth.install(server)--注册credat:challenge和credat:authenticate工具auth.protect(options, handler)--用auth+作用域检查包裹工具处理程序auth.isAuthenticated(sessionId?)--检查会话是否经过身份验证auth.getSessionAuth(sessionId?)--获取会话的委派结果auth.revokeSession(sessionId?)--强制重新身份验证
protect(options, handler)
auth.protect({
scopes: ['email:read'], // ALL required
anyScope: ['admin', 'email:*'], // ANY required
constraintContext: { domain: 'example.com' }, // Runtime constraints
// or dynamic:
constraintContext: (args) => ({ transactionValue: args.amount }),
}, (args, extra) => {
extra.auth.agentDid // Authenticated agent DID
extra.auth.ownerDid // Owner who issued the delegation
extra.auth.scopes // Granted scopes
extra.auth.constraints // Delegation constraints
return { content: [{ type: 'text', text: 'result' }] }
})
错误代码
身份验证错误作为工具级错误返回(isError: true)因此代理可以解析和反应:
{
"content": [{ "type": "text", "text": "{\"error\":\"...\",\"code\":\"...\"}" }],
"isError": true
}
| 代码 | 何时 |
|---|---|
NOT_AUTHENTICATED |
未找到会话,或挑战已消耗/过期 |
SESSION_EXPIRED |
会话已超出 sessionMaxAgeMs |
SESSION_MISMATCH |
挑战已发送至另一场会议 |
INSUFFICIENT_SCOPES |
代理缺少所需的作用域 |
CONSTRAINT_VIOLATION |
运行时约束检查失败 |
CONFIGURATION_ERROR |
没有 agentPublicKey 或 resolveAgentKey 已配置 |
Credat SDK错误代码(HANDSHAKE_EXPIRED, DELEGATION_EXPIRED等)在验证失败时通过。
安全
- 重放保护:非一次性使用(在验证时消耗)
- 会话绑定:提出要求的会议必然会遇到挑战
- 工具参数中没有标记:身份验证状态是按会话设置的,而不是按请求设置的
- 延迟到期:无计时器--访问时检查TTL
- 加密验证:ES256(P-256)和EdDSA(Ed25519)通过 @贵族/曲线
建立在信誉之上
此套餐是 相信 生态系统——使用DID和可验证凭据的AI代理的信任层。
请参阅 SDK信用 用于:
- 代理身份管理
- 授权证书发放
- 范围和约束系统
- 凭证吊销
许可证
阿帕奇-2.0
