@inkeep/agents-manage-api 0.0.0-dev-20250930131913 → 0.0.0-dev-20250930181023
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.cjs +356 -335
- package/dist/index.js +357 -336
- package/package.json +5 -3
package/dist/index.cjs
CHANGED
|
@@ -3480,245 +3480,7 @@ app14.openapi(
|
|
|
3480
3480
|
}
|
|
3481
3481
|
);
|
|
3482
3482
|
var projects_default = app14;
|
|
3483
|
-
var logger3 = agentsCore.getLogger("
|
|
3484
|
-
var pkceStore = /* @__PURE__ */ new Map();
|
|
3485
|
-
function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId) {
|
|
3486
|
-
pkceStore.set(state, { codeVerifier, toolId, tenantId, projectId, clientId });
|
|
3487
|
-
setTimeout(
|
|
3488
|
-
() => {
|
|
3489
|
-
pkceStore.delete(state);
|
|
3490
|
-
},
|
|
3491
|
-
10 * 60 * 1e3
|
|
3492
|
-
);
|
|
3493
|
-
}
|
|
3494
|
-
function retrievePKCEVerifier(state) {
|
|
3495
|
-
const data = pkceStore.get(state);
|
|
3496
|
-
if (data) {
|
|
3497
|
-
pkceStore.delete(state);
|
|
3498
|
-
return data;
|
|
3499
|
-
}
|
|
3500
|
-
return null;
|
|
3501
|
-
}
|
|
3502
|
-
var OAuthService = class {
|
|
3503
|
-
constructor(config = {}) {
|
|
3504
|
-
__publicField(this, "defaultConfig");
|
|
3505
|
-
this.defaultConfig = {
|
|
3506
|
-
defaultClientId: config.defaultClientId || process.env.DEFAULT_OAUTH_CLIENT_ID || "mcp-client",
|
|
3507
|
-
clientName: config.clientName || process.env.OAUTH_CLIENT_NAME || "Inkeep Agent Framework",
|
|
3508
|
-
clientUri: config.clientUri || process.env.OAUTH_CLIENT_URI || "https://inkeep.com",
|
|
3509
|
-
logoUri: config.logoUri || process.env.OAUTH_CLIENT_LOGO_URI || "https://inkeep.com/images/logos/inkeep-logo-blue.svg",
|
|
3510
|
-
redirectBaseUrl: config.redirectBaseUrl || process.env.OAUTH_REDIRECT_BASE_URL || "http://localhost:3002"
|
|
3511
|
-
};
|
|
3512
|
-
}
|
|
3513
|
-
/**
|
|
3514
|
-
* Initiate OAuth flow for an MCP tool
|
|
3515
|
-
*/
|
|
3516
|
-
async initiateOAuthFlow(params) {
|
|
3517
|
-
const { tool, tenantId, projectId, toolId } = params;
|
|
3518
|
-
const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger3);
|
|
3519
|
-
if (!oAuthConfig) {
|
|
3520
|
-
throw new Error("OAuth not supported by this server");
|
|
3521
|
-
}
|
|
3522
|
-
const { codeVerifier, codeChallenge } = await this.generatePKCEInternal();
|
|
3523
|
-
const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
|
|
3524
|
-
let clientId = this.defaultConfig.defaultClientId;
|
|
3525
|
-
if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
|
|
3526
|
-
clientId = await this.performDynamicClientRegistration(
|
|
3527
|
-
oAuthConfig.registrationUrl,
|
|
3528
|
-
redirectUri
|
|
3529
|
-
);
|
|
3530
|
-
}
|
|
3531
|
-
const state = `tool_${toolId}`;
|
|
3532
|
-
const authUrl = this.buildAuthorizationUrl({
|
|
3533
|
-
oAuthConfig,
|
|
3534
|
-
clientId,
|
|
3535
|
-
redirectUri,
|
|
3536
|
-
state,
|
|
3537
|
-
codeChallenge,
|
|
3538
|
-
resource: tool.config.mcp.server.url
|
|
3539
|
-
});
|
|
3540
|
-
storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
|
|
3541
|
-
logger3.info({ toolId, oAuthConfig, tenantId, projectId }, "OAuth flow initiated successfully");
|
|
3542
|
-
return {
|
|
3543
|
-
redirectUrl: authUrl,
|
|
3544
|
-
state
|
|
3545
|
-
};
|
|
3546
|
-
}
|
|
3547
|
-
/**
|
|
3548
|
-
* Exchange authorization code for access tokens
|
|
3549
|
-
*/
|
|
3550
|
-
async exchangeCodeForTokens(params) {
|
|
3551
|
-
const { code, codeVerifier, clientId, tool } = params;
|
|
3552
|
-
const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger3);
|
|
3553
|
-
if (!oAuthConfig?.tokenUrl) {
|
|
3554
|
-
throw new Error("Could not discover OAuth token endpoint");
|
|
3555
|
-
}
|
|
3556
|
-
const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
|
|
3557
|
-
let tokens;
|
|
3558
|
-
try {
|
|
3559
|
-
tokens = await this.exchangeWithOpenIdClient({
|
|
3560
|
-
oAuthConfig,
|
|
3561
|
-
clientId,
|
|
3562
|
-
code,
|
|
3563
|
-
codeVerifier,
|
|
3564
|
-
redirectUri
|
|
3565
|
-
});
|
|
3566
|
-
logger3.info({ tokenType: tokens.token_type }, "Token exchange successful with openid-client");
|
|
3567
|
-
} catch (error) {
|
|
3568
|
-
logger3.warn(
|
|
3569
|
-
{ error: error instanceof Error ? error.message : error },
|
|
3570
|
-
"openid-client failed, falling back to manual token exchange"
|
|
3571
|
-
);
|
|
3572
|
-
tokens = await this.exchangeManually({
|
|
3573
|
-
oAuthConfig,
|
|
3574
|
-
clientId,
|
|
3575
|
-
code,
|
|
3576
|
-
codeVerifier,
|
|
3577
|
-
redirectUri
|
|
3578
|
-
});
|
|
3579
|
-
logger3.info({ tokenType: tokens.token_type }, "Manual token exchange successful");
|
|
3580
|
-
}
|
|
3581
|
-
return { tokens, oAuthConfig };
|
|
3582
|
-
}
|
|
3583
|
-
/**
|
|
3584
|
-
* Perform dynamic client registration
|
|
3585
|
-
*/
|
|
3586
|
-
async performDynamicClientRegistration(registrationUrl, redirectUri) {
|
|
3587
|
-
logger3.info({ registrationUrl }, "Attempting dynamic client registration");
|
|
3588
|
-
try {
|
|
3589
|
-
const registrationResponse = await fetch(registrationUrl, {
|
|
3590
|
-
method: "POST",
|
|
3591
|
-
headers: {
|
|
3592
|
-
"Content-Type": "application/json",
|
|
3593
|
-
Accept: "application/json"
|
|
3594
|
-
},
|
|
3595
|
-
body: JSON.stringify({
|
|
3596
|
-
client_name: this.defaultConfig.clientName,
|
|
3597
|
-
client_uri: this.defaultConfig.clientUri,
|
|
3598
|
-
logo_uri: this.defaultConfig.logoUri,
|
|
3599
|
-
redirect_uris: [redirectUri],
|
|
3600
|
-
grant_types: ["authorization_code"],
|
|
3601
|
-
response_types: ["code"],
|
|
3602
|
-
token_endpoint_auth_method: "none",
|
|
3603
|
-
// PKCE only, no client secret
|
|
3604
|
-
application_type: "native"
|
|
3605
|
-
// For PKCE flows
|
|
3606
|
-
})
|
|
3607
|
-
});
|
|
3608
|
-
if (registrationResponse.ok) {
|
|
3609
|
-
const registration = await registrationResponse.json();
|
|
3610
|
-
logger3.info({ clientId: registration.client_id }, "Dynamic client registration successful");
|
|
3611
|
-
return registration.client_id;
|
|
3612
|
-
} else {
|
|
3613
|
-
const errorText = await registrationResponse.text();
|
|
3614
|
-
logger3.warn(
|
|
3615
|
-
{
|
|
3616
|
-
status: registrationResponse.status,
|
|
3617
|
-
errorText
|
|
3618
|
-
},
|
|
3619
|
-
"Dynamic client registration failed, using default client_id"
|
|
3620
|
-
);
|
|
3621
|
-
}
|
|
3622
|
-
} catch (regError) {
|
|
3623
|
-
logger3.warn(
|
|
3624
|
-
{ error: regError },
|
|
3625
|
-
"Dynamic client registration error, using default client_id"
|
|
3626
|
-
);
|
|
3627
|
-
}
|
|
3628
|
-
return this.defaultConfig.defaultClientId;
|
|
3629
|
-
}
|
|
3630
|
-
/**
|
|
3631
|
-
* Build authorization URL
|
|
3632
|
-
*/
|
|
3633
|
-
buildAuthorizationUrl(params) {
|
|
3634
|
-
const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
|
|
3635
|
-
const authUrl = new URL(oAuthConfig.authorizationUrl);
|
|
3636
|
-
authUrl.searchParams.set("response_type", "code");
|
|
3637
|
-
authUrl.searchParams.set("client_id", clientId);
|
|
3638
|
-
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
3639
|
-
authUrl.searchParams.set("state", state);
|
|
3640
|
-
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
3641
|
-
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
3642
|
-
authUrl.searchParams.set("resource", resource);
|
|
3643
|
-
return authUrl.toString();
|
|
3644
|
-
}
|
|
3645
|
-
/**
|
|
3646
|
-
* Exchange code using openid-client library
|
|
3647
|
-
*/
|
|
3648
|
-
async exchangeWithOpenIdClient(params) {
|
|
3649
|
-
const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
|
|
3650
|
-
const oauth = await import('openid-client');
|
|
3651
|
-
const tokenUrl = new URL(oAuthConfig.tokenUrl);
|
|
3652
|
-
const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
|
|
3653
|
-
logger3.info({ oauthServerUrl, clientId }, "Attempting openid-client discovery");
|
|
3654
|
-
const config = await oauth.discovery(
|
|
3655
|
-
new URL(oauthServerUrl),
|
|
3656
|
-
clientId,
|
|
3657
|
-
void 0
|
|
3658
|
-
// No client secret for PKCE
|
|
3659
|
-
);
|
|
3660
|
-
const callbackUrl = new URL(
|
|
3661
|
-
`${redirectUri}?${new URLSearchParams({ code, state: "unused" }).toString()}`
|
|
3662
|
-
);
|
|
3663
|
-
return await oauth.authorizationCodeGrant(config, callbackUrl, {
|
|
3664
|
-
pkceCodeVerifier: codeVerifier
|
|
3665
|
-
});
|
|
3666
|
-
}
|
|
3667
|
-
/**
|
|
3668
|
-
* Internal PKCE generation
|
|
3669
|
-
*/
|
|
3670
|
-
async generatePKCEInternal() {
|
|
3671
|
-
const codeVerifier = Buffer.from(
|
|
3672
|
-
Array.from(crypto.getRandomValues(new Uint8Array(32)))
|
|
3673
|
-
).toString("base64url");
|
|
3674
|
-
const encoder = new TextEncoder();
|
|
3675
|
-
const data = encoder.encode(codeVerifier);
|
|
3676
|
-
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
3677
|
-
const codeChallenge = Buffer.from(hash).toString("base64url");
|
|
3678
|
-
return { codeVerifier, codeChallenge };
|
|
3679
|
-
}
|
|
3680
|
-
/**
|
|
3681
|
-
* Manual token exchange fallback
|
|
3682
|
-
*/
|
|
3683
|
-
async exchangeManually(params) {
|
|
3684
|
-
const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
|
|
3685
|
-
logger3.info({ tokenUrl: oAuthConfig.tokenUrl }, "Attempting manual token exchange");
|
|
3686
|
-
const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
|
|
3687
|
-
method: "POST",
|
|
3688
|
-
headers: {
|
|
3689
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
3690
|
-
Accept: "application/json"
|
|
3691
|
-
},
|
|
3692
|
-
body: new URLSearchParams({
|
|
3693
|
-
grant_type: "authorization_code",
|
|
3694
|
-
code,
|
|
3695
|
-
redirect_uri: redirectUri,
|
|
3696
|
-
client_id: clientId,
|
|
3697
|
-
code_verifier: codeVerifier
|
|
3698
|
-
// PKCE verification
|
|
3699
|
-
})
|
|
3700
|
-
});
|
|
3701
|
-
if (!tokenResponse.ok) {
|
|
3702
|
-
const errorText = await tokenResponse.text();
|
|
3703
|
-
logger3.error(
|
|
3704
|
-
{
|
|
3705
|
-
status: tokenResponse.status,
|
|
3706
|
-
statusText: tokenResponse.statusText,
|
|
3707
|
-
...process.env.NODE_ENV === "development" && { errorText },
|
|
3708
|
-
clientId,
|
|
3709
|
-
tokenUrl: oAuthConfig.tokenUrl
|
|
3710
|
-
},
|
|
3711
|
-
"Token exchange failed"
|
|
3712
|
-
);
|
|
3713
|
-
throw new Error("Authentication failed. Please try again or contact support.");
|
|
3714
|
-
}
|
|
3715
|
-
return await tokenResponse.json();
|
|
3716
|
-
}
|
|
3717
|
-
};
|
|
3718
|
-
var oauthService = new OAuthService();
|
|
3719
|
-
|
|
3720
|
-
// src/routes/tools.ts
|
|
3721
|
-
var logger4 = agentsCore.getLogger("tools");
|
|
3483
|
+
var logger3 = agentsCore.getLogger("tools");
|
|
3722
3484
|
var app15 = new zodOpenapi.OpenAPIHono();
|
|
3723
3485
|
app15.openapi(
|
|
3724
3486
|
zodOpenapi.createRoute({
|
|
@@ -3850,7 +3612,7 @@ app15.openapi(
|
|
|
3850
3612
|
const { tenantId, projectId } = c.req.valid("param");
|
|
3851
3613
|
const body = c.req.valid("json");
|
|
3852
3614
|
const credentialStores = c.get("credentialStores");
|
|
3853
|
-
|
|
3615
|
+
logger3.info({ body }, "body");
|
|
3854
3616
|
const id = body.id || nanoid.nanoid();
|
|
3855
3617
|
const tool = await agentsCore.createTool(dbClient_default)({
|
|
3856
3618
|
tenantId,
|
|
@@ -3967,16 +3729,312 @@ app15.openapi(
|
|
|
3967
3729
|
return c.body(null, 204);
|
|
3968
3730
|
}
|
|
3969
3731
|
);
|
|
3970
|
-
app15
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3732
|
+
var tools_default = app15;
|
|
3733
|
+
|
|
3734
|
+
// src/routes/index.ts
|
|
3735
|
+
var app16 = new zodOpenapi.OpenAPIHono();
|
|
3736
|
+
app16.route("/projects", projects_default);
|
|
3737
|
+
app16.route("/projects/:projectId/graphs/:graphId/agents", agents_default);
|
|
3738
|
+
app16.route("/projects/:projectId/graphs/:graphId/agent-relations", agentRelations_default);
|
|
3739
|
+
app16.route("/projects/:projectId/agent-graphs", agentGraph_default);
|
|
3740
|
+
app16.route("/projects/:projectId/graphs/:graphId/agent-tool-relations", agentToolRelations_default);
|
|
3741
|
+
app16.route("/projects/:projectId/graphs/:graphId/agent-artifact-components", agentArtifactComponents_default);
|
|
3742
|
+
app16.route("/projects/:projectId/graphs/:graphId/agent-data-components", agentDataComponents_default);
|
|
3743
|
+
app16.route("/projects/:projectId/artifact-components", artifactComponents_default);
|
|
3744
|
+
app16.route("/projects/:projectId/context-configs", contextConfigs_default);
|
|
3745
|
+
app16.route("/projects/:projectId/credentials", credentials_default);
|
|
3746
|
+
app16.route("/projects/:projectId/data-components", dataComponents_default);
|
|
3747
|
+
app16.route("/projects/:projectId/graphs/:graphId/external-agents", externalAgents_default);
|
|
3748
|
+
app16.route("/projects/:projectId/tools", tools_default);
|
|
3749
|
+
app16.route("/projects/:projectId/api-keys", apiKeys_default);
|
|
3750
|
+
app16.route("/projects/:projectId/graph", graphFull_default);
|
|
3751
|
+
var routes_default = app16;
|
|
3752
|
+
var logger4 = agentsCore.getLogger("oauth-service");
|
|
3753
|
+
var pkceStore = /* @__PURE__ */ new Map();
|
|
3754
|
+
function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId) {
|
|
3755
|
+
pkceStore.set(state, { codeVerifier, toolId, tenantId, projectId, clientId });
|
|
3756
|
+
setTimeout(
|
|
3757
|
+
() => {
|
|
3758
|
+
pkceStore.delete(state);
|
|
3759
|
+
},
|
|
3760
|
+
10 * 60 * 1e3
|
|
3761
|
+
);
|
|
3762
|
+
}
|
|
3763
|
+
function retrievePKCEVerifier(state) {
|
|
3764
|
+
const data = pkceStore.get(state);
|
|
3765
|
+
if (data) {
|
|
3766
|
+
pkceStore.delete(state);
|
|
3767
|
+
return data;
|
|
3768
|
+
}
|
|
3769
|
+
return null;
|
|
3770
|
+
}
|
|
3771
|
+
var OAuthService = class {
|
|
3772
|
+
constructor(config = {}) {
|
|
3773
|
+
__publicField(this, "defaultConfig");
|
|
3774
|
+
this.defaultConfig = {
|
|
3775
|
+
defaultClientId: config.defaultClientId || process.env.DEFAULT_OAUTH_CLIENT_ID || "mcp-client",
|
|
3776
|
+
clientName: config.clientName || process.env.OAUTH_CLIENT_NAME || "Inkeep Agent Framework",
|
|
3777
|
+
clientUri: config.clientUri || process.env.OAUTH_CLIENT_URI || "https://inkeep.com",
|
|
3778
|
+
logoUri: config.logoUri || process.env.OAUTH_CLIENT_LOGO_URI || "https://inkeep.com/images/logos/inkeep-logo-blue.svg",
|
|
3779
|
+
redirectBaseUrl: config.redirectBaseUrl || process.env.OAUTH_REDIRECT_BASE_URL || "http://localhost:3002"
|
|
3780
|
+
};
|
|
3781
|
+
}
|
|
3782
|
+
/**
|
|
3783
|
+
* Initiate OAuth flow for an MCP tool
|
|
3784
|
+
*/
|
|
3785
|
+
async initiateOAuthFlow(params) {
|
|
3786
|
+
const { tool, tenantId, projectId, toolId } = params;
|
|
3787
|
+
const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger4);
|
|
3788
|
+
if (!oAuthConfig) {
|
|
3789
|
+
throw new Error("OAuth not supported by this server");
|
|
3790
|
+
}
|
|
3791
|
+
const { codeVerifier, codeChallenge } = await this.generatePKCEInternal();
|
|
3792
|
+
const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
|
|
3793
|
+
let clientId = this.defaultConfig.defaultClientId;
|
|
3794
|
+
if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
|
|
3795
|
+
clientId = await this.performDynamicClientRegistration(
|
|
3796
|
+
oAuthConfig.registrationUrl,
|
|
3797
|
+
redirectUri
|
|
3798
|
+
);
|
|
3799
|
+
}
|
|
3800
|
+
const state = `tool_${toolId}`;
|
|
3801
|
+
const authUrl = this.buildAuthorizationUrl({
|
|
3802
|
+
oAuthConfig,
|
|
3803
|
+
clientId,
|
|
3804
|
+
redirectUri,
|
|
3805
|
+
state,
|
|
3806
|
+
codeChallenge,
|
|
3807
|
+
resource: tool.config.mcp.server.url
|
|
3808
|
+
});
|
|
3809
|
+
storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
|
|
3810
|
+
logger4.info({ toolId, oAuthConfig, tenantId, projectId }, "OAuth flow initiated successfully");
|
|
3811
|
+
return {
|
|
3812
|
+
redirectUrl: authUrl,
|
|
3813
|
+
state
|
|
3814
|
+
};
|
|
3815
|
+
}
|
|
3816
|
+
/**
|
|
3817
|
+
* Exchange authorization code for access tokens
|
|
3818
|
+
*/
|
|
3819
|
+
async exchangeCodeForTokens(params) {
|
|
3820
|
+
const { code, codeVerifier, clientId, tool } = params;
|
|
3821
|
+
const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger4);
|
|
3822
|
+
if (!oAuthConfig?.tokenUrl) {
|
|
3823
|
+
throw new Error("Could not discover OAuth token endpoint");
|
|
3824
|
+
}
|
|
3825
|
+
const redirectUri = `${this.defaultConfig.redirectBaseUrl}/oauth/callback`;
|
|
3826
|
+
let tokens;
|
|
3827
|
+
try {
|
|
3828
|
+
tokens = await this.exchangeWithOpenIdClient({
|
|
3829
|
+
oAuthConfig,
|
|
3830
|
+
clientId,
|
|
3831
|
+
code,
|
|
3832
|
+
codeVerifier,
|
|
3833
|
+
redirectUri
|
|
3834
|
+
});
|
|
3835
|
+
logger4.info({ tokenType: tokens.token_type }, "Token exchange successful with openid-client");
|
|
3836
|
+
} catch (error) {
|
|
3837
|
+
logger4.warn(
|
|
3838
|
+
{ error: error instanceof Error ? error.message : error },
|
|
3839
|
+
"openid-client failed, falling back to manual token exchange"
|
|
3840
|
+
);
|
|
3841
|
+
tokens = await this.exchangeManually({
|
|
3842
|
+
oAuthConfig,
|
|
3843
|
+
clientId,
|
|
3844
|
+
code,
|
|
3845
|
+
codeVerifier,
|
|
3846
|
+
redirectUri
|
|
3847
|
+
});
|
|
3848
|
+
logger4.info({ tokenType: tokens.token_type }, "Manual token exchange successful");
|
|
3849
|
+
}
|
|
3850
|
+
return { tokens, oAuthConfig };
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* Perform dynamic client registration
|
|
3854
|
+
*/
|
|
3855
|
+
async performDynamicClientRegistration(registrationUrl, redirectUri) {
|
|
3856
|
+
logger4.info({ registrationUrl }, "Attempting dynamic client registration");
|
|
3857
|
+
try {
|
|
3858
|
+
const registrationResponse = await fetch(registrationUrl, {
|
|
3859
|
+
method: "POST",
|
|
3860
|
+
headers: {
|
|
3861
|
+
"Content-Type": "application/json",
|
|
3862
|
+
Accept: "application/json"
|
|
3863
|
+
},
|
|
3864
|
+
body: JSON.stringify({
|
|
3865
|
+
client_name: this.defaultConfig.clientName,
|
|
3866
|
+
client_uri: this.defaultConfig.clientUri,
|
|
3867
|
+
logo_uri: this.defaultConfig.logoUri,
|
|
3868
|
+
redirect_uris: [redirectUri],
|
|
3869
|
+
grant_types: ["authorization_code"],
|
|
3870
|
+
response_types: ["code"],
|
|
3871
|
+
token_endpoint_auth_method: "none",
|
|
3872
|
+
// PKCE only, no client secret
|
|
3873
|
+
application_type: "native"
|
|
3874
|
+
// For PKCE flows
|
|
3875
|
+
})
|
|
3876
|
+
});
|
|
3877
|
+
if (registrationResponse.ok) {
|
|
3878
|
+
const registration = await registrationResponse.json();
|
|
3879
|
+
logger4.info({ clientId: registration.client_id }, "Dynamic client registration successful");
|
|
3880
|
+
return registration.client_id;
|
|
3881
|
+
} else {
|
|
3882
|
+
const errorText = await registrationResponse.text();
|
|
3883
|
+
logger4.warn(
|
|
3884
|
+
{
|
|
3885
|
+
status: registrationResponse.status,
|
|
3886
|
+
errorText
|
|
3887
|
+
},
|
|
3888
|
+
"Dynamic client registration failed, using default client_id"
|
|
3889
|
+
);
|
|
3890
|
+
}
|
|
3891
|
+
} catch (regError) {
|
|
3892
|
+
logger4.warn(
|
|
3893
|
+
{ error: regError },
|
|
3894
|
+
"Dynamic client registration error, using default client_id"
|
|
3895
|
+
);
|
|
3896
|
+
}
|
|
3897
|
+
return this.defaultConfig.defaultClientId;
|
|
3898
|
+
}
|
|
3899
|
+
/**
|
|
3900
|
+
* Build authorization URL
|
|
3901
|
+
*/
|
|
3902
|
+
buildAuthorizationUrl(params) {
|
|
3903
|
+
const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
|
|
3904
|
+
const authUrl = new URL(oAuthConfig.authorizationUrl);
|
|
3905
|
+
authUrl.searchParams.set("response_type", "code");
|
|
3906
|
+
authUrl.searchParams.set("client_id", clientId);
|
|
3907
|
+
authUrl.searchParams.set("redirect_uri", redirectUri);
|
|
3908
|
+
authUrl.searchParams.set("state", state);
|
|
3909
|
+
authUrl.searchParams.set("code_challenge", codeChallenge);
|
|
3910
|
+
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
3911
|
+
authUrl.searchParams.set("resource", resource);
|
|
3912
|
+
return authUrl.toString();
|
|
3913
|
+
}
|
|
3914
|
+
/**
|
|
3915
|
+
* Exchange code using openid-client library
|
|
3916
|
+
*/
|
|
3917
|
+
async exchangeWithOpenIdClient(params) {
|
|
3918
|
+
const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
|
|
3919
|
+
const oauth = await import('openid-client');
|
|
3920
|
+
const tokenUrl = new URL(oAuthConfig.tokenUrl);
|
|
3921
|
+
const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
|
|
3922
|
+
logger4.info({ oauthServerUrl, clientId }, "Attempting openid-client discovery");
|
|
3923
|
+
const config = await oauth.discovery(
|
|
3924
|
+
new URL(oauthServerUrl),
|
|
3925
|
+
clientId,
|
|
3926
|
+
void 0
|
|
3927
|
+
// No client secret for PKCE
|
|
3928
|
+
);
|
|
3929
|
+
const callbackUrl = new URL(
|
|
3930
|
+
`${redirectUri}?${new URLSearchParams({ code, state: "unused" }).toString()}`
|
|
3931
|
+
);
|
|
3932
|
+
return await oauth.authorizationCodeGrant(config, callbackUrl, {
|
|
3933
|
+
pkceCodeVerifier: codeVerifier
|
|
3934
|
+
});
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Internal PKCE generation
|
|
3938
|
+
*/
|
|
3939
|
+
async generatePKCEInternal() {
|
|
3940
|
+
const codeVerifier = Buffer.from(
|
|
3941
|
+
Array.from(crypto.getRandomValues(new Uint8Array(32)))
|
|
3942
|
+
).toString("base64url");
|
|
3943
|
+
const encoder = new TextEncoder();
|
|
3944
|
+
const data = encoder.encode(codeVerifier);
|
|
3945
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
3946
|
+
const codeChallenge = Buffer.from(hash).toString("base64url");
|
|
3947
|
+
return { codeVerifier, codeChallenge };
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* Manual token exchange fallback
|
|
3951
|
+
*/
|
|
3952
|
+
async exchangeManually(params) {
|
|
3953
|
+
const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
|
|
3954
|
+
logger4.info({ tokenUrl: oAuthConfig.tokenUrl }, "Attempting manual token exchange");
|
|
3955
|
+
const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
|
|
3956
|
+
method: "POST",
|
|
3957
|
+
headers: {
|
|
3958
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
3959
|
+
Accept: "application/json"
|
|
3960
|
+
},
|
|
3961
|
+
body: new URLSearchParams({
|
|
3962
|
+
grant_type: "authorization_code",
|
|
3963
|
+
code,
|
|
3964
|
+
redirect_uri: redirectUri,
|
|
3965
|
+
client_id: clientId,
|
|
3966
|
+
code_verifier: codeVerifier
|
|
3967
|
+
// PKCE verification
|
|
3968
|
+
})
|
|
3969
|
+
});
|
|
3970
|
+
if (!tokenResponse.ok) {
|
|
3971
|
+
const errorText = await tokenResponse.text();
|
|
3972
|
+
logger4.error(
|
|
3973
|
+
{
|
|
3974
|
+
status: tokenResponse.status,
|
|
3975
|
+
statusText: tokenResponse.statusText,
|
|
3976
|
+
...process.env.NODE_ENV === "development" && { errorText },
|
|
3977
|
+
clientId,
|
|
3978
|
+
tokenUrl: oAuthConfig.tokenUrl
|
|
3979
|
+
},
|
|
3980
|
+
"Token exchange failed"
|
|
3981
|
+
);
|
|
3982
|
+
throw new Error("Authentication failed. Please try again or contact support.");
|
|
3983
|
+
}
|
|
3984
|
+
return await tokenResponse.json();
|
|
3985
|
+
}
|
|
3986
|
+
};
|
|
3987
|
+
var oauthService = new OAuthService();
|
|
3988
|
+
|
|
3989
|
+
// src/routes/oauth.ts
|
|
3990
|
+
async function findOrCreateCredential(tenantId, projectId, credentialData) {
|
|
3991
|
+
try {
|
|
3992
|
+
const existingCredential = await agentsCore.getCredentialReferenceWithTools(dbClient_default)({
|
|
3993
|
+
scopes: { tenantId, projectId },
|
|
3994
|
+
id: credentialData.id
|
|
3995
|
+
});
|
|
3996
|
+
if (existingCredential) {
|
|
3997
|
+
const validatedCredential = agentsCore.CredentialReferenceApiSelectSchema.parse(existingCredential);
|
|
3998
|
+
return validatedCredential;
|
|
3999
|
+
}
|
|
4000
|
+
} catch {
|
|
4001
|
+
}
|
|
4002
|
+
try {
|
|
4003
|
+
const credential = await agentsCore.createCredentialReference(dbClient_default)({
|
|
4004
|
+
...credentialData,
|
|
4005
|
+
tenantId,
|
|
4006
|
+
projectId
|
|
4007
|
+
});
|
|
4008
|
+
const validatedCredential = agentsCore.CredentialReferenceApiSelectSchema.parse(credential);
|
|
4009
|
+
return validatedCredential;
|
|
4010
|
+
} catch (error) {
|
|
4011
|
+
console.error("Failed to save credential to database:", error);
|
|
4012
|
+
throw new Error(`Failed to save credential '${credentialData.id}' to database`);
|
|
4013
|
+
}
|
|
4014
|
+
}
|
|
4015
|
+
var app17 = new zodOpenapi.OpenAPIHono();
|
|
4016
|
+
var logger5 = agentsCore.getLogger("oauth-callback");
|
|
4017
|
+
var OAuthLoginQuerySchema = zodOpenapi.z.object({
|
|
4018
|
+
tenantId: zodOpenapi.z.string().min(1, "Tenant ID is required"),
|
|
4019
|
+
projectId: zodOpenapi.z.string().min(1, "Project ID is required"),
|
|
4020
|
+
toolId: zodOpenapi.z.string().min(1, "Tool ID is required")
|
|
4021
|
+
});
|
|
4022
|
+
var OAuthCallbackQuerySchema = zodOpenapi.z.object({
|
|
4023
|
+
code: zodOpenapi.z.string().min(1, "Authorization code is required"),
|
|
4024
|
+
state: zodOpenapi.z.string().min(1, "State parameter is required"),
|
|
4025
|
+
error: zodOpenapi.z.string().optional(),
|
|
4026
|
+
error_description: zodOpenapi.z.string().optional()
|
|
4027
|
+
});
|
|
4028
|
+
app17.openapi(
|
|
4029
|
+
zodOpenapi.createRoute({
|
|
4030
|
+
method: "get",
|
|
4031
|
+
path: "/login",
|
|
4032
|
+
summary: "Initiate OAuth login for MCP tool",
|
|
4033
|
+
description: "Detects OAuth requirements and redirects to authorization server (public endpoint)",
|
|
4034
|
+
operationId: "initiate-oauth-login-public",
|
|
4035
|
+
tags: ["OAuth"],
|
|
4036
|
+
request: {
|
|
4037
|
+
query: OAuthLoginQuerySchema
|
|
3980
4038
|
},
|
|
3981
4039
|
responses: {
|
|
3982
4040
|
302: {
|
|
@@ -3985,38 +4043,36 @@ app15.openapi(
|
|
|
3985
4043
|
400: {
|
|
3986
4044
|
description: "OAuth not supported or configuration error",
|
|
3987
4045
|
content: {
|
|
3988
|
-
"
|
|
3989
|
-
schema:
|
|
4046
|
+
"text/html": {
|
|
4047
|
+
schema: zodOpenapi.z.string()
|
|
3990
4048
|
}
|
|
3991
4049
|
}
|
|
3992
4050
|
},
|
|
3993
4051
|
404: {
|
|
3994
4052
|
description: "Tool not found",
|
|
3995
4053
|
content: {
|
|
3996
|
-
"
|
|
3997
|
-
schema:
|
|
4054
|
+
"text/html": {
|
|
4055
|
+
schema: zodOpenapi.z.string()
|
|
3998
4056
|
}
|
|
3999
4057
|
}
|
|
4000
4058
|
},
|
|
4001
4059
|
500: {
|
|
4002
4060
|
description: "Internal server error",
|
|
4003
4061
|
content: {
|
|
4004
|
-
"
|
|
4005
|
-
schema:
|
|
4062
|
+
"text/html": {
|
|
4063
|
+
schema: zodOpenapi.z.string()
|
|
4006
4064
|
}
|
|
4007
4065
|
}
|
|
4008
4066
|
}
|
|
4009
4067
|
}
|
|
4010
4068
|
}),
|
|
4011
4069
|
async (c) => {
|
|
4012
|
-
const { tenantId, projectId,
|
|
4070
|
+
const { tenantId, projectId, toolId } = c.req.valid("query");
|
|
4013
4071
|
try {
|
|
4014
|
-
const tool = await agentsCore.getToolById(dbClient_default)({ scopes: { tenantId, projectId }, toolId
|
|
4072
|
+
const tool = await agentsCore.getToolById(dbClient_default)({ scopes: { tenantId, projectId }, toolId });
|
|
4015
4073
|
if (!tool) {
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
message: "Tool not found"
|
|
4019
|
-
});
|
|
4074
|
+
logger5.error({ toolId, tenantId, projectId }, "Tool not found for OAuth login");
|
|
4075
|
+
return c.text("Tool not found", 404);
|
|
4020
4076
|
}
|
|
4021
4077
|
const credentialStores = c.get("credentialStores");
|
|
4022
4078
|
const mcpTool = await agentsCore.dbResultToMcpTool(tool, dbClient_default, credentialStores);
|
|
@@ -4024,52 +4080,16 @@ app15.openapi(
|
|
|
4024
4080
|
tool: mcpTool,
|
|
4025
4081
|
tenantId,
|
|
4026
4082
|
projectId,
|
|
4027
|
-
toolId
|
|
4083
|
+
toolId
|
|
4028
4084
|
});
|
|
4029
4085
|
return c.redirect(redirectUrl, 302);
|
|
4030
4086
|
} catch (error) {
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
return c.json({ error: apiError.message }, apiError.code === "not_found" ? 404 : 400);
|
|
4035
|
-
}
|
|
4036
|
-
return c.json(
|
|
4037
|
-
{
|
|
4038
|
-
error: "Failed to initiate OAuth login"
|
|
4039
|
-
},
|
|
4040
|
-
500
|
|
4041
|
-
);
|
|
4087
|
+
logger5.error({ toolId, tenantId, projectId, error }, "OAuth login failed");
|
|
4088
|
+
const errorMessage = error instanceof Error ? error.message : "Failed to initiate OAuth login";
|
|
4089
|
+
return c.text(`OAuth Error: ${errorMessage}`, 500);
|
|
4042
4090
|
}
|
|
4043
4091
|
}
|
|
4044
4092
|
);
|
|
4045
|
-
var tools_default = app15;
|
|
4046
|
-
|
|
4047
|
-
// src/routes/index.ts
|
|
4048
|
-
var app16 = new zodOpenapi.OpenAPIHono();
|
|
4049
|
-
app16.route("/projects", projects_default);
|
|
4050
|
-
app16.route("/projects/:projectId/graphs/:graphId/agents", agents_default);
|
|
4051
|
-
app16.route("/projects/:projectId/graphs/:graphId/agent-relations", agentRelations_default);
|
|
4052
|
-
app16.route("/projects/:projectId/agent-graphs", agentGraph_default);
|
|
4053
|
-
app16.route("/projects/:projectId/graphs/:graphId/agent-tool-relations", agentToolRelations_default);
|
|
4054
|
-
app16.route("/projects/:projectId/graphs/:graphId/agent-artifact-components", agentArtifactComponents_default);
|
|
4055
|
-
app16.route("/projects/:projectId/graphs/:graphId/agent-data-components", agentDataComponents_default);
|
|
4056
|
-
app16.route("/projects/:projectId/artifact-components", artifactComponents_default);
|
|
4057
|
-
app16.route("/projects/:projectId/context-configs", contextConfigs_default);
|
|
4058
|
-
app16.route("/projects/:projectId/credentials", credentials_default);
|
|
4059
|
-
app16.route("/projects/:projectId/data-components", dataComponents_default);
|
|
4060
|
-
app16.route("/projects/:projectId/graphs/:graphId/external-agents", externalAgents_default);
|
|
4061
|
-
app16.route("/projects/:projectId/tools", tools_default);
|
|
4062
|
-
app16.route("/projects/:projectId/api-keys", apiKeys_default);
|
|
4063
|
-
app16.route("/projects/:projectId/graph", graphFull_default);
|
|
4064
|
-
var routes_default = app16;
|
|
4065
|
-
var app17 = new zodOpenapi.OpenAPIHono();
|
|
4066
|
-
var logger5 = agentsCore.getLogger("oauth-callback");
|
|
4067
|
-
var OAuthCallbackQuerySchema = zodOpenapi.z.object({
|
|
4068
|
-
code: zodOpenapi.z.string().min(1, "Authorization code is required"),
|
|
4069
|
-
state: zodOpenapi.z.string().min(1, "State parameter is required"),
|
|
4070
|
-
error: zodOpenapi.z.string().optional(),
|
|
4071
|
-
error_description: zodOpenapi.z.string().optional()
|
|
4072
|
-
});
|
|
4073
4093
|
app17.openapi(
|
|
4074
4094
|
zodOpenapi.createRoute({
|
|
4075
4095
|
method: "get",
|
|
@@ -4142,49 +4162,50 @@ app17.openapi(
|
|
|
4142
4162
|
{ toolId, tokenType: tokens.token_type, hasRefresh: !!tokens.refresh_token },
|
|
4143
4163
|
"Token exchange successful"
|
|
4144
4164
|
);
|
|
4165
|
+
const credentialTokenKey = `oauth_token_${toolId}`;
|
|
4166
|
+
let newCredentialData;
|
|
4145
4167
|
const keychainStore = credentialStores.get("keychain-default");
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4168
|
+
if (keychainStore) {
|
|
4169
|
+
try {
|
|
4170
|
+
await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
|
|
4171
|
+
newCredentialData = {
|
|
4172
|
+
id: mcpTool.name,
|
|
4173
|
+
type: agentsCore.CredentialStoreType.keychain,
|
|
4174
|
+
credentialStoreId: "keychain-default",
|
|
4175
|
+
retrievalParams: {
|
|
4176
|
+
key: credentialTokenKey
|
|
4177
|
+
}
|
|
4178
|
+
};
|
|
4179
|
+
} catch {
|
|
4158
4180
|
}
|
|
4159
|
-
};
|
|
4160
|
-
let credential;
|
|
4161
|
-
if (existingCredential) {
|
|
4162
|
-
logger5.info({ credentialId: existingCredential.id }, "Updating existing credential");
|
|
4163
|
-
credential = await agentsCore.updateCredentialReference(dbClient_default)({
|
|
4164
|
-
scopes: { tenantId, projectId },
|
|
4165
|
-
id: existingCredential.id,
|
|
4166
|
-
data: credentialData
|
|
4167
|
-
});
|
|
4168
|
-
} else {
|
|
4169
|
-
logger5.info({ credentialId }, "Creating new credential");
|
|
4170
|
-
credential = await agentsCore.createCredentialReference(dbClient_default)({
|
|
4171
|
-
tenantId,
|
|
4172
|
-
projectId,
|
|
4173
|
-
id: credentialId,
|
|
4174
|
-
...credentialData
|
|
4175
|
-
});
|
|
4176
4181
|
}
|
|
4177
|
-
if (!
|
|
4178
|
-
|
|
4182
|
+
if (!newCredentialData && process.env.NANGO_SECRET_KEY) {
|
|
4183
|
+
const nangoStore = credentialStores.get("nango-default");
|
|
4184
|
+
await nangoStore?.set(credentialTokenKey, JSON.stringify(tokens));
|
|
4185
|
+
newCredentialData = {
|
|
4186
|
+
id: mcpTool.name,
|
|
4187
|
+
type: agentsCore.CredentialStoreType.nango,
|
|
4188
|
+
credentialStoreId: "nango-default",
|
|
4189
|
+
retrievalParams: {
|
|
4190
|
+
connectionId: credentialTokenKey,
|
|
4191
|
+
providerConfigKey: credentialTokenKey,
|
|
4192
|
+
provider: "private-api-bearer",
|
|
4193
|
+
authMode: "API_KEY"
|
|
4194
|
+
}
|
|
4195
|
+
};
|
|
4196
|
+
}
|
|
4197
|
+
if (!newCredentialData) {
|
|
4198
|
+
throw new Error("No credential store found");
|
|
4179
4199
|
}
|
|
4200
|
+
const newCredential = await findOrCreateCredential(tenantId, projectId, newCredentialData);
|
|
4180
4201
|
await agentsCore.updateTool(dbClient_default)({
|
|
4181
4202
|
scopes: { tenantId, projectId },
|
|
4182
4203
|
toolId,
|
|
4183
4204
|
data: {
|
|
4184
|
-
credentialReferenceId:
|
|
4205
|
+
credentialReferenceId: newCredential.id
|
|
4185
4206
|
}
|
|
4186
4207
|
});
|
|
4187
|
-
logger5.info({ toolId, credentialId:
|
|
4208
|
+
logger5.info({ toolId, credentialId: newCredential.id }, "OAuth flow completed successfully");
|
|
4188
4209
|
const successPage = `
|
|
4189
4210
|
<!DOCTYPE html>
|
|
4190
4211
|
<html>
|