@kya-os/mcp-i-core 1.4.1 → 1.4.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/dist/index.d.ts +4 -3
- package/dist/index.js +8 -2
- package/dist/logging/index.d.ts +5 -0
- package/dist/logging/index.js +10 -0
- package/dist/logging/logger.d.ts +33 -0
- package/dist/logging/logger.js +104 -0
- package/dist/services/access-control.service.js +2 -2
- package/dist/services/oauth-service.d.ts +46 -1
- package/dist/services/oauth-service.js +74 -5
- package/dist/services/provider-resolver.d.ts +23 -0
- package/dist/services/provider-resolver.js +43 -2
- package/dist/services/tool-context-builder.js +13 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -21,12 +21,12 @@ export { SessionRegistrationService, createSessionRegistrationService, } from ".
|
|
|
21
21
|
export type { SessionRegistrationServiceConfig, SessionRegistrationResult, } from "./services/session-registration.service";
|
|
22
22
|
export { OAuthConfigService } from "./services/oauth-config.service";
|
|
23
23
|
export type { OAuthConfigServiceConfig } from "./services/oauth-config.service";
|
|
24
|
-
export { OAuthService } from "./services/oauth-service";
|
|
25
|
-
export type { OAuthServiceConfig } from "./services/oauth-service";
|
|
24
|
+
export { OAuthService, defaultSecretResolver } from "./services/oauth-service";
|
|
25
|
+
export type { OAuthServiceConfig, SecretResolver } 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, ConsentOnlyModeError, } from "./services/provider-resolver";
|
|
29
|
+
export { ProviderResolver, ConsentOnlyModeError, CredentialAuthModeError, } from "./services/provider-resolver";
|
|
30
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";
|
|
@@ -68,4 +68,5 @@ export type { UserDidStorage, UserDidManagerConfig, UserKeyPair, OAuthIdentity,
|
|
|
68
68
|
export { IdpTokenResolver } from "./identity/idp-token-resolver";
|
|
69
69
|
export type { IdpTokenResolverConfig } from "./identity/idp-token-resolver";
|
|
70
70
|
export type { IIdpTokenStorage, TokenUsageMetadata, IdpTokensWithMetadata, } from "./identity/idp-token-storage.interface";
|
|
71
|
+
export { logger, createDefaultConsoleLogger, type Logger, type Level, } from "./logging";
|
|
71
72
|
//# sourceMappingURL=index.d.ts.map
|
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 = exports.createCascadingRevocationManager = exports.CascadingRevocationManager = exports.createDelegationGraph = void 0;
|
|
23
|
+
exports.BitstringManager = exports.createStatusListManager = exports.StatusList2021Manager = exports.createDelegationVerifier = exports.DelegationCredentialVerifier = exports.createDelegationIssuer = exports.DelegationCredentialIssuer = exports.createDelegationErrorFormatter = exports.DelegationErrorFormatter = 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.CredentialAuthModeError = exports.ConsentOnlyModeError = exports.ProviderResolver = exports.OAuthProviderRegistry = exports.ToolContextBuilder = exports.defaultSecretResolver = 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.createDefaultConsoleLogger = exports.logger = 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 = exports.CascadingRevocationManager = exports.createDelegationGraph = exports.DelegationGraphManager = exports.isIndexSet = 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; } });
|
|
@@ -63,6 +63,7 @@ Object.defineProperty(exports, "OAuthConfigService", { enumerable: true, get: fu
|
|
|
63
63
|
// OAuth Service (Phase 1)
|
|
64
64
|
var oauth_service_1 = require("./services/oauth-service");
|
|
65
65
|
Object.defineProperty(exports, "OAuthService", { enumerable: true, get: function () { return oauth_service_1.OAuthService; } });
|
|
66
|
+
Object.defineProperty(exports, "defaultSecretResolver", { enumerable: true, get: function () { return oauth_service_1.defaultSecretResolver; } });
|
|
66
67
|
// Tool Context Builder (Phase 1)
|
|
67
68
|
var tool_context_builder_1 = require("./services/tool-context-builder");
|
|
68
69
|
Object.defineProperty(exports, "ToolContextBuilder", { enumerable: true, get: function () { return tool_context_builder_1.ToolContextBuilder; } });
|
|
@@ -73,6 +74,7 @@ Object.defineProperty(exports, "OAuthProviderRegistry", { enumerable: true, get:
|
|
|
73
74
|
var provider_resolver_1 = require("./services/provider-resolver");
|
|
74
75
|
Object.defineProperty(exports, "ProviderResolver", { enumerable: true, get: function () { return provider_resolver_1.ProviderResolver; } });
|
|
75
76
|
Object.defineProperty(exports, "ConsentOnlyModeError", { enumerable: true, get: function () { return provider_resolver_1.ConsentOnlyModeError; } });
|
|
77
|
+
Object.defineProperty(exports, "CredentialAuthModeError", { enumerable: true, get: function () { return provider_resolver_1.CredentialAuthModeError; } });
|
|
76
78
|
// Provider Validator (Phase 3)
|
|
77
79
|
var provider_validator_1 = require("./services/provider-validator");
|
|
78
80
|
Object.defineProperty(exports, "ProviderValidator", { enumerable: true, get: function () { return provider_validator_1.ProviderValidator; } });
|
|
@@ -180,4 +182,8 @@ Object.defineProperty(exports, "UserDidManager", { enumerable: true, get: functi
|
|
|
180
182
|
// IDP Token Resolver (Phase 1 - MH-7, updated for CRED-003)
|
|
181
183
|
var idp_token_resolver_1 = require("./identity/idp-token-resolver");
|
|
182
184
|
Object.defineProperty(exports, "IdpTokenResolver", { enumerable: true, get: function () { return idp_token_resolver_1.IdpTokenResolver; } });
|
|
185
|
+
// Logger (transport-aware logging)
|
|
186
|
+
var logging_1 = require("./logging");
|
|
187
|
+
Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return logging_1.logger; } });
|
|
188
|
+
Object.defineProperty(exports, "createDefaultConsoleLogger", { enumerable: true, get: function () { return logging_1.createDefaultConsoleLogger; } });
|
|
183
189
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logging module exports
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createDefaultConsoleLogger = exports.logger = void 0;
|
|
7
|
+
var logger_1 = require("./logger");
|
|
8
|
+
Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return logger_1.logger; } });
|
|
9
|
+
Object.defineProperty(exports, "createDefaultConsoleLogger", { enumerable: true, get: function () { return logger_1.createDefaultConsoleLogger; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-aware Logger for MCP-I
|
|
3
|
+
*
|
|
4
|
+
* Provides a lightweight, dependency-free logging interface that:
|
|
5
|
+
* - Maps log levels correctly for Cloudflare Workers (so wrangler tail shows correct severities)
|
|
6
|
+
* - Routes all logs to stderr for stdio transport (so stdout remains protocol-only)
|
|
7
|
+
* - Supports runtime configuration of log level and transport mode
|
|
8
|
+
*/
|
|
9
|
+
export type Level = "debug" | "info" | "warn" | "error";
|
|
10
|
+
export interface Logger {
|
|
11
|
+
configure: (opts?: {
|
|
12
|
+
level?: Level;
|
|
13
|
+
transport?: string;
|
|
14
|
+
forceStderr?: boolean;
|
|
15
|
+
}) => void;
|
|
16
|
+
debug: (...args: any[]) => void;
|
|
17
|
+
info: (...args: any[]) => void;
|
|
18
|
+
warn: (...args: any[]) => void;
|
|
19
|
+
error: (...args: any[]) => void;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a default console logger with transport-aware behavior
|
|
23
|
+
*
|
|
24
|
+
* - Cloudflare Workers: Maps levels to console.debug/info/warn/error
|
|
25
|
+
* - stdio transport: Routes all levels to console.error (stderr) to keep stdout protocol-only
|
|
26
|
+
*/
|
|
27
|
+
export declare function createDefaultConsoleLogger(): Logger;
|
|
28
|
+
/**
|
|
29
|
+
* Default singleton logger instance
|
|
30
|
+
* Configure via logger.configure() before use
|
|
31
|
+
*/
|
|
32
|
+
export declare const logger: Logger;
|
|
33
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Transport-aware Logger for MCP-I
|
|
4
|
+
*
|
|
5
|
+
* Provides a lightweight, dependency-free logging interface that:
|
|
6
|
+
* - Maps log levels correctly for Cloudflare Workers (so wrangler tail shows correct severities)
|
|
7
|
+
* - Routes all logs to stderr for stdio transport (so stdout remains protocol-only)
|
|
8
|
+
* - Supports runtime configuration of log level and transport mode
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.logger = void 0;
|
|
12
|
+
exports.createDefaultConsoleLogger = createDefaultConsoleLogger;
|
|
13
|
+
const SEVERITY = {
|
|
14
|
+
debug: 10,
|
|
15
|
+
info: 20,
|
|
16
|
+
warn: 30,
|
|
17
|
+
error: 40,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Create a default console logger with transport-aware behavior
|
|
21
|
+
*
|
|
22
|
+
* - Cloudflare Workers: Maps levels to console.debug/info/warn/error
|
|
23
|
+
* - stdio transport: Routes all levels to console.error (stderr) to keep stdout protocol-only
|
|
24
|
+
*/
|
|
25
|
+
function createDefaultConsoleLogger() {
|
|
26
|
+
let level = "info";
|
|
27
|
+
let forceStderr = false;
|
|
28
|
+
function shouldLog(l) {
|
|
29
|
+
return SEVERITY[l] >= SEVERITY[level];
|
|
30
|
+
}
|
|
31
|
+
function write(l, ...args) {
|
|
32
|
+
if (!shouldLog(l))
|
|
33
|
+
return;
|
|
34
|
+
if (forceStderr) {
|
|
35
|
+
// stdio transport: all logs go to stderr (console.error)
|
|
36
|
+
// This ensures stdout remains protocol-only
|
|
37
|
+
console.error(...args);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Cloudflare Workers: map to appropriate console method
|
|
41
|
+
switch (l) {
|
|
42
|
+
case "debug":
|
|
43
|
+
if (typeof console.debug === "function") {
|
|
44
|
+
console.debug(...args);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.log(...args);
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
case "info":
|
|
51
|
+
if (typeof console.info === "function") {
|
|
52
|
+
console.info(...args);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
console.log(...args);
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
case "warn":
|
|
59
|
+
if (typeof console.warn === "function") {
|
|
60
|
+
console.warn(...args);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.error(...args);
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
case "error":
|
|
67
|
+
console.error(...args);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
configure(opts = {}) {
|
|
73
|
+
if (opts.level)
|
|
74
|
+
level = opts.level;
|
|
75
|
+
// Handle transport-based stderr routing
|
|
76
|
+
// - stdio transport: force all logs to stderr (stdout is protocol-only)
|
|
77
|
+
// - sse/http transports: use normal console routing
|
|
78
|
+
// - explicit forceStderr overrides transport-based behavior
|
|
79
|
+
if (opts.forceStderr === true) {
|
|
80
|
+
forceStderr = true;
|
|
81
|
+
}
|
|
82
|
+
else if (opts.forceStderr === false) {
|
|
83
|
+
forceStderr = false;
|
|
84
|
+
}
|
|
85
|
+
else if (opts.transport === "stdio") {
|
|
86
|
+
forceStderr = true;
|
|
87
|
+
}
|
|
88
|
+
else if (opts.transport === "sse" || opts.transport === "http") {
|
|
89
|
+
// Reset to normal console routing for non-stdio transports
|
|
90
|
+
forceStderr = false;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
debug: (...a) => write("debug", ...a),
|
|
94
|
+
info: (...a) => write("info", ...a),
|
|
95
|
+
warn: (...a) => write("warn", ...a),
|
|
96
|
+
error: (...a) => write("error", ...a),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Default singleton logger instance
|
|
101
|
+
* Configure via logger.configure() before use
|
|
102
|
+
*/
|
|
103
|
+
exports.logger = createDefaultConsoleLogger();
|
|
104
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -708,8 +708,8 @@ function authorizationMatches(delegationAuth, toolAuth) {
|
|
|
708
708
|
return true;
|
|
709
709
|
return delegationAuth.provider === toolAuth.provider;
|
|
710
710
|
}
|
|
711
|
-
// For
|
|
712
|
-
if (delegationAuth.type === "
|
|
711
|
+
// For verifiable_credential, compare credentialType
|
|
712
|
+
if (delegationAuth.type === "verifiable_credential") {
|
|
713
713
|
// If tool doesn't specify a credential type, any type is acceptable
|
|
714
714
|
if (!toolAuth.credentialType)
|
|
715
715
|
return true;
|
|
@@ -4,11 +4,39 @@
|
|
|
4
4
|
* Handles OAuth token exchange and refresh using PKCE (Proof Key for Code Exchange).
|
|
5
5
|
* Supports both direct PKCE exchange with OAuth providers and proxy mode via AgentShield.
|
|
6
6
|
*
|
|
7
|
+
* MCP-I Provider Registry Model:
|
|
8
|
+
* - Client secrets are NOT sent via the API response
|
|
9
|
+
* - Agents receive metadata.clientSecretName from AgentShield
|
|
10
|
+
* - Secrets are resolved at runtime from secure storage (Cloudflare Worker env)
|
|
11
|
+
* - The SecretResolver interface enables platform-specific secret resolution
|
|
12
|
+
*
|
|
7
13
|
* @package @kya-os/mcp-i-core
|
|
8
14
|
*/
|
|
9
15
|
import type { FetchProvider } from "../providers/base.js";
|
|
10
16
|
import type { OAuthConfigService } from "./oauth-config.service.js";
|
|
11
|
-
import type { IdpTokens } from "@kya-os/contracts/config";
|
|
17
|
+
import type { IdpTokens, OAuthProvider } from "@kya-os/contracts/config";
|
|
18
|
+
/**
|
|
19
|
+
* Secret Resolver Interface
|
|
20
|
+
*
|
|
21
|
+
* Platform-specific interface for resolving secrets from secure storage.
|
|
22
|
+
* Implementations:
|
|
23
|
+
* - Cloudflare Workers: Resolves from env bindings
|
|
24
|
+
* - Node.js: Resolves from process.env
|
|
25
|
+
* - Other platforms: Custom implementations
|
|
26
|
+
*/
|
|
27
|
+
export interface SecretResolver {
|
|
28
|
+
/**
|
|
29
|
+
* Resolve a secret by its name from secure storage
|
|
30
|
+
* @param secretName - The name/key of the secret (e.g., 'KYA_PROD_MYPROJ_GITHUB_CLIENT_SECRET')
|
|
31
|
+
* @returns The secret value, or undefined if not found
|
|
32
|
+
*/
|
|
33
|
+
resolveSecret(secretName: string): Promise<string | undefined>;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Default secret resolver that uses environment variables
|
|
37
|
+
* Used when no custom resolver is provided
|
|
38
|
+
*/
|
|
39
|
+
export declare const defaultSecretResolver: SecretResolver;
|
|
12
40
|
export interface OAuthServiceConfig {
|
|
13
41
|
/** OAuth config service for fetching provider configurations */
|
|
14
42
|
configService: OAuthConfigService;
|
|
@@ -22,6 +50,12 @@ export interface OAuthServiceConfig {
|
|
|
22
50
|
projectId: string;
|
|
23
51
|
/** Optional logger callback for diagnostics */
|
|
24
52
|
logger?: (message: string, data?: unknown) => void;
|
|
53
|
+
/**
|
|
54
|
+
* Optional secret resolver for MCP-I provider-registry model
|
|
55
|
+
* Used to resolve client secrets from secure storage
|
|
56
|
+
* Default: Uses environment variables (process.env)
|
|
57
|
+
*/
|
|
58
|
+
secretResolver?: SecretResolver;
|
|
25
59
|
}
|
|
26
60
|
/**
|
|
27
61
|
* Service for OAuth token exchange and refresh
|
|
@@ -29,6 +63,17 @@ export interface OAuthServiceConfig {
|
|
|
29
63
|
export declare class OAuthService {
|
|
30
64
|
private config;
|
|
31
65
|
constructor(config: OAuthServiceConfig);
|
|
66
|
+
/**
|
|
67
|
+
* Resolve client secret for a provider using the MCP-I provider-registry model
|
|
68
|
+
*
|
|
69
|
+
* Resolution order:
|
|
70
|
+
* 1. If metadata.clientSecretName exists, resolve from secure storage
|
|
71
|
+
* 2. Fall back to clientSecret field (legacy/backward compatibility)
|
|
72
|
+
*
|
|
73
|
+
* @param providerConfig - The OAuth provider configuration
|
|
74
|
+
* @returns The resolved client secret, or undefined if not available
|
|
75
|
+
*/
|
|
76
|
+
resolveClientSecret(providerConfig: OAuthProvider): Promise<string | undefined>;
|
|
32
77
|
/**
|
|
33
78
|
* Exchange authorization code for IDP tokens using PKCE
|
|
34
79
|
*
|
|
@@ -5,10 +5,29 @@
|
|
|
5
5
|
* Handles OAuth token exchange and refresh using PKCE (Proof Key for Code Exchange).
|
|
6
6
|
* Supports both direct PKCE exchange with OAuth providers and proxy mode via AgentShield.
|
|
7
7
|
*
|
|
8
|
+
* MCP-I Provider Registry Model:
|
|
9
|
+
* - Client secrets are NOT sent via the API response
|
|
10
|
+
* - Agents receive metadata.clientSecretName from AgentShield
|
|
11
|
+
* - Secrets are resolved at runtime from secure storage (Cloudflare Worker env)
|
|
12
|
+
* - The SecretResolver interface enables platform-specific secret resolution
|
|
13
|
+
*
|
|
8
14
|
* @package @kya-os/mcp-i-core
|
|
9
15
|
*/
|
|
10
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.OAuthService = void 0;
|
|
17
|
+
exports.OAuthService = exports.defaultSecretResolver = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Default secret resolver that uses environment variables
|
|
20
|
+
* Used when no custom resolver is provided
|
|
21
|
+
*/
|
|
22
|
+
exports.defaultSecretResolver = {
|
|
23
|
+
async resolveSecret(secretName) {
|
|
24
|
+
// For Node.js environments, check process.env
|
|
25
|
+
if (typeof process !== "undefined" && process.env) {
|
|
26
|
+
return process.env[secretName];
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
},
|
|
30
|
+
};
|
|
12
31
|
/**
|
|
13
32
|
* Service for OAuth token exchange and refresh
|
|
14
33
|
*/
|
|
@@ -22,8 +41,55 @@ class OAuthService {
|
|
|
22
41
|
agentShieldApiKey: config.agentShieldApiKey,
|
|
23
42
|
projectId: config.projectId,
|
|
24
43
|
logger: config.logger || (() => { }),
|
|
44
|
+
secretResolver: config.secretResolver || exports.defaultSecretResolver,
|
|
25
45
|
};
|
|
26
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve client secret for a provider using the MCP-I provider-registry model
|
|
49
|
+
*
|
|
50
|
+
* Resolution order:
|
|
51
|
+
* 1. If metadata.clientSecretName exists, resolve from secure storage
|
|
52
|
+
* 2. Fall back to clientSecret field (legacy/backward compatibility)
|
|
53
|
+
*
|
|
54
|
+
* @param providerConfig - The OAuth provider configuration
|
|
55
|
+
* @returns The resolved client secret, or undefined if not available
|
|
56
|
+
*/
|
|
57
|
+
async resolveClientSecret(providerConfig) {
|
|
58
|
+
// MCP-I Provider Registry model: resolve secret by name
|
|
59
|
+
if (providerConfig.metadata?.clientSecretName) {
|
|
60
|
+
const secretName = providerConfig.metadata.clientSecretName;
|
|
61
|
+
this.config.logger("[OAuthService] Resolving client secret by name", {
|
|
62
|
+
secretName,
|
|
63
|
+
version: providerConfig.metadata.clientSecretVersion,
|
|
64
|
+
});
|
|
65
|
+
const secret = await this.config.secretResolver.resolveSecret(secretName);
|
|
66
|
+
if (secret) {
|
|
67
|
+
this.config.logger("[OAuthService] Client secret resolved successfully", {
|
|
68
|
+
secretName,
|
|
69
|
+
});
|
|
70
|
+
return secret;
|
|
71
|
+
}
|
|
72
|
+
this.config.logger("[OAuthService] Client secret not found in secure storage", {
|
|
73
|
+
secretName,
|
|
74
|
+
});
|
|
75
|
+
// Fall through to try clientSecret field
|
|
76
|
+
}
|
|
77
|
+
// Legacy model: use clientSecret directly (if provided)
|
|
78
|
+
// DEPRECATED: This path is for backward compatibility only
|
|
79
|
+
// New deployments should use metadata.clientSecretName with secure storage
|
|
80
|
+
if (providerConfig.clientSecret) {
|
|
81
|
+
this.config.logger("[OAuthService] DEPRECATED: Using legacy clientSecret field - " +
|
|
82
|
+
"migrate to metadata.clientSecretName for better security", {
|
|
83
|
+
telemetry: {
|
|
84
|
+
event: "oauth.deprecated_client_secret_used",
|
|
85
|
+
hasMetadata: !!providerConfig.metadata,
|
|
86
|
+
hasSecretName: !!providerConfig.metadata?.clientSecretName,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
return providerConfig.clientSecret;
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
27
93
|
/**
|
|
28
94
|
* Exchange authorization code for IDP tokens using PKCE
|
|
29
95
|
*
|
|
@@ -60,10 +126,13 @@ class OAuthService {
|
|
|
60
126
|
* Exchange token directly with OAuth provider using PKCE
|
|
61
127
|
*/
|
|
62
128
|
async exchangeTokenPKCE(providerConfig, code, codeVerifier, redirectUri) {
|
|
129
|
+
// Resolve client secret using MCP-I provider-registry model
|
|
130
|
+
const clientSecret = await this.resolveClientSecret(providerConfig);
|
|
63
131
|
this.config.logger("[OAuthService] Exchanging token with PKCE", {
|
|
64
132
|
provider: providerConfig.authorizationUrl,
|
|
65
133
|
tokenUrl: providerConfig.tokenUrl,
|
|
66
|
-
hasClientSecret: !!
|
|
134
|
+
hasClientSecret: !!clientSecret,
|
|
135
|
+
resolvedViaSecretName: !!providerConfig.metadata?.clientSecretName,
|
|
67
136
|
});
|
|
68
137
|
// Build token exchange parameters
|
|
69
138
|
// Note: GitHub OAuth Apps require client_secret even with PKCE
|
|
@@ -75,10 +144,10 @@ class OAuthService {
|
|
|
75
144
|
client_id: providerConfig.clientId,
|
|
76
145
|
code_verifier: codeVerifier,
|
|
77
146
|
};
|
|
78
|
-
// Include client_secret if
|
|
147
|
+
// Include client_secret if resolved (either from secure storage or legacy field)
|
|
79
148
|
// This is required for GitHub OAuth Apps and other providers that need it
|
|
80
|
-
if (
|
|
81
|
-
params.client_secret =
|
|
149
|
+
if (clientSecret) {
|
|
150
|
+
params.client_secret = clientSecret;
|
|
82
151
|
}
|
|
83
152
|
const response = await this.config.fetchProvider.fetch(providerConfig.tokenUrl, {
|
|
84
153
|
method: "POST",
|
|
@@ -17,6 +17,17 @@ export declare class ConsentOnlyModeError extends Error {
|
|
|
17
17
|
readonly toolProtection: ToolProtection;
|
|
18
18
|
constructor(toolProtection: ToolProtection);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Error thrown when a tool requires credential/password-based authentication
|
|
22
|
+
* This is NOT an error condition - it's a signal to use credential auth flow
|
|
23
|
+
* (username/password, email/password, etc.) instead of OAuth
|
|
24
|
+
*/
|
|
25
|
+
export declare class CredentialAuthModeError extends Error {
|
|
26
|
+
readonly toolProtection: ToolProtection;
|
|
27
|
+
/** The credential provider ID configured for this tool */
|
|
28
|
+
readonly provider: string;
|
|
29
|
+
constructor(toolProtection: ToolProtection, provider: string);
|
|
30
|
+
}
|
|
20
31
|
/**
|
|
21
32
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
22
33
|
*
|
|
@@ -39,6 +50,17 @@ export declare class ProviderResolver {
|
|
|
39
50
|
* @returns true if consent-only mode
|
|
40
51
|
*/
|
|
41
52
|
isConsentOnlyMode(toolProtection: ToolProtection): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Check if a tool is configured for credential/password-based authentication
|
|
55
|
+
* This includes username/password, email/password, etc.
|
|
56
|
+
*
|
|
57
|
+
* @param toolProtection - Tool protection configuration
|
|
58
|
+
* @returns Object with isCredential flag and optional provider ID
|
|
59
|
+
*/
|
|
60
|
+
isCredentialAuthMode(toolProtection: ToolProtection): {
|
|
61
|
+
isCredential: boolean;
|
|
62
|
+
provider?: string;
|
|
63
|
+
};
|
|
42
64
|
/**
|
|
43
65
|
* Resolve OAuth provider for a tool
|
|
44
66
|
*
|
|
@@ -46,6 +68,7 @@ export declare class ProviderResolver {
|
|
|
46
68
|
* @param projectId - Project ID for fetching provider config
|
|
47
69
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
48
70
|
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
71
|
+
* @throws CredentialAuthModeError if tool requires password/credential auth
|
|
49
72
|
* @throws Error if provider cannot be resolved
|
|
50
73
|
*/
|
|
51
74
|
resolveProvider(toolProtection: ToolProtection, projectId: string): Promise<string>;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @package @kya-os/mcp-i-core
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.ProviderResolver = exports.ConsentOnlyModeError = void 0;
|
|
11
|
+
exports.ProviderResolver = exports.CredentialAuthModeError = exports.ConsentOnlyModeError = void 0;
|
|
12
12
|
/**
|
|
13
13
|
* Error thrown when a tool is configured for consent-only mode (no OAuth needed)
|
|
14
14
|
* This is NOT an error condition - it's a signal to skip OAuth flow
|
|
@@ -22,6 +22,23 @@ class ConsentOnlyModeError extends Error {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
exports.ConsentOnlyModeError = ConsentOnlyModeError;
|
|
25
|
+
/**
|
|
26
|
+
* Error thrown when a tool requires credential/password-based authentication
|
|
27
|
+
* This is NOT an error condition - it's a signal to use credential auth flow
|
|
28
|
+
* (username/password, email/password, etc.) instead of OAuth
|
|
29
|
+
*/
|
|
30
|
+
class CredentialAuthModeError extends Error {
|
|
31
|
+
toolProtection;
|
|
32
|
+
/** The credential provider ID configured for this tool */
|
|
33
|
+
provider;
|
|
34
|
+
constructor(toolProtection, provider) {
|
|
35
|
+
super(`Tool requires credential authentication with provider "${provider}"`);
|
|
36
|
+
this.toolProtection = toolProtection;
|
|
37
|
+
this.name = "CredentialAuthModeError";
|
|
38
|
+
this.provider = provider;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.CredentialAuthModeError = CredentialAuthModeError;
|
|
25
42
|
/**
|
|
26
43
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
27
44
|
*
|
|
@@ -53,6 +70,23 @@ class ProviderResolver {
|
|
|
53
70
|
}
|
|
54
71
|
return false;
|
|
55
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if a tool is configured for credential/password-based authentication
|
|
75
|
+
* This includes username/password, email/password, etc.
|
|
76
|
+
*
|
|
77
|
+
* @param toolProtection - Tool protection configuration
|
|
78
|
+
* @returns Object with isCredential flag and optional provider ID
|
|
79
|
+
*/
|
|
80
|
+
isCredentialAuthMode(toolProtection) {
|
|
81
|
+
// Check explicit authorization.type === 'password'
|
|
82
|
+
if (toolProtection.authorization?.type === "password") {
|
|
83
|
+
return {
|
|
84
|
+
isCredential: true,
|
|
85
|
+
provider: toolProtection.authorization.provider || "credentials",
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return { isCredential: false };
|
|
89
|
+
}
|
|
56
90
|
/**
|
|
57
91
|
* Resolve OAuth provider for a tool
|
|
58
92
|
*
|
|
@@ -60,14 +94,21 @@ class ProviderResolver {
|
|
|
60
94
|
* @param projectId - Project ID for fetching provider config
|
|
61
95
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
62
96
|
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
97
|
+
* @throws CredentialAuthModeError if tool requires password/credential auth
|
|
63
98
|
* @throws Error if provider cannot be resolved
|
|
64
99
|
*/
|
|
65
100
|
async resolveProvider(toolProtection, projectId) {
|
|
66
|
-
// Priority
|
|
101
|
+
// Priority 0a: Check for consent-only mode (no OAuth required)
|
|
67
102
|
if (this.isConsentOnlyMode(toolProtection)) {
|
|
68
103
|
console.error(`[ProviderResolver] Tool is consent-only mode (authorization.type=none), skipping OAuth resolution`);
|
|
69
104
|
throw new ConsentOnlyModeError(toolProtection);
|
|
70
105
|
}
|
|
106
|
+
// Priority 0b: Check for credential/password auth mode (not OAuth)
|
|
107
|
+
const credentialCheck = this.isCredentialAuthMode(toolProtection);
|
|
108
|
+
if (credentialCheck.isCredential) {
|
|
109
|
+
console.error(`[ProviderResolver] Tool requires credential auth (authorization.type=password), provider="${credentialCheck.provider}"`);
|
|
110
|
+
throw new CredentialAuthModeError(toolProtection, credentialCheck.provider);
|
|
111
|
+
}
|
|
71
112
|
// Priority 1: Tool-specific provider (Phase 2+ preferred)
|
|
72
113
|
if (toolProtection.oauthProvider) {
|
|
73
114
|
// Ensure registry is loaded before checking
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.ToolContextBuilder = void 0;
|
|
15
15
|
const oauth_required_error_js_1 = require("../types/oauth-required-error.js");
|
|
16
|
+
const provider_resolver_js_1 = require("./provider-resolver.js");
|
|
16
17
|
/**
|
|
17
18
|
* Builder for tool execution context
|
|
18
19
|
*
|
|
@@ -53,7 +54,18 @@ class ToolContextBuilder {
|
|
|
53
54
|
provider = await this.resolveProvider(toolProtection);
|
|
54
55
|
}
|
|
55
56
|
catch (error) {
|
|
56
|
-
//
|
|
57
|
+
// ConsentOnlyModeError and CredentialAuthModeError are NOT errors
|
|
58
|
+
// They're signals about what auth flow to use - re-throw for caller to handle
|
|
59
|
+
if (error instanceof provider_resolver_js_1.ConsentOnlyModeError ||
|
|
60
|
+
error instanceof provider_resolver_js_1.CredentialAuthModeError) {
|
|
61
|
+
this.config.logger("[ToolContextBuilder] Special auth mode detected, re-throwing", {
|
|
62
|
+
toolName,
|
|
63
|
+
errorType: error.name,
|
|
64
|
+
message: error.message,
|
|
65
|
+
});
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
// Other provider resolution errors - cannot build context
|
|
57
69
|
this.config.logger("[ToolContextBuilder] Provider not resolved", {
|
|
58
70
|
toolName,
|
|
59
71
|
userDid: userDid.substring(0, 20) + "...",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kya-os/mcp-i-core",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.4",
|
|
4
4
|
"description": "Core runtime and types for MCP-I framework",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"prepublishOnly": "npm run build && node ../create-mcpi-app/scripts/validate-no-workspace.js"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@kya-os/contracts": "^1.7.
|
|
31
|
+
"@kya-os/contracts": "^1.7.7",
|
|
32
32
|
"jose": "^5.6.3",
|
|
33
33
|
"json-canonicalize": "^2.0.0",
|
|
34
34
|
"zod": "^3.25.76"
|