@kya-os/mcp-i 1.5.2 → 1.5.3-canary.0
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.
- package/dist/compiler/config/injection.d.ts +5 -1
- package/dist/compiler/config/injection.js +25 -0
- package/dist/compiler/get-webpack-config/get-injected-variables.js +2 -0
- package/dist/runtime/adapter-express.js +1 -1
- package/dist/runtime/adapter-nextjs.js +1 -1
- package/dist/runtime/http.js +1 -1
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +13 -1
- package/dist/runtime/mcpi-runtime.d.ts +15 -0
- package/dist/runtime/mcpi-runtime.js +58 -0
- package/dist/runtime/proof-batch-queue.d.ts +3 -0
- package/dist/runtime/proof-batch-queue.js +21 -4
- package/dist/runtime/proof.d.ts +8 -6
- package/dist/runtime/proof.js +35 -16
- package/dist/runtime/request-context.d.ts +37 -0
- package/dist/runtime/request-context.js +56 -0
- package/dist/runtime/session.js +1 -0
- package/dist/runtime/stdio.js +1 -1
- package/dist/runtime/tool-protection-registry.d.ts +94 -0
- package/dist/runtime/tool-protection-registry.js +140 -0
- package/dist/runtime/tool-protection.d.ts +120 -0
- package/dist/runtime/tool-protection.js +192 -0
- package/dist/runtime/transports/http/index.js +2 -1
- package/dist/runtime/utils/tools.js +293 -76
- package/package.json +1 -1
|
@@ -4,6 +4,15 @@ exports.addToolsToServer = addToolsToServer;
|
|
|
4
4
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
5
5
|
const proof_1 = require("../proof");
|
|
6
6
|
const identity_1 = require("../identity");
|
|
7
|
+
const tool_protection_registry_1 = require("../tool-protection-registry");
|
|
8
|
+
const auth_handshake_1 = require("../auth-handshake");
|
|
9
|
+
const session_1 = require("../session");
|
|
10
|
+
const request_context_1 = require("../request-context");
|
|
11
|
+
const proof_batch_queue_1 = require("../proof-batch-queue");
|
|
12
|
+
// Parse runtime config path from injected variable
|
|
13
|
+
// @ts-expect-error: injected by compiler
|
|
14
|
+
const rawRuntimeConfigPath = typeof RUNTIME_CONFIG_PATH !== "undefined" ? RUNTIME_CONFIG_PATH : undefined;
|
|
15
|
+
const runtimeConfigPath = rawRuntimeConfigPath ? JSON.parse(rawRuntimeConfigPath) : null;
|
|
7
16
|
/** Validates if a value is a valid Zod schema object */
|
|
8
17
|
function isZodRawShape(value) {
|
|
9
18
|
if (typeof value !== "object" || value === null) {
|
|
@@ -24,6 +33,83 @@ function pathToName(path) {
|
|
|
24
33
|
const fileName = path.split("/").pop() || path;
|
|
25
34
|
return fileName.replace(/\.[^/.]+$/, "");
|
|
26
35
|
}
|
|
36
|
+
// Global ProofBatchQueue instance (singleton)
|
|
37
|
+
let globalProofQueue = null;
|
|
38
|
+
/** Load runtime config and initialize ProofBatchQueue if configured */
|
|
39
|
+
async function initializeProofBatchQueue(debug) {
|
|
40
|
+
// Return existing instance if already initialized
|
|
41
|
+
if (globalProofQueue) {
|
|
42
|
+
return globalProofQueue;
|
|
43
|
+
}
|
|
44
|
+
// Check if runtime config path is available
|
|
45
|
+
if (!runtimeConfigPath) {
|
|
46
|
+
if (debug) {
|
|
47
|
+
console.error("[MCPI] No runtime config found - proof submission disabled");
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
if (debug) {
|
|
53
|
+
console.error("[MCPI] Loading runtime config from:", runtimeConfigPath);
|
|
54
|
+
}
|
|
55
|
+
// Dynamically import the runtime config
|
|
56
|
+
const configModule = await import(runtimeConfigPath);
|
|
57
|
+
const runtimeConfig = typeof configModule.getRuntimeConfig === "function"
|
|
58
|
+
? configModule.getRuntimeConfig()
|
|
59
|
+
: configModule.default;
|
|
60
|
+
// Check if proofing is enabled
|
|
61
|
+
if (!runtimeConfig?.proofing?.enabled) {
|
|
62
|
+
if (debug) {
|
|
63
|
+
console.error("[MCPI] Proofing is disabled in runtime config");
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
const proofingConfig = runtimeConfig.proofing;
|
|
68
|
+
if (!proofingConfig.batchQueue?.destinations || proofingConfig.batchQueue.destinations.length === 0) {
|
|
69
|
+
if (debug) {
|
|
70
|
+
console.error("[MCPI] No proof destinations configured");
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
// Create proof destinations
|
|
75
|
+
const destinations = [];
|
|
76
|
+
for (const destConfig of proofingConfig.batchQueue.destinations) {
|
|
77
|
+
if (destConfig.type === "agentshield") {
|
|
78
|
+
destinations.push(new proof_batch_queue_1.AgentShieldProofDestination(destConfig.apiUrl, destConfig.apiKey));
|
|
79
|
+
if (debug) {
|
|
80
|
+
console.error(`[MCPI] Added AgentShield destination: ${destConfig.apiUrl}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Add other destination types here (KTA, etc.)
|
|
84
|
+
}
|
|
85
|
+
if (destinations.length === 0) {
|
|
86
|
+
if (debug) {
|
|
87
|
+
console.error("[MCPI] No valid proof destinations configured");
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
// Initialize ProofBatchQueue
|
|
92
|
+
globalProofQueue = new proof_batch_queue_1.ProofBatchQueue({
|
|
93
|
+
destinations,
|
|
94
|
+
maxBatchSize: proofingConfig.batchQueue.maxBatchSize || 10,
|
|
95
|
+
flushIntervalMs: proofingConfig.batchQueue.flushIntervalMs || 5000,
|
|
96
|
+
maxRetries: proofingConfig.batchQueue.maxRetries || 3,
|
|
97
|
+
debug: proofingConfig.batchQueue.debug || debug,
|
|
98
|
+
});
|
|
99
|
+
if (debug) {
|
|
100
|
+
console.error(`[MCPI] ProofBatchQueue initialized (batch: ${proofingConfig.batchQueue.maxBatchSize}, ` +
|
|
101
|
+
`flush: ${proofingConfig.batchQueue.flushIntervalMs}ms, ` +
|
|
102
|
+
`destinations: ${destinations.length})`);
|
|
103
|
+
}
|
|
104
|
+
return globalProofQueue;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (debug) {
|
|
108
|
+
console.error("[MCPI] Failed to initialize ProofBatchQueue:", error);
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
27
113
|
if (typeof global !== 'undefined') {
|
|
28
114
|
global.__MCPI_HANDLERS_REGISTERED__ = global.__MCPI_HANDLERS_REGISTERED__ || false;
|
|
29
115
|
}
|
|
@@ -44,6 +130,8 @@ async function addToolsToServer(server, toolModules, identityConfig) {
|
|
|
44
130
|
if (identityConfig?.debug) {
|
|
45
131
|
console.error("[MCPI] Registering handlers for the first time");
|
|
46
132
|
}
|
|
133
|
+
// Initialize session manager for handshake support
|
|
134
|
+
const sessionManager = new session_1.SessionManager();
|
|
47
135
|
// Initialize identity manager if identity is enabled
|
|
48
136
|
let identityManager = null;
|
|
49
137
|
if (identityConfig?.enabled) {
|
|
@@ -68,6 +156,8 @@ async function addToolsToServer(server, toolModules, identityConfig) {
|
|
|
68
156
|
// Continue without identity if initialization fails
|
|
69
157
|
}
|
|
70
158
|
}
|
|
159
|
+
// Initialize ProofBatchQueue for proof submission (if runtime config exists)
|
|
160
|
+
await initializeProofBatchQueue(identityConfig?.debug);
|
|
71
161
|
// Collect all tools and their handlers
|
|
72
162
|
const tools = [];
|
|
73
163
|
const toolHandlers = new Map();
|
|
@@ -113,8 +203,12 @@ async function addToolsToServer(server, toolModules, identityConfig) {
|
|
|
113
203
|
});
|
|
114
204
|
// Debug: log server state before registering handler
|
|
115
205
|
if (identityConfig?.debug) {
|
|
116
|
-
console.error("[MCPI] About to register tools/list
|
|
206
|
+
console.error("[MCPI] About to register handlers (tools/list, tools/call)");
|
|
117
207
|
}
|
|
208
|
+
// NOTE: Custom handshake handler removed - it was causing registration errors
|
|
209
|
+
// Handshake/session management should be handled via HTTP headers or custom tools
|
|
210
|
+
// The setRequestHandler expects a Zod schema, not a request object
|
|
211
|
+
// TODO: Implement handshake as a custom tool if needed
|
|
118
212
|
// Register tools/list handler
|
|
119
213
|
server.setRequestHandler(types_js_1.ListToolsRequestSchema, async (request) => {
|
|
120
214
|
return {
|
|
@@ -123,92 +217,215 @@ async function addToolsToServer(server, toolModules, identityConfig) {
|
|
|
123
217
|
});
|
|
124
218
|
// Register tools/call handler
|
|
125
219
|
server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
},
|
|
135
|
-
],
|
|
136
|
-
isError: true,
|
|
137
|
-
};
|
|
220
|
+
// Extract sessionId from request metadata (XMCP-I extension)
|
|
221
|
+
const sessionId = request._meta?.sessionId;
|
|
222
|
+
let session = null;
|
|
223
|
+
if (sessionId) {
|
|
224
|
+
session = await sessionManager.getSession(sessionId);
|
|
225
|
+
if (!session && identityConfig?.debug) {
|
|
226
|
+
console.error(`[MCPI] Session not found or expired: ${sessionId}`);
|
|
227
|
+
}
|
|
138
228
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
229
|
+
// Wrap tool execution with request context for AsyncLocalStorage
|
|
230
|
+
return (0, request_context_1.runWithContext)({
|
|
231
|
+
session: session || undefined,
|
|
232
|
+
requestId: request._meta?.requestId,
|
|
233
|
+
startTime: Date.now(),
|
|
234
|
+
}, async () => {
|
|
235
|
+
const { name, arguments: args } = request.params;
|
|
236
|
+
const handler = toolHandlers.get(name);
|
|
237
|
+
if (!handler) {
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: "text",
|
|
242
|
+
text: `Tool "${name}" not found`,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
isError: true,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
// PHASE 1.5: Check if tool is protected BEFORE executing
|
|
249
|
+
const toolProtection = tool_protection_registry_1.toolProtectionRegistry.get(name);
|
|
250
|
+
if (toolProtection?.requiresDelegation) {
|
|
251
|
+
const authConfig = tool_protection_registry_1.toolProtectionRegistry.getAuthConfig();
|
|
252
|
+
const delegationVerifier = tool_protection_registry_1.toolProtectionRegistry.getDelegationVerifier();
|
|
253
|
+
if (!delegationVerifier || !authConfig) {
|
|
254
|
+
if (identityConfig?.debug) {
|
|
255
|
+
console.error(`[MCPI] Tool "${name}" requires delegation but verifier not configured`);
|
|
256
|
+
}
|
|
257
|
+
return {
|
|
258
|
+
content: [
|
|
259
|
+
{
|
|
260
|
+
type: "text",
|
|
261
|
+
text: `❌ Tool "${name}" requires delegation verification, but the delegation system is not configured.\n\n` +
|
|
262
|
+
`To fix this:\n` +
|
|
263
|
+
`1. Configure a delegation verifier:\n` +
|
|
264
|
+
` - MemoryDelegationVerifier (development/testing)\n` +
|
|
265
|
+
` - KVDelegationVerifier (production with KV store)\n` +
|
|
266
|
+
` - AgentShieldVerifier (production with AgentShield service)\n\n` +
|
|
267
|
+
`2. Enable delegation in your runtime config:\n` +
|
|
268
|
+
` const runtime = new MCPIRuntime({\n` +
|
|
269
|
+
` delegation: { enabled: true },\n` +
|
|
270
|
+
` // ... other config\n` +
|
|
271
|
+
` });\n\n` +
|
|
272
|
+
`3. Ensure the verifier is initialized before tool execution.\n\n` +
|
|
273
|
+
`📚 Documentation: https://docs.mcp-i.dev/delegation/setup`,
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
isError: true,
|
|
176
277
|
};
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
278
|
+
}
|
|
279
|
+
// Extract agent DID from current request context (AsyncLocalStorage)
|
|
280
|
+
const agentDid = (0, request_context_1.getCurrentAgentDid)();
|
|
281
|
+
if (!agentDid) {
|
|
180
282
|
if (identityConfig?.debug) {
|
|
181
|
-
console.error(`[MCPI]
|
|
283
|
+
console.error(`[MCPI] Tool "${name}" requires delegation but no agent DID in session`);
|
|
182
284
|
}
|
|
183
|
-
// Return response with proof metadata
|
|
184
285
|
return {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
286
|
+
content: [
|
|
287
|
+
{
|
|
288
|
+
type: "text",
|
|
289
|
+
text: `❌ Tool "${name}" requires delegation, but no agent identity was found in the current session.\n\n` +
|
|
290
|
+
`This usually means one of the following:\n\n` +
|
|
291
|
+
`1. **No handshake was performed**: The MCP client must send a handshake request before calling protected tools.\n` +
|
|
292
|
+
` Example handshake:\n` +
|
|
293
|
+
` {\n` +
|
|
294
|
+
` "agentDid": "did:key:z6Mk...",\n` +
|
|
295
|
+
` "nonce": "unique-value",\n` +
|
|
296
|
+
` "audience": "server-did",\n` +
|
|
297
|
+
` "timestamp": ${Date.now()}\n` +
|
|
298
|
+
` }\n\n` +
|
|
299
|
+
`2. **Handshake missing agentDid**: Ensure the handshake request includes the 'agentDid' field.\n\n` +
|
|
300
|
+
`3. **Session expired**: The session may have expired. Try performing a new handshake.\n\n` +
|
|
301
|
+
`4. **Request missing sessionId**: Ensure tool call requests include _meta.sessionId from the handshake response.\n\n` +
|
|
302
|
+
`📚 Learn more: https://docs.mcp-i.dev/delegation/handshake`,
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
isError: true,
|
|
189
306
|
};
|
|
190
307
|
}
|
|
191
|
-
|
|
308
|
+
if (identityConfig?.debug) {
|
|
309
|
+
console.error(`[MCPI] Tool "${name}" requires delegation - verifying scopes: ${toolProtection.requiredScopes?.join(', ')}`);
|
|
310
|
+
}
|
|
311
|
+
// Verify delegation
|
|
312
|
+
const verifyResult = await (0, auth_handshake_1.verifyOrHints)(agentDid, toolProtection.requiredScopes || [], authConfig);
|
|
313
|
+
// If not authorized, return needs_authorization error
|
|
314
|
+
if (!verifyResult.authorized) {
|
|
192
315
|
if (identityConfig?.debug) {
|
|
193
|
-
console.error(`[MCPI]
|
|
316
|
+
console.error(`[MCPI] Tool "${name}" blocked - authorization required`);
|
|
194
317
|
}
|
|
195
|
-
// Return
|
|
196
|
-
return
|
|
318
|
+
// Return MCP error format with authorization hints
|
|
319
|
+
return {
|
|
320
|
+
content: [
|
|
321
|
+
{
|
|
322
|
+
type: "text",
|
|
323
|
+
text: JSON.stringify({
|
|
324
|
+
error: "needs_authorization",
|
|
325
|
+
message: `Tool "${name}" requires user authorization`,
|
|
326
|
+
authorizationUrl: verifyResult.authError?.authorizationUrl,
|
|
327
|
+
resumeToken: verifyResult.authError?.resumeToken,
|
|
328
|
+
scopes: verifyResult.authError?.scopes,
|
|
329
|
+
display: verifyResult.authError?.display,
|
|
330
|
+
}),
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
isError: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (identityConfig?.debug) {
|
|
337
|
+
console.error(`[MCPI] Tool "${name}" authorized - executing handler`);
|
|
197
338
|
}
|
|
198
339
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
340
|
+
try {
|
|
341
|
+
// Execute the tool handler
|
|
342
|
+
const result = await handler(args || {});
|
|
343
|
+
// Build base response
|
|
344
|
+
const baseResponse = {
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: typeof result === "string" ? result : JSON.stringify(result),
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
};
|
|
352
|
+
// If identity is enabled, generate cryptographic proof
|
|
353
|
+
if (identityManager) {
|
|
354
|
+
try {
|
|
355
|
+
// Ensure identity exists
|
|
356
|
+
const identity = await identityManager.ensureIdentity();
|
|
357
|
+
// Create a session context for this request
|
|
358
|
+
const toolRequest = {
|
|
359
|
+
method: name,
|
|
360
|
+
params: args || {},
|
|
361
|
+
};
|
|
362
|
+
const toolResponse = {
|
|
363
|
+
data: result,
|
|
364
|
+
};
|
|
365
|
+
// Create a session context for standalone tool calls
|
|
366
|
+
// In a full implementation, this would use proper handshake
|
|
367
|
+
// TODO: Use proper handshake
|
|
368
|
+
const timestamp = Math.floor(Date.now() / 1000);
|
|
369
|
+
const session = {
|
|
370
|
+
sessionId: `tool-${Date.now()}`,
|
|
371
|
+
nonce: `${Date.now()}`,
|
|
372
|
+
audience: "client",
|
|
373
|
+
createdAt: timestamp,
|
|
374
|
+
timestamp,
|
|
375
|
+
lastActivity: timestamp,
|
|
376
|
+
ttlMinutes: 30,
|
|
377
|
+
};
|
|
378
|
+
// Generate proof using the proof generator
|
|
379
|
+
const proofGen = new proof_1.ProofGenerator(identity);
|
|
380
|
+
const proof = await proofGen.generateProof(toolRequest, toolResponse, session);
|
|
381
|
+
if (identityConfig?.debug) {
|
|
382
|
+
console.error(`[MCPI] Generated proof for tool "${name}" - DID: ${proof.meta.did}`);
|
|
383
|
+
}
|
|
384
|
+
// Submit proof to ProofBatchQueue (if initialized)
|
|
385
|
+
if (globalProofQueue) {
|
|
386
|
+
try {
|
|
387
|
+
globalProofQueue.enqueue(proof);
|
|
388
|
+
if (identityConfig?.debug) {
|
|
389
|
+
console.error(`[MCPI] Proof enqueued for submission to AgentShield`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
catch (queueError) {
|
|
393
|
+
if (identityConfig?.debug) {
|
|
394
|
+
console.error(`[MCPI] Failed to enqueue proof:`, queueError);
|
|
395
|
+
}
|
|
396
|
+
// Continue even if queueing fails - proof is still in response
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// Return response with proof metadata
|
|
400
|
+
return {
|
|
401
|
+
...baseResponse,
|
|
402
|
+
_meta: {
|
|
403
|
+
proof,
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
catch (proofError) {
|
|
408
|
+
if (identityConfig?.debug) {
|
|
409
|
+
console.error(`[MCPI] Failed to generate proof for tool "${name}":`, proofError);
|
|
410
|
+
}
|
|
411
|
+
// Return base response without proof if generation fails
|
|
412
|
+
return baseResponse;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return baseResponse;
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
return {
|
|
419
|
+
content: [
|
|
420
|
+
{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: `Error executing tool "${name}": ${error instanceof Error ? error.message : String(error)}`,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
isError: true,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
});
|
|
212
429
|
});
|
|
213
430
|
if (identityConfig?.debug) {
|
|
214
431
|
console.error("[MCPI] Handlers registered successfully");
|