@kya-os/mcp-i-core 1.3.24 → 1.3.26
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/delegation/bitstring.d.ts +2 -1
- package/dist/delegation/bitstring.js +8 -2
- package/dist/identity/user-did-manager.d.ts +3 -2
- package/dist/identity/user-did-manager.js +41 -26
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/runtime/base.d.ts +4 -0
- package/dist/runtime/base.js +21 -12
- package/dist/services/provider-resolver.d.ts +18 -0
- package/dist/services/provider-resolver.js +36 -2
- package/dist/services/session-registration.service.js +4 -2
- package/dist/services/tool-protection.service.d.ts +25 -3
- package/dist/services/tool-protection.service.js +161 -249
- package/dist/types/tool-protection.d.ts +27 -4
- package/dist/utils/base64.d.ts +2 -1
- package/dist/utils/base64.js +58 -24
- package/dist/utils/storage-keys.js +6 -6
- package/package.json +2 -2
|
@@ -111,7 +111,8 @@ export declare class BitstringManager {
|
|
|
111
111
|
*/
|
|
112
112
|
private bytesToBase64;
|
|
113
113
|
/**
|
|
114
|
-
* Convert base64 to bytes
|
|
114
|
+
* Convert base64 or base64url to bytes
|
|
115
|
+
* Handles both standard base64 (+/) and base64url (-_) formats
|
|
115
116
|
*/
|
|
116
117
|
private static base64ToBytes;
|
|
117
118
|
/**
|
|
@@ -161,10 +161,16 @@ class BitstringManager {
|
|
|
161
161
|
return btoa(binary);
|
|
162
162
|
}
|
|
163
163
|
/**
|
|
164
|
-
* Convert base64 to bytes
|
|
164
|
+
* Convert base64 or base64url to bytes
|
|
165
|
+
* Handles both standard base64 (+/) and base64url (-_) formats
|
|
165
166
|
*/
|
|
166
167
|
static base64ToBytes(base64) {
|
|
167
|
-
|
|
168
|
+
// Convert base64url to standard base64 if needed
|
|
169
|
+
let standardBase64 = base64.replace(/-/g, '+').replace(/_/g, '/');
|
|
170
|
+
// Add padding if needed
|
|
171
|
+
const paddingNeeded = (4 - (standardBase64.length % 4)) % 4;
|
|
172
|
+
standardBase64 += '='.repeat(paddingNeeded);
|
|
173
|
+
const binary = atob(standardBase64);
|
|
168
174
|
const bytes = new Uint8Array(binary.length);
|
|
169
175
|
for (let i = 0; i < binary.length; i++) {
|
|
170
176
|
bytes[i] = binary.charCodeAt(i);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* 2. Session storage lookup
|
|
14
14
|
* 3. Return null (session stays anonymous)
|
|
15
15
|
*/
|
|
16
|
-
import { CryptoProvider } from
|
|
16
|
+
import { CryptoProvider } from "../providers/base";
|
|
17
17
|
/**
|
|
18
18
|
* OAuth identity for persistent user DID lookup
|
|
19
19
|
*/
|
|
@@ -208,7 +208,8 @@ export declare class UserDidManager {
|
|
|
208
208
|
*/
|
|
209
209
|
private base58Encode;
|
|
210
210
|
/**
|
|
211
|
-
* Convert base64 string to Uint8Array
|
|
211
|
+
* Convert base64 or base64url string to Uint8Array
|
|
212
|
+
* Handles both standard base64 (+/) and base64url (-_) formats
|
|
212
213
|
*/
|
|
213
214
|
private base64ToBytes;
|
|
214
215
|
/**
|
|
@@ -56,7 +56,7 @@ class UserDidManager {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
|
-
console.warn(
|
|
59
|
+
console.warn("[UserDidManager] OAuth key pair lookup failed:", error);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
// Check session storage if available
|
|
@@ -69,7 +69,7 @@ class UserDidManager {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
catch (error) {
|
|
72
|
-
console.warn(
|
|
72
|
+
console.warn("[UserDidManager] Session key pair lookup failed:", error);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
@@ -96,13 +96,16 @@ class UserDidManager {
|
|
|
96
96
|
return this.sessionDidCache.get(sessionId);
|
|
97
97
|
}
|
|
98
98
|
// PRIORITY 1: If OAuth identity provided, check for persistent user DID mapping
|
|
99
|
-
if (oauthIdentity &&
|
|
99
|
+
if (oauthIdentity &&
|
|
100
|
+
oauthIdentity.provider &&
|
|
101
|
+
oauthIdentity.subject &&
|
|
102
|
+
this.config.storage?.getByOAuth) {
|
|
100
103
|
try {
|
|
101
104
|
const persistentUserDid = await this.config.storage.getByOAuth(oauthIdentity.provider, oauthIdentity.subject);
|
|
102
105
|
if (persistentUserDid) {
|
|
103
|
-
console.
|
|
106
|
+
console.error("[UserDidManager] Found persistent user DID from OAuth mapping:", {
|
|
104
107
|
provider: oauthIdentity.provider,
|
|
105
|
-
userDid: persistentUserDid.substring(0, 20) +
|
|
108
|
+
userDid: persistentUserDid.substring(0, 20) + "...",
|
|
106
109
|
});
|
|
107
110
|
// Cache it for this session
|
|
108
111
|
this.sessionDidCache.set(sessionId, persistentUserDid);
|
|
@@ -113,7 +116,7 @@ class UserDidManager {
|
|
|
113
116
|
}
|
|
114
117
|
catch (error) {
|
|
115
118
|
// Log but continue - DID is cached and will be returned
|
|
116
|
-
console.warn(
|
|
119
|
+
console.warn("[UserDidManager] Failed to cache persistent DID in session storage:", error);
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
return persistentUserDid;
|
|
@@ -121,7 +124,7 @@ class UserDidManager {
|
|
|
121
124
|
}
|
|
122
125
|
catch (error) {
|
|
123
126
|
// Log but continue - will check session storage or generate new DID
|
|
124
|
-
console.warn(
|
|
127
|
+
console.warn("[UserDidManager] OAuth lookup failed, falling back to session storage:", error);
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
// PRIORITY 2: Check session storage if available
|
|
@@ -131,18 +134,21 @@ class UserDidManager {
|
|
|
131
134
|
if (storedDid) {
|
|
132
135
|
this.sessionDidCache.set(sessionId, storedDid);
|
|
133
136
|
// If OAuth identity provided but no persistent mapping found, create one now
|
|
134
|
-
if (oauthIdentity &&
|
|
137
|
+
if (oauthIdentity &&
|
|
138
|
+
oauthIdentity.provider &&
|
|
139
|
+
oauthIdentity.subject &&
|
|
140
|
+
this.config.storage.setByOAuth) {
|
|
135
141
|
try {
|
|
136
142
|
await this.config.storage.setByOAuth(oauthIdentity.provider, oauthIdentity.subject, storedDid, 90 * 24 * 60 * 60 // 90 days TTL for persistent mapping
|
|
137
143
|
);
|
|
138
|
-
console.
|
|
144
|
+
console.error("[UserDidManager] Created persistent OAuth mapping for existing user DID:", {
|
|
139
145
|
provider: oauthIdentity.provider,
|
|
140
|
-
userDid: storedDid.substring(0, 20) +
|
|
146
|
+
userDid: storedDid.substring(0, 20) + "...",
|
|
141
147
|
});
|
|
142
148
|
}
|
|
143
149
|
catch (error) {
|
|
144
150
|
// Log but continue - mapping creation failed, but DID is still valid
|
|
145
|
-
console.warn(
|
|
151
|
+
console.warn("[UserDidManager] Failed to create OAuth mapping:", error);
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
154
|
return storedDid;
|
|
@@ -150,7 +156,7 @@ class UserDidManager {
|
|
|
150
156
|
}
|
|
151
157
|
catch (error) {
|
|
152
158
|
// Log but continue - session will be anonymous
|
|
153
|
-
console.warn(
|
|
159
|
+
console.warn("[UserDidManager] Storage.get failed:", error);
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
// PHASE 5: No ephemeral DID generation - session stays anonymous
|
|
@@ -176,21 +182,23 @@ class UserDidManager {
|
|
|
176
182
|
await this.config.storage.set(sessionId, userDid, 1800); // 30 minutes TTL
|
|
177
183
|
}
|
|
178
184
|
catch (error) {
|
|
179
|
-
console.warn(
|
|
185
|
+
console.warn("[UserDidManager] Failed to store user DID in session storage:", error);
|
|
180
186
|
}
|
|
181
187
|
}
|
|
182
188
|
// Create OAuth mapping if provided
|
|
183
|
-
if (oauthIdentity?.provider &&
|
|
189
|
+
if (oauthIdentity?.provider &&
|
|
190
|
+
oauthIdentity?.subject &&
|
|
191
|
+
this.config.storage?.setByOAuth) {
|
|
184
192
|
try {
|
|
185
193
|
await this.config.storage.setByOAuth(oauthIdentity.provider, oauthIdentity.subject, userDid, 90 * 24 * 60 * 60 // 90 days TTL for persistent mapping
|
|
186
194
|
);
|
|
187
|
-
console.
|
|
195
|
+
console.error("[UserDidManager] Created OAuth → DID mapping:", {
|
|
188
196
|
provider: oauthIdentity.provider,
|
|
189
|
-
userDid: userDid.substring(0, 20) +
|
|
197
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
190
198
|
});
|
|
191
199
|
}
|
|
192
200
|
catch (error) {
|
|
193
|
-
console.warn(
|
|
201
|
+
console.warn("[UserDidManager] Failed to create OAuth mapping:", error);
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
}
|
|
@@ -217,7 +225,7 @@ class UserDidManager {
|
|
|
217
225
|
// Generate did:web (requires web server setup)
|
|
218
226
|
// For now, fall back to did:key
|
|
219
227
|
// TODO: Implement did:web generation if needed
|
|
220
|
-
console.warn(
|
|
228
|
+
console.warn("[UserDidManager] did:web not yet implemented, using did:key");
|
|
221
229
|
}
|
|
222
230
|
// Generate Ed25519 keypair for user DID
|
|
223
231
|
const keyPair = await this.config.crypto.generateKeyPair();
|
|
@@ -258,35 +266,42 @@ class UserDidManager {
|
|
|
258
266
|
* Simple implementation for did:key generation
|
|
259
267
|
*/
|
|
260
268
|
base58Encode(bytes) {
|
|
261
|
-
const alphabet =
|
|
269
|
+
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
262
270
|
let num = BigInt(0);
|
|
263
271
|
// Convert bytes to big integer
|
|
264
272
|
for (let i = 0; i < bytes.length; i++) {
|
|
265
273
|
num = num * BigInt(256) + BigInt(bytes[i]);
|
|
266
274
|
}
|
|
267
275
|
// Convert to base58
|
|
268
|
-
let result =
|
|
276
|
+
let result = "";
|
|
269
277
|
while (num > 0) {
|
|
270
278
|
result = alphabet[Number(num % BigInt(58))] + result;
|
|
271
279
|
num = num / BigInt(58);
|
|
272
280
|
}
|
|
273
281
|
// Add leading zeros
|
|
274
282
|
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
|
|
275
|
-
result =
|
|
283
|
+
result = "1" + result;
|
|
276
284
|
}
|
|
277
285
|
return result;
|
|
278
286
|
}
|
|
279
287
|
/**
|
|
280
|
-
* Convert base64 string to Uint8Array
|
|
288
|
+
* Convert base64 or base64url string to Uint8Array
|
|
289
|
+
* Handles both standard base64 (+/) and base64url (-_) formats
|
|
281
290
|
*/
|
|
282
291
|
base64ToBytes(base64) {
|
|
283
|
-
|
|
292
|
+
// Convert base64url to standard base64 if needed
|
|
293
|
+
// Base64url uses - instead of + and _ instead of /
|
|
294
|
+
let standardBase64 = base64.replace(/-/g, "+").replace(/_/g, "/");
|
|
295
|
+
// Add padding if needed (base64url often omits padding)
|
|
296
|
+
const paddingNeeded = (4 - (standardBase64.length % 4)) % 4;
|
|
297
|
+
standardBase64 += "=".repeat(paddingNeeded);
|
|
298
|
+
if (typeof Buffer !== "undefined") {
|
|
284
299
|
// Node.js environment
|
|
285
|
-
return new Uint8Array(Buffer.from(
|
|
300
|
+
return new Uint8Array(Buffer.from(standardBase64, "base64"));
|
|
286
301
|
}
|
|
287
302
|
else {
|
|
288
303
|
// Browser/Workers environment
|
|
289
|
-
const binaryString = atob(
|
|
304
|
+
const binaryString = atob(standardBase64);
|
|
290
305
|
const bytes = new Uint8Array(binaryString.length);
|
|
291
306
|
for (let i = 0; i < binaryString.length; i++) {
|
|
292
307
|
bytes[i] = binaryString.charCodeAt(i);
|
|
@@ -323,7 +338,7 @@ class UserDidManager {
|
|
|
323
338
|
}
|
|
324
339
|
catch (error) {
|
|
325
340
|
// Log but continue - cache is already cleared
|
|
326
|
-
console.warn(
|
|
341
|
+
console.warn("[UserDidManager] Storage.delete failed, continuing:", error);
|
|
327
342
|
}
|
|
328
343
|
}
|
|
329
344
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export type { OAuthServiceConfig } from "./services/oauth-service";
|
|
|
26
26
|
export { ToolContextBuilder } from "./services/tool-context-builder";
|
|
27
27
|
export type { ToolContextBuilderConfig } from "./services/tool-context-builder";
|
|
28
28
|
export { OAuthProviderRegistry } from "./services/oauth-provider-registry";
|
|
29
|
-
export { ProviderResolver } from "./services/provider-resolver";
|
|
30
|
-
export { ProviderValidator, ProviderValidationError } from "./services/provider-validator";
|
|
29
|
+
export { ProviderResolver, ConsentOnlyModeError, } from "./services/provider-resolver";
|
|
30
|
+
export { ProviderValidator, ProviderValidationError, } from "./services/provider-validator";
|
|
31
31
|
export { OAuthTokenRetrievalService } from "./services/oauth-token-retrieval.service";
|
|
32
32
|
export type { OAuthTokenRetrievalServiceConfig } from "./services/oauth-token-retrieval.service";
|
|
33
33
|
export { BatchDelegationService } from "./services/batch-delegation.service";
|
|
@@ -52,7 +52,7 @@ export { CascadingRevocationManager, createCascadingRevocationManager, type Revo
|
|
|
52
52
|
export { MemoryStatusListStorage } from "./delegation/storage/memory-statuslist-storage";
|
|
53
53
|
export { MemoryDelegationGraphStorage } from "./delegation/storage/memory-graph-storage";
|
|
54
54
|
export { createDidKeyResolver, isEd25519DidKey, extractPublicKeyFromDidKey, publicKeyToJwk, resolveDidKeySync, } from "./delegation/did-key-resolver";
|
|
55
|
-
export { base58Encode, base58Decode, isValidBase58
|
|
55
|
+
export { base58Encode, base58Decode, isValidBase58 } from "./utils/base58";
|
|
56
56
|
export { SchemaVerifier, createSchemaVerifier, type SchemaMetadata, type FieldComplianceResult, type SchemaComplianceReport, type FullComplianceReport, } from "./compliance/schema-verifier";
|
|
57
57
|
export { SCHEMA_REGISTRY, getAllSchemas, getSchemasByCategory, getSchemaById, getCriticalSchemas, getSchemaStats, } from "./compliance/schema-registry";
|
|
58
58
|
export { canonicalizeJSON, createUnsignedVCJWT, completeVCJWT, parseVCJWT, type VCJWTHeader, type VCJWTPayload, type EncodeVCAsJWTOptions, } from "./delegation/utils";
|
package/dist/index.js
CHANGED
|
@@ -20,8 +20,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
20
20
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.
|
|
24
|
-
exports.IdpTokenResolver = exports.UserDidManager = exports.fetchRemoteConfig = exports.bytesToBase64 = exports.base64urlDecodeToString = exports.base64urlDecodeToBytes = exports.base64urlEncodeFromString = exports.base64urlEncodeFromBytes = exports.parseVCJWT = exports.completeVCJWT = exports.createUnsignedVCJWT = exports.canonicalizeJSON = exports.getSchemaStats = exports.getCriticalSchemas = exports.getSchemaById = exports.getSchemasByCategory = exports.getAllSchemas = exports.SCHEMA_REGISTRY = exports.createSchemaVerifier = exports.SchemaVerifier = exports.isValidBase58 = exports.base58Decode = exports.base58Encode = exports.resolveDidKeySync = exports.publicKeyToJwk = exports.extractPublicKeyFromDidKey = exports.isEd25519DidKey = exports.createDidKeyResolver = exports.MemoryDelegationGraphStorage = exports.MemoryStatusListStorage = void 0;
|
|
23
|
+
exports.CascadingRevocationManager = exports.createDelegationGraph = exports.DelegationGraphManager = exports.isIndexSet = exports.BitstringManager = exports.createStatusListManager = exports.StatusList2021Manager = exports.createDelegationVerifier = exports.DelegationCredentialVerifier = exports.createDelegationIssuer = exports.DelegationCredentialIssuer = exports.OAuthRequiredError = exports.DelegationRequiredError = exports.NoOpToolProtectionCache = exports.InMemoryToolProtectionCache = exports.createProofVerificationError = exports.PROOF_VERIFICATION_ERROR_CODES = exports.ProofVerificationError = exports.migrateLegacyKeys = exports.StorageKeyHelpers = exports.createStorageProviders = exports.NoOpOAuthConfigCache = exports.InMemoryOAuthConfigCache = exports.BatchDelegationService = exports.OAuthTokenRetrievalService = exports.ProviderValidationError = exports.ProviderValidator = exports.ConsentOnlyModeError = exports.ProviderResolver = exports.OAuthProviderRegistry = exports.ToolContextBuilder = exports.OAuthService = exports.OAuthConfigService = exports.createSessionRegistrationService = exports.SessionRegistrationService = exports.authorizationMatches = exports.AccessControlApiService = exports.ProofVerifier = exports.CryptoService = exports.ToolProtectionService = exports.MCPIRuntimeBase = exports.MemoryIdentityProvider = exports.MemoryNonceCacheProvider = exports.MemoryStorageProvider = exports.IdentityProvider = exports.NonceCacheProvider = exports.StorageProvider = exports.FetchProvider = exports.ClockProvider = exports.CryptoProvider = void 0;
|
|
24
|
+
exports.IdpTokenResolver = exports.UserDidManager = exports.fetchRemoteConfig = exports.bytesToBase64 = exports.base64urlDecodeToString = exports.base64urlDecodeToBytes = exports.base64urlEncodeFromString = exports.base64urlEncodeFromBytes = exports.parseVCJWT = exports.completeVCJWT = exports.createUnsignedVCJWT = exports.canonicalizeJSON = exports.getSchemaStats = exports.getCriticalSchemas = exports.getSchemaById = exports.getSchemasByCategory = exports.getAllSchemas = exports.SCHEMA_REGISTRY = exports.createSchemaVerifier = exports.SchemaVerifier = exports.isValidBase58 = exports.base58Decode = exports.base58Encode = exports.resolveDidKeySync = exports.publicKeyToJwk = exports.extractPublicKeyFromDidKey = exports.isEd25519DidKey = exports.createDidKeyResolver = exports.MemoryDelegationGraphStorage = exports.MemoryStatusListStorage = exports.createCascadingRevocationManager = void 0;
|
|
25
25
|
// Base providers
|
|
26
26
|
var base_1 = require("./providers/base");
|
|
27
27
|
Object.defineProperty(exports, "CryptoProvider", { enumerable: true, get: function () { return base_1.CryptoProvider; } });
|
|
@@ -72,6 +72,7 @@ Object.defineProperty(exports, "OAuthProviderRegistry", { enumerable: true, get:
|
|
|
72
72
|
// Provider Resolver (Phase 2)
|
|
73
73
|
var provider_resolver_1 = require("./services/provider-resolver");
|
|
74
74
|
Object.defineProperty(exports, "ProviderResolver", { enumerable: true, get: function () { return provider_resolver_1.ProviderResolver; } });
|
|
75
|
+
Object.defineProperty(exports, "ConsentOnlyModeError", { enumerable: true, get: function () { return provider_resolver_1.ConsentOnlyModeError; } });
|
|
75
76
|
// Provider Validator (Phase 3)
|
|
76
77
|
var provider_validator_1 = require("./services/provider-validator");
|
|
77
78
|
Object.defineProperty(exports, "ProviderValidator", { enumerable: true, get: function () { return provider_validator_1.ProviderValidator; } });
|
package/dist/runtime/base.d.ts
CHANGED
|
@@ -190,6 +190,10 @@ export declare class MCPIRuntimeBase {
|
|
|
190
190
|
rotateKeys(): Promise<AgentIdentity>;
|
|
191
191
|
private signData;
|
|
192
192
|
private generateNonce;
|
|
193
|
+
/**
|
|
194
|
+
* Generate a unique session ID
|
|
195
|
+
* Uses mcpi_{uuid} format for MCP protocol compliance and traceability
|
|
196
|
+
*/
|
|
193
197
|
private generateSessionId;
|
|
194
198
|
/**
|
|
195
199
|
* Log structured events in JSON format (NOT frozen audit format).
|
package/dist/runtime/base.js
CHANGED
|
@@ -113,14 +113,14 @@ class MCPIRuntimeBase {
|
|
|
113
113
|
userDid = resolvedDid ?? undefined;
|
|
114
114
|
if (this.config.audit?.enabled) {
|
|
115
115
|
if (userDid) {
|
|
116
|
-
console.
|
|
116
|
+
console.error("[MCP-I] Resolved existing user DID for session:", {
|
|
117
117
|
userDid: userDid.substring(0, 20) + "...",
|
|
118
118
|
hasOAuth: !!oauthIdentity,
|
|
119
119
|
provider: oauthIdentity?.provider,
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
123
|
-
console.
|
|
123
|
+
console.error("[MCP-I] Session started anonymous (no userDid):", {
|
|
124
124
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
125
125
|
hasOAuth: !!oauthIdentity,
|
|
126
126
|
});
|
|
@@ -219,7 +219,7 @@ class MCPIRuntimeBase {
|
|
|
219
219
|
await this.userDidManager.setUserDidForSession(sessionId, userDid, oauthIdentity);
|
|
220
220
|
}
|
|
221
221
|
if (this.config.audit?.enabled) {
|
|
222
|
-
console.
|
|
222
|
+
console.error("[MCP-I] Session identity updated (Phase 5):", {
|
|
223
223
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
224
224
|
userDid: userDid.substring(0, 20) + "...",
|
|
225
225
|
provider: oauthIdentity?.provider,
|
|
@@ -248,7 +248,7 @@ class MCPIRuntimeBase {
|
|
|
248
248
|
// Get agent identity to check protection
|
|
249
249
|
const identity = await this.getIdentity();
|
|
250
250
|
if (this.config.audit?.enabled) {
|
|
251
|
-
console.
|
|
251
|
+
console.error("[MCP-I] Checking tool protection:", {
|
|
252
252
|
tool: toolName,
|
|
253
253
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
254
254
|
hasDelegation: !!(session?.delegationToken || session?.consentProof),
|
|
@@ -312,7 +312,7 @@ class MCPIRuntimeBase {
|
|
|
312
312
|
// Verify delegation token with AccessControlApiService
|
|
313
313
|
try {
|
|
314
314
|
if (this.config.audit?.enabled) {
|
|
315
|
-
console.
|
|
315
|
+
console.error("[MCP-I] 🔐 Verifying delegation token with AccessControlApiService", {
|
|
316
316
|
tool: toolName,
|
|
317
317
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
318
318
|
hasDelegationToken: !!delegationToken,
|
|
@@ -331,7 +331,7 @@ class MCPIRuntimeBase {
|
|
|
331
331
|
if (session?.userDid) {
|
|
332
332
|
verifyRequest.user_did = session.userDid;
|
|
333
333
|
if (this.config.audit?.enabled) {
|
|
334
|
-
console.
|
|
334
|
+
console.error("[MCP-I] 🔐 Including user_did in verification request", {
|
|
335
335
|
userDid: session.userDid.slice(0, 20) + "...",
|
|
336
336
|
});
|
|
337
337
|
}
|
|
@@ -442,7 +442,7 @@ class MCPIRuntimeBase {
|
|
|
442
442
|
}
|
|
443
443
|
// User identifier validation passed (direct match or trusted AgentShield verification)
|
|
444
444
|
if (this.config.audit?.enabled) {
|
|
445
|
-
console.
|
|
445
|
+
console.error("[MCP-I] ✅ User identifier validation PASSED", {
|
|
446
446
|
tool: toolName,
|
|
447
447
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
448
448
|
userDid: sessionUserDid.substring(0, 20) + "...",
|
|
@@ -530,7 +530,7 @@ class MCPIRuntimeBase {
|
|
|
530
530
|
}
|
|
531
531
|
// Authorization method validation passed
|
|
532
532
|
if (this.config.audit?.enabled) {
|
|
533
|
-
console.
|
|
533
|
+
console.error("[MCP-I] ✅ Authorization method validation PASSED", {
|
|
534
534
|
tool: toolName,
|
|
535
535
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
536
536
|
authType: delegationAuth.type,
|
|
@@ -541,7 +541,7 @@ class MCPIRuntimeBase {
|
|
|
541
541
|
// If credential is missing entirely (KV/Memory verifiers), skip validation for backward compatibility
|
|
542
542
|
// Verification succeeded
|
|
543
543
|
if (this.config.audit?.enabled) {
|
|
544
|
-
console.
|
|
544
|
+
console.error("[MCP-I] ✅ Delegation verification SUCCEEDED", {
|
|
545
545
|
tool: toolName,
|
|
546
546
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
547
547
|
delegationId: verificationResult.data.delegation_id,
|
|
@@ -614,7 +614,7 @@ class MCPIRuntimeBase {
|
|
|
614
614
|
else {
|
|
615
615
|
// No protection required - tool can be executed freely
|
|
616
616
|
if (this.config.audit?.enabled) {
|
|
617
|
-
console.
|
|
617
|
+
console.error("[MCP-I] Tool protection check passed (no delegation required)", {
|
|
618
618
|
tool: toolName,
|
|
619
619
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
620
620
|
reason: "Tool not configured to require delegation",
|
|
@@ -1063,9 +1063,18 @@ class MCPIRuntimeBase {
|
|
|
1063
1063
|
const bytes = await this.crypto.randomBytes(32);
|
|
1064
1064
|
return this.bytesToBase64(bytes);
|
|
1065
1065
|
}
|
|
1066
|
+
/**
|
|
1067
|
+
* Generate a unique session ID
|
|
1068
|
+
* Uses mcpi_{uuid} format for MCP protocol compliance and traceability
|
|
1069
|
+
*/
|
|
1066
1070
|
async generateSessionId() {
|
|
1067
1071
|
const bytes = await this.crypto.randomBytes(16);
|
|
1068
|
-
|
|
1072
|
+
// Set version (4) and variant (RFC4122)
|
|
1073
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
|
1074
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
1075
|
+
const hex = this.bytesToHex(bytes);
|
|
1076
|
+
const uuid = `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
1077
|
+
return `mcpi_${uuid}`;
|
|
1069
1078
|
}
|
|
1070
1079
|
/**
|
|
1071
1080
|
* Log structured events in JSON format (NOT frozen audit format).
|
|
@@ -1120,7 +1129,7 @@ class MCPIRuntimeBase {
|
|
|
1120
1129
|
this.config.audit.logFunction(logLine);
|
|
1121
1130
|
}
|
|
1122
1131
|
else {
|
|
1123
|
-
console.
|
|
1132
|
+
console.error("[AUDIT]", logLine);
|
|
1124
1133
|
}
|
|
1125
1134
|
}
|
|
1126
1135
|
createDIDDocument(identity) {
|
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
import type { ToolProtection } from "@kya-os/contracts/tool-protection";
|
|
10
10
|
import type { OAuthProviderRegistry } from "./oauth-provider-registry.js";
|
|
11
11
|
import type { OAuthConfigService } from "./oauth-config.service.js";
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a tool is configured for consent-only mode (no OAuth needed)
|
|
14
|
+
* This is NOT an error condition - it's a signal to skip OAuth flow
|
|
15
|
+
*/
|
|
16
|
+
export declare class ConsentOnlyModeError extends Error {
|
|
17
|
+
readonly toolProtection: ToolProtection;
|
|
18
|
+
constructor(toolProtection: ToolProtection);
|
|
19
|
+
}
|
|
12
20
|
/**
|
|
13
21
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
14
22
|
*
|
|
15
23
|
* Priority order:
|
|
24
|
+
* 0. Check for consent-only mode (authorization.type === 'none') - skip OAuth
|
|
16
25
|
* 1. Tool-specific oauthProvider field (Phase 2+ preferred)
|
|
17
26
|
* 2. Scope prefix inference (fallback)
|
|
18
27
|
* 3. Project-configured provider from AgentShield dashboard
|
|
@@ -22,12 +31,21 @@ export declare class ProviderResolver {
|
|
|
22
31
|
private registry;
|
|
23
32
|
private configService;
|
|
24
33
|
constructor(registry: OAuthProviderRegistry, configService: OAuthConfigService);
|
|
34
|
+
/**
|
|
35
|
+
* Check if a tool is configured for consent-only mode
|
|
36
|
+
* Consent-only means delegation is required but no OAuth provider is needed
|
|
37
|
+
*
|
|
38
|
+
* @param toolProtection - Tool protection configuration
|
|
39
|
+
* @returns true if consent-only mode
|
|
40
|
+
*/
|
|
41
|
+
isConsentOnlyMode(toolProtection: ToolProtection): boolean;
|
|
25
42
|
/**
|
|
26
43
|
* Resolve OAuth provider for a tool
|
|
27
44
|
*
|
|
28
45
|
* @param toolProtection - Tool protection configuration
|
|
29
46
|
* @param projectId - Project ID for fetching provider config
|
|
30
47
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
48
|
+
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
31
49
|
* @throws Error if provider cannot be resolved
|
|
32
50
|
*/
|
|
33
51
|
resolveProvider(toolProtection: ToolProtection, projectId: string): Promise<string>;
|
|
@@ -8,11 +8,25 @@
|
|
|
8
8
|
* @package @kya-os/mcp-i-core
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.ProviderResolver = void 0;
|
|
11
|
+
exports.ProviderResolver = exports.ConsentOnlyModeError = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a tool is configured for consent-only mode (no OAuth needed)
|
|
14
|
+
* This is NOT an error condition - it's a signal to skip OAuth flow
|
|
15
|
+
*/
|
|
16
|
+
class ConsentOnlyModeError extends Error {
|
|
17
|
+
toolProtection;
|
|
18
|
+
constructor(toolProtection) {
|
|
19
|
+
super("Tool is configured for consent-only mode (no OAuth provider required)");
|
|
20
|
+
this.toolProtection = toolProtection;
|
|
21
|
+
this.name = "ConsentOnlyModeError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.ConsentOnlyModeError = ConsentOnlyModeError;
|
|
12
25
|
/**
|
|
13
26
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
14
27
|
*
|
|
15
28
|
* Priority order:
|
|
29
|
+
* 0. Check for consent-only mode (authorization.type === 'none') - skip OAuth
|
|
16
30
|
* 1. Tool-specific oauthProvider field (Phase 2+ preferred)
|
|
17
31
|
* 2. Scope prefix inference (fallback)
|
|
18
32
|
* 3. Project-configured provider from AgentShield dashboard
|
|
@@ -25,15 +39,35 @@ class ProviderResolver {
|
|
|
25
39
|
this.registry = registry;
|
|
26
40
|
this.configService = configService;
|
|
27
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a tool is configured for consent-only mode
|
|
44
|
+
* Consent-only means delegation is required but no OAuth provider is needed
|
|
45
|
+
*
|
|
46
|
+
* @param toolProtection - Tool protection configuration
|
|
47
|
+
* @returns true if consent-only mode
|
|
48
|
+
*/
|
|
49
|
+
isConsentOnlyMode(toolProtection) {
|
|
50
|
+
// Check explicit authorization.type === 'none'
|
|
51
|
+
if (toolProtection.authorization?.type === "none") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
28
56
|
/**
|
|
29
57
|
* Resolve OAuth provider for a tool
|
|
30
58
|
*
|
|
31
59
|
* @param toolProtection - Tool protection configuration
|
|
32
60
|
* @param projectId - Project ID for fetching provider config
|
|
33
61
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
62
|
+
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
34
63
|
* @throws Error if provider cannot be resolved
|
|
35
64
|
*/
|
|
36
65
|
async resolveProvider(toolProtection, projectId) {
|
|
66
|
+
// Priority 0: Check for consent-only mode (no OAuth required)
|
|
67
|
+
if (this.isConsentOnlyMode(toolProtection)) {
|
|
68
|
+
console.error(`[ProviderResolver] Tool is consent-only mode (authorization.type=none), skipping OAuth resolution`);
|
|
69
|
+
throw new ConsentOnlyModeError(toolProtection);
|
|
70
|
+
}
|
|
37
71
|
// Priority 1: Tool-specific provider (Phase 2+ preferred)
|
|
38
72
|
if (toolProtection.oauthProvider) {
|
|
39
73
|
// Ensure registry is loaded before checking
|
|
@@ -54,7 +88,7 @@ class ProviderResolver {
|
|
|
54
88
|
await this.registry.loadFromAgentShield(projectId);
|
|
55
89
|
}
|
|
56
90
|
if (this.registry.hasProvider(inferredProvider)) {
|
|
57
|
-
console.
|
|
91
|
+
console.error(`[ProviderResolver] Inferred provider "${inferredProvider}" from scopes`);
|
|
58
92
|
return inferredProvider;
|
|
59
93
|
}
|
|
60
94
|
}
|
|
@@ -54,7 +54,9 @@ class SessionRegistrationService {
|
|
|
54
54
|
return { success: false, sessionId, error: errorMsg };
|
|
55
55
|
}
|
|
56
56
|
const url = `${this.config.baseUrl}${agentshield_api_1.AGENTSHIELD_ENDPOINTS.SESSIONS}`;
|
|
57
|
-
|
|
57
|
+
// NOTE: This registers the SESSION with AgentShield for dashboard visibility
|
|
58
|
+
// This is NOT the same as agent registration with Know That AI (which happens during deploy)
|
|
59
|
+
this.config.logger("[SessionRegistration] Registering session for dashboard visibility", {
|
|
58
60
|
sessionId,
|
|
59
61
|
agentDid: request.agent_did,
|
|
60
62
|
clientName: request.client_info.name,
|
|
@@ -99,7 +101,7 @@ class SessionRegistrationService {
|
|
|
99
101
|
// Still consider it a success if we got a 200 OK
|
|
100
102
|
return { success: true, sessionId };
|
|
101
103
|
}
|
|
102
|
-
this.config.logger("[SessionRegistration] Session
|
|
104
|
+
this.config.logger("[SessionRegistration] Session now visible in AgentShield dashboard", {
|
|
103
105
|
sessionId,
|
|
104
106
|
registered: parseResult.data.registered,
|
|
105
107
|
});
|
|
@@ -4,11 +4,23 @@
|
|
|
4
4
|
* This service manages tool protection configuration from AgentShield API with
|
|
5
5
|
* efficient caching and automatic synchronization support.
|
|
6
6
|
*
|
|
7
|
+
* CONFIGURATION PRECEDENCE (highest to lowest):
|
|
8
|
+
* ---------------------------------------------
|
|
9
|
+
* 1. Remote config (AgentShield API) - WINS (source of truth)
|
|
10
|
+
* 2. Local config (localConfig) - Base defaults, overridden by remote
|
|
11
|
+
* 3. Framework defaults
|
|
12
|
+
*
|
|
13
|
+
* OFFLINE FALLBACK (only when API unavailable):
|
|
14
|
+
* ---------------------------------------------
|
|
15
|
+
* 1. Stale cache (if allowStaleCache=true and within maxStaleCacheAge)
|
|
16
|
+
* 2. offlineFallbackConfig (explicit offline-only config)
|
|
17
|
+
* 3. failSafeBehavior (deny-all or allow-all)
|
|
18
|
+
*
|
|
7
19
|
* CORE FUNCTIONALITY:
|
|
8
20
|
* -------------------
|
|
9
|
-
* 1. Fetches tool protection config from AgentShield API
|
|
10
|
-
* 2.
|
|
11
|
-
* 3.
|
|
21
|
+
* 1. Fetches tool protection config from AgentShield API (remote wins)
|
|
22
|
+
* 2. Merges with local config (local serves as base defaults)
|
|
23
|
+
* 3. Caches responses with configurable TTL (default 5 minutes)
|
|
12
24
|
* 4. Provides delegation requirement checking before tool execution
|
|
13
25
|
*
|
|
14
26
|
* SYNCHRONIZATION WITH AGENTSHIELD:
|
|
@@ -133,6 +145,16 @@ export declare class ToolProtectionService {
|
|
|
133
145
|
* @param options.bypassCDNCache When true, adds cache-busting to bypass CDN caches (used by clearAndRefresh)
|
|
134
146
|
*/
|
|
135
147
|
private fetchFromApi;
|
|
148
|
+
/**
|
|
149
|
+
* Parse tool protections from API response
|
|
150
|
+
*
|
|
151
|
+
* Extracts tool protection configs from merged config format:
|
|
152
|
+
* { data: { config: { toolProtection: { tools: {...} } } } }
|
|
153
|
+
*
|
|
154
|
+
* @param response API response from AgentShield
|
|
155
|
+
* @returns Record of tool name to ToolProtection
|
|
156
|
+
*/
|
|
157
|
+
private parseToolProtectionsFromResponse;
|
|
136
158
|
/**
|
|
137
159
|
* Clear the cache for a project or agent (useful for testing or manual refresh)
|
|
138
160
|
*
|