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