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

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 (35) hide show
  1. package/README.md +43 -20
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +2 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/middleware/index.d.ts +5 -1
  7. package/dist/middleware/index.d.ts.map +1 -1
  8. package/dist/middleware/index.js +5 -1
  9. package/dist/middleware/index.js.map +1 -1
  10. package/dist/middleware/with-mcpi-server.d.ts +62 -0
  11. package/dist/middleware/with-mcpi-server.d.ts.map +1 -0
  12. package/dist/middleware/with-mcpi-server.js +94 -0
  13. package/dist/middleware/with-mcpi-server.js.map +1 -0
  14. package/dist/middleware/with-mcpi.d.ts +25 -10
  15. package/dist/middleware/with-mcpi.d.ts.map +1 -1
  16. package/dist/middleware/with-mcpi.js +21 -7
  17. package/dist/middleware/with-mcpi.js.map +1 -1
  18. package/dist/providers/index.d.ts +1 -0
  19. package/dist/providers/index.d.ts.map +1 -1
  20. package/dist/providers/index.js +1 -0
  21. package/dist/providers/index.js.map +1 -1
  22. package/dist/providers/node-crypto.d.ts +26 -0
  23. package/dist/providers/node-crypto.d.ts.map +1 -0
  24. package/dist/providers/node-crypto.js +69 -0
  25. package/dist/providers/node-crypto.js.map +1 -0
  26. package/package.json +8 -3
  27. package/src/__tests__/integration/mcp-enhance-server.test.ts +311 -0
  28. package/src/__tests__/integration/mcp-transport-context7.test.ts +413 -0
  29. package/src/__tests__/integration/mcp-transport.test.ts +390 -0
  30. package/src/index.ts +5 -0
  31. package/src/middleware/index.ts +10 -1
  32. package/src/middleware/with-mcpi-server.ts +185 -0
  33. package/src/middleware/with-mcpi.ts +35 -13
  34. package/src/providers/index.ts +2 -0
  35. package/src/providers/node-crypto.ts +107 -0
@@ -1,13 +1,18 @@
1
1
  /**
2
- * MCP-I Middleware for @modelcontextprotocol/sdk Server
2
+ * MCP-I Middleware Core Implementation
3
3
  *
4
- * Adds identity, session management, and proof generation to a standard
5
- * MCP SDK Server.
4
+ * Adds identity, session management, and proof generation to MCP servers.
6
5
  *
7
- * Usage:
8
- * const { handshakeTool, registerToolWithProof } = createMCPIMiddleware(config, crypto);
9
- * server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: [handshakeTool, ...] }));
10
- * registerToolWithProof(server, myToolDef, myHandler);
6
+ * For most use cases, prefer the high-level `withMCPI()` adapter from
7
+ * `./with-mcpi-server.ts` which auto-registers the handshake tool and
8
+ * auto-attaches proofs to all tool responses:
9
+ *
10
+ * import { withMCPI } from '@mcp-i/core';
11
+ * await withMCPI(server, { crypto: new NodeCryptoProvider() });
12
+ *
13
+ * `createMCPIMiddleware()` in this file is the lower-level API used
14
+ * internally by `withMCPI()` and for advanced use cases like the
15
+ * low-level `Server` API or custom request handler patterns.
11
16
  */
12
17
 
13
18
  import {
@@ -115,9 +120,11 @@ export interface MCPIToolDefinition {
115
120
  };
116
121
  }
117
122
 
