@mcp-i/core 1.1.0-canary.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +81 -363
  2. package/dist/auth/handshake.d.ts +19 -4
  3. package/dist/auth/handshake.d.ts.map +1 -1
  4. package/dist/auth/handshake.js +52 -15
  5. package/dist/auth/handshake.js.map +1 -1
  6. package/dist/auth/index.d.ts +1 -1
  7. package/dist/auth/index.d.ts.map +1 -1
  8. package/dist/auth/index.js.map +1 -1
  9. package/dist/delegation/did-key-resolver.d.ts.map +1 -1
  10. package/dist/delegation/did-key-resolver.js +9 -6
  11. package/dist/delegation/did-key-resolver.js.map +1 -1
  12. package/dist/delegation/outbound-headers.d.ts +2 -4
  13. package/dist/delegation/outbound-headers.d.ts.map +1 -1
  14. package/dist/delegation/outbound-headers.js +2 -3
  15. package/dist/delegation/outbound-headers.js.map +1 -1
  16. package/dist/delegation/statuslist-manager.d.ts.map +1 -1
  17. package/dist/delegation/statuslist-manager.js +1 -1
  18. package/dist/delegation/statuslist-manager.js.map +1 -1
  19. package/dist/delegation/vc-verifier.d.ts.map +1 -1
  20. package/dist/delegation/vc-verifier.js +2 -2
  21. package/dist/delegation/vc-verifier.js.map +1 -1
  22. package/dist/errors.d.ts +42 -0
  23. package/dist/errors.d.ts.map +1 -0
  24. package/dist/errors.js +45 -0
  25. package/dist/errors.js.map +1 -0
  26. package/dist/index.d.ts +3 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/middleware/index.d.ts +1 -0
  31. package/dist/middleware/index.d.ts.map +1 -1
  32. package/dist/middleware/index.js +1 -0
  33. package/dist/middleware/index.js.map +1 -1
  34. package/dist/middleware/mcpi-transport.d.ts +39 -0
  35. package/dist/middleware/mcpi-transport.d.ts.map +1 -0
  36. package/dist/middleware/mcpi-transport.js +121 -0
  37. package/dist/middleware/mcpi-transport.js.map +1 -0
  38. package/dist/middleware/with-mcpi-server.d.ts +25 -9
  39. package/dist/middleware/with-mcpi-server.d.ts.map +1 -1
  40. package/dist/middleware/with-mcpi-server.js +62 -47
  41. package/dist/middleware/with-mcpi-server.js.map +1 -1
  42. package/dist/middleware/with-mcpi.d.ts +26 -5
  43. package/dist/middleware/with-mcpi.d.ts.map +1 -1
  44. package/dist/middleware/with-mcpi.js +108 -10
  45. package/dist/middleware/with-mcpi.js.map +1 -1
  46. package/dist/providers/memory.js +2 -2
  47. package/dist/providers/memory.js.map +1 -1
  48. package/dist/session/manager.d.ts +7 -1
  49. package/dist/session/manager.d.ts.map +1 -1
  50. package/dist/session/manager.js +20 -4
  51. package/dist/session/manager.js.map +1 -1
  52. package/dist/utils/crypto-service.d.ts.map +1 -1
  53. package/dist/utils/crypto-service.js +11 -10
  54. package/dist/utils/crypto-service.js.map +1 -1
  55. package/dist/utils/did-helpers.d.ts +12 -0
  56. package/dist/utils/did-helpers.d.ts.map +1 -1
  57. package/dist/utils/did-helpers.js +18 -0
  58. package/dist/utils/did-helpers.js.map +1 -1
  59. package/package.json +3 -3
  60. package/src/__tests__/errors.test.ts +56 -0
  61. package/src/__tests__/integration/full-flow.test.ts +1 -1
  62. package/src/__tests__/integration/mcp-enhance-server.test.ts +48 -5
  63. package/src/__tests__/integration/mcp-transport-context7.test.ts +19 -15
  64. package/src/__tests__/integration/mcp-transport.test.ts +13 -10
  65. package/src/__tests__/providers/base.test.ts +1 -1
  66. package/src/__tests__/providers/memory.test.ts +2 -2
  67. package/src/__tests__/utils/mock-providers.ts +2 -2
  68. package/src/auth/__tests__/handshake.test.ts +190 -0
  69. package/src/auth/handshake.ts +88 -21
  70. package/src/auth/index.ts +1 -0
  71. package/src/delegation/__tests__/did-key-resolver.test.ts +2 -2
  72. package/src/delegation/__tests__/outbound-headers.test.ts +16 -20
  73. package/src/delegation/__tests__/statuslist-manager.test.ts +120 -7
  74. package/src/delegation/__tests__/vc-verifier.test.ts +45 -3
  75. package/src/delegation/did-key-resolver.ts +11 -6
  76. package/src/delegation/outbound-headers.ts +1 -4
  77. package/src/delegation/statuslist-manager.ts +3 -1
  78. package/src/delegation/vc-verifier.ts +3 -2
  79. package/src/errors.ts +65 -0
  80. package/src/index.ts +10 -0
  81. package/src/middleware/__tests__/mcpi-transport.test.ts +150 -0
  82. package/src/middleware/__tests__/with-mcpi-server.test.ts +117 -0
  83. package/src/middleware/__tests__/with-mcpi.test.ts +124 -6
  84. package/src/middleware/index.ts +6 -0
  85. package/src/middleware/mcpi-transport.ts +162 -0
  86. package/src/middleware/with-mcpi-server.ts +83 -92
  87. package/src/middleware/with-mcpi.ts +147 -11
  88. package/src/proof/__tests__/errors.test.ts +79 -0
  89. package/src/proof/__tests__/verifier.test.ts +5 -5
  90. package/src/providers/memory.ts +2 -2
  91. package/src/session/__tests__/session-manager.test.ts +3 -3
  92. package/src/session/manager.ts +28 -6
  93. package/src/utils/crypto-service.ts +11 -10
  94. package/src/utils/did-helpers.ts +19 -0
