@kya-os/mcp-i 1.12.2 → 1.12.4
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/README.md +5 -5
- package/dist/125.js +1 -0
- package/dist/runtime/248.js +1 -0
- package/dist/runtime/adapter-express.js +3 -3
- package/dist/runtime/adapter-nextjs.js +3 -3
- package/dist/runtime/delegation-verifier-agentshield.d.ts +5 -5
- package/dist/runtime/delegation-verifier-agentshield.js +20 -20
- package/dist/runtime/delegation-verifier.d.ts +3 -3
- package/dist/runtime/delegation-verifier.js +1 -1
- package/dist/runtime/http.js +3 -3
- package/dist/runtime/identity.d.ts +26 -0
- package/dist/runtime/identity.js +37 -4
- package/dist/runtime/mcpi-runtime-wrapper.js +4 -2
- package/dist/runtime/mcpi-runtime.d.ts +2 -2
- package/dist/runtime/mcpi-runtime.js +63 -62
- package/dist/runtime/proof-batch-queue.d.ts +3 -3
- package/dist/runtime/proof-batch-queue.js +9 -9
- package/dist/runtime/stdio.js +3 -3
- package/dist/runtime/tool-protection.d.ts +4 -4
- package/dist/runtime/tool-protection.js +5 -5
- package/dist/runtime/utils/tools.js +5 -5
- package/dist/test/examples/test-usage-example.d.ts +8 -8
- package/package.json +3 -3
- package/dist/365.js +0 -1
- package/dist/runtime/160.js +0 -1
|
@@ -75,8 +75,34 @@ export declare class IdentityManager {
|
|
|
75
75
|
/**
|
|
76
76
|
* Generate new development identity
|
|
77
77
|
* Requirements: 4.1, 4.4
|
|
78
|
+
*
|
|
79
|
+
* @param preserveDid - use this DID instead of minting a fresh
|
|
80
|
+
* did:web:localhost:... one. Added for `rotateDevIdentity()`'s benefit
|
|
81
|
+
* (a rotation keeps the agent's DID stable by default); every existing
|
|
82
|
+
* caller passes no argument and is unaffected.
|
|
78
83
|
*/
|
|
79
84
|
private generateDevIdentity;
|
|
85
|
+
/**
|
|
86
|
+
* Force-generate a fresh Ed25519 keypair and persist it to
|
|
87
|
+
* `devIdentityPath`, unconditionally overwriting whatever identity (if
|
|
88
|
+
* any) is already there. Unlike `ensureIdentity()`, which loads an
|
|
89
|
+
* existing valid identity rather than replacing it, this always mints a
|
|
90
|
+
* new one -- the primitive `mcpi keys rotate` needs.
|
|
91
|
+
*
|
|
92
|
+
* Deliberately reuses this same jose-based, 32-byte-raw-key generation
|
|
93
|
+
* path (rather than a second, independent key generator) so a rotated
|
|
94
|
+
* identity file is always in the one format every other identity path in
|
|
95
|
+
* this codebase already produces and consumes -- `CLIIdentityFileSchema`
|
|
96
|
+
* (`packages/contracts/src/cli.ts`) requires exactly 44 base64 characters
|
|
97
|
+
* (a raw 32-byte key), which the CLI's own `node:crypto`-based DER
|
|
98
|
+
* keypair (PKCS8/SPKI, `utils/identity-manager.ts`'s `generateKeyPair`)
|
|
99
|
+
* does not produce -- confirmed by direct inspection, not assumed.
|
|
100
|
+
*
|
|
101
|
+
* @param preserveDid - reuse this DID instead of minting a fresh one,
|
|
102
|
+
* mirroring `mcpi keys rotate`'s own default of keeping the DID stable
|
|
103
|
+
* across a rotation unless `--new-did` is passed.
|
|
104
|
+
*/
|
|
105
|
+
rotateDevIdentity(preserveDid?: string): Promise<AgentIdentity>;
|
|
80
106
|
/**
|
|
81
107
|
* Generate multibase-encoded key identifier (z-prefix base58btc)
|
|
82
108
|
*/
|
package/dist/runtime/identity.js
CHANGED
|
@@ -120,8 +120,13 @@ class IdentityManager {
|
|
|
120
120
|
/**
|
|
121
121
|
* Generate new development identity
|
|
122
122
|
* Requirements: 4.1, 4.4
|
|
123
|
+
*
|
|
124
|
+
* @param preserveDid - use this DID instead of minting a fresh
|
|
125
|
+
* did:web:localhost:... one. Added for `rotateDevIdentity()`'s benefit
|
|
126
|
+
* (a rotation keeps the agent's DID stable by default); every existing
|
|
127
|
+
* caller passes no argument and is unaffected.
|
|
123
128
|
*/
|
|
124
|
-
async generateDevIdentity() {
|
|
129
|
+
async generateDevIdentity(preserveDid) {
|
|
125
130
|
// Generate Ed25519 keypair
|
|
126
131
|
const keyPair = await (0, jose_1.generateKeyPair)("EdDSA", { crv: "Ed25519" });
|
|
127
132
|
// Export keys to JWK format
|
|
@@ -133,10 +138,11 @@ class IdentityManager {
|
|
|
133
138
|
const publicKey = Buffer.from(privateKeyJwk.x, "base64url").toString("base64");
|
|
134
139
|
// Generate multibase-encoded key ID
|
|
135
140
|
const kid = this.generateMultibaseKid(publicKey);
|
|
136
|
-
// Generate DID (for dev, use localhost)
|
|
137
|
-
// Extract a short identifier for the DID path
|
|
141
|
+
// Generate DID (for dev, use localhost), unless the caller wants to
|
|
142
|
+
// keep an existing one. Extract a short identifier for the DID path
|
|
143
|
+
// (first 8 chars of hash for readability).
|
|
138
144
|
const shortId = (0, crypto_1.createHash)("sha256").update(publicKey).digest("hex").substring(0, 8);
|
|
139
|
-
const did = `did:web:localhost:3000:agents:${shortId}`;
|
|
145
|
+
const did = preserveDid ?? `did:web:localhost:3000:agents:${shortId}`;
|
|
140
146
|
const now = new Date().toISOString();
|
|
141
147
|
const identity = {
|
|
142
148
|
did,
|
|
@@ -149,6 +155,33 @@ class IdentityManager {
|
|
|
149
155
|
await this.saveDevIdentity(identity);
|
|
150
156
|
return identity;
|
|
151
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Force-generate a fresh Ed25519 keypair and persist it to
|
|
160
|
+
* `devIdentityPath`, unconditionally overwriting whatever identity (if
|
|
161
|
+
* any) is already there. Unlike `ensureIdentity()`, which loads an
|
|
162
|
+
* existing valid identity rather than replacing it, this always mints a
|
|
163
|
+
* new one -- the primitive `mcpi keys rotate` needs.
|
|
164
|
+
*
|
|
165
|
+
* Deliberately reuses this same jose-based, 32-byte-raw-key generation
|
|
166
|
+
* path (rather than a second, independent key generator) so a rotated
|
|
167
|
+
* identity file is always in the one format every other identity path in
|
|
168
|
+
* this codebase already produces and consumes -- `CLIIdentityFileSchema`
|
|
169
|
+
* (`packages/contracts/src/cli.ts`) requires exactly 44 base64 characters
|
|
170
|
+
* (a raw 32-byte key), which the CLI's own `node:crypto`-based DER
|
|
171
|
+
* keypair (PKCS8/SPKI, `utils/identity-manager.ts`'s `generateKeyPair`)
|
|
172
|
+
* does not produce -- confirmed by direct inspection, not assumed.
|
|
173
|
+
*
|
|
174
|
+
* @param preserveDid - reuse this DID instead of minting a fresh one,
|
|
175
|
+
* mirroring `mcpi keys rotate`'s own default of keeping the DID stable
|
|
176
|
+
* across a rotation unless `--new-did` is passed.
|
|
177
|
+
*/
|
|
178
|
+
async rotateDevIdentity(preserveDid) {
|
|
179
|
+
const identity = await this.generateDevIdentity(preserveDid);
|
|
180
|
+
const rotated = { ...identity, lastRotated: identity.createdAt };
|
|
181
|
+
await this.saveDevIdentity(rotated);
|
|
182
|
+
this.cachedIdentity = rotated;
|
|
183
|
+
return rotated;
|
|
184
|
+
}
|
|
152
185
|
/**
|
|
153
186
|
* Generate multibase-encoded key identifier (z-prefix base58btc)
|
|
154
187
|
*/
|
|
@@ -14,6 +14,7 @@ exports.createMCPIRuntime = createMCPIRuntime;
|
|
|
14
14
|
const mcp_i_runtime_1 = require("@kya-os/mcp-i-runtime");
|
|
15
15
|
const node_providers_1 = require("../providers/node-providers");
|
|
16
16
|
const path_1 = __importDefault(require("path"));
|
|
17
|
+
const config_1 = require("@kya-os/contracts/config");
|
|
17
18
|
/**
|
|
18
19
|
* Convert legacy config to provider-based config
|
|
19
20
|
*/
|
|
@@ -67,8 +68,9 @@ class MCPINodeRuntimeWrapper extends mcp_i_runtime_1.MCPIRuntimeBase {
|
|
|
67
68
|
timestampSkewSeconds: coreConfig.session?.timestampSkewSeconds || 120,
|
|
68
69
|
});
|
|
69
70
|
// Instantiate AccessControlApiService if API key is available
|
|
70
|
-
const
|
|
71
|
-
const
|
|
71
|
+
const checkpoint = (0, config_1.withCheckpointNames)(process.env);
|
|
72
|
+
const apiKey = checkpoint.CHECKPOINT_API_KEY;
|
|
73
|
+
const apiUrl = checkpoint.CHECKPOINT_API_URL || "https://kya.vouched.id";
|
|
72
74
|
if (apiKey) {
|
|
73
75
|
this.accessControlService = new mcp_i_runtime_1.AccessControlApiService({
|
|
74
76
|
baseUrl: apiUrl,
|
|
@@ -47,7 +47,7 @@ export interface MCPIRuntimeConfig {
|
|
|
47
47
|
enabled?: boolean;
|
|
48
48
|
/** Proof batch queue configuration */
|
|
49
49
|
batchQueue?: {
|
|
50
|
-
/** Proof submission destinations (
|
|
50
|
+
/** Proof submission destinations (Checkpoint, KTA, etc.) */
|
|
51
51
|
destinations?: Array<{
|
|
52
52
|
/** Destination type */
|
|
53
53
|
type: "agentshield" | "kta";
|
|
@@ -101,7 +101,7 @@ export interface MCPIRuntimeConfig {
|
|
|
101
101
|
toolProtections?: ToolProtectionMap;
|
|
102
102
|
/** Local tool protection file path (default: tool-protections.json) */
|
|
103
103
|
toolProtectionsFile?: string | false;
|
|
104
|
-
/**
|
|
104
|
+
/** Checkpoint API configuration for fetching tool protections */
|
|
105
105
|
agentShield?: {
|
|
106
106
|
apiUrl: string;
|
|
107
107
|
apiKey?: string;
|
|
@@ -66,7 +66,7 @@ class MCPIRuntime {
|
|
|
66
66
|
this.delegationVerifier = (0, delegation_verifier_1.createDelegationVerifier)(config.delegation.verifier);
|
|
67
67
|
}
|
|
68
68
|
// NOTE: Tool protection resolver is created in initialize()
|
|
69
|
-
// after identity is loaded, because
|
|
69
|
+
// after identity is loaded, because Checkpoint API needs the agent DID
|
|
70
70
|
}
|
|
71
71
|
/**
|
|
72
72
|
* Initialize the runtime (async setup)
|
|
@@ -176,69 +176,70 @@ class MCPIRuntime {
|
|
|
176
176
|
if (!this.cachedIdentity) {
|
|
177
177
|
throw new Error("Runtime not initialized - call initialize() first");
|
|
178
178
|
}
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
if (!this.delegationVerifier) {
|
|
182
|
-
throw new Error("Delegation verifier not configured");
|
|
183
|
-
}
|
|
184
|
-
if (!options.agentDid) {
|
|
185
|
-
throw new Error("agentDid required when requiresDelegation=true");
|
|
186
|
-
}
|
|
187
|
-
const requiredScopes = options.requiredScopes || [];
|
|
188
|
-
// Build auth handshake config
|
|
189
|
-
const authConfig = {
|
|
190
|
-
delegationVerifier: this.delegationVerifier,
|
|
191
|
-
resumeTokenStore: this.resumeTokenStore,
|
|
192
|
-
kta: this.config.delegation.authorization?.kta,
|
|
193
|
-
bouncer: {
|
|
194
|
-
authorizationUrl: this.config.delegation.authorization?.authorizationUrl ||
|
|
195
|
-
"https://agentshield.example.com/consent",
|
|
196
|
-
resumeTokenTtl: this.config.delegation.authorization?.resumeTokenTtl,
|
|
197
|
-
minReputationScore: this.config.delegation.authorization?.minReputationScore,
|
|
198
|
-
requireAuthForUnknown: this.config.delegation.authorization?.requireAuthForUnknown,
|
|
199
|
-
},
|
|
200
|
-
debug: this.config.identity?.environment === "development",
|
|
201
|
-
};
|
|
202
|
-
// Verify delegation or return authorization hints
|
|
203
|
-
const verifyResult = await (0, auth_handshake_1.verifyOrHints)(options.agentDid, requiredScopes, authConfig);
|
|
204
|
-
// If not authorized, return needs_authorization error
|
|
205
|
-
if (!verifyResult.authorized) {
|
|
206
|
-
if (!verifyResult.authError) {
|
|
207
|
-
throw new Error('Authorization failed but no authError was provided');
|
|
208
|
-
}
|
|
209
|
-
return verifyResult.authError;
|
|
210
|
-
}
|
|
211
|
-
// If authorized, capture delegation metadata for outbound header propagation
|
|
212
|
-
if (verifyResult.delegation) {
|
|
213
|
-
options.delegationRef = verifyResult.delegation.id;
|
|
214
|
-
options.delegationChain = (0, mcp_i_core_1.buildChainString)(verifyResult.delegation);
|
|
215
|
-
options.delegationScopes = options.requiredScopes ?? [];
|
|
216
|
-
const identity = await this.identityManager.ensureIdentity();
|
|
217
|
-
(0, request_context_1.setDelegationContextFromIdentity)({
|
|
218
|
-
delegationId: options.delegationRef,
|
|
219
|
-
delegationChain: options.delegationChain,
|
|
220
|
-
delegationScopes: options.delegationScopes,
|
|
221
|
-
// JWS-compact DelegationCredential VC from the verified result —
|
|
222
|
-
// emitted on KYA-OS-Delegation-Credential when the verifier
|
|
223
|
-
// returned a presentable credential.
|
|
224
|
-
delegationCredential: verifyResult.credential?.credential_jwt,
|
|
225
|
-
identity,
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
const auditInvocation = this.config.auditTrail
|
|
230
|
-
? await (0, mcp_i_runtime_1.beginRuntimeToolAudit)(this.config.auditTrail, {
|
|
231
|
-
toolName: request.method,
|
|
232
|
-
args: request.params,
|
|
233
|
-
session: {
|
|
234
|
-
...session,
|
|
235
|
-
id: session.sessionId,
|
|
236
|
-
},
|
|
237
|
-
})
|
|
238
|
-
: undefined;
|
|
239
|
-
// Capture before async boundary — TS narrowing doesn't survive across async callbacks
|
|
179
|
+
// Isolate verification and delegation setup before either can change a caller's
|
|
180
|
+
// context. The tool and proof generation then share this call's own store.
|
|
240
181
|
const cachedIdentity = this.cachedIdentity;
|
|
241
182
|
return (0, request_context_1.runWithContext)({ session: session ?? undefined }, async () => {
|
|
183
|
+
// PHASE 1 INTERCEPTOR: Check delegation BEFORE executing tool
|
|
184
|
+
if (this.config.delegation?.enabled && options.requiresDelegation) {
|
|
185
|
+
if (!this.delegationVerifier) {
|
|
186
|
+
throw new Error("Delegation verifier not configured");
|
|
187
|
+
}
|
|
188
|
+
if (!options.agentDid) {
|
|
189
|
+
throw new Error("agentDid required when requiresDelegation=true");
|
|
190
|
+
}
|
|
191
|
+
const requiredScopes = options.requiredScopes || [];
|
|
192
|
+
// Build auth handshake config
|
|
193
|
+
const authConfig = {
|
|
194
|
+
delegationVerifier: this.delegationVerifier,
|
|
195
|
+
resumeTokenStore: this.resumeTokenStore,
|
|
196
|
+
kta: this.config.delegation.authorization?.kta,
|
|
197
|
+
bouncer: {
|
|
198
|
+
authorizationUrl: this.config.delegation.authorization?.authorizationUrl ||
|
|
199
|
+
"https://agentshield.example.com/consent",
|
|
200
|
+
resumeTokenTtl: this.config.delegation.authorization?.resumeTokenTtl,
|
|
201
|
+
minReputationScore: this.config.delegation.authorization?.minReputationScore,
|
|
202
|
+
requireAuthForUnknown: this.config.delegation.authorization?.requireAuthForUnknown,
|
|
203
|
+
},
|
|
204
|
+
debug: this.config.identity?.environment === "development",
|
|
205
|
+
};
|
|
206
|
+
// Verify delegation or return authorization hints
|
|
207
|
+
const verifyResult = await (0, auth_handshake_1.verifyOrHints)(options.agentDid, requiredScopes, authConfig);
|
|
208
|
+
// If not authorized, return needs_authorization error
|
|
209
|
+
if (!verifyResult.authorized) {
|
|
210
|
+
if (!verifyResult.authError) {
|
|
211
|
+
throw new Error('Authorization failed but no authError was provided');
|
|
212
|
+
}
|
|
213
|
+
return verifyResult.authError;
|
|
214
|
+
}
|
|
215
|
+
// If authorized, capture delegation metadata for outbound header propagation
|
|
216
|
+
if (verifyResult.delegation) {
|
|
217
|
+
options.delegationRef = verifyResult.delegation.id;
|
|
218
|
+
options.delegationChain = (0, mcp_i_core_1.buildChainString)(verifyResult.delegation);
|
|
219
|
+
options.delegationScopes = options.requiredScopes ?? [];
|
|
220
|
+
const identity = await this.identityManager.ensureIdentity();
|
|
221
|
+
(0, request_context_1.setDelegationContextFromIdentity)({
|
|
222
|
+
delegationId: options.delegationRef,
|
|
223
|
+
delegationChain: options.delegationChain,
|
|
224
|
+
delegationScopes: options.delegationScopes,
|
|
225
|
+
// JWS-compact DelegationCredential VC from the verified result —
|
|
226
|
+
// emitted on KYA-OS-Delegation-Credential when the verifier
|
|
227
|
+
// returned a presentable credential.
|
|
228
|
+
delegationCredential: verifyResult.credential?.credential_jwt,
|
|
229
|
+
identity,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const auditInvocation = this.config.auditTrail
|
|
234
|
+
? await (0, mcp_i_runtime_1.beginRuntimeToolAudit)(this.config.auditTrail, {
|
|
235
|
+
toolName: request.method,
|
|
236
|
+
args: request.params,
|
|
237
|
+
session: {
|
|
238
|
+
...session,
|
|
239
|
+
id: session.sessionId,
|
|
240
|
+
},
|
|
241
|
+
})
|
|
242
|
+
: undefined;
|
|
242
243
|
try {
|
|
243
244
|
// Execute the tool (only if delegation check passed)
|
|
244
245
|
let data = await toolHandler(request);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Proof Batch Queue
|
|
3
3
|
*
|
|
4
|
-
* Collects proofs in memory and submits them in batches to KTA and
|
|
4
|
+
* Collects proofs in memory and submits them in batches to KTA and Checkpoint.
|
|
5
5
|
* This prevents blocking tool execution while ensuring proofs are eventually submitted.
|
|
6
6
|
*
|
|
7
7
|
* Performance:
|
|
@@ -74,9 +74,9 @@ export declare class KTAProofDestination implements ProofDestination {
|
|
|
74
74
|
private submitOne;
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
*
|
|
77
|
+
* Checkpoint proof submission destination
|
|
78
78
|
*
|
|
79
|
-
* Submits proofs to
|
|
79
|
+
* Submits proofs to Checkpoint's /api/v1/bouncer/proofs endpoint
|
|
80
80
|
* with proper authentication and session grouping.
|
|
81
81
|
*/
|
|
82
82
|
export declare class AgentShieldProofDestination implements ProofDestination {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Proof Batch Queue
|
|
4
4
|
*
|
|
5
|
-
* Collects proofs in memory and submits them in batches to KTA and
|
|
5
|
+
* Collects proofs in memory and submits them in batches to KTA and Checkpoint.
|
|
6
6
|
* This prevents blocking tool execution while ensuring proofs are eventually submitted.
|
|
7
7
|
*
|
|
8
8
|
* Performance:
|
|
@@ -121,13 +121,13 @@ class KTAProofDestination {
|
|
|
121
121
|
}
|
|
122
122
|
exports.KTAProofDestination = KTAProofDestination;
|
|
123
123
|
/**
|
|
124
|
-
*
|
|
124
|
+
* Checkpoint proof submission destination
|
|
125
125
|
*
|
|
126
|
-
* Submits proofs to
|
|
126
|
+
* Submits proofs to Checkpoint's /api/v1/bouncer/proofs endpoint
|
|
127
127
|
* with proper authentication and session grouping.
|
|
128
128
|
*/
|
|
129
129
|
class AgentShieldProofDestination {
|
|
130
|
-
name = "
|
|
130
|
+
name = "Checkpoint";
|
|
131
131
|
apiUrl;
|
|
132
132
|
apiKey;
|
|
133
133
|
constructor(apiUrl, apiKey) {
|
|
@@ -139,13 +139,13 @@ class AgentShieldProofDestination {
|
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
141
|
const proofs = submissions.map((submission) => submission.proof);
|
|
142
|
-
// Extract session_id from first proof for
|
|
143
|
-
//
|
|
142
|
+
// Extract session_id from first proof for Checkpoint session grouping
|
|
143
|
+
// Checkpoint uses this for analytics and detection monitoring
|
|
144
144
|
const sessionId = proofs[0]?.meta?.sessionId || "unknown";
|
|
145
|
-
//
|
|
145
|
+
// Checkpoint API format requires delegation_id and session_id wrapper
|
|
146
146
|
const requestBody = {
|
|
147
147
|
delegation_id: null, // null for proofs without delegation context
|
|
148
|
-
session_id: sessionId, //
|
|
148
|
+
session_id: sessionId, // Checkpoint session grouping (same as meta.sessionId)
|
|
149
149
|
proofs: proofs,
|
|
150
150
|
};
|
|
151
151
|
const response = await fetch(`${this.apiUrl}/api/v1/bouncer/proofs`, {
|
|
@@ -161,7 +161,7 @@ class AgentShieldProofDestination {
|
|
|
161
161
|
const errorBody = await response
|
|
162
162
|
.text()
|
|
163
163
|
.catch(() => "Unable to read error body");
|
|
164
|
-
throw new Error(`
|
|
164
|
+
throw new Error(`Checkpoint proof submission failed: ${response.status} ${response.statusText}\n${errorBody}`);
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
}
|