118
- export interface MCPIToolHandler {
123
+ export interface MCPIToolHandler<
124
+ T extends Record<string, unknown> = Record<string, unknown>,
125
+ > {
119
126
  (
120
- args: Record<string, unknown>,
127
+ args: T,
121
128
  sessionId?: string,
122
129
  ): Promise<{
123
130
  content: Array<{ type: string; text: string; [key: string]: unknown }>;
@@ -138,6 +145,9 @@ export interface MCPIServer {
138
145
  }
139
146
 
140
147
  export interface MCPIMiddleware {
148
+ /** The identity config used by this middleware instance */
149
+ identity: MCPIIdentityConfig;
150
+
141
151
  /** The SessionManager instance for manual session operations */
142
152
  sessionManager: SessionManager;
143
153
 
@@ -163,7 +173,10 @@ export interface MCPIMiddleware {
163
173
  * Wrap a tool handler to automatically generate proofs.
164
174
  * Returns a new handler that appends proof metadata to the response.
165
175
  */
166
- wrapWithProof(toolName: string, handler: MCPIToolHandler): MCPIToolHandler;
176
+ wrapWithProof<T extends Record<string, unknown> = Record<string, unknown>>(
177
+ toolName: string,
178
+ handler: MCPIToolHandler<T>,
179
+ ): MCPIToolHandler;
167
180
 
168
181
  /**
169
182
  * Wrap a tool handler to require a valid W3C Delegation Credential.
@@ -253,6 +266,14 @@ function validateScopeAttenuation(
253
266
  /**
254
267
  * Create MCP-I middleware for a standard MCP SDK Server.
255
268
  *
269
+ * For most use cases, prefer {@link withMCPI} from `./with-mcpi-server.ts`
270
+ * which wraps this function and auto-registers handshake + auto-attaches proofs.
271
+ *
272
+ * Use `createMCPIMiddleware` directly when:
273
+ * - You use the low-level `Server` API (not `McpServer`)
274
+ * - You need custom request handler patterns
275
+ * - You want per-tool control over proof/delegation wrapping
276
+ *
256
277
  * @param config - Agent identity and session configuration
257
278
  * @param cryptoProvider - Platform-specific crypto implementation
258
279
  * @returns Middleware components for session management and proof generation
@@ -394,12 +415,12 @@ export function createMCPIMiddleware(
394
415
  return undefined;
395
416
  }
396
417
 
397
- function wrapWithProof(
418
+ function wrapWithProof<T extends Record<string, unknown> = Record<string, unknown>>(
398
419
  toolName: string,
399
- handler: MCPIToolHandler,
420
+ handler: MCPIToolHandler<T>,
400
421
  ): MCPIToolHandler {
401
422
  return async (args: Record<string, unknown>, sessionId?: string) => {
402
- const result = await handler(args, sessionId);
423
+ const result = await handler(args as T, sessionId);
403
424
 
404
425
  if (result.isError) {
405
426
  return result;
@@ -756,6 +777,7 @@ export function createMCPIMiddleware(
756
777
  }
757
778
 
758
779
  return {
780
+ identity: config.identity,
759
781
  sessionManager,
760
782
  proofGenerator,
761
783
  handshakeTool,
@@ -13,3 +13,5 @@ export {
13
13
  MemoryNonceCacheProvider,
14
14
  MemoryIdentityProvider,
15
15
  } from './memory.js';
16
+
17
+ export { NodeCryptoProvider } from './node-crypto.js';
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Node.js CryptoProvider
3
+ *
4
+ * Ed25519 crypto backed by Node.js built-in `node:crypto`.
5
+ * Use this on any Node.js 20+ server.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { NodeCryptoProvider } from '@mcp-i/core/providers';
10
+ * import { withMCPI } from '@mcp-i/core/middleware';
11
+ *
12
+ * await withMCPI(server, { crypto: new NodeCryptoProvider() });
13
+ * ```
14
+ */
15
+
16
+ import {
17
+ createHash,
18
+ createPrivateKey,
19
+ createPublicKey,
20
+ generateKeyPairSync,
21
+ sign,
22
+ verify,
23
+ randomBytes,
24
+ } from "node:crypto";
25
+ import { CryptoProvider } from "./base.js";
26
+
27
+ /** PKCS8 DER header for Ed25519 private keys (16 bytes) */
28
+ const ED25519_PKCS8_PREFIX = Buffer.from(
29
+ "302e020100300506032b657004220420",
30
+ "hex",
31
+ );
32
+
33
+ /** SPKI DER header for Ed25519 public keys (12 bytes) */
34
+ const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
35
+
36
+ export class NodeCryptoProvider extends CryptoProvider {
37
+ async sign(
38
+ data: Uint8Array,
39
+ privateKeyBase64: string,
40
+ ): Promise<Uint8Array> {
41
+ const privateKey = Buffer.from(privateKeyBase64, "base64");
42
+
43
+ // Handle both raw 32-byte and full 64-byte Ed25519 keys
44
+ const keyBytes =
45
+ privateKey.length === 64 ? privateKey.subarray(0, 32) : privateKey;
46
+
47
+ const keyObject = createPrivateKey({
48
+ key: Buffer.concat([ED25519_PKCS8_PREFIX, keyBytes]),
49
+ format: "der",
50
+ type: "pkcs8",
51
+ });
52
+
53
+ return new Uint8Array(sign(null, Buffer.from(data), keyObject));
54
+ }
55
+
56
+ async verify(
57
+ data: Uint8Array,
58
+ signature: Uint8Array,
59
+ publicKeyBase64: string,
60
+ ): Promise<boolean> {
61
+ try {
62
+ const keyObject = createPublicKey({
63
+ key: Buffer.concat([
64
+ ED25519_SPKI_PREFIX,
65
+ Buffer.from(publicKeyBase64, "base64"),
66
+ ]),
67
+ format: "der",
68
+ type: "spki",
69
+ });
70
+
71
+ return verify(
72
+ null,
73
+ Buffer.from(data),
74
+ keyObject,
75
+ Buffer.from(signature),
76
+ );
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ async generateKeyPair(): Promise<{
83
+ privateKey: string;
84
+ publicKey: string;
85
+ }> {
86
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519", {
87
+ publicKeyEncoding: { type: "spki", format: "der" },
88
+ privateKeyEncoding: { type: "pkcs8", format: "der" },
89
+ });
90
+
91
+ return {
92
+ privateKey: (privateKey as Buffer).subarray(16, 48).toString("base64"),
93
+ publicKey: (publicKey as Buffer).subarray(12, 44).toString("base64"),
94
+ };
95
+ }
96
+
97
+ async hash(data: Uint8Array): Promise<string> {
98
+ const hex = createHash("sha256")
99
+ .update(Buffer.from(data))
100
+ .digest("hex");
101
+ return `sha256:${hex}`;
102
+ }
103
+
104
+ async randomBytes(length: number): Promise<Uint8Array> {
105
+ return new Uint8Array(randomBytes(length));
106
+ }
107
+ }