@inkeep/agents-manage-api 0.9.0 → 0.10.2

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.
Files changed (3) hide show
  1. package/dist/index.cjs +372 -341
  2. package/dist/index.js +373 -342
  3. 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("oauth-service");
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
- logger4.info({ body }, "body");
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,109 +3729,375 @@ app15.openapi(
3967
3729
  return c.body(null, 204);
3968
3730
  }
3969
3731
  );
3970
- app15.openapi(
3971
- zodOpenapi.createRoute({
3972
- method: "get",
3973
- path: "/{id}/oauth-login",
3974
- summary: "Initiate OAuth login for MCP tool",
3975
- description: "Detects OAuth requirements and redirects to authorization server",
3976
- operationId: "initiate-oauth-login",
3977
- tags: ["Tools"],
3978
- request: {
3979
- params: agentsCore.TenantProjectParamsSchema.merge(agentsCore.IdParamsSchema)
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);
3980
3759
  },
3981
- responses: {
3982
- 302: {
3983
- description: "Redirect to OAuth authorization server"
3984
- },
3985
- 400: {
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 || env.AGENTS_MANAGE_API_URL
3780
+ };
3781
+ }
3782
+ /**
3783
+ * Initiate OAuth flow for an MCP tool
3784
+ */
3785
+ async initiateOAuthFlow(params) {
3786
+ const { tool, tenantId, projectId, toolId, baseUrl } = 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 redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
3793
+ const redirectUri = `${redirectBaseUrl}/oauth/callback`;
3794
+ let clientId = this.defaultConfig.defaultClientId;
3795
+ if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
3796
+ clientId = await this.performDynamicClientRegistration(
3797
+ oAuthConfig.registrationUrl,
3798
+ redirectUri
3799
+ );
3800
+ }
3801
+ const state = `tool_${toolId}`;
3802
+ const authUrl = this.buildAuthorizationUrl({
3803
+ oAuthConfig,
3804
+ clientId,
3805
+ redirectUri,
3806
+ state,
3807
+ codeChallenge,
3808
+ resource: tool.config.mcp.server.url
3809
+ });
3810
+ storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
3811
+ logger4.info({ toolId, oAuthConfig, tenantId, projectId }, "OAuth flow initiated successfully");
3812
+ return {
3813
+ redirectUrl: authUrl,
3814
+ state
3815
+ };
3816
+ }
3817
+ /**
3818
+ * Exchange authorization code for access tokens
3819
+ */
3820
+ async exchangeCodeForTokens(params) {
3821
+ const { code, codeVerifier, clientId, tool, baseUrl } = params;
3822
+ const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger4);
3823
+ if (!oAuthConfig?.tokenUrl) {
3824
+ throw new Error("Could not discover OAuth token endpoint");
3825
+ }
3826
+ const redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
3827
+ const redirectUri = `${redirectBaseUrl}/oauth/callback`;
3828
+ let tokens;
3829
+ try {
3830
+ tokens = await this.exchangeWithOpenIdClient({
3831
+ oAuthConfig,
3832
+ clientId,
3833
+ code,
3834
+ codeVerifier,
3835
+ redirectUri
3836
+ });
3837
+ logger4.info({ tokenType: tokens.token_type }, "Token exchange successful with openid-client");
3838
+ } catch (error) {
3839
+ logger4.warn(
3840
+ { error: error instanceof Error ? error.message : error },
3841
+ "openid-client failed, falling back to manual token exchange"
3842
+ );
3843
+ tokens = await this.exchangeManually({
3844
+ oAuthConfig,
3845
+ clientId,
3846
+ code,
3847
+ codeVerifier,
3848
+ redirectUri
3849
+ });
3850
+ logger4.info({ tokenType: tokens.token_type }, "Manual token exchange successful");
3851
+ }
3852
+ return { tokens, oAuthConfig };
3853
+ }
3854
+ /**
3855
+ * Perform dynamic client registration
3856
+ */
3857
+ async performDynamicClientRegistration(registrationUrl, redirectUri) {
3858
+ logger4.info({ registrationUrl }, "Attempting dynamic client registration");
3859
+ try {
3860
+ const registrationResponse = await fetch(registrationUrl, {
3861
+ method: "POST",
3862
+ headers: {
3863
+ "Content-Type": "application/json",
3864
+ Accept: "application/json"
3865
+ },
3866
+ body: JSON.stringify({
3867
+ client_name: this.defaultConfig.clientName,
3868
+ client_uri: this.defaultConfig.clientUri,
3869
+ logo_uri: this.defaultConfig.logoUri,
3870
+ redirect_uris: [redirectUri],
3871
+ grant_types: ["authorization_code"],
3872
+ response_types: ["code"],
3873
+ token_endpoint_auth_method: "none",
3874
+ // PKCE only, no client secret
3875
+ application_type: "native"
3876
+ // For PKCE flows
3877
+ })
3878
+ });
3879
+ if (registrationResponse.ok) {
3880
+ const registration = await registrationResponse.json();
3881
+ logger4.info({ clientId: registration.client_id }, "Dynamic client registration successful");
3882
+ return registration.client_id;
3883
+ } else {
3884
+ const errorText = await registrationResponse.text();
3885
+ logger4.warn(
3886
+ {
3887
+ status: registrationResponse.status,
3888
+ errorText
3889
+ },
3890
+ "Dynamic client registration failed, using default client_id"
3891
+ );
3892
+ }
3893
+ } catch (regError) {
3894
+ logger4.warn(
3895
+ { error: regError },
3896
+ "Dynamic client registration error, using default client_id"
3897
+ );
3898
+ }
3899
+ return this.defaultConfig.defaultClientId;
3900
+ }
3901
+ /**
3902
+ * Build authorization URL
3903
+ */
3904
+ buildAuthorizationUrl(params) {
3905
+ const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
3906
+ const authUrl = new URL(oAuthConfig.authorizationUrl);
3907
+ authUrl.searchParams.set("response_type", "code");
3908
+ authUrl.searchParams.set("client_id", clientId);
3909
+ authUrl.searchParams.set("redirect_uri", redirectUri);
3910
+ authUrl.searchParams.set("state", state);
3911
+ authUrl.searchParams.set("code_challenge", codeChallenge);
3912
+ authUrl.searchParams.set("code_challenge_method", "S256");
3913
+ authUrl.searchParams.set("resource", resource);
3914
+ return authUrl.toString();
3915
+ }
3916
+ /**
3917
+ * Exchange code using openid-client library
3918
+ */
3919
+ async exchangeWithOpenIdClient(params) {
3920
+ const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
3921
+ const oauth = await import('openid-client');
3922
+ const tokenUrl = new URL(oAuthConfig.tokenUrl);
3923
+ const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
3924
+ logger4.info({ oauthServerUrl, clientId }, "Attempting openid-client discovery");
3925
+ const config = await oauth.discovery(
3926
+ new URL(oauthServerUrl),
3927
+ clientId,
3928
+ void 0
3929
+ // No client secret for PKCE
3930
+ );
3931
+ const callbackUrl = new URL(
3932
+ `${redirectUri}?${new URLSearchParams({ code, state: "unused" }).toString()}`
3933
+ );
3934
+ return await oauth.authorizationCodeGrant(config, callbackUrl, {
3935
+ pkceCodeVerifier: codeVerifier
3936
+ });
3937
+ }
3938
+ /**
3939
+ * Internal PKCE generation
3940
+ */
3941
+ async generatePKCEInternal() {
3942
+ const codeVerifier = Buffer.from(
3943
+ Array.from(crypto.getRandomValues(new Uint8Array(32)))
3944
+ ).toString("base64url");
3945
+ const encoder = new TextEncoder();
3946
+ const data = encoder.encode(codeVerifier);
3947
+ const hash = await crypto.subtle.digest("SHA-256", data);
3948
+ const codeChallenge = Buffer.from(hash).toString("base64url");
3949
+ return { codeVerifier, codeChallenge };
3950
+ }
3951
+ /**
3952
+ * Manual token exchange fallback
3953
+ */
3954
+ async exchangeManually(params) {
3955
+ const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
3956
+ logger4.info({ tokenUrl: oAuthConfig.tokenUrl }, "Attempting manual token exchange");
3957
+ const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
3958
+ method: "POST",
3959
+ headers: {
3960
+ "Content-Type": "application/x-www-form-urlencoded",
3961
+ Accept: "application/json"
3962
+ },
3963
+ body: new URLSearchParams({
3964
+ grant_type: "authorization_code",
3965
+ code,
3966
+ redirect_uri: redirectUri,
3967
+ client_id: clientId,
3968
+ code_verifier: codeVerifier
3969
+ // PKCE verification
3970
+ })
3971
+ });
3972
+ if (!tokenResponse.ok) {
3973
+ const errorText = await tokenResponse.text();
3974
+ logger4.error(
3975
+ {
3976
+ status: tokenResponse.status,
3977
+ statusText: tokenResponse.statusText,
3978
+ ...process.env.NODE_ENV === "development" && { errorText },
3979
+ clientId,
3980
+ tokenUrl: oAuthConfig.tokenUrl
3981
+ },
3982
+ "Token exchange failed"
3983
+ );
3984
+ throw new Error("Authentication failed. Please try again or contact support.");
3985
+ }
3986
+ return await tokenResponse.json();
3987
+ }
3988
+ };
3989
+ var oauthService = new OAuthService();
3990
+
3991
+ // src/routes/oauth.ts
3992
+ async function findOrCreateCredential(tenantId, projectId, credentialData) {
3993
+ try {
3994
+ const existingCredential = await agentsCore.getCredentialReferenceWithTools(dbClient_default)({
3995
+ scopes: { tenantId, projectId },
3996
+ id: credentialData.id
3997
+ });
3998
+ if (existingCredential) {
3999
+ const validatedCredential = agentsCore.CredentialReferenceApiSelectSchema.parse(existingCredential);
4000
+ return validatedCredential;
4001
+ }
4002
+ } catch {
4003
+ }
4004
+ try {
4005
+ const credential = await agentsCore.createCredentialReference(dbClient_default)({
4006
+ ...credentialData,
4007
+ tenantId,
4008
+ projectId
4009
+ });
4010
+ const validatedCredential = agentsCore.CredentialReferenceApiSelectSchema.parse(credential);
4011
+ return validatedCredential;
4012
+ } catch (error) {
4013
+ console.error("Failed to save credential to database:", error);
4014
+ throw new Error(`Failed to save credential '${credentialData.id}' to database`);
4015
+ }
4016
+ }
4017
+ var app17 = new zodOpenapi.OpenAPIHono();
4018
+ var logger5 = agentsCore.getLogger("oauth-callback");
4019
+ function getBaseUrlFromRequest(c) {
4020
+ const url = new URL(c.req.url);
4021
+ return `${url.protocol}//${url.host}`;
4022
+ }
4023
+ var OAuthLoginQuerySchema = zodOpenapi.z.object({
4024
+ tenantId: zodOpenapi.z.string().min(1, "Tenant ID is required"),
4025
+ projectId: zodOpenapi.z.string().min(1, "Project ID is required"),
4026
+ toolId: zodOpenapi.z.string().min(1, "Tool ID is required")
4027
+ });
4028
+ var OAuthCallbackQuerySchema = zodOpenapi.z.object({
4029
+ code: zodOpenapi.z.string().min(1, "Authorization code is required"),
4030
+ state: zodOpenapi.z.string().min(1, "State parameter is required"),
4031
+ error: zodOpenapi.z.string().optional(),
4032
+ error_description: zodOpenapi.z.string().optional()
4033
+ });
4034
+ app17.openapi(
4035
+ zodOpenapi.createRoute({
4036
+ method: "get",
4037
+ path: "/login",
4038
+ summary: "Initiate OAuth login for MCP tool",
4039
+ description: "Detects OAuth requirements and redirects to authorization server (public endpoint)",
4040
+ operationId: "initiate-oauth-login-public",
4041
+ tags: ["OAuth"],
4042
+ request: {
4043
+ query: OAuthLoginQuerySchema
4044
+ },
4045
+ responses: {
4046
+ 302: {
4047
+ description: "Redirect to OAuth authorization server"
4048
+ },
4049
+ 400: {
3986
4050
  description: "OAuth not supported or configuration error",
3987
4051
  content: {
3988
- "application/json": {
3989
- schema: agentsCore.ErrorResponseSchema
4052
+ "text/html": {
4053
+ schema: zodOpenapi.z.string()
3990
4054
  }
3991
4055
  }
3992
4056
  },
3993
4057
  404: {
3994
4058
  description: "Tool not found",
3995
4059
  content: {
3996
- "application/json": {
3997
- schema: agentsCore.ErrorResponseSchema
4060
+ "text/html": {
4061
+ schema: zodOpenapi.z.string()
3998
4062
  }
3999
4063
  }
4000
4064
  },
4001
4065
  500: {
4002
4066
  description: "Internal server error",
4003
4067
  content: {
4004
- "application/json": {
4005
- schema: agentsCore.ErrorResponseSchema
4068
+ "text/html": {
4069
+ schema: zodOpenapi.z.string()
4006
4070
  }
4007
4071
  }
4008
4072
  }
4009
4073
  }
4010
4074
  }),
4011
4075
  async (c) => {
4012
- const { tenantId, projectId, id } = c.req.valid("param");
4076
+ const { tenantId, projectId, toolId } = c.req.valid("query");
4013
4077
  try {
4014
- const tool = await agentsCore.getToolById(dbClient_default)({ scopes: { tenantId, projectId }, toolId: id });
4078
+ const tool = await agentsCore.getToolById(dbClient_default)({ scopes: { tenantId, projectId }, toolId });
4015
4079
  if (!tool) {
4016
- throw agentsCore.createApiError({
4017
- code: "not_found",
4018
- message: "Tool not found"
4019
- });
4080
+ logger5.error({ toolId, tenantId, projectId }, "Tool not found for OAuth login");
4081
+ return c.text("Tool not found", 404);
4020
4082
  }
4021
4083
  const credentialStores = c.get("credentialStores");
4022
4084
  const mcpTool = await agentsCore.dbResultToMcpTool(tool, dbClient_default, credentialStores);
4085
+ const baseUrl = getBaseUrlFromRequest(c);
4023
4086
  const { redirectUrl } = await oauthService.initiateOAuthFlow({
4024
4087
  tool: mcpTool,
4025
4088
  tenantId,
4026
4089
  projectId,
4027
- toolId: id
4090
+ toolId,
4091
+ baseUrl
4028
4092
  });
4029
4093
  return c.redirect(redirectUrl, 302);
4030
4094
  } catch (error) {
4031
- logger4.error({ toolId: id, error }, "OAuth login failed");
4032
- if (error && typeof error === "object" && "code" in error) {
4033
- const apiError = error;
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
- );
4095
+ logger5.error({ toolId, tenantId, projectId, error }, "OAuth login failed");
4096
+ const errorMessage = error instanceof Error ? error.message : "Failed to initiate OAuth login";
4097
+ return c.text(`OAuth Error: ${errorMessage}`, 500);
4042
4098
  }
4043
4099
  }
4044
4100
  );
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
4101
  app17.openapi(
4074
4102
  zodOpenapi.createRoute({
4075
4103
  method: "get",
@@ -4132,59 +4160,62 @@ app17.openapi(
4132
4160
  logger5.info({ toolId }, "Exchanging authorization code for access token");
4133
4161
  const credentialStores = c.get("credentialStores");
4134
4162
  const mcpTool = await agentsCore.dbResultToMcpTool(tool, dbClient_default, credentialStores);
4163
+ const baseUrl = getBaseUrlFromRequest(c);
4135
4164
  const { tokens } = await oauthService.exchangeCodeForTokens({
4136
4165
  code,
4137
4166
  codeVerifier,
4138
4167
  clientId,
4139
- tool: mcpTool
4168
+ tool: mcpTool,
4169
+ baseUrl
4140
4170
  });
4141
4171
  logger5.info(
4142
4172
  { toolId, tokenType: tokens.token_type, hasRefresh: !!tokens.refresh_token },
4143
4173
  "Token exchange successful"
4144
4174
  );
4175
+ const credentialTokenKey = `oauth_token_${toolId}`;
4176
+ let newCredentialData;
4145
4177
  const keychainStore = credentialStores.get("keychain-default");
4146
- const keychainKey = `oauth_token_${toolId}`;
4147
- await keychainStore?.set(keychainKey, JSON.stringify(tokens));
4148
- const credentialId = tool.name;
4149
- const existingCredential = await agentsCore.getCredentialReference(dbClient_default)({
4150
- scopes: { tenantId, projectId },
4151
- id: credentialId
4152
- });
4153
- const credentialData = {
4154
- type: agentsCore.CredentialStoreType.keychain,
4155
- credentialStoreId: "keychain-default",
4156
- retrievalParams: {
4157
- key: keychainKey
4178
+ if (keychainStore) {
4179
+ try {
4180
+ await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
4181
+ newCredentialData = {
4182
+ id: mcpTool.name,
4183
+ type: agentsCore.CredentialStoreType.keychain,
4184
+ credentialStoreId: "keychain-default",
4185
+ retrievalParams: {
4186
+ key: credentialTokenKey
4187
+ }
4188
+ };
4189
+ } catch {
4158
4190
  }
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
4191
  }
4177
- if (!credential) {
4178
- throw new Error("Failed to create or update credential");
4192
+ if (!newCredentialData && process.env.NANGO_SECRET_KEY) {
4193
+ const nangoStore = credentialStores.get("nango-default");
4194
+ await nangoStore?.set(credentialTokenKey, JSON.stringify(tokens));
4195
+ newCredentialData = {
4196
+ id: mcpTool.name,
4197
+ type: agentsCore.CredentialStoreType.nango,
4198
+ credentialStoreId: "nango-default",
4199
+ retrievalParams: {
4200
+ connectionId: credentialTokenKey,
4201
+ providerConfigKey: credentialTokenKey,
4202
+ provider: "private-api-bearer",
4203
+ authMode: "API_KEY"
4204
+ }
4205
+ };
4206
+ }
4207
+ if (!newCredentialData) {
4208
+ throw new Error("No credential store found");
4179
4209
  }
4210
+ const newCredential = await findOrCreateCredential(tenantId, projectId, newCredentialData);
4180
4211
  await agentsCore.updateTool(dbClient_default)({
4181
4212
  scopes: { tenantId, projectId },
4182
4213
  toolId,
4183
4214
  data: {
4184
- credentialReferenceId: credential.id
4215
+ credentialReferenceId: newCredential.id
4185
4216
  }
4186
4217
  });
4187
- logger5.info({ toolId, credentialId: credential.id }, "OAuth flow completed successfully");
4218
+ logger5.info({ toolId, credentialId: newCredential.id }, "OAuth flow completed successfully");
4188
4219
  const successPage = `
4189
4220
  <!DOCTYPE html>
4190
4221
  <html>