package/README.md CHANGED
@@ -1,413 +1,131 @@
1
- # @mcp-i/core
2
-
3
- **MCP-I protocol reference implementation** — delegation, proof, and session for the Model Context Protocol Identity (MCP-I) standard.
4
-
5
- > This package is a [DIF TAAWG](https://identity.foundation/working-groups/agent-and-authorization.html) protocol reference implementation donated to the Decentralized Identity Foundation.
6
-
7
- ---
8
-
9
- ## What is MCP-I?
10
-
11
- MCP-I (Model Context Protocol Identity) is a protocol extension for the Model Context Protocol (MCP) that adds cryptographic identity, delegation chains, and non-repudiation proofs to AI agent interactions. It enables MCP servers to verify *who* is calling (agent DID), *on whose behalf* (user delegation), and *what* was done (signed proof). Delegations are issued as W3C Verifiable Credentials with Ed25519 signatures, revocation is tracked via StatusList2021, and every tool call produces a detached JWS proof for audit trails.
12
-
13
- Spec: [modelcontextprotocol-identity.io](https://modelcontextprotocol-identity.io)
14
-
15
- ---
16
-
17
- ## What this package provides
18
-
19
- | Module | Description |
20
- |--------|-------------|
21
- | **delegation** | W3C VC delegation issuance (`DelegationCredentialIssuer`), verification (`DelegationCredentialVerifier`), graph management, StatusList2021 revocation, cascading revocation, outbound delegation propagation (`buildOutboundDelegationHeaders`), DID:key resolution (`createDidKeyResolver`), and DID:web resolution (`createDidWebResolver`) |
22
- | **proof** | Platform-agnostic proof generation (`ProofGenerator`) and server-side verification (`ProofVerifier`) — JCS canonicalization, SHA-256 hashing, Ed25519 JWS signing/verification |
23
- | **session** | Handshake validation and session management (`SessionManager`) with nonce replay prevention |
24
- | **auth** | Authorization handshake orchestration (`verifyOrHints`) — checks delegation and returns authorization hints |
25
- | **middleware** | MCP SDK integration — `withMCPI(server, { crypto })` adds identity, sessions, and auto-proof to any `McpServer` in one call. Lower-level `createMCPIMiddleware` available for the `Server` API. |
26
- | **providers** | Abstract base classes (`CryptoProvider`, `StorageProvider`, etc.) and in-memory implementations for testing |
27
- | **types** | Pure TypeScript interfaces for all protocol types — zero runtime dependencies |
1
+ <p align="center">
2
+ <a href="https://modelcontextprotocol-identity.io">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://modelcontextprotocol-identity.io/images/logo-mark_white.svg">
5
+ <img alt="MCP-I" src="https://modelcontextprotocol-identity.io/images/logo-mark_black.svg" width="64">
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
10
+ <p align="center">
11
+ <strong>Identity, delegation, and proof for the Model Context Protocol.</strong>
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/@mcp-i/core"><img src="https://img.shields.io/npm/v/@mcp-i/core" alt="npm"></a>
16
+ <a href="https://modelcontextprotocol-identity.io"><img src="https://img.shields.io/badge/spec-modelcontextprotocol--identity.io-blue" alt="spec"></a>
17
+ <a href="https://identity.foundation/working-groups/agent-and-authorization.html"><img src="https://img.shields.io/badge/DIF-TAAWG-purple" alt="DIF TAAWG"></a>
18
+ <a href="./LICENSE"><img src="https://img.shields.io/github/license/modelcontextprotocol-identity/mcp-i-core" alt="license"></a>
19
+ </p>
28
20
 
29
21
  ---
30
22
 
31
- ## Quick Start
23
+ AI agents call tools on your behalf. But today, there's no way to know *who* called, *whether they were allowed to*, or *what actually happened*. MCP-I fixes that.
32
24
 
33
- The fastest way to see MCP-I in action is the example server:
25
+ - **Every server gets a cryptographic identity** (DID) no accounts, no API keys, no central registry
26
+ - **Every tool call gets a signed proof** — a tamper-evident receipt the agent can't forge or deny
27
+ - **Protected tools require human consent** — per-tool authorization via W3C Delegation Credentials
28
+ - **The AI never knows** — identity, proofs, and consent happen transparently in the protocol layer
34
29
 
35
- ```bash
36
- git clone https://github.com/modelcontextprotocol-identity/mcp-i-core.git
37
- cd mcp-i-core
38
- pnpm install
39
- npx tsx examples/node-server/server.ts
40
30
  ```
41
-
42
- This starts an MCP server on stdio with identity handshake and proof generation. See [`examples/node-server/README.md`](./examples/node-server/README.md) for details.
43
-
44
- For outbound delegation propagation (forwarding delegation context to downstream services), see [`examples/outbound-delegation/README.md`](./examples/outbound-delegation/README.md).
45
-
46
- ---
47
-
48
- ## Installation
49
-
50
- ```bash
51
31
  npm install @mcp-i/core
52
32
  ```
53
33
 
54
34
  ---
55
35
 
56
- ## Delegation Hardening Compatibility
57
-
58
- MCP-I now enforces strict delegation-chain and status-list checks by default.
36
+ ## Migrate Any MCP Server in 2 Lines
59
37
 
60
- - Credentials with `parentId` require `delegation.resolveDelegationChain`.
61
- - Credentials with `credentialStatus` require `delegation.statusListResolver`.
62
-
63
- For temporary migration support in legacy integrations, you can opt in to compatibility mode:
38
+ **Before** a standard MCP server with no identity or proofs:
64
39
 
65
40
  ```typescript
66
- await withMCPI(server, {
67
- crypto: new NodeCryptoProvider(),
68
- delegation: {
69
- allowLegacyUnsafeDelegation: true, // temporary compatibility escape hatch
70
- },
71
- });
72
- ```
73
-
74
- Compatibility mode weakens security guarantees and should only be used during migration.
75
-
76
- ---
77
-
78
- ## Examples
41
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
79
42
 
80
- > Requires Node.js 20+. Save each block as `example.ts` and run with `npx tsx example.ts`.
43
+ const server = new McpServer({ name: 'my-server', version: '1.0.0' });
81
44
 
82
- ---
45
+ server.registerTool('greet', { description: 'Say hello' }, async (args) => ({
46
+ content: [{ type: 'text', text: `Hello, ${args.name}!` }],
47
+ }));
48
+ ```
83
49
 
84
- ## Example 1 Issue a Delegation VC
50
+ **After** every tool response now carries a signed cryptographic proof:
85
51
 
86
52
  ```typescript
87
- import {
88
- createHash,
89
- createPrivateKey,
90
- generateKeyPairSync,
91
- randomBytes,
92
- sign as nodeSign,
93
- } from 'node:crypto';
94
- import {
95
- CryptoProvider,
96
- MemoryIdentityProvider,
97
- DelegationCredentialIssuer,
98
- type DelegationIdentityProvider,
99
- type VCSigningFunction,
100
- } from '@mcp-i/core';
101
-
102
- // Minimal Node.js CryptoProvider backed by node:crypto
103
- class NodeCryptoProvider extends CryptoProvider {
104
- async generateKeyPair() {
105
- const { privateKey: pk, publicKey: pub } = generateKeyPairSync('ed25519', {
106
- privateKeyEncoding: { type: 'pkcs8', format: 'der' },
107
- publicKeyEncoding: { type: 'spki', format: 'der' },
108
- });
109
- // Ed25519 PKCS8 DER: 16-byte header + 32-byte seed
110
- // Ed25519 SPKI DER: 12-byte header + 32-byte public key
111
- return {
112
- privateKey: (pk as Buffer).subarray(16).toString('base64'),
113
- publicKey: (pub as Buffer).subarray(12).toString('base64'),
114
- };
115
- }
116
- async hash(data: Uint8Array) {
117
- return 'sha256:' + createHash('sha256').update(data).digest('hex');
118
- }
119
- async randomBytes(n: number) { return new Uint8Array(randomBytes(n)); }
120
- async sign(data: Uint8Array, privateKeyBase64: string) {
121
- const seed = Buffer.from(privateKeyBase64, 'base64').subarray(0, 32);
122
- const hdr = Buffer.from([
123
- 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05,
124
- 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
125
- ]);
126
- const key = createPrivateKey({ key: Buffer.concat([hdr, seed]), format: 'der', type: 'pkcs8' });
127
- return new Uint8Array(nodeSign(null, data, key));
128
- }
129
- async verify(): Promise<boolean> { throw new Error('unused'); }
130
- }
131
-
132
- const cryptoProvider = new NodeCryptoProvider();
133
-
134
- // MemoryIdentityProvider generates a real Ed25519 DID + key pair
135
- const identityProvider = new MemoryIdentityProvider(cryptoProvider);
136
- const agent = await identityProvider.getIdentity();
137
-
138
- // Adapt AgentIdentity to the DelegationIdentityProvider interface
139
- const identity: DelegationIdentityProvider = {
140
- getDid: () => agent.did,
141
- getKeyId: () => agent.kid,
142
- getPrivateKey: () => agent.privateKey,
143
- };
144
-
145
- // Real Ed25519 signing function — delegates to NodeCryptoProvider
146
- const signingFunction: VCSigningFunction = async (canonicalVC, issuerDid) => {
147
- const sig = await cryptoProvider.sign(
148
- new TextEncoder().encode(canonicalVC),
149
- agent.privateKey
150
- );
151
- return {
152
- type: 'Ed25519Signature2020',
153
- created: new Date().toISOString(),
154
- verificationMethod: `${issuerDid}#key-1`,
155
- proofPurpose: 'assertionMethod',
156
- proofValue: Buffer.from(sig).toString('base64url'),
157
- };
158
- };
159
-
160
- const issuer = new DelegationCredentialIssuer(identity, signingFunction);
161
-
162
- const vc = await issuer.createAndIssueDelegation({
163
- id: 'delegation-001',
164
- issuerDid: agent.did,
165
- subjectDid: 'did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuias8sisDArDJF74t',
166
- constraints: {
167
- scopes: ['tool:execute', 'resource:read'],
168
- notAfter: Math.floor(Date.now() / 1000) + 3600,
169
- },
170
- });
171
-
172
- console.log('VC type:', vc.type);
173
- // ['VerifiableCredential', 'DelegationCredential']
174
- console.log('Scopes:', vc.credentialSubject.delegation.constraints.scopes);
175
- // ['tool:execute', 'resource:read']
176
- console.log('Proof type:', vc.proof?.type);
177
- // 'Ed25519Signature2020'
178
- ```
179
-
180
- ---
53
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
54
+ import { withMCPI, NodeCryptoProvider } from '@mcp-i/core'; // +1 line
181
55
 
182
- ## Example 2 Session Handshake
56
+ const server = new McpServer({ name: 'my-server', version: '1.0.0' });
57
+ await withMCPI(server, { crypto: new NodeCryptoProvider() }); // +1 line
183
58
 
184
- ```typescript
185
- import { randomBytes } from 'node:crypto';
186
- import {
187
- CryptoProvider,
188
- SessionManager,
189
- MemoryNonceCacheProvider,
190
- createHandshakeRequest,
191
- } from '@mcp-i/core';
192
-
193
- // SessionManager only needs randomBytes() for session ID generation
194
- class NodeCryptoProvider extends CryptoProvider {
195
- async randomBytes(n: number) { return new Uint8Array(randomBytes(n)); }
196
- async generateKeyPair(): Promise<{ privateKey: string; publicKey: string }> { throw new Error('unused'); }
197
- async hash(_: Uint8Array): Promise<string> { throw new Error('unused'); }
198
- async sign(_: Uint8Array, __: string): Promise<Uint8Array> { throw new Error('unused'); }
199
- async verify(_: Uint8Array, __: Uint8Array, ___: string): Promise<boolean> { throw new Error('unused'); }
200
- }
201
-
202
- const cryptoProvider = new NodeCryptoProvider();
203
- const nonceCache = new MemoryNonceCacheProvider();
204
-
205
- const sessionManager = new SessionManager(cryptoProvider, {
206
- nonceCache,
207
- sessionTtlMinutes: 30,
208
- timestampSkewSeconds: 120,
209
- serverDid: 'did:web:my-mcp-server.example.com',
210
- });
211
-
212
- // Client: build handshake request (uses globalThis.crypto, built into Node 20+)
213
- const request = createHandshakeRequest('did:web:my-mcp-server.example.com');
214
- request.agentDid = 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK';
215
-
216
- // Server: validate it
217
- const result = await sessionManager.validateHandshake(request);
218
-
219
- if (result.success && result.session) {
220
- console.log('Session ID:', result.session.sessionId);
221
- // e.g. 'mcpi_4f3e2a1b-c7d2-4e5f-b6a3-...'
222
- console.log('Audience:', result.session.audience);
223
- // 'did:web:my-mcp-server.example.com'
224
- console.log('Agent DID:', result.session.agentDid);
225
- // 'did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK'
226
- }
227
- console.log('Stats:', sessionManager.getStats());
228
- // { activeSessions: 1, config: { sessionTtlMinutes: 30, ... } }
59
+ server.registerTool('greet', { description: 'Say hello' }, async (args) => ({
60
+ content: [{ type: 'text', text: `Hello, ${args.name}!` }],
61
+ }));
229
62
  ```
230
63
 
231
- ---
64
+ That's it. `withMCPI` auto-generates an Ed25519 identity, registers the `_mcpi` protocol tool, and wraps the transport so every tool response includes a detached JWS proof in `_meta` — invisible to the LLM, verifiable by anyone.
232
65
 
233
- ## Example 3Generate a Tool Call Proof
234
-
235
- ```typescript
236
- import {
237
- createHash,
238
- createPrivateKey,
239
- generateKeyPairSync,
240
- randomBytes,
241
- sign as nodeSign,
242
- } from 'node:crypto';
243
- import {
244
- CryptoProvider,
245
- MemoryIdentityProvider,
246
- ProofGenerator,
247
- SessionManager,
248
- } from '@mcp-i/core';
249
-
250
- class NodeCryptoProvider extends CryptoProvider {
251
- async generateKeyPair() {
252
- const { privateKey: pk, publicKey: pub } = generateKeyPairSync('ed25519', {
253
- privateKeyEncoding: { type: 'pkcs8', format: 'der' },
254
- publicKeyEncoding: { type: 'spki', format: 'der' },
255
- });
256
- return {
257
- privateKey: (pk as Buffer).subarray(16).toString('base64'),
258
- publicKey: (pub as Buffer).subarray(12).toString('base64'),
259
- };
260
- }
261
- async hash(data: Uint8Array) {
262
- return 'sha256:' + createHash('sha256').update(data).digest('hex');
263
- }
264
- async randomBytes(n: number) { return new Uint8Array(randomBytes(n)); }
265
- async sign(data: Uint8Array, privateKeyBase64: string) {
266
- const seed = Buffer.from(privateKeyBase64, 'base64').subarray(0, 32);
267
- const hdr = Buffer.from([
268
- 0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05,
269
- 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22, 0x04, 0x20,
270
- ]);
271
- const key = createPrivateKey({ key: Buffer.concat([hdr, seed]), format: 'der', type: 'pkcs8' });
272
- return new Uint8Array(nodeSign(null, data, key));
273
- }
274
- async verify(): Promise<boolean> { throw new Error('unused'); }
275
- }
276
-
277
- const cryptoProvider = new NodeCryptoProvider();
278
-
279
- // MemoryIdentityProvider generates a fresh DID + Ed25519 key pair
280
- const identityProvider = new MemoryIdentityProvider(cryptoProvider);
281
- const agent = await identityProvider.getIdentity();
282
-
283
- // ProofGenerator signs tool call request+response pairs with the agent's key
284
- const generator = new ProofGenerator(agent, cryptoProvider);
285
-
286
- const request = {
287
- method: 'tools/call',
288
- params: { name: 'read_file', arguments: { path: '/etc/hosts' } },
289
- };
290
- const response = { data: { content: '127.0.0.1 localhost' } };
291
-
292
- // SessionContext — in production this comes from SessionManager.validateHandshake()
293
- const session = {
294
- sessionId: 'mcpi_demo-session',
295
- audience: 'did:web:my-mcp-server.example.com',
296
- nonce: SessionManager.generateNonce(),
297
- timestamp: Math.floor(Date.now() / 1000),
298
- createdAt: Math.floor(Date.now() / 1000),
299
- lastActivity: Math.floor(Date.now() / 1000),
300
- ttlMinutes: 30,
301
- identityState: 'anonymous' as const,
302
- };
303
-
304
- const proof = await generator.generateProof(request, response, session);
305
-
306
- console.log('JWS (first 40 chars):', proof.jws.slice(0, 40) + '...');
307
- // 'eyJhbGciOiJFZERTQSIsImtpZCI6ImRpZDprZXk...'
308
- console.log('Request hash:', proof.meta.requestHash);
309
- // 'sha256:e3b0...'
310
- console.log('Agent DID:', proof.meta.did);
311
- // 'did:key:z...'
312
- ```
66
+ > See the full working example: [examples/context7-with-mcpi](./examples/context7-with-mcpi/) — a real MCP server (Context7) migrated with exactly 2 lines of code.
313
67
 
314
68
  ---
315
69
 
316
- ## Example 4 — MCP Server with Middleware (`withMCPI`)
70
+ ## Protect Tools with Human Consent
317
71
 
318
- The recommended way to add MCP-I to any `McpServer`-based server:
72
+ Some tools shouldn't run without a human saying "yes." MCP-I adds per-tool authorization using W3C Verifiable Credentials:
319
73
 
320
74
  ```typescript
321
- import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
322
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
323
- import { withMCPI, CryptoProvider } from '@mcp-i/core';
324
- import { z } from 'zod';
325
-
326
- // ... (NodeCryptoProvider as above)
327
-
328
- const server = new McpServer({ name: 'my-server', version: '1.0.0' });
329
-
330
- // One call: auto-generates identity, registers handshake, auto-proofs all tools
331
- await withMCPI(server, { crypto: new NodeCryptoProvider() });
332
-
333
- // Register tools normally — proofs are attached automatically
334
- server.registerTool(
335
- 'echo',
336
- { description: 'Echo a message', inputSchema: { message: z.string() } },
337
- async ({ message }) => ({ content: [{ type: 'text' as const, text: `Echo: ${message}` }] }),
75
+ const checkout = mcpi.wrapWithDelegation(
76
+ 'checkout',
77
+ { scopeId: 'cart:write', consentUrl: 'https://example.com/consent' },
78
+ mcpi.wrapWithProof('checkout', async (args) => ({
79
+ content: [{ type: 'text', text: `Order placed: ${args.item}` }],
80
+ })),
338
81
  );
339
-
340
- await server.connect(new StdioServerTransport());
341
82
  ```
342
83
 
343
- <details>
344
- <summary>Low-level Server API (advanced)</summary>
345
-
346
- For the low-level `Server` API with manual request handlers, use `createMCPIMiddleware` directly:
347
-
348
- ```typescript
349
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
350
- import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
351
- import { createMCPIMiddleware, generateIdentity } from '@mcp-i/core';
84
+ When an agent calls `checkout` without a delegation credential, it gets back a `needs_authorization` response with a consent URL. The human approves, a scoped credential is issued, and the agent retries — now authorized.
352
85
 
353
- const crypto = new NodeCryptoProvider();
354
- const identity = await generateIdentity(crypto);
86
+ > Try it yourself: [examples/consent-basic](./examples/consent-basic/) walks through the full consent flow end-to-end.
355
87
 
356
- const mcpi = createMCPIMiddleware({ identity, session: { sessionTtlMinutes: 60 } }, crypto);
357
-
358
- const echo = mcpi.wrapWithProof('echo', async (args) => ({
359
- content: [{ type: 'text', text: `Echo: ${args['message']}` }],
360
- }));
361
-
362
- const server = new Server({ name: 'my-server', version: '1.0.0' }, { capabilities: { tools: {} } });
363
-
364
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
365
- tools: [mcpi.handshakeTool, { name: 'echo', inputSchema: { type: 'object' } }],
366
- }));
88
+ ---
367
89
 
