@inkeep/agents-manage-api 0.22.11 → 0.23.0

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 +90 -233
  2. package/dist/index.js +91 -234
  3. package/package.json +4 -4
package/dist/index.cjs CHANGED
@@ -4534,8 +4534,16 @@ app19.route("/projects/:projectId/agent", agentFull_default);
4534
4534
  var routes_default = app19;
4535
4535
  var logger6 = agentsCore.getLogger("oauth-service");
4536
4536
  var pkceStore = /* @__PURE__ */ new Map();
4537
- function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId) {
4538
- pkceStore.set(state, { codeVerifier, toolId, tenantId, projectId, clientId });
4537
+ function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientInformation, metadata, resourceUrl) {
4538
+ pkceStore.set(state, {
4539
+ codeVerifier,
4540
+ toolId,
4541
+ tenantId,
4542
+ projectId,
4543
+ clientInformation,
4544
+ metadata,
4545
+ resourceUrl
4546
+ });
4539
4547
  setTimeout(
4540
4548
  () => {
4541
4549
  pkceStore.delete(state);
@@ -4563,216 +4571,74 @@ var OAuthService = class {
4563
4571
  };
4564
4572
  }
4565
4573
  /**
4566
- * Initiate OAuth flow for an MCP tool
4574
+ * Initiate OAuth flow for an MCP tool using MCP SDK
4567
4575
  */
4568
4576
  async initiateOAuthFlow(params) {
4569
- const { tool, tenantId, projectId, toolId, baseUrl } = params;
4570
- if (tool.config.type !== "mcp") {
4571
- throw new Error("OAuth is only supported for MCP tools");
4572
- }
4573
- const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger6);
4574
- if (!oAuthConfig) {
4575
- throw new Error("OAuth not supported by this server");
4576
- }
4577
- const { codeVerifier, codeChallenge } = await this.generatePKCEInternal();
4577
+ const { tenantId, projectId, toolId, mcpServerUrl, baseUrl } = params;
4578
4578
  const redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
4579
4579
  const redirectUri = `${redirectBaseUrl}/oauth/callback`;
4580
- let clientId = this.defaultConfig.defaultClientId;
4581
- if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
4582
- clientId = await this.performDynamicClientRegistration(
4583
- oAuthConfig.registrationUrl,
4584
- redirectUri
4585
- );
4586
- }
4587
4580
  const state = `tool_${toolId}`;
4588
- const authUrl = this.buildAuthorizationUrl({
4589
- oAuthConfig,
4590
- clientId,
4581
+ const authResult = await agentsCore.initiateMcpOAuthFlow({
4582
+ mcpServerUrl,
4591
4583
  redirectUri,
4592
4584
  state,
4593
- codeChallenge,
4594
- resource: tool.config.mcp.server.url
4585
+ clientName: this.defaultConfig.clientName,
4586
+ clientUri: this.defaultConfig.clientUri,
4587
+ logoUri: this.defaultConfig.logoUri,
4588
+ defaultClientId: this.defaultConfig.defaultClientId,
4589
+ logger: logger6
4595
4590
  });
4596
- storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
4597
- logger6.info({ toolId, oAuthConfig, tenantId, projectId }, "OAuth flow initiated successfully");
4591
+ storePKCEVerifier(
4592
+ state,
4593
+ authResult.codeVerifier,
4594
+ toolId,
4595
+ tenantId,
4596
+ projectId,
4597
+ authResult.clientInformation,
4598
+ authResult.metadata,
4599
+ authResult.resourceUrl
4600
+ );
4601
+ logger6.info(
4602
+ {
4603
+ toolId,
4604
+ authorizationUrl: authResult.authorizationUrl,
4605
+ tenantId,
4606
+ projectId,
4607
+ scopes: authResult.scopes
4608
+ },
4609
+ "MCP OAuth flow initiated successfully"
4610
+ );
4598
4611
  return {
4599
- redirectUrl: authUrl,
4612
+ redirectUrl: authResult.authorizationUrl,
4600
4613
  state
4601
4614
  };
4602
4615
  }
4603
4616
  /**
4604
- * Exchange authorization code for access tokens
4617
+ * Exchange authorization code for access tokens using MCP SDK with stored metadata
4605
4618
  */
4606
4619
  async exchangeCodeForTokens(params) {
4607
- const { code, codeVerifier, clientId, tool, baseUrl } = params;
4608
- if (tool.config.type !== "mcp") {
4609
- throw new Error("OAuth is only supported for MCP tools");
4610
- }
4611
- const oAuthConfig = await agentsCore.discoverOAuthEndpoints(tool.config.mcp.server.url, logger6);
4612
- if (!oAuthConfig?.tokenUrl) {
4613
- throw new Error("Could not discover OAuth token endpoint");
4614
- }
4620
+ const { code, codeVerifier, clientInformation, metadata, resourceUrl, mcpServerUrl, baseUrl } = params;
4615
4621
  const redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
4616
4622
  const redirectUri = `${redirectBaseUrl}/oauth/callback`;
4617
- let tokens;
4618
- try {
4619
- tokens = await this.exchangeWithOpenIdClient({
4620
- oAuthConfig,
4621
- clientId,
4622
- code,
4623
- codeVerifier,
4624
- redirectUri
4625
- });
4626
- logger6.info({ tokenType: tokens.token_type }, "Token exchange successful with openid-client");
4627
- } catch (error) {
4628
- logger6.warn(
4629
- { error: error instanceof Error ? error.message : error },
4630
- "openid-client failed, falling back to manual token exchange"
4631
- );
4632
- tokens = await this.exchangeManually({
4633
- oAuthConfig,
4634
- clientId,
4635
- code,
4636
- codeVerifier,
4637
- redirectUri
4638
- });
4639
- logger6.info({ tokenType: tokens.token_type }, "Manual token exchange successful");
4640
- }
4641
- return { tokens, oAuthConfig };
4642
- }
4643
- /**
4644
- * Perform dynamic client registration
4645
- */
4646
- async performDynamicClientRegistration(registrationUrl, redirectUri) {
4647
- logger6.info({ registrationUrl }, "Attempting dynamic client registration");
4648
- try {
4649
- const registrationResponse = await fetch(registrationUrl, {
4650
- method: "POST",
4651
- headers: {
4652
- "Content-Type": "application/json",
4653
- Accept: "application/json"
4654
- },
4655
- body: JSON.stringify({
4656
- client_name: this.defaultConfig.clientName,
4657
- client_uri: this.defaultConfig.clientUri,
4658
- logo_uri: this.defaultConfig.logoUri,
4659
- redirect_uris: [redirectUri],
4660
- grant_types: ["authorization_code"],
4661
- response_types: ["code"],
4662
- token_endpoint_auth_method: "none",
4663
- // PKCE only, no client secret
4664
- application_type: "native"
4665
- // For PKCE flows
4666
- })
4667
- });
4668
- if (registrationResponse.ok) {
4669
- const registration = await registrationResponse.json();
4670
- logger6.info({ clientId: registration.client_id }, "Dynamic client registration successful");
4671
- return registration.client_id;
4672
- } else {
4673
- const errorText = await registrationResponse.text();
4674
- logger6.warn(
4675
- {
4676
- status: registrationResponse.status,
4677
- errorText
4678
- },
4679
- "Dynamic client registration failed, using default client_id"
4680
- );
4681
- }
4682
- } catch (regError) {
4683
- logger6.warn(
4684
- { error: regError },
4685
- "Dynamic client registration error, using default client_id"
4686
- );
4687
- }
4688
- return this.defaultConfig.defaultClientId;
4689
- }
4690
- /**
4691
- * Build authorization URL
4692
- */
4693
- buildAuthorizationUrl(params) {
4694
- const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
4695
- const authUrl = new URL(oAuthConfig.authorizationUrl);
4696
- authUrl.searchParams.set("response_type", "code");
4697
- authUrl.searchParams.set("client_id", clientId);
4698
- authUrl.searchParams.set("redirect_uri", redirectUri);
4699
- authUrl.searchParams.set("state", state);
4700
- authUrl.searchParams.set("code_challenge", codeChallenge);
4701
- authUrl.searchParams.set("code_challenge_method", "S256");
4702
- authUrl.searchParams.set("resource", resource);
4703
- return authUrl.toString();
4704
- }
4705
- /**
4706
- * Exchange code using openid-client library
4707
- */
4708
- async exchangeWithOpenIdClient(params) {
4709
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
4710
- const oauth = await import('openid-client');
4711
- const tokenUrl = new URL(oAuthConfig.tokenUrl);
4712
- const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
4713
- logger6.info({ oauthServerUrl, clientId }, "Attempting openid-client discovery");
4714
- const config = await oauth.discovery(
4715
- new URL(oauthServerUrl),
4716
- clientId,
4717
- void 0
4718
- // No client secret for PKCE
4719
- );
4720
- const callbackUrl = new URL(
4721
- `${redirectUri}?${new URLSearchParams({ code, state: "unused" }).toString()}`
4722
- );
4723
- return await oauth.authorizationCodeGrant(config, callbackUrl, {
4724
- pkceCodeVerifier: codeVerifier
4623
+ const tokens = await agentsCore.exchangeMcpAuthorizationCode({
4624
+ mcpServerUrl,
4625
+ metadata,
4626
+ clientInformation,
4627
+ authorizationCode: code,
4628
+ codeVerifier,
4629
+ redirectUri,
4630
+ resourceUrl,
4631
+ logger: logger6
4725
4632
  });
4726
- }
4727
- /**
4728
- * Internal PKCE generation
4729
- */
4730
- async generatePKCEInternal() {
4731
- const codeVerifier = Buffer.from(
4732
- Array.from(crypto.getRandomValues(new Uint8Array(32)))
4733
- ).toString("base64url");
4734
- const encoder = new TextEncoder();
4735
- const data = encoder.encode(codeVerifier);
4736
- const hash = await crypto.subtle.digest("SHA-256", data);
4737
- const codeChallenge = Buffer.from(hash).toString("base64url");
4738
- return { codeVerifier, codeChallenge };
4739
- }
4740
- /**
4741
- * Manual token exchange fallback
4742
- */
4743
- async exchangeManually(params) {
4744
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
4745
- logger6.info({ tokenUrl: oAuthConfig.tokenUrl }, "Attempting manual token exchange");
4746
- const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
4747
- method: "POST",
4748
- headers: {
4749
- "Content-Type": "application/x-www-form-urlencoded",
4750
- Accept: "application/json"
4633
+ logger6.info(
4634
+ {
4635
+ tokenType: tokens.token_type,
4636
+ hasRefreshToken: !!tokens.refresh_token,
4637
+ clientId: clientInformation.client_id
4751
4638
  },
4752
- body: new URLSearchParams({
4753
- grant_type: "authorization_code",
4754
- code,
4755
- redirect_uri: redirectUri,
4756
- client_id: clientId,
4757
- code_verifier: codeVerifier
4758
- // PKCE verification
4759
- })
4760
- });
4761
- if (!tokenResponse.ok) {
4762
- const errorText = await tokenResponse.text();
4763
- logger6.error(
4764
- {
4765
- status: tokenResponse.status,
4766
- statusText: tokenResponse.statusText,
4767
- ...process.env.NODE_ENV === "development" && { errorText },
4768
- clientId,
4769
- tokenUrl: oAuthConfig.tokenUrl
4770
- },
4771
- "Token exchange failed"
4772
- );
4773
- throw new Error("Authentication failed. Please try again or contact support.");
4774
- }
4775
- return await tokenResponse.json();
4639
+ "MCP token exchange successful"
4640
+ );
4641
+ return { tokens };
4776
4642
  }
4777
4643
  };
4778
4644
  var oauthService = new OAuthService();
@@ -4939,14 +4805,12 @@ app20.openapi(
4939
4805
  logger7.error({ toolId, tenantId, projectId }, "Tool not found for OAuth login");
4940
4806
  return c.text("Tool not found", 404);
4941
4807
  }
4942
- const credentialStores = c.get("credentialStores");
4943
- const mcpTool = await agentsCore.dbResultToMcpTool(tool, dbClient_default, credentialStores);
4944
4808
  const baseUrl = getBaseUrlFromRequest(c);
4945
4809
  const { redirectUrl } = await oauthService.initiateOAuthFlow({
4946
- tool: mcpTool,
4947
4810
  tenantId,
4948
4811
  projectId,
4949
4812
  toolId,
4813
+ mcpServerUrl: tool.config.mcp.server.url,
4950
4814
  baseUrl
4951
4815
  });
4952
4816
  return c.redirect(redirectUrl, 302);
@@ -5015,7 +4879,15 @@ app20.openapi(
5015
4879
  });
5016
4880
  return c.html(expiredPage);
5017
4881
  }