368
- server.setRequestHandler(CallToolRequestSchema, async (req) => {
369
- if (req.params.name === '_mcpi_handshake') return mcpi.handleHandshake(req.params.arguments ?? {});
370
- if (req.params.name === 'echo') return echo(req.params.arguments ?? {});
371
- return { content: [{ type: 'text', text: 'Unknown tool' }], isError: true };
372
- });
90
+ ## See It in Action
373
91
 
374
- await server.connect(new StdioServerTransport());
92
+ ```bash
93
+ git clone https://github.com/modelcontextprotocol-identity/mcp-i-core.git
94
+ cd mcp-i-core && npm install
95
+ bash scripts/demo.sh
375
96
  ```
376
97
 
377
- </details>
98
+ This starts all example servers and opens [MCP Inspector](https://github.com/modelcontextprotocol/inspector). Connect to any server, call a tool, and inspect the proof in `_meta`:
378
99
 
379
- ---
100
+ | Port | Example | What it demonstrates |
101
+ |------|---------|---------------------|
102
+ | 3001 | [node-server](./examples/node-server/) | Proofs + restricted tools (low-level API) |
103
+ | 3002 | [consent-basic](./examples/consent-basic/) | Human consent flow with built-in UI |
104
+ | 3003 | [consent-full](./examples/consent-full/) | Production consent UI ([@kya-os/consent](https://www.npmjs.com/package/@kya-os/consent)) |
105
+ | 3004 | [context7-with-mcpi](./examples/context7-with-mcpi/) | 2-line migration of a real MCP server |
380
106
 
381
- ## Example 5 Verify a Proof with DID:key Resolution
107
+ Also available: [outbound-delegation](./examples/outbound-delegation/) (gateway pattern), [verify-proof](./examples/verify-proof/) (standalone verification), [statuslist](./examples/statuslist/) (revocation lifecycle).
382
108
 
383
- ```typescript
384
- import { ProofVerifier, createDidKeyResolver, CryptoProvider } from '@mcp-i/core';
385
-
386
- // ... (NodeCryptoProvider with verify + hash)
387
-
388
- const crypto = new NodeCryptoProvider();
389
- const didResolver = createDidKeyResolver();
109
+ ---
390
110
 
391
- const verifier = new ProofVerifier({
392
- cryptoProvider: crypto,
393
- fetchPublicKeyFromDID: async (did) => {
394
- const result = didResolver(did);
395
- return result?.publicKeyJwk ?? null;
396
- },
397
- timestampSkewSeconds: 120,
398
- });
111
+ ## What's Under the Hood
399
112
 
400
- const result = await verifier.verifyProofDetached(proof);
401
- console.log('Valid:', result.valid);
402
- ```
113
+ | Capability | How it works |
114
+ |-----------|-------------|
115
+ | **Cryptographic identity** | Ed25519 key pairs, `did:key` and `did:web` resolution |
116
+ | **Signed proofs** | Detached JWS over JCS-canonicalized request/response hashes |
117
+ | **Delegation credentials** | W3C Verifiable Credentials with scope constraints |
118
+ | **Revocation** | StatusList2021 bitstring with cascading revocation |
119
+ | **Replay prevention** | Nonce-based handshake with timestamp skew validation |
120
+ | **Extensible** | Bring your own KMS, HSM, nonce cache (Redis, DynamoDB, KV), or DID method |
403
121
 
404
122
  ---
405
123
 
406
- ## License
124
+ ## Links
407
125
 
408
- MIT see [LICENSE](./LICENSE)
126
+ - [Spec](https://modelcontextprotocol-identity.io) | [DIF TAAWG](https://identity.foundation/working-groups/agent-and-authorization.html) | [npm](https://www.npmjs.com/package/@mcp-i/core)
127
+ - [CONTRIBUTING.md](./CONTRIBUTING.md) | [CONFORMANCE.md](./CONFORMANCE.md) | [SECURITY.md](./SECURITY.md) | [GOVERNANCE.md](./GOVERNANCE.md)
409
128
 
410
- ---
129
+ ## License
411
130
 
412
- *This package is a DIF TAAWG protocol reference implementation.*
413
- *Spec: [modelcontextprotocol-identity.io](https://modelcontextprotocol-identity.io)*
131
+ MIT
@@ -15,12 +15,20 @@ import type { DelegationVerifier, VerifyDelegationResult } from './types.js';
15
15
  export type { DelegationVerifier, VerifyDelegationResult };
16
16
  export interface AgentReputation {
17
17
  agentDid: string;
18
- score: number;
18
+ score: number | null;
19
19
  totalInteractions: number;
20
20
  successRate: number;
21
21
  riskLevel: 'low' | 'medium' | 'high' | 'unknown';
22
22
  updatedAt: number;
23
23
  }
24
+ /**
25
+ * Policy for handling agents with no reputation history.
26
+ *
27
+ * - 'deny' — reject unknown agents outright (strict environments)
28
+ * - 'require-consent' — route to the consent/authorization flow (default)
29
+ * - 'allow' — let unknown agents through (reputation is advisory only)
30
+ */
31
+ export type UnknownAgentPolicy = 'deny' | 'require-consent' | 'allow';
24
32
  export interface AuthHandshakeConfig {
25
33
  delegationVerifier: DelegationVerifier;
26
34
  resumeTokenStore: ResumeTokenStore;
@@ -32,7 +40,15 @@ export interface AuthHandshakeConfig {
32
40
  authorization: {
33
41
  authorizationUrl: string;
34
42
  resumeTokenTtl?: number;
35
- requireAuthForUnknown?: boolean;
43
+ /**
44
+ * How to handle agents with no reputation history (404 from reputation
45
+ * service, network error, or first-time agent).
46
+ *
47
+ * - 'deny' — reject outright
48
+ * - 'require-consent' — route to consent flow (default)
49
+ * - 'allow' — skip reputation gate for unknowns
50
+ */
51
+ unknownAgentPolicy?: UnknownAgentPolicy;
36
52
  minReputationScore?: number;
37
53
  };
38
54
  debug?: boolean;
@@ -96,9 +112,8 @@ export declare class MemoryResumeTokenStore implements ResumeTokenStore {
96
112
  * @param agentDid - The agent's DID to verify
97
113
  * @param scopes - Required scopes for the operation
98
114
  * @param config - Authorization configuration including verifier, token store, etc.
99
- * @param _resumeToken - Optional resume token from previous authorization attempt
100
115
  * @returns Result indicating authorization status, delegation, or auth hints
101
116
  */
102
- export declare function verifyOrHints(agentDid: string, scopes: string[], config: AuthHandshakeConfig, _resumeToken?: string): Promise<VerifyOrHintsResult>;
117
+ export declare function verifyOrHints(agentDid: string, scopes: string[], config: AuthHandshakeConfig): Promise<VerifyOrHintsResult>;
103
118
  export declare function hasSensitiveScopes(scopes: string[]): boolean;
104
119
  //# sourceMappingURL=handshake.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/auth/handshake.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handshake.d.ts","sourceRoot":"","sources":["../../src/auth/handshake.ts"],"names":[],"mappings":"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"}