5018
- const { codeVerifier, toolId, tenantId, projectId, clientId } = pkceData;
4882
+ const {
4883
+ codeVerifier,
4884
+ toolId,
4885
+ tenantId,
4886
+ projectId,
4887
+ clientInformation,
4888
+ metadata,
4889
+ resourceUrl
4890
+ } = pkceData;
5019
4891
  const tool = await agentsCore.getToolById(dbClient_default)({
5020
4892
  scopes: { tenantId, projectId },
5021
4893
  toolId
@@ -5026,13 +4898,14 @@ app20.openapi(
5026
4898
  logger7.info({ toolId, tenantId, projectId }, "Processing OAuth callback");
5027
4899
  logger7.info({ toolId }, "Exchanging authorization code for access token");
5028
4900
  const credentialStores = c.get("credentialStores");
5029
- const mcpTool = await agentsCore.dbResultToMcpTool(tool, dbClient_default, credentialStores);
5030
4901
  const baseUrl = getBaseUrlFromRequest(c);
5031
4902
  const { tokens } = await oauthService.exchangeCodeForTokens({
5032
4903
  code,
5033
4904
  codeVerifier,
5034
- clientId,
5035
- tool: mcpTool,
4905
+ clientInformation,
4906
+ metadata,
4907
+ resourceUrl,
4908
+ mcpServerUrl: tool.config.mcp.server.url,
5036
4909
  baseUrl
5037
4910
  });
5038
4911
  logger7.info(
@@ -5041,39 +4914,23 @@ app20.openapi(
5041
4914
  );
5042
4915
  const credentialTokenKey = `oauth_token_${toolId}`;
5043
4916
  let newCredentialData;
5044
- if (process.env.NANGO_SECRET_KEY) {
5045
- const nangoStore = credentialStores.get("nango-default");
5046
- await nangoStore?.set(credentialTokenKey, JSON.stringify(tokens));
5047
- newCredentialData = {
5048
- id: agentsCore.generateIdFromName(mcpTool.name),
5049
- type: agentsCore.CredentialStoreType.nango,
5050
- credentialStoreId: "nango-default",
5051
- retrievalParams: {
5052
- connectionId: credentialTokenKey,
5053
- providerConfigKey: credentialTokenKey,
5054
- provider: "private-api-bearer",
5055
- authMode: "API_KEY"
5056
- }
5057
- };
5058
- } else {
5059
- const keychainStore = credentialStores.get("keychain-default");
5060
- if (keychainStore) {
5061
- try {
5062
- await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
5063
- newCredentialData = {
5064
- id: agentsCore.generateIdFromName(mcpTool.name),
5065
- type: agentsCore.CredentialStoreType.keychain,
5066
- credentialStoreId: "keychain-default",
5067
- retrievalParams: {
5068
- key: credentialTokenKey
5069
- }
5070
- };
5071
- } catch (error2) {
5072
- logger7.info(
5073
- { error: error2 instanceof Error ? error2.message : error2 },
5074
- "Keychain store not available."
5075
- );
5076
- }
4917
+ const keychainStore = credentialStores.get("keychain-default");
4918
+ if (keychainStore) {
4919
+ try {
4920
+ await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
4921
+ newCredentialData = {
4922
+ id: agentsCore.generateIdFromName(tool.name),
4923
+ type: agentsCore.CredentialStoreType.keychain,
4924
+ credentialStoreId: "keychain-default",
4925
+ retrievalParams: {
4926
+ key: credentialTokenKey
4927
+ }
4928
+ };
4929
+ } catch (error2) {
4930
+ logger7.info(
4931
+ { error: error2 instanceof Error ? error2.message : error2 },
4932
+ "Keychain store not available."
4933
+ );
5077
4934
  }
5078
4935
  }
5079
4936
  if (!newCredentialData) {
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { loadEnvironmentFiles, getLogger, createDatabaseClient, commonGetErrorResponses, AgentListResponse, PaginationQueryParamsSchema, TenantProjectParamsSchema, listAgents, AgentResponse, TenantProjectIdParamsSchema, getAgentById, createApiError, ListResponseSchema, getAgentSubAgentInfos, SingleResponseSchema, TenantProjectAgentParamsSchema, AgentWithinContextOfProjectSchema, getFullAgentDefinition, AgentApiInsertSchema, createAgent, AgentApiUpdateSchema, updateAgent, ErrorResponseSchema, deleteAgent, createFullAgentServerSide, getFullAgent, updateFullAgentServerSide, deleteFullAgent, ApiKeyListResponse, listApiKeysPaginated, ApiKeyResponse, getApiKeyById, ApiKeyApiCreationResponseSchema, ApiKeyApiInsertSchema, generateApiKey, createApiKey, ApiKeyApiUpdateSchema, updateApiKey, deleteApiKey, ArtifactComponentListResponse, listArtifactComponentsPaginated, ArtifactComponentResponse, getArtifactComponentById, ArtifactComponentApiInsertSchema, validatePropsAsJsonSchema, createArtifactComponent, ArtifactComponentApiUpdateSchema, updateArtifactComponent, deleteArtifactComponent, ContextConfigListResponse, listContextConfigsPaginated, ContextConfigResponse, TenantProjectAgentIdParamsSchema, getContextConfigById, ContextConfigApiInsertSchema, createContextConfig, commonUpdateErrorResponses, ContextConfigApiUpdateSchema, updateContextConfig, commonDeleteErrorResponses, deleteContextConfig, CredentialStoreType, CredentialReferenceListResponse, listCredentialReferencesPaginated, CredentialReferenceApiSelectSchema, CredentialReferenceResponse, getCredentialReferenceWithTools, CredentialReferenceApiInsertSchema, createCredentialReference, CredentialReferenceApiUpdateSchema, updateCredentialReference, getCredentialReferenceById, getCredentialStoreLookupKeyFromRetrievalParams, deleteCredentialReference, DataComponentListResponse, listDataComponentsPaginated, DataComponentResponse, getDataComponent, DataComponentApiInsertSchema, createDataComponent, DataComponentApiUpdateSchema, updateDataComponent, deleteDataComponent, ExternalAgentListResponse, listExternalAgentsPaginated, ExternalAgentResponse, getExternalAgent, ExternalAgentApiInsertSchema, createExternalAgent, ExternalAgentApiUpdateSchema, updateExternalAgent, deleteExternalAgent, FunctionListResponse, listFunctions, FunctionResponse, getFunction, FunctionApiInsertSchema, upsertFunction, FunctionApiUpdateSchema, deleteFunction, FunctionToolApiSelectSchema, listFunctionTools, getFunctionToolById, FunctionToolApiInsertSchema, createFunctionTool, FunctionToolApiUpdateSchema, updateFunctionTool, deleteFunctionTool, ProjectListResponse, TenantParamsSchema, listProjectsPaginated, ProjectResponse, TenantIdParamsSchema, getProject, ProjectApiInsertSchema, createProject, ProjectApiUpdateSchema, updateProject, deleteProject, ArtifactComponentApiSelectSchema, getArtifactComponentsForAgent, getAgentsUsingArtifactComponent, SubAgentArtifactComponentApiInsertSchema, SubAgentArtifactComponentApiSelectSchema, getSubAgentById, isArtifactComponentAssociatedWithAgent, associateArtifactComponentWithAgent, RemovedResponseSchema, removeArtifactComponentFromAgent, ExistsResponseSchema, DataComponentApiSelectSchema, getDataComponentsForAgent, getAgentsUsingDataComponent, SubAgentDataComponentApiInsertSchema, SubAgentDataComponentApiSelectSchema, isDataComponentAssociatedWithAgent, associateDataComponentWithAgent, removeDataComponentFromAgent, SubAgentRelationApiSelectSchema, SubAgentRelationQuerySchema, getAgentRelationsBySource, getSubAgentRelationsByTarget, getExternalAgentRelations, listAgentRelations, getAgentRelationById, SubAgentRelationApiInsertSchema, validateExternalAgent, validateInternalSubAgent, createSubAgentRelation, SubAgentRelationApiUpdateSchema, updateAgentRelation, deleteSubAgentRelation, SubAgentListResponse, listSubAgentsPaginated, SubAgentResponse, SubAgentApiInsertSchema, createSubAgent, SubAgentApiUpdateSchema, updateSubAgent, deleteSubAgent, SubAgentToolRelationApiSelectSchema, getAgentToolRelationByAgent, getAgentToolRelationByTool, listAgentToolRelations, getAgentToolRelationById, getAgentsForTool, SubAgentToolRelationApiInsertSchema, createAgentToolRelation, SubAgentToolRelationApiUpdateSchema, updateAgentToolRelation, deleteAgentToolRelation, McpToolSchema, ToolStatusSchema, listTools, dbResultToMcpTool, getToolById, ToolApiInsertSchema, createTool, ToolApiUpdateSchema, updateTool, deleteTool, generateIdFromName, FullProjectDefinitionSchema, createFullProjectServerSide, getFullProject, updateFullProjectServerSide, deleteFullProject, createDefaultCredentialStores, CredentialStoreRegistry, discoverOAuthEndpoints, handleApiError } from '@inkeep/agents-core';
1
+ import { loadEnvironmentFiles, getLogger, createDatabaseClient, commonGetErrorResponses, AgentListResponse, PaginationQueryParamsSchema, TenantProjectParamsSchema, listAgents, AgentResponse, TenantProjectIdParamsSchema, getAgentById, createApiError, ListResponseSchema, getAgentSubAgentInfos, SingleResponseSchema, TenantProjectAgentParamsSchema, AgentWithinContextOfProjectSchema, getFullAgentDefinition, AgentApiInsertSchema, createAgent, AgentApiUpdateSchema, updateAgent, ErrorResponseSchema, deleteAgent, createFullAgentServerSide, getFullAgent, updateFullAgentServerSide, deleteFullAgent, ApiKeyListResponse, listApiKeysPaginated, ApiKeyResponse, getApiKeyById, ApiKeyApiCreationResponseSchema, ApiKeyApiInsertSchema, generateApiKey, createApiKey, ApiKeyApiUpdateSchema, updateApiKey, deleteApiKey, ArtifactComponentListResponse, listArtifactComponentsPaginated, ArtifactComponentResponse, getArtifactComponentById, ArtifactComponentApiInsertSchema, validatePropsAsJsonSchema, createArtifactComponent, ArtifactComponentApiUpdateSchema, updateArtifactComponent, deleteArtifactComponent, ContextConfigListResponse, listContextConfigsPaginated, ContextConfigResponse, TenantProjectAgentIdParamsSchema, getContextConfigById, ContextConfigApiInsertSchema, createContextConfig, commonUpdateErrorResponses, ContextConfigApiUpdateSchema, updateContextConfig, commonDeleteErrorResponses, deleteContextConfig, CredentialStoreType, CredentialReferenceListResponse, listCredentialReferencesPaginated, CredentialReferenceApiSelectSchema, CredentialReferenceResponse, getCredentialReferenceWithTools, CredentialReferenceApiInsertSchema, createCredentialReference, CredentialReferenceApiUpdateSchema, updateCredentialReference, getCredentialReferenceById, getCredentialStoreLookupKeyFromRetrievalParams, deleteCredentialReference, DataComponentListResponse, listDataComponentsPaginated, DataComponentResponse, getDataComponent, DataComponentApiInsertSchema, createDataComponent, DataComponentApiUpdateSchema, updateDataComponent, deleteDataComponent, ExternalAgentListResponse, listExternalAgentsPaginated, ExternalAgentResponse, getExternalAgent, ExternalAgentApiInsertSchema, createExternalAgent, ExternalAgentApiUpdateSchema, updateExternalAgent, deleteExternalAgent, FunctionListResponse, listFunctions, FunctionResponse, getFunction, FunctionApiInsertSchema, upsertFunction, FunctionApiUpdateSchema, deleteFunction, FunctionToolApiSelectSchema, listFunctionTools, getFunctionToolById, FunctionToolApiInsertSchema, createFunctionTool, FunctionToolApiUpdateSchema, updateFunctionTool, deleteFunctionTool, ProjectListResponse, TenantParamsSchema, listProjectsPaginated, ProjectResponse, TenantIdParamsSchema, getProject, ProjectApiInsertSchema, createProject, ProjectApiUpdateSchema, updateProject, deleteProject, ArtifactComponentApiSelectSchema, getArtifactComponentsForAgent, getAgentsUsingArtifactComponent, SubAgentArtifactComponentApiInsertSchema, SubAgentArtifactComponentApiSelectSchema, getSubAgentById, isArtifactComponentAssociatedWithAgent, associateArtifactComponentWithAgent, RemovedResponseSchema, removeArtifactComponentFromAgent, ExistsResponseSchema, DataComponentApiSelectSchema, getDataComponentsForAgent, getAgentsUsingDataComponent, SubAgentDataComponentApiInsertSchema, SubAgentDataComponentApiSelectSchema, isDataComponentAssociatedWithAgent, associateDataComponentWithAgent, removeDataComponentFromAgent, SubAgentRelationApiSelectSchema, SubAgentRelationQuerySchema, getAgentRelationsBySource, getSubAgentRelationsByTarget, getExternalAgentRelations, listAgentRelations, getAgentRelationById, SubAgentRelationApiInsertSchema, validateExternalAgent, validateInternalSubAgent, createSubAgentRelation, SubAgentRelationApiUpdateSchema, updateAgentRelation, deleteSubAgentRelation, SubAgentListResponse, listSubAgentsPaginated, SubAgentResponse, SubAgentApiInsertSchema, createSubAgent, SubAgentApiUpdateSchema, updateSubAgent, deleteSubAgent, SubAgentToolRelationApiSelectSchema, getAgentToolRelationByAgent, getAgentToolRelationByTool, listAgentToolRelations, getAgentToolRelationById, getAgentsForTool, SubAgentToolRelationApiInsertSchema, createAgentToolRelation, SubAgentToolRelationApiUpdateSchema, updateAgentToolRelation, deleteAgentToolRelation, McpToolSchema, ToolStatusSchema, listTools, dbResultToMcpTool, getToolById, ToolApiInsertSchema, createTool, ToolApiUpdateSchema, updateTool, deleteTool, generateIdFromName, FullProjectDefinitionSchema, createFullProjectServerSide, getFullProject, updateFullProjectServerSide, deleteFullProject, createDefaultCredentialStores, CredentialStoreRegistry, initiateMcpOAuthFlow, exchangeMcpAuthorizationCode, 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';
@@ -4530,8 +4530,16 @@ app19.route("/projects/:projectId/agent", agentFull_default);
4530
4530
  var routes_default = app19;
4531
4531
  var logger6 = getLogger("oauth-service");
4532
4532
  var pkceStore = /* @__PURE__ */ new Map();
4533
- function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId) {
4534
- pkceStore.set(state, { codeVerifier, toolId, tenantId, projectId, clientId });
4533
+ function storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientInformation, metadata, resourceUrl) {
4534
+ pkceStore.set(state, {
4535
+ codeVerifier,
4536
+ toolId,
4537
+ tenantId,
4538
+ projectId,
4539
+ clientInformation,
4540
+ metadata,
4541
+ resourceUrl
4542
+ });
4535
4543
  setTimeout(
4536
4544
  () => {
4537
4545
  pkceStore.delete(state);
@@ -4559,216 +4567,74 @@ var OAuthService = class {
4559
4567
  };
4560
4568
  }
4561
4569
  /**
4562
- * Initiate OAuth flow for an MCP tool
4570
+ * Initiate OAuth flow for an MCP tool using MCP SDK
4563
4571
  */
4564
4572
  async initiateOAuthFlow(params) {
4565
- const { tool, tenantId, projectId, toolId, baseUrl } = params;
4566
- if (tool.config.type !== "mcp") {
4567
- throw new Error("OAuth is only supported for MCP tools");
4568
- }
4569
- const oAuthConfig = await discoverOAuthEndpoints(tool.config.mcp.server.url, logger6);
4570
- if (!oAuthConfig) {
4571
- throw new Error("OAuth not supported by this server");
4572
- }
4573
- const { codeVerifier, codeChallenge } = await this.generatePKCEInternal();
4573
+ const { tenantId, projectId, toolId, mcpServerUrl, baseUrl } = params;
4574
4574
  const redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
4575
4575
  const redirectUri = `${redirectBaseUrl}/oauth/callback`;
4576
- let clientId = this.defaultConfig.defaultClientId;
4577
- if (oAuthConfig.supportsDynamicRegistration && oAuthConfig.registrationUrl) {
4578
- clientId = await this.performDynamicClientRegistration(
4579
- oAuthConfig.registrationUrl,
4580
- redirectUri
4581
- );
4582
- }
4583
4576
  const state = `tool_${toolId}`;
4584
- const authUrl = this.buildAuthorizationUrl({
4585
- oAuthConfig,
4586
- clientId,
4577
+ const authResult = await initiateMcpOAuthFlow({
4578
+ mcpServerUrl,
4587
4579
  redirectUri,
4588
4580
  state,
4589
- codeChallenge,
4590
- resource: tool.config.mcp.server.url
4581
+ clientName: this.defaultConfig.clientName,
4582
+ clientUri: this.defaultConfig.clientUri,
4583
+ logoUri: this.defaultConfig.logoUri,
4584
+ defaultClientId: this.defaultConfig.defaultClientId,
4585
+ logger: logger6
4591
4586
  });
4592
- storePKCEVerifier(state, codeVerifier, toolId, tenantId, projectId, clientId);
4593
- logger6.info({ toolId, oAuthConfig, tenantId, projectId }, "OAuth flow initiated successfully");
4587
+ storePKCEVerifier(
4588
+ state,
4589
+ authResult.codeVerifier,
4590
+ toolId,
4591
+ tenantId,
4592
+ projectId,
4593
+ authResult.clientInformation,
4594
+ authResult.metadata,
4595
+ authResult.resourceUrl
4596
+ );
4597
+ logger6.info(
4598
+ {
4599
+ toolId,
4600
+ authorizationUrl: authResult.authorizationUrl,
4601
+ tenantId,
4602
+ projectId,
4603
+ scopes: authResult.scopes
4604
+ },
4605
+ "MCP OAuth flow initiated successfully"
4606
+ );
4594
4607
  return {
4595
- redirectUrl: authUrl,
4608
+ redirectUrl: authResult.authorizationUrl,
4596
4609
  state
4597
4610
  };
4598
4611
  }
4599
4612
  /**
4600
- * Exchange authorization code for access tokens
4613
+ * Exchange authorization code for access tokens using MCP SDK with stored metadata
4601
4614
  */
4602
4615
  async exchangeCodeForTokens(params) {
4603
- const { code, codeVerifier, clientId, tool, baseUrl } = params;
4604
- if (tool.config.type !== "mcp") {
4605
- throw new Error("OAuth is only supported for MCP tools");
4606
- }
4607
- const oAuthConfig = await discoverOAuthEndpoints(tool.config.mcp.server.url, logger6);
4608
- if (!oAuthConfig?.tokenUrl) {
4609
- throw new Error("Could not discover OAuth token endpoint");
4610
- }
4616
+ const { code, codeVerifier, clientInformation, metadata, resourceUrl, mcpServerUrl, baseUrl } = params;
4611
4617
  const redirectBaseUrl = baseUrl || this.defaultConfig.redirectBaseUrl;
4612
4618
  const redirectUri = `${redirectBaseUrl}/oauth/callback`;
4613
- let tokens;
4614
- try {
4615
- tokens = await this.exchangeWithOpenIdClient({
4616
- oAuthConfig,
4617
- clientId,
4618
- code,
4619
- codeVerifier,
4620
- redirectUri
4621
- });
4622
- logger6.info({ tokenType: tokens.token_type }, "Token exchange successful with openid-client");
4623
- } catch (error) {
4624
- logger6.warn(
4625
- { error: error instanceof Error ? error.message : error },
4626
- "openid-client failed, falling back to manual token exchange"
4627
- );
4628
- tokens = await this.exchangeManually({
4629
- oAuthConfig,
4630
- clientId,
4631
- code,
4632
- codeVerifier,
4633
- redirectUri
4634
- });
4635
- logger6.info({ tokenType: tokens.token_type }, "Manual token exchange successful");
4636
- }
4637
- return { tokens, oAuthConfig };
4638
- }
4639
- /**
4640
- * Perform dynamic client registration
4641
- */
4642
- async performDynamicClientRegistration(registrationUrl, redirectUri) {
4643
- logger6.info({ registrationUrl }, "Attempting dynamic client registration");
4644
- try {
4645
- const registrationResponse = await fetch(registrationUrl, {
4646
- method: "POST",
4647
- headers: {
4648
- "Content-Type": "application/json",
4649
- Accept: "application/json"
4650
- },
4651
- body: JSON.stringify({
4652
- client_name: this.defaultConfig.clientName,
4653
- client_uri: this.defaultConfig.clientUri,
4654
- logo_uri: this.defaultConfig.logoUri,
4655
- redirect_uris: [redirectUri],
4656
- grant_types: ["authorization_code"],
4657
- response_types: ["code"],
4658
- token_endpoint_auth_method: "none",
4659
- // PKCE only, no client secret
4660
- application_type: "native"
4661
- // For PKCE flows
4662
- })
4663
- });
4664
- if (registrationResponse.ok) {
4665
- const registration = await registrationResponse.json();
4666
- logger6.info({ clientId: registration.client_id }, "Dynamic client registration successful");
4667
- return registration.client_id;
4668
- } else {
4669
- const errorText = await registrationResponse.text();
4670
- logger6.warn(
4671
- {
4672
- status: registrationResponse.status,
4673
- errorText
4674
- },
4675
- "Dynamic client registration failed, using default client_id"
4676
- );
4677
- }
4678
- } catch (regError) {
4679
- logger6.warn(
4680
- { error: regError },
4681
- "Dynamic client registration error, using default client_id"
4682
- );
4683
- }
4684
- return this.defaultConfig.defaultClientId;
4685
- }
4686
- /**
4687
- * Build authorization URL
4688
- */
4689
- buildAuthorizationUrl(params) {
4690
- const { oAuthConfig, clientId, redirectUri, state, codeChallenge, resource } = params;
4691
- const authUrl = new URL(oAuthConfig.authorizationUrl);
4692
- authUrl.searchParams.set("response_type", "code");
4693
- authUrl.searchParams.set("client_id", clientId);
4694
- authUrl.searchParams.set("redirect_uri", redirectUri);
4695
- authUrl.searchParams.set("state", state);
4696
- authUrl.searchParams.set("code_challenge", codeChallenge);
4697
- authUrl.searchParams.set("code_challenge_method", "S256");
4698
- authUrl.searchParams.set("resource", resource);
4699
- return authUrl.toString();
4700
- }
4701
- /**
4702
- * Exchange code using openid-client library
4703
- */
4704
- async exchangeWithOpenIdClient(params) {
4705
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
4706
- const oauth = await import('openid-client');
4707
- const tokenUrl = new URL(oAuthConfig.tokenUrl);
4708
- const oauthServerUrl = `${tokenUrl.protocol}//${tokenUrl.host}`;
4709
- logger6.info({ oauthServerUrl, clientId }, "Attempting openid-client discovery");
4710
- const config = await oauth.discovery(
4711
- new URL(oauthServerUrl),
4712
- clientId,
4713
- void 0
4714
- // No client secret for PKCE
4715
- );
4716
- const callbackUrl = new URL(
4717
- `${redirectUri}?${new URLSearchParams({ code, state: "unused" }).toString()}`
4718
- );
4719
- return await oauth.authorizationCodeGrant(config, callbackUrl, {
4720
- pkceCodeVerifier: codeVerifier
4619
+ const tokens = await exchangeMcpAuthorizationCode({
4620
+ mcpServerUrl,
4621
+ metadata,
4622
+ clientInformation,
4623
+ authorizationCode: code,
4624
+ codeVerifier,
4625
+ redirectUri,
4626
+ resourceUrl,
4627
+ logger: logger6
4721
4628
  });
4722
- }
4723
- /**
4724
- * Internal PKCE generation
4725
- */
4726
- async generatePKCEInternal() {
4727
- const codeVerifier = Buffer.from(
4728
- Array.from(crypto.getRandomValues(new Uint8Array(32)))
4729
- ).toString("base64url");
4730
- const encoder = new TextEncoder();
4731
- const data = encoder.encode(codeVerifier);
4732
- const hash = await crypto.subtle.digest("SHA-256", data);
4733
- const codeChallenge = Buffer.from(hash).toString("base64url");
4734
- return { codeVerifier, codeChallenge };
4735
- }
4736
- /**
4737
- * Manual token exchange fallback
4738
- */
4739
- async exchangeManually(params) {
4740
- const { oAuthConfig, clientId, code, codeVerifier, redirectUri } = params;
4741
- logger6.info({ tokenUrl: oAuthConfig.tokenUrl }, "Attempting manual token exchange");
4742
- const tokenResponse = await fetch(oAuthConfig.tokenUrl, {
4743
- method: "POST",
4744
- headers: {
4745
- "Content-Type": "application/x-www-form-urlencoded",
4746
- Accept: "application/json"
4629
+ logger6.info(
4630
+ {
4631
+ tokenType: tokens.token_type,
4632
+ hasRefreshToken: !!tokens.refresh_token,
4633
+ clientId: clientInformation.client_id
4747
4634
  },
4748
- body: new URLSearchParams({
4749
- grant_type: "authorization_code",
4750
- code,
4751
- redirect_uri: redirectUri,
4752
- client_id: clientId,
4753
- code_verifier: codeVerifier
4754
- // PKCE verification
4755
- })
4756
- });
4757
- if (!tokenResponse.ok) {
4758
- const errorText = await tokenResponse.text();
4759
- logger6.error(
4760
- {
4761
- status: tokenResponse.status,
4762
- statusText: tokenResponse.statusText,
4763
- ...process.env.NODE_ENV === "development" && { errorText },
4764
- clientId,
4765
- tokenUrl: oAuthConfig.tokenUrl
4766
- },
4767
- "Token exchange failed"
4768
- );
4769
- throw new Error("Authentication failed. Please try again or contact support.");
4770
- }
4771
- return await tokenResponse.json();
4635
+ "MCP token exchange successful"
4636
+ );
4637
+ return { tokens };
4772
4638
  }
4773
4639
  };
4774
4640
  var oauthService = new OAuthService();
@@ -4935,14 +4801,12 @@ app20.openapi(
4935
4801
  logger7.error({ toolId, tenantId, projectId }, "Tool not found for OAuth login");
4936
4802
  return c.text("Tool not found", 404);
4937
4803
  }
4938
- const credentialStores = c.get("credentialStores");
4939
- const mcpTool = await dbResultToMcpTool(tool, dbClient_default, credentialStores);
4940
4804
  const baseUrl = getBaseUrlFromRequest(c);
4941
4805
  const { redirectUrl } = await oauthService.initiateOAuthFlow({
4942
- tool: mcpTool,
4943
4806
  tenantId,
4944
4807
  projectId,
4945
4808
  toolId,
4809
+ mcpServerUrl: tool.config.mcp.server.url,
4946
4810
  baseUrl
4947
4811
  });
4948
4812
  return c.redirect(redirectUrl, 302);
@@ -5011,7 +4875,15 @@ app20.openapi(
5011
4875
  });
5012
4876
  return c.html(expiredPage);
5013
4877
  }
5014
- const { codeVerifier, toolId, tenantId, projectId, clientId } = pkceData;
4878
+ const {
4879
+ codeVerifier,
4880
+ toolId,
4881
+ tenantId,
4882
+ projectId,
4883
+ clientInformation,
4884
+ metadata,
4885
+ resourceUrl
4886
+ } = pkceData;
5015
4887
  const tool = await getToolById(dbClient_default)({
5016
4888
  scopes: { tenantId, projectId },
5017
4889
  toolId
@@ -5022,13 +4894,14 @@ app20.openapi(
5022
4894
  logger7.info({ toolId, tenantId, projectId }, "Processing OAuth callback");
5023
4895
  logger7.info({ toolId }, "Exchanging authorization code for access token");
5024
4896
  const credentialStores = c.get("credentialStores");
5025
- const mcpTool = await dbResultToMcpTool(tool, dbClient_default, credentialStores);
5026
4897
  const baseUrl = getBaseUrlFromRequest(c);
5027
4898
  const { tokens } = await oauthService.exchangeCodeForTokens({
5028
4899
  code,
5029
4900
  codeVerifier,
5030
- clientId,
5031
- tool: mcpTool,
4901
+ clientInformation,
4902
+ metadata,
4903
+ resourceUrl,
4904
+ mcpServerUrl: tool.config.mcp.server.url,
5032
4905
  baseUrl
5033
4906
  });
5034
4907
  logger7.info(
@@ -5037,39 +4910,23 @@ app20.openapi(
5037
4910
  );
5038
4911
  const credentialTokenKey = `oauth_token_${toolId}`;
5039
4912
  let newCredentialData;
5040
- if (process.env.NANGO_SECRET_KEY) {
5041
- const nangoStore = credentialStores.get("nango-default");
5042
- await nangoStore?.set(credentialTokenKey, JSON.stringify(tokens));
5043
- newCredentialData = {
5044
- id: generateIdFromName(mcpTool.name),
5045
- type: CredentialStoreType.nango,
5046
- credentialStoreId: "nango-default",
5047
- retrievalParams: {
5048
- connectionId: credentialTokenKey,
5049
- providerConfigKey: credentialTokenKey,
5050
- provider: "private-api-bearer",
5051
- authMode: "API_KEY"
5052
- }
5053
- };
5054
- } else {
5055
- const keychainStore = credentialStores.get("keychain-default");
5056
- if (keychainStore) {
5057
- try {
5058
- await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
5059
- newCredentialData = {
5060
- id: generateIdFromName(mcpTool.name),
5061
- type: CredentialStoreType.keychain,
5062
- credentialStoreId: "keychain-default",
5063
- retrievalParams: {
5064
- key: credentialTokenKey
5065
- }
5066
- };
5067
- } catch (error2) {
5068
- logger7.info(
5069
- { error: error2 instanceof Error ? error2.message : error2 },
5070
- "Keychain store not available."
5071
- );
5072
- }
4913
+ const keychainStore = credentialStores.get("keychain-default");
4914
+ if (keychainStore) {
4915
+ try {
4916
+ await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
4917
+ newCredentialData = {
4918
+ id: generateIdFromName(tool.name),
4919
+ type: CredentialStoreType.keychain,
4920
+ credentialStoreId: "keychain-default",
4921
+ retrievalParams: {
4922
+ key: credentialTokenKey
4923
+ }
4924
+ };
4925
+ } catch (error2) {
4926
+ logger7.info(
4927
+ { error: error2 instanceof Error ? error2.message : error2 },
4928
+ "Keychain store not available."
4929
+ );
5073
4930
  }
5074
4931
  }
5075
4932
  if (!newCredentialData) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inkeep/agents-manage-api",
3
- "version": "0.22.11",
3
+ "version": "0.23.0",
4
4
  "description": "Agents Manage API for Inkeep Agent Framework - handles CRUD operations and OAuth",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -13,8 +13,8 @@
13
13
  "@hono/node-server": "^1.14.3",
14
14
  "@hono/swagger-ui": "^0.5.1",
15
15
  "@hono/zod-openapi": "^1.0.2",
16
- "@nangohq/node": "^0.66.0",
17
- "@nangohq/types": "^0.66.0",
16
+ "@nangohq/node": "^0.69.3",
17
+ "@nangohq/types": "^0.69.3",
18
18
  "dotenv": "^17.2.1",
19
19
  "drizzle-orm": "^0.44.4",
20
20
  "hono": "^4.9.7",
@@ -23,7 +23,7 @@
23
23
  "openid-client": "^6.6.4",
24
24
  "pino": "^9.7.0",
25
25
  "zod": "^4.1.11",
26
- "@inkeep/agents-core": "^0.22.11"
26
+ "@inkeep/agents-core": "^0.23.0"
27
27
  },
28
28
  "optionalDependencies": {
29
29
  "keytar": "^7.9.0"