@redocly/revel 0.138.0-next.0 → 0.138.0-next.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 (102) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/cli/eject/resolveEjectParams.js +1 -1
  3. package/dist/client/TestProvider.js +1 -1
  4. package/dist/client/api-request-handlers-entry.d.ts +1 -1
  5. package/dist/client/app/Sidebar/Sidebar.js +1 -1
  6. package/dist/client/app/hooks/index.d.ts +0 -1
  7. package/dist/client/app/hooks/index.js +1 -1
  8. package/dist/client/app/l10n/L10nContext.d.ts +10 -0
  9. package/dist/client/app/l10n/L10nContext.js +1 -0
  10. package/dist/client/app/l10n/hooks/useL10n.js +1 -1
  11. package/dist/client/app/l10n/hooks/useTranslate.js +1 -1
  12. package/dist/client/app/l10n/hooks/useTranslationKeys.d.ts +2 -0
  13. package/dist/client/app/l10n/hooks/useTranslationKeys.js +1 -1
  14. package/dist/client/app/l10n/ssrL10n.d.ts +4 -0
  15. package/dist/client/app/l10n/ssrL10n.js +1 -0
  16. package/dist/client/mcp-tool-handlers-entry.d.ts +1 -1
  17. package/dist/client/middleware-entry.d.ts +1 -1
  18. package/dist/client/providers/theme/ThemeDataProvider.js +1 -1
  19. package/dist/client/server-entry.d.ts +2 -1
  20. package/dist/client/server-entry.js +1 -1
  21. package/dist/client/server-props-entry.d.ts +1 -1
  22. package/dist/constants/l10n/langs/ar.js +1 -1
  23. package/dist/constants/l10n/langs/de.js +1 -1
  24. package/dist/constants/l10n/langs/en.js +1 -1
  25. package/dist/constants/l10n/langs/es.js +1 -1
  26. package/dist/constants/l10n/langs/fr.js +1 -1
  27. package/dist/constants/l10n/langs/hi.js +1 -1
  28. package/dist/constants/l10n/langs/it.js +1 -1
  29. package/dist/constants/l10n/langs/ja.js +1 -1
  30. package/dist/constants/l10n/langs/ko.js +1 -1
  31. package/dist/constants/l10n/langs/pl.js +1 -1
  32. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  33. package/dist/constants/l10n/langs/pt.js +1 -1
  34. package/dist/constants/l10n/langs/ru.js +1 -1
  35. package/dist/constants/l10n/langs/uk.js +1 -1
  36. package/dist/constants/l10n/langs/zh.js +1 -1
  37. package/dist/server/constants/common.d.ts +2 -0
  38. package/dist/server/constants/common.js +1 -1
  39. package/dist/server/constants/plugins/search.js +1 -1
  40. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.d.ts +1 -1
  41. package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -1
  42. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.d.ts +5 -1
  43. package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +7 -1
  44. package/dist/server/plugins/catalog-entities/database/repositories/entities/entities-write-repository.js +1 -1
  45. package/dist/server/plugins/catalog-entities/extensions/extractors/api-description/arazzo-entities-extractor.js +1 -1
  46. package/dist/server/plugins/config-parser/index.js +1 -1
  47. package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
  48. package/dist/server/plugins/markdown/attribute-resolvers/code-walkthrough/file-chunk-parser.js +2 -2
  49. package/dist/server/plugins/mcp/auth/assistant-anonymous-token.d.ts +10 -0
  50. package/dist/server/plugins/mcp/auth/assistant-anonymous-token.js +1 -0
  51. package/dist/server/plugins/mcp/auth/auth-handlers.d.ts +5 -3
  52. package/dist/server/plugins/mcp/auth/auth-handlers.js +1 -1
  53. package/dist/server/plugins/mcp/codemode/build-tool-type-declarations.js +4 -4
  54. package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +0 -1
  55. package/dist/server/plugins/mcp/codemode/capabilities/utils.d.ts +1 -2
  56. package/dist/server/plugins/mcp/codemode/capabilities/utils.js +4 -4
  57. package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +2 -2
  58. package/dist/server/plugins/mcp/codemode/tools/execute-schema.d.ts +0 -2
  59. package/dist/server/plugins/mcp/codemode/tools/execute-schema.js +1 -1
  60. package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
  61. package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
  62. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
  63. package/dist/server/plugins/mcp/docs-mcp/tools/core/create-anonymous-session.js +1 -1
  64. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.d.ts +2 -0
  65. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/constants.js +1 -1
  66. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.d.ts +6 -0
  67. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/endpoint-suggestions.js +1 -1
  68. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/get-endpoint-info.js +1 -1
  69. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +2 -0
  70. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
  71. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.d.ts +5 -0
  72. package/dist/server/plugins/mcp/docs-mcp/tools/openapi/utils.js +1 -1
  73. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.d.ts +20 -14
  74. package/dist/server/plugins/mcp/docs-mcp/utils/resolve-refs.js +1 -1
  75. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  76. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  77. package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
  78. package/dist/server/plugins/mcp/utils/disabled-tools.d.ts +3 -0
  79. package/dist/server/plugins/mcp/utils/disabled-tools.js +1 -0
  80. package/dist/server/plugins/mcp/utils/tool-registration.d.ts +2 -1
  81. package/dist/server/plugins/mcp/utils/tool-registration.js +1 -1
  82. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  83. package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
  84. package/dist/server/providers/database/pagination/after-and-before.js +1 -1
  85. package/dist/server/providers/database/pagination/utils/decode-cursor.js +1 -1
  86. package/dist/server/providers/database/pagination/utils/is-base-64.js +1 -1
  87. package/dist/server/ssr/render.js +1 -1
  88. package/dist/server/tools/notifiers/helpers/privacy/remove-pii-from-url.js +1 -1
  89. package/dist/server/types/plugins/search.d.ts +1 -1
  90. package/dist/server/utils/rbac-expand.js +1 -1
  91. package/dist/server/utils/rbac.js +1 -1
  92. package/dist/server/utils/search/highlight-text-for-search.d.ts +1 -0
  93. package/dist/server/utils/search/highlight-text-for-search.js +1 -1
  94. package/dist/server/utils/search/text-match.d.ts +6 -0
  95. package/dist/server/utils/search/text-match.js +1 -0
  96. package/dist/server/web-server/middleware/apiKeyMiddleware.js +1 -1
  97. package/dist/server/web-server/middleware/catalogAuthMiddleware.js +1 -1
  98. package/dist/server/web-server/routes/ask-ai.js +1 -1
  99. package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
  100. package/package.json +5 -5
  101. package/dist/client/app/hooks/useAnchorPositioning.d.ts +0 -6
  102. package/dist/client/app/hooks/useAnchorPositioning.js +0 -1
@@ -1 +1 @@
1
- import{and as u,desc as v,eq as n,isNull as c,sql as d}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as f}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{compareVersions as h}from"../../../../../../utils/version/compare-versions.js";import{VersionRepository as m}from"./version-repository.js";import{buildSemanticVersionSortExpr as C}from"../utils/semantic-version-sort.js";import{normalizeRevisionFlags as y}from"../utils/normalize-revision-flags.js";class g{#i;#e;constructor(i){this.#e=i,this.#i=new m(i)}async getCurrentRevisionInfo({key:i,version:t,revision:r}){const o=await this.#t(i);if(!o)return{shouldSetNewCurrentRevision:!0,hasCurrentRevision:!1};if(t){const a=await this.#i.doesVersionExist(i,t),s=h(t,o.version)>0,l=o.version===f&&t!==f;if(!a&&(s||l))return{shouldSetNewCurrentRevision:!0,hasCurrentRevision:!0}}return t&&r&&t===o.version&&r>=o.revision?{shouldSetNewCurrentRevision:!0,hasCurrentRevision:!0}:{shouldSetNewCurrentRevision:!1,hasCurrentRevision:!0}}async markAllRevisionsAsNotCurrent(i){await this.#e.client.update(e).set({isCurrent:!1}).where(n(e.key,i)).run()}async ensureDefaultAndCurrentRevisionForKey(i){const t=await this.#i.getOrderedVersionListForKey(i),r=this.#i.getLatestVersion(t);r&&await this.#e.client.update(e).set({isDefaultVersion:!0,isCurrent:!0}).where(u(n(e.key,i),n(e.revision,r.revision),r.version?n(e.version,r.version):c(e.version))).run()}async shouldSkipRevisionCreation(i,t,r,o,a){if(o||a)return!1;const s=await this.#e.client.select({hash:e.hash,isDeleted:e.isDeleted}).from(e).where(u(n(e.key,i),t?n(e.version,t):c(e.version))).orderBy(v(e.revision)).limit(1).get();return!s||!!s.isDeleted?!1:s.hash===r}async#t(i){return await this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(u(n(e.key,i),n(e.isCurrent,!0))).orderBy(v(e.revision)).limit(1).get()??null}async listEntityRevisions(i,t){const r=[n(e.key,i),n(e.isDeleted,!1),...t?[n(e.version,t)]:[]],a=(await this.#e.client.select({version:e.version,revision:e.revision,isCurrent:e.isCurrent,createdAt:e.createdAt,updatedAt:e.updatedAt,isDefaultVersion:e.isDefaultVersion}).from(e).where(u(...r)).orderBy(d`${C("version")} DESC`,d.raw("revision DESC")).all()).map(s=>({version:s.version,revision:s.revision??"",isCurrent:s.isCurrent!==null?s.isCurrent:!1,createdAt:s.createdAt,updatedAt:s.updatedAt,isDefaultVersion:s.isDefaultVersion!==null?s.isDefaultVersion:!1}));return y(a),a}}export{g as RevisionRepository};
1
+ import{and as l,desc as d,eq as r,isNull as f,sql as v}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as u}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{compareVersions as h}from"../../../../../../utils/version/compare-versions.js";import{VersionRepository as m}from"./version-repository.js";import{buildSemanticVersionSortExpr as y}from"../utils/semantic-version-sort.js";import{normalizeRevisionFlags as C}from"../utils/normalize-revision-flags.js";class g{#t;#e;constructor(t){this.#e=t,this.#t=new m(t)}async getCurrentRevisionInfo({key:t,version:i,revision:n}){const o=await this.#i(t);if(!o)return{shouldSetNewCurrentRevision:!0};if(i){const a=await this.#t.doesVersionExist(t,i),s=i!==u&&h(i,o.version)>0,c=o.version===u&&i!==u;if(!a&&(s||c))return{shouldSetNewCurrentRevision:!0}}return i&&n&&i===o.version&&n>=o.revision?{shouldSetNewCurrentRevision:!0}:{shouldSetNewCurrentRevision:!1}}async markAllRevisionsAsNotCurrent(t){await this.#e.client.update(e).set({isCurrent:!1}).where(r(e.key,t)).run()}async demoteVersion(t,i){await this.#e.client.update(e).set({isCurrent:!1,isDefaultVersion:!1}).where(l(r(e.key,t),r(e.version,i))).run()}async ensureDefaultAndCurrentRevisionForKey(t){const i=await this.#t.getOrderedVersionListForKey(t),n=this.#t.getLatestVersion(i);n&&await this.#e.client.update(e).set({isDefaultVersion:!0,isCurrent:!0}).where(l(r(e.key,t),r(e.revision,n.revision),n.version?r(e.version,n.version):f(e.version))).run()}async shouldSkipRevisionCreation(t,i,n,o,a){if(o||a)return!1;const s=await this.#e.client.select({hash:e.hash,isDeleted:e.isDeleted}).from(e).where(l(r(e.key,t),i?r(e.version,i):f(e.version))).orderBy(d(e.revision)).limit(1).get();return!s||!!s.isDeleted?!1:s.hash===n}async#i(t){return await this.#e.client.select({version:e.version,revision:e.revision}).from(e).where(l(r(e.key,t),r(e.isCurrent,!0),r(e.isDeleted,!1))).orderBy(d(e.revision)).limit(1).get()??null}async listEntityRevisions(t,i){const n=[r(e.key,t),r(e.isDeleted,!1),...i?[r(e.version,i)]:[]],a=(await this.#e.client.select({version:e.version,revision:e.revision,isCurrent:e.isCurrent,createdAt:e.createdAt,updatedAt:e.updatedAt,isDefaultVersion:e.isDefaultVersion}).from(e).where(l(...n)).orderBy(v`${y("version")} DESC`,v.raw("revision DESC")).all()).map(s=>({version:s.version,revision:s.revision??"",isCurrent:s.isCurrent!==null?s.isCurrent:!1,createdAt:s.createdAt,updatedAt:s.updatedAt,isDefaultVersion:s.isDefaultVersion!==null?s.isDefaultVersion:!1}));return C(a),a}}export{g as RevisionRepository};
@@ -3,9 +3,13 @@ export declare class VersionRepository {
3
3
  private readonly db;
4
4
  constructor(db: DatabaseClient);
5
5
  markAllVersionsAsNotDefault(key: string): Promise<void>;
6
+ /**
7
+ * A version whose most recent revision is soft-deleted counts as not existing.
8
+ */
6
9
  doesVersionExist(key: string, version: string): Promise<boolean>;
7
10
  /**
8
- * Gets the ordered list of versions for a given key.
11
+ * Gets the ordered list of live versions for a given key: soft-deleted rows and rows of a
12
+ * version whose most recent revision is soft-deleted are excluded.
9
13
  * The list is ordered by creation date, and then by version.
10
14
  */
11
15
  getOrderedVersionListForKey(key: string): Promise<{
@@ -1 +1,7 @@
1
- import{eq as a,and as u,desc as c}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as f}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{compareVersions as m}from"../../../../../../utils/version/compare-versions.js";class A{db;constructor(t){this.db=t}async markAllVersionsAsNotDefault(t){await this.db.client.update(e).set({isDefaultVersion:!1}).where(u(a(e.key,t),a(e.isDefaultVersion,!0))).run()}async doesVersionExist(t,n){return await this.db.client.select().from(e).where(u(a(e.key,t),a(e.version,n))).get()!==void 0}async getOrderedVersionListForKey(t){return(await this.db.client.select({version:e.version,isDefaultVersion:e.isDefaultVersion,revision:e.revision,createdAt:e.createdAt}).from(e).where(u(a(e.key,t))).orderBy(c(e.createdAt)).all()).map(i=>({version:i.version,isDefaultVersion:!!i.isDefaultVersion,revision:i.revision,createdAt:new Date(i.createdAt)}))}getLatestVersion(t){const n=t.find(r=>r.isDefaultVersion);if(n)return n;const{versionedEntities:i,unversionedEntities:d}=t.reduce((r,s)=>(s.version!==null&&s.version!==f?r.versionedEntities.push(s):r.unversionedEntities.push(s),r),{versionedEntities:[],unversionedEntities:[]});if(i.length>0){i.sort((o,l)=>m(l.version,o.version));const r=i[0].version,s=i.filter(o=>o.version===r);return s.sort((o,l)=>l.createdAt.getTime()-o.createdAt.getTime()),s[0]}return d.length>0?(d.sort((r,s)=>s.createdAt.getTime()-r.createdAt.getTime()),d[0]):null}}export{A as VersionRepository};
1
+ import{eq as n,and as u,desc as c,sql as f}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as m}from"@redocly/theme/core/constants";import{entitiesTable as e}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{compareVersions as v}from"../../../../../../utils/version/compare-versions.js";class g{db;constructor(i){this.db=i}async markAllVersionsAsNotDefault(i){await this.db.client.update(e).set({isDefaultVersion:!1}).where(u(n(e.key,i),n(e.isDefaultVersion,!0))).run()}async doesVersionExist(i,o){const t=await this.db.client.select({isDeleted:e.isDeleted}).from(e).where(u(n(e.key,i),n(e.version,o))).orderBy(c(e.revision)).limit(1).get();return t!==void 0&&!t.isDeleted}async getOrderedVersionListForKey(i){return(await this.db.client.select({version:e.version,isDefaultVersion:e.isDefaultVersion,revision:e.revision,createdAt:e.createdAt}).from(e).where(u(n(e.key,i),n(e.isDeleted,!1),f`NOT EXISTS (
2
+ SELECT 1 FROM entities tombstone
3
+ WHERE tombstone.key = ${e.key}
4
+ AND tombstone.version IS ${e.version}
5
+ AND tombstone.is_deleted = 1
6
+ AND tombstone.revision > ${e.revision}
7
+ )`)).orderBy(c(e.createdAt)).all()).map(t=>({version:t.version,isDefaultVersion:!!t.isDefaultVersion,revision:t.revision,createdAt:new Date(t.createdAt)}))}getLatestVersion(i){const o=i.find(s=>s.isDefaultVersion);if(o)return o;const{versionedEntities:t,unversionedEntities:d}=i.reduce((s,r)=>(r.version!==null&&r.version!==m?s.versionedEntities.push(r):s.unversionedEntities.push(r),s),{versionedEntities:[],unversionedEntities:[]});if(t.length>0){t.sort((a,l)=>v(l.version,a.version));const s=t[0].version,r=t.filter(a=>a.version===s);return r.sort((a,l)=>l.createdAt.getTime()-a.createdAt.getTime()),r[0]}return d.length>0?(d.sort((s,r)=>r.createdAt.getTime()-s.createdAt.getTime()),d[0]):null}}export{g as VersionRepository};
@@ -1 +1 @@
1
- import{and as w,eq as c,isNull as k,or as x,sql as V}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as F}from"@redocly/theme/core/constants";import{sha1 as K}from"../../../../../utils/crypto/sha1.js";import{envConfig as O}from"../../../../../config/env-config.js";import{entitiesTable as t}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{convertFilterToWhereCondition as W}from"../../../../../providers/database/pagination/filter.js";import{entitiesRelationsTable as a}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-relations-table.js";import{promiseMapLimit as v}from"../../../../../utils/async/promise-map-limit.js";import{RevisionRepository as z}from"../common/revision-repository.js";import{VersionRepository as H}from"../common/version-repository.js";import{EntityAttributesWriteRepository as P}from"../entityAttributes/entity-attributes-write-repository.js";import{createEntityDbRecord as L}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as A}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromFileSchema as U}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{createEntityRelationDbRecordFromDto as j}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{EntitiesReadRepository as G}from"./entities-read-repository.js";import{RelationsReadRepository as M}from"../relations/relations-read-repository.js";import{RelationsWriteRepository as $}from"../relations/relations-write-repository.js";const g=15;class de{#e;#t;#n;#o;#i;#s;#a;#l;#r;constructor(e,s,n){this.#e=e,this.#i=s,this.#s=n,this.#t=new z(e),this.#n=new H(e),this.#o=new P(e),this.#a=new G(e),this.#l=new M(e),this.#r=new $(e,s,n)}async createEntity({entity:e,source:s,fileHash:n,sourceFile:o,isRootEntity:i,isDeleted:r,rbacTeams:l,errorOnSkip:d=!1,revision:h}){const{relations:u=[],...f}=e,p=K(JSON.stringify(f)),E=e.version??F,R=h??new Date().toISOString(),C=await this.#t.shouldSkipRevisionCreation(e.key,E,p,i,r);Array.isArray(l)&&await this.#o.upsertEntityAttributes({entityKey:e.key,rbacTeams:l,organizationId:this.#i,projectId:this.#s});const m=O.isDevelopMode&&!O.REDOCLY_INTERNAL_DEV;if(u?.length&&!m&&await this.#r.createEntityRelations(u.map(I=>({...I,sourceKey:e.key,targetKey:I.key,sourceFile:o??null,fileHash:n??null}))),C){if(d)throw new Error("Entity validation failed: entity already exists");return null}const{shouldSetNewCurrentRevision:y,hasCurrentRevision:_}=await this.#t.getCurrentRevisionInfo({key:e.key,version:E,revision:R}),D=L({entity:{...e,revision:R,hash:p,isCurrent:y,isDefaultVersion:y,isDeleted:r,version:E},organizationId:this.#i,projectId:this.#s,source:s,sourceFile:o??null,fileHash:n??null}),{key:S,...T}=D;if(y&&_&&(await this.#t.markAllRevisionsAsNotCurrent(S),await this.#n.markAllVersionsAsNotDefault(S)),m)return await this.#u(D,u,y);const N=await this.#e.client.insert(t).values(D).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:T}).returning();return N.length?A(N[0]):null}async updateEntity(e,s){const{shouldSetNewCurrentRevision:n,hasCurrentRevision:o}=await this.#t.getCurrentRevisionInfo({key:s.key,version:e.version??s.version??F,revision:s.revision});n&&o&&(await this.#t.markAllRevisionsAsNotCurrent(s.key),await this.#n.markAllVersionsAsNotDefault(s.key));const i=L({entity:{...s,...e,hash:K(JSON.stringify({...s,...e})),isCurrent:n,isDefaultVersion:n,createdAt:s.createdAt??void 0},organizationId:this.#i,projectId:this.#s,source:"remote",sourceFile:null,fileHash:null}),{key:r,source:l,scorecardsStatus:d,...h}=i,u=await this.#e.client.insert(t).values(i).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:{...h,scorecardsStatus:V`CASE WHEN ${t.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return u.length?A(u[0]):null}async setEntitiesAsOutdated(e){const s=W(e);await this.#e.client.update(t).set({scorecardsStatus:"OUTDATED"}).where(s)}async#u(e,s,n){const{key:o,source:i,version:r,...l}=e,{isCurrent:d,isDefaultVersion:h,...u}=l,f=n?l:u,R=await this.#e.client.select({id:t.id}).from(t).where(w(c(t.key,o),r?c(t.version,r):k(t.version))).limit(1).get()!=null?await this.#e.client.update(t).set(f).where(w(c(t.key,o),r?c(t.version,r):k(t.version))).returning():await this.#e.client.insert(t).values(e).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:f}).returning(),C=s?.map(m=>{const y=U({relation:m,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#i,projectId:this.#s});return this.#e.client.insert(a).values(y).onConflictDoUpdate({target:[a.sourceKey,a.targetKey,a.sourceVersion,a.targetVersion,a.sourceRevision,a.targetRevision,a.sourceToTargetRelation],set:y}).run()})??[];return await v(C,g,async m=>m),A(R[0])}async softDeleteEntitiesWithRelations({filter:e,revision:s,fileHash:n}){const i={op:"AND",conditions:[e,{field:"is_deleted",operator:"equal",value:!1}]};for(;;){const r=await this.#a.getEntities({paginationParams:{filter:i,limit:500}});if(r.items.length===0)break;const l=await this.#c({entities:r.items,revision:s,fileHash:n});await this.#d(l,s)}}async deleteEntity(e){return await this.#r.deleteEntityRelationsOnEntityRemoval(e),await this.#e.client.delete(t).where(c(t.id,e.id)),await this.#t.ensureDefaultAndCurrentRevisionForKey(e.key),e.id}async deleteEntities(e){const s=W(e);if(!s)return!1;const n=await this.#e.client.delete(t).where(s).returning({key:t.key,source:t.source,isCurrent:t.isCurrent,isDefaultVersion:t.isDefaultVersion,version:t.version});if(n.length===0)return!0;const o=n.reduce((i,r)=>((r.isCurrent||r.isDefaultVersion)&&i.add(r.key),i),new Set);if(o.size===0)return!0;await v(Array.from(o),g,async i=>this.#t.ensureDefaultAndCurrentRevisionForKey(i));for(const i of n)await this.#e.client.delete(a).where(x(w(c(a.sourceKey,i.key),...i.version?[c(a.sourceVersion,i.version)]:[k(a.sourceVersion)]),w(c(a.targetKey,i.key),...i.version?[c(a.targetVersion,i.version)]:[k(a.targetVersion)])));return!0}async updateEntityScorecardsStatus(e,s){return(await this.#e.client.update(t).set({scorecardsStatus:s}).where(c(t.id,e)).returning()).length>0}async updateEntityScorecardsStatusIfCalculating(e,s){return(await this.#e.client.update(t).set({scorecardsStatus:s}).where(V`${t.id} = ${e} AND ${t.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}async#c({entities:e,revision:s,fileHash:n}){return(await v(e,g,async i=>{const r={type:i.type,key:i.key,title:i.title,summary:i.summary??void 0,tags:i.tags??void 0,metadata:i.metadata??void 0,git:i.git??void 0,contact:i.contact??void 0,links:i.links??void 0,version:i.version??void 0};return this.createEntity({entity:r,sourceFile:i.sourceFile??"",fileHash:n,isDeleted:!0,errorOnSkip:!1,source:i.source,revision:s})})).filter(i=>i!==null)}async#d(e,s){if(e.length===0)return!0;const n=await v(e,g,async o=>(await this.#l.getRelationsForEntity(o.key,o.version,s)).map(r=>{if(!r)return null;const l=r.direction,d=r.sourceToTargetRelation,h=r.targetKey,u=l==="outgoing"?o.key:h,f=l==="outgoing"?h:o.key,p=l==="outgoing"?d:d.startsWith("reverse:")?d.slice(8):d;return!p||!u||!f?null:j({type:p,sourceKey:u,targetKey:f,sourceVersion:o.version,targetVersion:o.version,sourceRevision:s,targetRevision:s,isDeleted:!0},this.#i,this.#s)}).filter(r=>r!==null));return await v(n.flat(),g,async o=>this.#r.upsertEntityRelation(o)),!0}}export{de as EntitiesWriteRepository};
1
+ import{and as k,eq as u,isNull as C,or as x,sql as V}from"drizzle-orm";import{VERSION_NOT_SPECIFIED as F}from"@redocly/theme/core/constants";import{sha1 as K}from"../../../../../utils/crypto/sha1.js";import{envConfig as O}from"../../../../../config/env-config.js";import{entitiesTable as t}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-table.js";import{convertFilterToWhereCondition as W}from"../../../../../providers/database/pagination/filter.js";import{entitiesRelationsTable as a}from"../../../../../providers/database/databases/sqlite-db/schemas/entities-relations-table.js";import{promiseMapLimit as m}from"../../../../../utils/async/promise-map-limit.js";import{RevisionRepository as z}from"../common/revision-repository.js";import{VersionRepository as H}from"../common/version-repository.js";import{EntityAttributesWriteRepository as P}from"../entityAttributes/entity-attributes-write-repository.js";import{createEntityDbRecord as L}from"../../mappers/create-entity-db-record.js";import{createEntityReadModel as N}from"../../mappers/create-entity-read-model.js";import{createEntityRelationDbRecordFromFileSchema as U}from"../../mappers/create-entity-relation-db-record-from-file-schema.js";import{createEntityRelationDbRecordFromDto as j}from"../../mappers/create-entity-relation-db-record-from-dto.js";import{EntitiesReadRepository as G}from"./entities-read-repository.js";import{RelationsReadRepository as M}from"../relations/relations-read-repository.js";import{RelationsWriteRepository as $}from"../relations/relations-write-repository.js";const E=15;class de{#e;#t;#n;#o;#i;#s;#a;#l;#r;constructor(e,i,r){this.#e=e,this.#i=i,this.#s=r,this.#t=new z(e),this.#n=new H(e),this.#o=new P(e),this.#a=new G(e),this.#l=new M(e),this.#r=new $(e,i,r)}async createEntity({entity:e,source:i,fileHash:r,sourceFile:o,isRootEntity:s,isDeleted:n,rbacTeams:l,errorOnSkip:c=!1,revision:d}){const{relations:h=[],...f}=e,y=K(JSON.stringify(f)),v=e.version??F,R=d??new Date().toISOString(),D=await this.#t.shouldSkipRevisionCreation(e.key,v,y,s,n);Array.isArray(l)&&await this.#o.upsertEntityAttributes({entityKey:e.key,rbacTeams:l,organizationId:this.#i,projectId:this.#s});const p=O.isDevelopMode&&!O.REDOCLY_INTERNAL_DEV;if(h?.length&&!p&&await this.#r.createEntityRelations(h.map(I=>({...I,sourceKey:e.key,targetKey:I.key,sourceFile:o??null,fileHash:r??null}))),D){if(c)throw new Error("Entity validation failed: entity already exists");return null}const g=(await this.#t.getCurrentRevisionInfo({key:e.key,version:v,revision:R})).shouldSetNewCurrentRevision&&!n,S=L({entity:{...e,revision:R,hash:y,isCurrent:g,isDefaultVersion:g,isDeleted:n,version:v},organizationId:this.#i,projectId:this.#s,source:i,sourceFile:o??null,fileHash:r??null}),{key:w,..._}=S;g&&(await this.#t.markAllRevisionsAsNotCurrent(w),await this.#n.markAllVersionsAsNotDefault(w));const T=p?await this.#c(S,h,g):await this.#u(S,_);return n&&(await this.#t.demoteVersion(w,v),await this.#t.ensureDefaultAndCurrentRevisionForKey(w)),T}async#u(e,i){const r=await this.#e.client.insert(t).values(e).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:i}).returning();return r.length?N(r[0]):null}async updateEntity(e,i){const{shouldSetNewCurrentRevision:r}=await this.#t.getCurrentRevisionInfo({key:i.key,version:e.version??i.version??F,revision:i.revision});r&&(await this.#t.markAllRevisionsAsNotCurrent(i.key),await this.#n.markAllVersionsAsNotDefault(i.key));const o=L({entity:{...i,...e,hash:K(JSON.stringify({...i,...e})),isCurrent:r,isDefaultVersion:r,createdAt:i.createdAt??void 0},organizationId:this.#i,projectId:this.#s,source:"remote",sourceFile:null,fileHash:null}),{key:s,source:n,scorecardsStatus:l,...c}=o,d=await this.#e.client.insert(t).values(o).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:{...c,scorecardsStatus:V`CASE WHEN ${t.scorecardsStatus} = 'CALCULATING' THEN 'CANCELLED' ELSE 'OUTDATED' END`}}).returning();return d.length?N(d[0]):null}async setEntitiesAsOutdated(e){const i=W(e);await this.#e.client.update(t).set({scorecardsStatus:"OUTDATED"}).where(i)}async#c(e,i,r){const{key:o,source:s,version:n,...l}=e,{isCurrent:c,isDefaultVersion:d,...h}=l,f=r?l:h,R=await this.#e.client.select({id:t.id}).from(t).where(k(u(t.key,o),n?u(t.version,n):C(t.version))).limit(1).get()!=null?await this.#e.client.update(t).set(f).where(k(u(t.key,o),n?u(t.version,n):C(t.version))).returning():await this.#e.client.insert(t).values(e).onConflictDoUpdate({target:[t.key,t.source,t.revision,t.version],set:f}).returning(),D=i?.map(p=>{const A=U({relation:p,sourceFile:e.sourceFile??"",fileHash:e.fileHash??"",sourceKey:e.key,sourceVersion:e.version??null,sourceRevision:e.revision??null,organizationId:this.#i,projectId:this.#s});return this.#e.client.insert(a).values(A).onConflictDoUpdate({target:[a.sourceKey,a.targetKey,a.sourceVersion,a.targetVersion,a.sourceRevision,a.targetRevision,a.sourceToTargetRelation],set:A}).run()})??[];return await m(D,E,async p=>p),N(R[0])}async softDeleteEntitiesWithRelations({filter:e,revision:i,fileHash:r}){const s={op:"AND",conditions:[e,{field:"is_deleted",operator:"equal",value:!1}]};for(;;){const n=await this.#a.getEntities({paginationParams:{filter:s,limit:500}});if(n.items.length===0)break;const l=await this.#d({entities:n.items,revision:i,fileHash:r});await this.#h(l,i)}}async deleteEntity(e){return await this.#r.deleteEntityRelationsOnEntityRemoval(e),await this.#e.client.delete(t).where(u(t.id,e.id)),await this.#t.ensureDefaultAndCurrentRevisionForKey(e.key),e.id}async deleteEntities(e){const i=W(e);if(!i)return!1;const r=await this.#e.client.delete(t).where(i).returning({key:t.key,source:t.source,isCurrent:t.isCurrent,isDefaultVersion:t.isDefaultVersion,version:t.version});if(r.length===0)return!0;const o=r.reduce((s,n)=>((n.isCurrent||n.isDefaultVersion)&&s.add(n.key),s),new Set);if(o.size===0)return!0;await m(Array.from(o),E,async s=>this.#t.ensureDefaultAndCurrentRevisionForKey(s));for(const s of r)await this.#e.client.delete(a).where(x(k(u(a.sourceKey,s.key),...s.version?[u(a.sourceVersion,s.version)]:[C(a.sourceVersion)]),k(u(a.targetKey,s.key),...s.version?[u(a.targetVersion,s.version)]:[C(a.targetVersion)])));return!0}async updateEntityScorecardsStatus(e,i){return(await this.#e.client.update(t).set({scorecardsStatus:i}).where(u(t.id,e)).returning()).length>0}async updateEntityScorecardsStatusIfCalculating(e,i){return(await this.#e.client.update(t).set({scorecardsStatus:i}).where(V`${t.id} = ${e} AND ${t.scorecardsStatus} = 'CALCULATING'`).returning()).length>0}async#d({entities:e,revision:i,fileHash:r}){return(await m(e,E,async s=>{const n={type:s.type,key:s.key,title:s.title,summary:s.summary??void 0,tags:s.tags??void 0,metadata:s.metadata??void 0,git:s.git??void 0,contact:s.contact??void 0,links:s.links??void 0,version:s.version??void 0};return this.createEntity({entity:n,sourceFile:s.sourceFile??"",fileHash:r,isDeleted:!0,errorOnSkip:!1,source:s.source,revision:i})})).filter(s=>s!==null)}async#h(e,i){if(e.length===0)return!0;const r=await m(e,E,async o=>(await this.#l.getRelationsForEntity(o.key,o.version,i)).map(n=>{if(!n)return null;const l=n.direction,c=n.sourceToTargetRelation,d=n.targetKey,h=l==="outgoing"?o.key:d,f=l==="outgoing"?d:o.key,y=l==="outgoing"?c:c.startsWith("reverse:")?c.slice(8):c;return!y||!h||!f?null:j({type:y,sourceKey:h,targetKey:f,sourceVersion:o.version,targetVersion:o.version,sourceRevision:i,targetRevision:i,isDeleted:!0},this.#i,this.#s)}).filter(n=>n!==null));return await m(r.flat(),E,async o=>this.#r.upsertEntityRelation(o)),!0}}export{de as EntitiesWriteRepository};
@@ -1 +1 @@
1
- import u from"node:path";import{removeLeadingSlash as w}from"@redocly/theme/core/utils";import{toKebabCase as f}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as g}from"../../../../../utils/async/promise-map-limit.js";import{removeMarkdocTags as v}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as E}from"./base.js";const d=15;class K extends E{constructor(t){super("arazzo",t)}mapApiDescriptionToEntity(t,o){const a=w(t.realRelativePath),c=t.document.info.title,e=this.resolveEntityKey({realRelativePath:t.realRelativePath,customKey:t.document["x-redocly-catalog-key"]});return{type:this.type,key:e,title:c,summary:t.document.info.description?v(t.document.info.description):null,tags:["arazzo"],metadata:{specType:this.specType,descriptionFile:a},version:o}}async loadApiDescriptions(){return(await this.context.cache.load(".","arazzo-docs")).data}async processApiDescription(t,o,a,c){if(!this.#t(t))throw new Error("Arazzo description has no source descriptions");const e=this.getRbacTeamsForDefinition(t.relativePath),i=this.mapApiDescriptionToEntity(t,a);await this.catalogEntitiesService.createEntity({entity:{...i},revision:o,sourceFile:t.realRelativePath,fileHash:t.hash,isRootEntity:!0,rbacTeams:e,source:"file"}),c.delete(`${i.key}:${a}`),await this.#e(t,i.key,i.version,a,o,c,e);const l=t.document.sourceDescriptions||[];await g(l,d,async r=>{if(!(r.type!=="openapi"||!r.url))try{const n=this.resolveSourceDescriptionUrl(r.url,t.realRelativePath);if(!n){!r.url.startsWith("http://")&&!r.url.startsWith("https://")&&this.context.logger.warn(`Could not resolve URL to local path: ${r.url}`);return}const s=((await this.context.cache.load(n,"api-doc")).data??[]).filter(m=>m.type==="openapi");if(s.length===0){this.context.logger.warn(`No OpenAPI definition found at path: ${n}`);return}const p=s[0],y=this.resolveEntityKey({realRelativePath:p.realRelativePath,customKey:p.document["x-redocly-catalog-key"]});await this.catalogEntitiesService.createEntityRelation({sourceKey:i.key,type:"relatesTo",targetKey:y,sourceFile:t.realRelativePath,fileHash:t.hash}),await this.#r(t,r.name,p,i.key,y,i.version,o)}catch(n){this.context.logger.warn(`Failed to create relation to OpenAPI source "${r.url}": ${n instanceof Error?n.message:"Unknown error"}`)}})}#t=t=>!!t?.document?.sourceDescriptions&&t.document.sourceDescriptions.length>0;#e=async(t,o,a,c,e,i,l)=>{try{const r=t.document.components?.inputs,n=r?Object.entries(r):[],h=[];for(const[s,p]of n)h.push(async()=>{const y=`${o}-${f(s)}`,m=await this.#a({schemaName:s,schema:p,description:t,parentKey:o,parentVersion:a,parentRevision:e,rbacTeams:l});i.delete(`${y}:${c}`),m&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,type:"uses",targetKey:m.key,sourceFile:t.realRelativePath,fileHash:t.hash,sourceVersion:a,targetVersion:a,sourceRevision:e,targetRevision:e})});if(h.length===0)return;await g(h,d,s=>s())}catch(r){this.context.logger.warn(`Failed to create data schema entities for Arazzo description: ${r instanceof Error?r.message:"Unknown error"}`)}};#a=async({schemaName:t,schema:o,description:a,parentKey:c,parentVersion:e,parentRevision:i,rbacTeams:l})=>{const r={type:"data-schema",key:`${c}-${f(t)}`,title:t,summary:o.description||o.title||null,tags:["arazzo"],metadata:{specType:this.specType,descriptionFile:a.realRelativePath,schema:"{}"},version:e};try{return await this.catalogEntitiesService.createEntity({entity:r,sourceFile:a.realRelativePath,fileHash:a.hash,rbacTeams:l,source:"file",revision:i})}catch(n){return this.context.logger.warn(`Failed to create data schema entity for ${t}: ${n instanceof Error?n.message:"Unknown error"}`),null}};#r=async(t,o,a,c,e,i,l)=>{const r=t.document.workflows||[];if(r.length===0)return;const n=new Set;for(const h of r)for(const s of h.steps||[]){if(!s.operationId)continue;const p=s.operationId.startsWith(`${o}.`)?s.operationId.substring(`${o}.`.length):s.operationId;p&&n.add(p)}n.size!==0&&await g(Array.from(n),d,async h=>{try{const s=`${e}-${f(h)}`;await this.catalogEntitiesService.createEntityRelation({sourceKey:c,type:"uses",targetKey:s,sourceFile:t.realRelativePath,fileHash:t.hash,sourceVersion:i,targetVersion:i,sourceRevision:l,targetRevision:l})}catch(s){this.context.logger.warn(`Failed to create relation to operation "${h}": ${s instanceof Error?s.message:"Unknown error"}`)}})};resolveSourceDescriptionUrl(t,o){try{const a=t.trim(),c=a.startsWith("@")?a.slice(1):a,[e]=c.split("#");if(e.startsWith("http://")||e.startsWith("https://"))return null;if(e.startsWith("/"))return w(u.posix.normalize(e));const i=u.posix.dirname(o);return e.startsWith(i+"/")?u.posix.normalize(e):u.posix.normalize(u.posix.join(i,e))}catch{return this.context.logger.warn(`Failed to resolve URL: ${t}`),null}}}export{K as ArazzoEntitiesExtractor};
1
+ import u from"node:path";import{removeLeadingSlash as w}from"@redocly/theme/core/utils";import{toKebabCase as f}from"../../../../../../utils/string/to-kebab-case.js";import{promiseMapLimit as g}from"../../../../../utils/async/promise-map-limit.js";import{removeMarkdocTags as v}from"../../../../../../markdoc/helpers/remove-markdoc-tags.js";import{BaseApiEntitiesExtractor as E}from"./base.js";const d=15;class K extends E{constructor(t){super("arazzo",t)}mapApiDescriptionToEntity(t,o){const r=w(t.realRelativePath),c=t.document.info.title,e=this.resolveEntityKey({realRelativePath:t.realRelativePath,customKey:t.document["x-redocly-catalog-key"]});return{type:this.type,key:e,title:c,summary:t.document.info.description?v(t.document.info.description):null,tags:["arazzo"],metadata:{specType:this.specType,descriptionFile:r},version:o}}async loadApiDescriptions(){return(await this.context.cache.load(".","arazzo-docs")).data}async processApiDescription(t,o,r,c){if(!this.#t(t))throw new Error("Arazzo description has no source descriptions");const e=this.getRbacTeamsForDefinition(t.relativePath),i=this.mapApiDescriptionToEntity(t,r);await this.catalogEntitiesService.createEntity({entity:{...i},revision:o,sourceFile:t.realRelativePath,fileHash:t.hash,isRootEntity:!0,rbacTeams:e,source:"file"}),c.delete(`${i.key}:${r}`),await this.#e(t,i.key,i.version,r,o,c,e);const l=t.document.sourceDescriptions||[];await g(l,d,async a=>{if(!(a.type!=="openapi"||!a.url))try{const n=this.resolveSourceDescriptionUrl(a.url,t.realRelativePath);if(!n){!a.url.startsWith("http://")&&!a.url.startsWith("https://")&&this.context.logger.warn(`Could not resolve URL to local path: ${a.url}`);return}const s=((await this.context.cache.load(n,"api-doc")).data??[]).filter(m=>m.type==="openapi");if(s.length===0){this.context.logger.warn(`No OpenAPI definition found at path: ${n}`);return}const p=s[0],y=this.resolveEntityKey({realRelativePath:p.realRelativePath,customKey:p.document["x-redocly-catalog-key"]});await this.catalogEntitiesService.createEntityRelation({sourceKey:i.key,type:"relatesTo",targetKey:y,sourceFile:t.realRelativePath,fileHash:t.hash}),await this.#r(t,a.name,p,i.key,y,i.version,o)}catch(n){this.context.logger.warn(`Failed to create relation to OpenAPI source "${a.url}": ${n instanceof Error?n.message:"Unknown error"}`)}})}#t=t=>!!t?.document?.sourceDescriptions&&t.document.sourceDescriptions.length>0;#e=async(t,o,r,c,e,i,l)=>{try{const a=t.document.components?.inputs,n=a?Object.entries(a):[],h=[];for(const[s,p]of n)h.push(async()=>{const y=`${o}-${f(s)}`,m=await this.#a({schemaName:s,schema:p,description:t,parentKey:o,parentVersion:r,parentRevision:e,rbacTeams:l});i.delete(`${y}:${c}`),m&&await this.catalogEntitiesService.createEntityRelation({sourceKey:o,type:"uses",targetKey:m.key,sourceFile:t.realRelativePath,fileHash:t.hash,sourceVersion:r,targetVersion:r,sourceRevision:e,targetRevision:e})});if(h.length===0)return;await g(h,d,s=>s())}catch(a){this.context.logger.warn(`Failed to create data schema entities for Arazzo description: ${a instanceof Error?a.message:"Unknown error"}`)}};#a=async({schemaName:t,schema:o,description:r,parentKey:c,parentVersion:e,parentRevision:i,rbacTeams:l})=>{const a={type:"data-schema",key:`${c}-${f(t)}`,title:t,summary:o.description||o.title||null,tags:["arazzo"],metadata:{specType:this.specType,descriptionFile:r.realRelativePath,schema:"{}"},version:e};try{return await this.catalogEntitiesService.createEntity({entity:a,sourceFile:r.realRelativePath,fileHash:r.hash,rbacTeams:l,source:"file",revision:i})}catch(n){return this.context.logger.warn(`Failed to create data schema entity for ${t}: ${n instanceof Error?n.message:"Unknown error"}`),null}};#r=async(t,o,r,c,e,i,l)=>{const a=t.document.workflows||[];if(a.length===0)return;const n=new Set;for(const h of a)for(const s of h.steps||[]){if(!s.operationId)continue;const p=s.operationId.startsWith(`${o}.`)?s.operationId.substring(`${o}.`.length):s.operationId;p&&n.add(p)}n.size!==0&&await g(Array.from(n),d,async h=>{try{const s=`${e}-${f(h)}`;await this.catalogEntitiesService.createEntityRelation({sourceKey:c,type:"uses",targetKey:s,sourceFile:t.realRelativePath,fileHash:t.hash,sourceVersion:i,targetVersion:i,sourceRevision:l,targetRevision:l})}catch(s){this.context.logger.warn(`Failed to create relation to operation "${h}": ${s instanceof Error?s.message:"Unknown error"}`)}})};resolveSourceDescriptionUrl(t,o){try{const r=t.trim(),c=r.startsWith("@")?r.slice(1):r,[e]=c.split("#");if(e.startsWith("http://")||e.startsWith("https://"))return null;if(e.startsWith("/"))return w(u.posix.normalize(e));const i=u.posix.dirname(o);return e.startsWith(i+"/")?u.posix.normalize(e):u.posix.normalize(u.posix.join(i,e))}catch{return this.context.logger.warn(`Failed to resolve URL: ${t}`),null}}}export{K as ArazzoEntitiesExtractor};
@@ -1 +1 @@
1
- import P from"path";import{REDOCLY_ROUTE_RBAC as g,REDOCLY_TEAMS_RBAC as b}from"@redocly/config";import{deepMerge as S}from"../../../utils/object/deep-merge.js";import{buildWildcardRedirectsTree as v}from"../../utils/redirects/build-wildcard-redirects-tree.js";import{shaHexShort as D}from"../../utils/crypto/sha-hex-short.js";import{formatCustomScripts as u,formatCustomLinks as T}from"./format-custom-tags.js";import{applyL10nToRbacConfig as w,resolveDirectoryHashes as L}from"../../utils/rbac.js";import{copySeoAssets as O}from"./copy-seo-assets.js";import{normalizeRedirectSources as k}from"./normalize-redirect-sources.js";import{applyL10nToRedirects as E}from"./apply-l10n-to-redirects.js";import{DEFAULT_LOADERS as G}from"./loaders/index.js";import{resolveSearchFacets as B}from"./resolve-search-facets.js";import{telemetryTraceStep as _}from"../../../cli/telemetry/helpers/trace-step.js";import{transformMdAst as j}from"../markdown/runtime-transform.js";async function V(){return{id:"Config Parser",loaders:G,async processContent(e,{getConfig:i,fs:t}){await _("com.redocly.build.plugin.configParser",async o=>{const{contentDir:s,setGlobalConfig:a,setGlobalData:l}=e,r=await i(),c=r.access||{},d=w(t,c.rbac||{}),f={outdir:e.outdir,contentDir:s},y=await u(r.scripts?.head,f),h=await T(r.links,f),R=await u(r.scripts?.body,f),p=S(r,{headScriptTags:y,linkTags:h,postBodyScriptTags:R});o?.setAttribute("config",JSON.stringify(p));const m=r.redirects?k(r.redirects):{},C=Object.keys(m),A=p.banner?.map(n=>typeof n=="object"&&n!==null&&n.rbac?{...n,[b]:n.rbac}:n);a({...p,banner:A,access:{...c,requiresLogin:!!c.requiresLogin,rbac:d},directoryPaths:await L(t,c.rbac),seo:r.seo&&await O(r.seo,f.contentDir,f.outdir),redirects:E(m,r.l10n),wildcardRedirectsTree:v(m),originalRedirectSources:C}),B(r.search?.filters?.facets||[],e.getSearchFacets,e.setSearchFacets),l({removeAttribution:!!r.removeAttribution})})},async afterRoutesCreated(e,i){x(e);const t=e.getGlobalConfig("banner");if(Array.isArray(t)){const o=await Promise.all(t.map(async(s,a)=>{const{content:l,...r}=s,{ast:c}=await e.parseMarkdoc({input:{content:l,relativePath:"redocly.yaml"},context:i,resource:`redocly.yaml#banner${a}`}),{renderableAst:d}=await j(c,{variables:{},partials:{}},e.serverOutDir);return{...r,ast:d,hash:D(l)}}));e.setGlobalConfig({banner:o})}}}}function x(e){const i=e.getGlobalConfig("access")?.rbac,t=e.getGlobalConfig("directoryPaths");if(!(!i||Object.keys(i).length===0))for(const o of e.getAllRoutes()){if(o.versions&&t)for(const s of o.versions){const a=t[s.folderId],r=a==="."||a==="/"?"":a;s[g]={slug:s.link,fsPath:P.posix.join(r,"@"+s.version,"index.md")}}o[g]||o[b]||(o[g]={slug:o.slug,fsPath:o.fsPath})}}export{V as configParserPlugin};
1
+ import A from"path";import{REDOCLY_ROUTE_RBAC as g,REDOCLY_TEAMS_RBAC as b}from"@redocly/config";import{deepMerge as S}from"../../../utils/object/deep-merge.js";import{parsePathVersions as v}from"../../../utils/path/parse-path-versions.js";import{buildWildcardRedirectsTree as D}from"../../utils/redirects/build-wildcard-redirects-tree.js";import{shaHexShort as T}from"../../utils/crypto/sha-hex-short.js";import{formatCustomScripts as u,formatCustomLinks as w}from"./format-custom-tags.js";import{applyL10nToRbacConfig as L,resolveDirectoryHashes as O}from"../../utils/rbac.js";import{copySeoAssets as k}from"./copy-seo-assets.js";import{normalizeRedirectSources as E}from"./normalize-redirect-sources.js";import{applyL10nToRedirects as G}from"./apply-l10n-to-redirects.js";import{DEFAULT_LOADERS as B}from"./loaders/index.js";import{resolveSearchFacets as _}from"./resolve-search-facets.js";import{telemetryTraceStep as j}from"../../../cli/telemetry/helpers/trace-step.js";import{transformMdAst as x}from"../markdown/runtime-transform.js";async function Z(){return{id:"Config Parser",loaders:B,async processContent(e,{getConfig:n,fs:t}){await j("com.redocly.build.plugin.configParser",async o=>{const{contentDir:l,setGlobalConfig:s,setGlobalData:a}=e,r=await n(),i=r.access||{},d=L(t,i.rbac||{}),f={outdir:e.outdir,contentDir:l},y=await u(r.scripts?.head,f),h=await w(r.links,f),R=await u(r.scripts?.body,f),p=S(r,{headScriptTags:y,linkTags:h,postBodyScriptTags:R});o?.setAttribute("config",JSON.stringify(p));const m=r.redirects?E(r.redirects):{},C=Object.keys(m),P=p.banner?.map(c=>typeof c=="object"&&c!==null&&c.rbac?{...c,[b]:c.rbac}:c);s({...p,banner:P,access:{...i,requiresLogin:!!i.requiresLogin,rbac:d},directoryPaths:await O(t,i.rbac),seo:r.seo&&await k(r.seo,f.contentDir,f.outdir),redirects:G(m,r.l10n),wildcardRedirectsTree:D(m),originalRedirectSources:C}),_(r.search?.filters?.facets||[],e.getSearchFacets,e.setSearchFacets),a({removeAttribution:!!r.removeAttribution})})},async afterRoutesCreated(e,n){F(e);const t=e.getGlobalConfig("banner");if(Array.isArray(t)){const o=await Promise.all(t.map(async(l,s)=>{const{content:a,...r}=l,{ast:i}=await e.parseMarkdoc({input:{content:a,relativePath:"redocly.yaml"},context:n,resource:`redocly.yaml#banner${s}`}),{renderableAst:d}=await x(i,{variables:{},partials:{}},e.serverOutDir);return{...r,ast:d,hash:T(a)}}));e.setGlobalConfig({banner:o})}}}}function F(e){const n=e.getGlobalConfig("access")?.rbac,t=e.getGlobalConfig("directoryPaths");if(!(!n||Object.keys(n).length===0))for(const o of e.getAllRoutes()){if(o.versions&&t){const l=v(o.fsPath)?.filePathInVersion||"index.md";for(const s of o.versions){const a=t[s.folderId],i=a==="."||a==="/"?"":a;s[g]={slug:s.link,fsPath:A.posix.join(i,"@"+s.version,l)}}}o[g]||o[b]||(o[g]={slug:o.slug,fsPath:o.fsPath})}}export{Z as configParserPlugin};
@@ -1 +1 @@
1
- import v from"path";import{I18N_DIR_NAME as c,L10N_DIR_NAME as p,RESERVED_DIR_NAMES as m,VERSIONED_FOLDER_REGEXP_MATCH as V}from"../../../constants/common.js";import{parsePathVersions as l}from"../../../../utils/path/parse-path-versions.js";import{compareVersions as P}from"../../../../utils/version/compare-versions.js";import{reporter as h}from"../../../tools/notifiers/reporter.js";import{logger as g}from"../../../tools/notifiers/logger.js";const R=async(r,{cache:i,fs:s})=>{const e=new Map,a=await s.scan();await F(a);for(const{relativePath:n}of a){if(v.posix.basename(n)==="versions.yaml"){await y(n,e,i);continue}const o=l(n);if(o==null)continue;const t=e.get(o.versionFolderPath)||{versionedFiles:new Map,versions:[]},f=t.versionedFiles.get(o.filePathInVersion)||new Set,d={version:o.versionName};f.add(d.version),!t.hasVersionsConfig&&t.versions.findIndex(u=>u.version===o.versionName)===-1&&t.versions.push(d),t.versionedFiles.set(o.filePathInVersion,f),e.set(o.versionFolderPath,t)}return w(e)};async function y(r,i,s){let e=v.posix.dirname(r);e==="."?e="/":e=e.endsWith("/")?e:e+"/",e.startsWith(c+"/")&&(e=e.slice(c.length+1)),e.startsWith(p+"/")&&(e=e.slice(p.length+1));const n=(await s.load(r,"yaml")).data;n.versions&&!Array.isArray(n.versions)?(await h.panicOnBuild(`Property 'versions' must be an array at %rp. Got '${typeof n.versions}'.`,r),n.versions=[]):(!n.versions||n.versions.length===0)&&g.warn("Property 'versions' is empty at %rp. This is likely a mistake.",r);const o=i.get(e)||{versionedFiles:new Map,versions:[]};o.hasVersionsConfig=!0,o.versions=n.versions||[],n.default&&(o.defaultVersion=n.default),i.set(e,o)}async function F(r){const i=new Set;for(const{relativePath:s}of r){const e=l(s);if(!(e?.versionFolderPath&&m.includes(e?.versionFolderPath))){if(e?.versionFolderPath==null){(s.match(V)?.length||0)>1&&await h.panicOnBuild(`Nested versions are not supported. Detected nested versions in path: "${s}".`);continue}i.has(e.versionFolderPath)||i.add(e.versionFolderPath)}}}function w(r){for(let[i,s]of r){const e=s.hasVersionsConfig?s.versions:s.versions.sort((o,t)=>P(t.version,o.version)),a=s.hasVersionsConfig?e[e.length-1]:e[0],n=s.defaultVersion||a?.version;r.set(i,{...s,versions:e,defaultVersion:n})}return r}function A(r,i){const s=l(r);return s==null?null:i.get(s.versionFolderPath)?.defaultVersion}export{A as getDefaultVersionByPath,R as versionsConfigLoader};
1
+ import v from"path";import{I18N_DIR_NAME as c,L10N_DIR_NAME as p,RESERVED_DIR_NAMES as u,VERSION_SEPARATOR as V,VERSIONED_FOLDER_REGEXP_MATCH as P}from"../../../constants/common.js";import{parsePathVersions as l}from"../../../../utils/path/parse-path-versions.js";import{compareVersions as g}from"../../../../utils/version/compare-versions.js";import{reporter as h}from"../../../tools/notifiers/reporter.js";import{logger as y}from"../../../tools/notifiers/logger.js";const A=async(i,{cache:r,fs:s})=>{const e=new Map,a=await s.scan();await E(a);for(const{relativePath:n}of a){if(v.posix.basename(n)==="versions.yaml"){await F(n,e,r);continue}const o=l(n);if(o==null||u.includes(V+o.versionName))continue;const t=e.get(o.versionFolderPath)||{versionedFiles:new Map,versions:[]},f=t.versionedFiles.get(o.filePathInVersion)||new Set,d={version:o.versionName};f.add(d.version),!t.hasVersionsConfig&&t.versions.findIndex(m=>m.version===o.versionName)===-1&&t.versions.push(d),t.versionedFiles.set(o.filePathInVersion,f),e.set(o.versionFolderPath,t)}return w(e)};async function F(i,r,s){let e=v.posix.dirname(i);e==="."?e="/":e=e.endsWith("/")?e:e+"/",e.startsWith(c+"/")&&(e=e.slice(c.length+1)),e.startsWith(p+"/")&&(e=e.slice(p.length+1));const n=(await s.load(i,"yaml")).data;n.versions&&!Array.isArray(n.versions)?(await h.panicOnBuild(`Property 'versions' must be an array at %rp. Got '${typeof n.versions}'.`,i),n.versions=[]):(!n.versions||n.versions.length===0)&&y.warn("Property 'versions' is empty at %rp. This is likely a mistake.",i);const o=r.get(e)||{versionedFiles:new Map,versions:[]};o.hasVersionsConfig=!0,o.versions=n.versions||[],n.default&&(o.defaultVersion=n.default),r.set(e,o)}async function E(i){const r=new Set;for(const{relativePath:s}of i){const e=l(s);if(!(e?.versionFolderPath&&u.includes(e?.versionFolderPath))){if(e?.versionFolderPath==null){(s.match(P)?.length||0)>1&&await h.panicOnBuild(`Nested versions are not supported. Detected nested versions in path: "${s}".`);continue}r.has(e.versionFolderPath)||r.add(e.versionFolderPath)}}}function w(i){for(let[r,s]of i){const e=s.hasVersionsConfig?s.versions:s.versions.sort((o,t)=>g(t.version,o.version)),a=s.hasVersionsConfig?e[e.length-1]:e[0],n=s.defaultVersion||a?.version;i.set(r,{...s,versions:e,defaultVersion:n})}return i}function S(i,r){const s=l(i);return s==null?null:r.get(s.versionFolderPath)?.defaultVersion}export{S as getDefaultVersionByPath,A as versionsConfigLoader};
@@ -1,2 +1,2 @@
1
- import{isObject as k}from"../../../../../utils/guards/is-object.js";import{reporter as a}from"../../../../tools/notifiers/reporter.js";async function C(u,s){const h=u.split(`
2
- `),c=[],n=[],l={steps:[]},d=/chunk\s*\{(.*)\}/,g=/(?:^|\s)@chunk-end(?!\S)/;for(let e=0;e<h.length;e++){const r=h[e],p=r.match(d);if(p){let t,i=p[1];try{t=JSON.parse(`{${i}}`)}catch{try{i=i.replace(/\}\s*\*\/$/,""),t=JSON.parse(`{${i}}`)}catch(f){await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. It should be a valid JSON object. ${f}`),t={steps:[]}}}k(t)||(await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. It should be a valid JSON object.`),t={steps:[]}),t.steps&&!Array.isArray(t.steps)&&(await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. "steps" should be an array of strings.`),t={...t,steps:[]}),t.steps?l.steps.push(...t.steps):t.steps=[];const o={start:e,condition:t,children:[]};n.length>0?n[n.length-1].children.push(o):c.push(o),n.push(o)}else g.test(r)?n.length>0?n.pop():await a.panicOnBuildContentError(`Unmatched closing tag '@chunk-end' at line ${e+1} in ${s}`):n.length>0?n[n.length-1].children.push(r):c.push(r)}if(n.length>0){let e;for(;e=n.pop();)await a.panicOnBuildContentError(`Unmatched opening tag '@chunk' at line ${e.start+1} in ${s}`)}return{root:c,metadata:l}}export{C as parseFileChunks};
1
+ import{isObject as k}from"../../../../../utils/guards/is-object.js";import{reporter as a}from"../../../../tools/notifiers/reporter.js";async function O(u,s){const h=u.split(`
2
+ `),c=[],n=[],l={steps:[]},d=/chunk\s*\{(.*)\}/,g=/(?:^|\s)@chunk-end(?!\S)/;for(let e=0;e<h.length;e++){const i=h[e],p=i.match(d);if(p){let t,r=p[1];try{t=JSON.parse(`{${r}}`)}catch{try{r=r.replace(/\}\s*\*\/$/,""),t=JSON.parse(`{${r}}`)}catch(f){await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. It should be a valid JSON object. ${f}`),t={steps:[]}}}k(t)||(await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. It should be a valid JSON object.`),t={steps:[]}),t.steps&&!Array.isArray(t.steps)&&(await a.panicOnBuildContentError(`Invalid format of the @chunk tag at ${e+1} in ${s}. "steps" should be an array of strings.`),t={...t,steps:[]}),t.steps?l.steps.push(...t.steps):t.steps=[];const o={start:e,condition:t,children:[]};n.length>0?n[n.length-1].children.push(o):c.push(o),n.push(o)}else g.test(i)?n.length>0?n.pop():await a.panicOnBuildContentError(`Unmatched closing tag '@chunk-end' at line ${e+1} in ${s}`):n.length>0?n[n.length-1].children.push(i):c.push(i)}if(n.length>0){let e;for(;e=n.pop();)await a.panicOnBuildContentError(`Unmatched opening tag '@chunk' at line ${e.start+1} in ${s}`)}return{root:c,metadata:l}}export{O as parseFileChunks};
@@ -0,0 +1,10 @@
1
+ export declare const ASSISTANT_ANONYMOUS_TOKEN_TYPE = "ai_assistant_anonymous";
2
+ /**
3
+ * Mints the token the portal sends with an anonymous visitor's assistant question, so that the
4
+ * Reunite API reaches `/mcp` with anonymous scope instead of an OAuth challenge. Returns nothing
5
+ * when this portal cannot mint a token its own `/mcp` would accept.
6
+ */
7
+ export declare function createAssistantAnonymousToken(): Promise<string | undefined>;
8
+ /** Tells whether the bearer is an unexpired token this project minted for its own AI assistant. */
9
+ export declare function isAssistantAnonymousToken(token: string): Promise<boolean>;
10
+ //# sourceMappingURL=assistant-anonymous-token.d.ts.map
@@ -0,0 +1 @@
1
+ import{hkdfSync as f}from"node:crypto";import{FALLBACK_JWT_SECRET_KEY as m}from"../../../constants/common.js";import{envConfig as o}from"../../../config/env-config.js";import{AlgorithmTypes as i}from"../../../web-server/jwt/types.js";import*as s from"../../../web-server/jwt/jwt.js";import{toAudienceList as S}from"./token-audience.js";const a="ai_assistant_anonymous",T=600,u="ai-assistant-anonymous";function c(){const n=o.JWT_SECRET_KEY;if(!(!n||n===m||!o.PROJECT_ID))return Buffer.from(f("sha256",n,u,u,32)).toString("hex")}async function E(){const n=c();if(!n)return;const t=Math.floor(Date.now()/1e3);return s.sign({type:a,anonymous:!0,aud:o.PROJECT_ID,iat:t,exp:t+T},n,i.HS256)}async function g(n){const t=c();if(!t)return!1;try{await s.verify(n,t,i.HS256)}catch{return!1}const{payload:e}=s.decode(n),r=o.PROJECT_ID;return e.type===a&&e.anonymous===!0&&!!r&&S(e.aud).includes(r)}export{a as ASSISTANT_ANONYMOUS_TOKEN_TYPE,E as createAssistantAnonymousToken,g as isAssistantAnonymousToken};
@@ -1,13 +1,15 @@
1
1
  import type { ApiFunctionsContext, ApiFunctionsUser } from '@redocly/config';
2
2
  import type { Store } from '../../../store.js';
3
3
  export declare function isMcpContentProtected(store: Store): boolean;
4
- export declare function handleMcpAuth(request: Request, context: ApiFunctionsContext): Promise<{
4
+ export type McpAuthResult = {
5
5
  isAuthenticated: boolean;
6
6
  isTokenValid?: boolean;
7
+ isFirstPartyAnonymous?: boolean;
7
8
  currentUser?: ApiFunctionsUser;
8
9
  accessToken?: string;
9
- }>;
10
+ };
11
+ export declare function handleMcpAuth(request: Request, context: ApiFunctionsContext): Promise<McpAuthResult>;
10
12
  export declare function constructUnauthorizedResponse(baseUrl: string, publicMcpUrl?: string): Response;
11
13
  export declare function constructForbiddenResponse(): Response;
12
- export declare function constructInvalidTokenResponse(baseUrl: string): Response;
14
+ export declare function constructInvalidTokenResponse(baseUrl: string, publicMcpUrl?: string): Response;
13
15
  //# sourceMappingURL=auth-handlers.d.ts.map
@@ -1 +1 @@
1
- import{extractTokenFromAuthHeader as d}from"../../../plugins/mcp/utils/jwt.js";import{SUPPORTED_MCP_SCOPES as p}from"../constants.js";import{getUserParamsFromCookies as h}from"../../../web-server/auth.js";import{ServerRoutes as c}from"../../../../constants/api.js";import{withPathPrefix as l}from"@redocly/theme/core/utils";import{shouldHandleMcpAuth as f}from"../../../utils/rbac/should-handle-mcp-auth.js";import{checkTokenAudience as m,isTokenAudienceEnforced as A,toAudienceList as T}from"./token-audience.js";import{reportMcpTokenAudienceMismatch as k}from"./token-audience-telemetry.js";function x(t){const e=t.getConfig().access;return f(!!e?.requiresLogin,e?.rbac||{})}async function E(t,e){const o=t.headers.get("Authorization");if(!o)return{isAuthenticated:!1};const i=d(o),r=e?.config?.ssoDirect||{},n=i?await h(r,{authorization:i}):{};if(!(i&&n&&n.isAuthenticated))return{isAuthenticated:!1,isTokenValid:!1};const s=n.aud;if(m(s)==="mismatch"){const a=A();if(k({audience:T(s).join(","),enforced:a}),a)return{isAuthenticated:!1,isTokenValid:!1}}return{isAuthenticated:!0,isTokenValid:!0,currentUser:{teams:n.teams||[],email:n.email||"",claims:n,isAuthenticated:!0,idpAccessToken:n.idpAccessToken||void 0,idpId:n.idpId||void 0},accessToken:i}}function u(t,e){const o=`${t}${c.MCP_OAUTH_PROTECTED_RESOURCE}${l("/mcp")}`,i=`${t}${c.MCP_OAUTH_AUTHORIZATION_SERVER}`,r=e?[`error="${e.code}"`,`error_description="${e.description}"`]:[];return r.push(`resource_metadata="${o}"`,`scope="${p.join(" ")}"`,`authorization_uri="${i}"`),`Bearer ${r.join(", ")}`}function S(t,e){const o=e?`Authentication required. A public endpoint that serves anonymous-accessible content is available at ${e}.`:"Authentication required";return new Response(JSON.stringify({error:"unauthorized",message:o}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t),"Access-Control-Allow-Origin":"*"}})}function w(){return new Response(JSON.stringify({error:"forbidden",message:"You do not have access to this MCP server"}),{status:403,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function y(t){return new Response(JSON.stringify({error:"invalid_token",message:"Invalid or expired token"}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t,{code:"invalid_token",description:"Invalid or expired token"}),"Access-Control-Allow-Origin":"*"}})}export{w as constructForbiddenResponse,y as constructInvalidTokenResponse,S as constructUnauthorizedResponse,E as handleMcpAuth,x as isMcpContentProtected};
1
+ import{extractTokenFromAuthHeader as p}from"../../../plugins/mcp/utils/jwt.js";import{SUPPORTED_MCP_SCOPES as f}from"../constants.js";import{getUserParamsFromCookies as h}from"../../../web-server/auth.js";import{ServerRoutes as c}from"../../../../constants/api.js";import{withPathPrefix as l}from"@redocly/theme/core/utils";import{shouldHandleMcpAuth as m}from"../../../utils/rbac/should-handle-mcp-auth.js";import{isAssistantAnonymousToken as A}from"./assistant-anonymous-token.js";import{checkTokenAudience as T,isTokenAudienceEnforced as k,toAudienceList as C}from"./token-audience.js";import{reportMcpTokenAudienceMismatch as g}from"./token-audience-telemetry.js";function x(t){const e=t.getConfig().access;return m(!!e?.requiresLogin,e?.rbac||{})}async function S(t,e){const n=t.headers.get("Authorization");if(!n)return{isAuthenticated:!1};const o=p(n);if(o&&await A(o))return{isAuthenticated:!1,isTokenValid:!0,isFirstPartyAnonymous:!0};const r=e?.config?.ssoDirect||{},i=o?await h(r,{authorization:o}):{};if(!(o&&i&&i.isAuthenticated))return{isAuthenticated:!1,isTokenValid:!1};const s=i.aud;if(T(s)==="mismatch"){const a=k();if(g({audience:C(s).join(","),enforced:a}),a)return{isAuthenticated:!1,isTokenValid:!1}}return{isAuthenticated:!0,isTokenValid:!0,currentUser:{teams:i.teams||[],email:i.email||"",claims:i,isAuthenticated:!0,idpAccessToken:i.idpAccessToken||void 0,idpId:i.idpId||void 0},accessToken:o}}function u(t,e){const n=`${t}${c.MCP_OAUTH_PROTECTED_RESOURCE}${l("/mcp")}`,o=`${t}${c.MCP_OAUTH_AUTHORIZATION_SERVER}`,r=e?[`error="${e.code}"`,`error_description="${e.description}"`]:[];return r.push(`resource_metadata="${n}"`,`scope="${f.join(" ")}"`,`authorization_uri="${o}"`),`Bearer ${r.join(", ")}`}function d(t,e){return e?`${t}. A public endpoint that serves anonymous-accessible content is available at ${e}.`:t}function U(t,e){const n=d("Authentication required",e);return new Response(JSON.stringify({error:"unauthorized",message:n}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t),"Access-Control-Allow-Origin":"*"}})}function b(){return new Response(JSON.stringify({error:"forbidden",message:"You do not have access to this MCP server"}),{status:403,headers:{"Content-Type":"application/json","Access-Control-Allow-Origin":"*"}})}function H(t,e){const n=d("Invalid or expired token",e);return new Response(JSON.stringify({error:"invalid_token",message:n}),{status:401,headers:{"Content-Type":"application/json","WWW-Authenticate":u(t,{code:"invalid_token",description:"Invalid or expired token"}),"Access-Control-Allow-Origin":"*"}})}export{b as constructForbiddenResponse,H as constructInvalidTokenResponse,U as constructUnauthorizedResponse,S as handleMcpAuth,x as isMcpContentProtected};
@@ -1,8 +1,8 @@
1
- const f=/^[A-Za-z_$][\w$]*$/;function y(n){const e=n.split(/[^A-Za-z0-9]+/).filter(Boolean).map((o,i)=>i===0?o:o[0].toUpperCase()+o.slice(1)).join("");return f.test(e)?e:`_${e}`}function T(n){return["```ts","declare const tools: {",n.map(e=>{const o=e.schema,i=e.outputSchema?u(e.outputSchema):"unknown",s=o.required?.length?"input":"input?";return` /** ${l(e.description)} */
1
+ const l=/^[A-Za-z_$][\w$]*$/;function y(n){const e=n.split(/[^A-Za-z0-9]+/).filter(Boolean).map((o,i)=>i===0?o:o[0].toUpperCase()+o.slice(1)).join("");return l.test(e)?e:`_${e}`}function w(n){return["```ts","declare const tools: {",n.map(e=>{const o=e.schema,i=e.outputSchema?u(e.outputSchema):"unknown",s=o.required?.length?"input":"input?";return` /** ${f(e.description)} */
2
2
  ${y(e.name)}(${s}: ${u(o," ")}): Promise<${i}>;`}).join(`
3
3
  `),"};","```"].join(`
4
- `)}function l(n){return(n??"").replace(/\*\//g,"*\\/")}function u(n,r=""){if(!n||typeof n!="object")return"unknown";const e=n.nullable?" | null":"";if(typeof n.codemodeType=="string")return n.codemodeType+e;if(Array.isArray(n.enum))return n.enum.map(m).join(" | ")+e;switch(n.type){case"string":return"string"+e;case"number":case"integer":return"number"+e;case"boolean":return"boolean"+e;case"array":return`Array<${u(n.items,r)}>`+e;case"object":return b(n,r)+e;default:return"unknown"}}function m(n){return typeof n=="string"?`'${n.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`:String(n)}function b(n,r){const e=n.properties??{},o=Object.keys(e),i=n.additionalProperties,s=i?i===!0?"unknown":u(i,r):void 0;if(o.length===0)return i===!1?"{}":`Record<string, ${s??"unknown"}>`;const $=new Set(n.required??[]),c=o.map(t=>{const p=e[t],d=$.has(t)?"":"?",g=f.test(t)?t:JSON.stringify(t);return{doc:p.description,field:`${g}${d}: ${u(p,r+" ")}`}});if(s&&c.push({field:`[key: string]: ${s}`}),!c.some(({doc:t})=>t))return`{ ${c.map(({field:t})=>t).join("; ")} }`;const a=r+" ";return`{
5
- ${c.map(({doc:t,field:p})=>`${t?`${a}/** ${l(t)} */
4
+ `)}function f(n){return(n??"").replace(/\*\//g,"*\\/")}function u(n,r=""){if(!n||typeof n!="object")return"unknown";const e=typeof n.codemodeType=="string"?[n.codemodeType]:Array.isArray(n.enum)&&n.enum.length>0?n.enum.map(b):[m(n,r)];return n.nullable&&e.push("null"),[...new Set(e)].join(" | ")}function m(n,r){switch(n.type){case"string":return"string";case"number":case"integer":return"number";case"boolean":return"boolean";case"array":return`Array<${u(n.items,r)}>`;case"object":return j(n,r);default:return"unknown"}}function b(n){return typeof n=="string"?`'${n.replace(/\\/g,"\\\\").replace(/'/g,"\\'")}'`:String(n)}function j(n,r){const e=n.properties??{},o=Object.keys(e),i=n.additionalProperties,s=i?i===!0?"unknown":u(i,r):void 0;if(o.length===0)return i===!1?"{}":`Record<string, ${s??"unknown"}>`;const $=new Set(n.required??[]),c=o.map(t=>{const p=e[t],d=$.has(t)?"":"?",g=l.test(t)?t:JSON.stringify(t);return{doc:p.description,field:`${g}${d}: ${u(p,r+" ")}`}});if(s&&c.push({field:`[key: string]: ${s}`}),!c.some(({doc:t})=>t))return`{ ${c.map(({field:t})=>t).join("; ")} }`;const a=r+" ";return`{
5
+ ${c.map(({doc:t,field:p})=>`${t?`${a}/** ${f(t)} */
6
6
  `:""}${a}${p};`).join(`
7
7
  `)}
8
- ${r}}`}export{T as buildToolTypeDeclarations,y as sanitizeToolName};
8
+ ${r}}`}export{w as buildToolTypeDeclarations,y as sanitizeToolName};
@@ -6,7 +6,6 @@ export type SandboxCapability = {
6
6
  purpose: string;
7
7
  /** Whether this capability is live for THIS request (RBAC/config/flag-dependent). */
8
8
  isActive(context: McpToolContext): boolean;
9
- /** How this capability changes `execute`'s MCP annotations when active. */
10
9
  annotations: Partial<ToolAnnotations>;
11
10
  /**
12
11
  * The self-contained `describe-tools` section for this capability: heading, TS declaration,
@@ -3,8 +3,7 @@ import type { McpToolSchema } from '../../../../types';
3
3
  import type { McpToolContext } from '../../types.js';
4
4
  import type { SandboxCapability } from './types.js';
5
5
  export declare function getActiveCapabilities(context: McpToolContext): SandboxCapability[];
6
- /** `execute`'s annotations = the read-only base with every active capability's delta merged over it. */
7
- export declare function composeExecuteAnnotations(active: SandboxCapability[]): ToolAnnotations;
6
+ export declare function composeExecuteAnnotations(tools: McpToolSchema[], capabilities: SandboxCapability[]): ToolAnnotations;
8
7
  /**
9
8
  * The describe-tools output: the tool signatures, the always-present
10
9
  * `execute` args note, then one self-contained section per selected capability.
@@ -1,7 +1,7 @@
1
- import{buildToolTypeDeclarations as c,sanitizeToolName as p}from"../build-tool-type-declarations.js";import{EXECUTE_BASE_ANNOTATIONS as u}from"../tools/execute-schema.js";import{buildExecuteExample as m,EXECUTE_ARGS_NOTE as a,USAGE_NOTES as E}from"../prompts.js";import{FETCH_TOOL_NAME as f}from"../constants.js";import{ALL_CAPABILITIES as A}from"./registry.js";function S(n){return A.filter(e=>e.isActive(n))}function _(n){return n.reduce((e,i)=>({...e,...i.annotations}),{...u})}function x(n,e,i){const r=n.map(o=>p(o.name)),t=[];if(n.length>0){const o=c(n),s=o.includes("OpenApi")?`
1
+ import{buildToolTypeDeclarations as p,sanitizeToolName as c}from"../build-tool-type-declarations.js";import{buildExecuteExample as u,EXECUTE_ARGS_NOTE as l,USAGE_NOTES as m}from"../prompts.js";import{FETCH_TOOL_NAME as a}from"../constants.js";import{ALL_CAPABILITIES as d}from"./registry.js";function H(t){return d.filter(o=>o.isActive(t))}function h(t,o){const r=[...t,...o].map(({annotations:e})=>e??{}),i=r.filter(e=>e.readOnlyHint!==!0);return{readOnlyHint:i.length===0,destructiveHint:i.some(e=>e.destructiveHint!==!1),idempotentHint:i.every(e=>e.idempotentHint===!0),openWorldHint:r.some(e=>e.openWorldHint!==!1)}}function O(t,o,r){const i=t.map(n=>c(n.name)),e=[];if(t.length>0){const n=p(t),s=n.includes("OpenApi")?`
2
2
 
3
- ${E}`:"";t.push(`## Sandbox tools
3
+ ${m}`:"";e.push(`## Sandbox tools
4
4
 
5
- ${o}${s}`)}t.push(a),n.length>0&&!e.some(o=>o.id===f)&&t.push(m(r));for(const o of e)t.push(o.renderSection(i,r));return t.join(`
5
+ ${n}${s}`)}e.push(l),t.length>0&&!o.some(n=>n.id===a)&&e.push(u(i));for(const n of o)e.push(n.renderSection(r,i));return e.join(`
6
6
 
7
- `)}export{_ as composeExecuteAnnotations,S as getActiveCapabilities,x as renderDescribeToolsOutput};
7
+ `)}export{h as composeExecuteAnnotations,H as getActiveCapabilities,O as renderDescribeToolsOutput};
@@ -1,3 +1,3 @@
1
- import{renderDescribeToolsOutput as O}from"../capabilities/index.js";import{McpServerType as M}from"../../constants.js";import{reportMcpToolCalled as _,reportMcpToolError as k}from"../../workers/mcp-tool-telemetry.js";import{DESCRIBE_TOOLS_TOOL_NAME as d}from"../constants.js";import{DESCRIBE_TOOLS_DESCRIPTION as D}from"../prompts.js";import{sanitizeToolName as n}from"../build-tool-type-declarations.js";import{UnknownToolError as v}from"./tool-errors.js";function x(i,c){const t=[...new Set([...i.map(s=>n(s.name)),...c.map(s=>n(s.id))])];return{name:d,title:"Describe tools",description:D,schema:{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:`Tool names to describe: ${t.join(", ")}. Omit to describe everything.`,items:t.length>0?{type:"string",enum:t}:{type:"string"},nullable:!0}}}}}function P(i,c,t,s){const l=new Map(i.map(r=>[n(r.name),r])),f=new Map(t.map(r=>[n(r.id),r]));return async r=>{const u=performance.now(),h=s.mcpMode??"code";try{const o=[...new Set((r.tools??[]).map(n))],a=o.length===0,T=o.filter(e=>!l.has(e)&&!f.has(e));if(T.length>0){const e=[...l.keys(),...f.keys()];throw new v(`Unknown tool(s): ${T.join(", ")}. Valid tools: ${e.join(", ")}.`)}const E=a?t:t.filter(e=>o.includes(n(e.id))),p=a?i:o.filter(e=>l.has(e)).map(e=>l.get(e)),y=await Promise.all(p.map(e=>c(e.name))),S=p.filter((e,m)=>y[m]),w=p.filter((e,m)=>!y[m]),g=[O(S,E,s)];w.length>0&&g.push(`Not available for this request: ${w.map(e=>n(e.name)).join(", ")}`);const b=g.join(`
1
+ import{renderDescribeToolsOutput as S}from"../capabilities/index.js";import{McpServerType as v}from"../../constants.js";import{reportMcpToolCalled as _,reportMcpToolError as k}from"../../workers/mcp-tool-telemetry.js";import{DESCRIBE_TOOLS_TOOL_NAME as d}from"../constants.js";import{DESCRIBE_TOOLS_DESCRIPTION as D}from"../prompts.js";import{sanitizeToolName as n}from"../build-tool-type-declarations.js";import{UnknownToolError as M}from"./tool-errors.js";function q(a,c){const t=[...new Set([...a.map(s=>n(s.name)),...c.map(s=>n(s.id))])];return{name:d,title:"Describe tools",description:D,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},schema:{type:"object",required:[],additionalProperties:!1,properties:{tools:{type:"array",description:`Tool names to describe: ${t.join(", ")}. Omit to describe everything.`,items:t.length>0?{type:"string",enum:t}:{type:"string"},nullable:!0}}}}}function x(a,c,t,s){const l=new Map(a.map(r=>[n(r.name),r])),f=new Map(t.map(r=>[n(r.id),r]));return async r=>{const u=performance.now(),h=s.mcpMode??"code";try{const o=[...new Set((r.tools??[]).map(n))],i=o.length===0,y=o.filter(e=>!l.has(e)&&!f.has(e));if(y.length>0){const e=[...l.keys(),...f.keys()];throw new M(`Unknown tool(s): ${y.join(", ")}. Valid tools: ${e.join(", ")}.`)}const E=i?t:t.filter(e=>o.includes(n(e.id))),p=i?a:o.filter(e=>l.has(e)).map(e=>l.get(e)),T=await Promise.all(p.map(e=>c(e.name))),O=p.filter((e,m)=>T[m]),w=p.filter((e,m)=>!T[m]),g=[S(O,E,s)];w.length>0&&g.push(`Not available for this request: ${w.map(e=>n(e.name)).join(", ")}`);const b=g.join(`
2
2
 
3
- `);return _({tool:d,mode:h,serverType:M.Docs,durationMs:Math.round(performance.now()-u),outputLength:b.length}),{content:[{type:"text",text:b}]}}catch(o){const a=o instanceof v;throw k({tool:d,mode:h,serverType:M.Docs,durationMs:Math.round(performance.now()-u),message:o instanceof Error?o.message:String(o),stack:!a&&o instanceof Error&&o.stack||"",errorClass:a?"tool_error":"server_error"}),o}}}export{P as describeTools,x as describeToolsSchema};
3
+ `);return _({tool:d,mode:h,serverType:v.Docs,durationMs:Math.round(performance.now()-u),outputLength:b.length}),{content:[{type:"text",text:b}]}}catch(o){const i=o instanceof M;throw k({tool:d,mode:h,serverType:v.Docs,durationMs:Math.round(performance.now()-u),message:o instanceof Error?o.message:String(o),stack:!i&&o instanceof Error&&o.stack||"",errorClass:i?"tool_error":"server_error"}),o}}}export{x as describeTools,q as describeToolsSchema};
@@ -1,5 +1,3 @@
1
- import type { ToolAnnotations } from '@modelcontextprotocol/server';
2
1
  import type { McpToolSchema } from '../../../../types';
3
- export declare const EXECUTE_BASE_ANNOTATIONS: ToolAnnotations;
4
2
  export declare const EXECUTE_TOOL_SCHEMA: McpToolSchema;
5
3
  //# sourceMappingURL=execute-schema.d.ts.map
@@ -1 +1 @@
1
- import{EXECUTE_TOOL_NAME as e}from"../constants.js";import{buildExecuteDescription as t}from"../prompts.js";const n={readOnlyHint:!0,openWorldHint:!1},r={name:e,title:"Execute code",description:t(!1),annotations:n,schema:{type:"object",required:["code","description"],additionalProperties:!1,properties:{code:{type:"string",description:"JavaScript to run in the sandbox. Return a value to receive it as the result.",minLength:1},description:{type:"string",description:"A short, human-readable summary of what this code does.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required when an API needs credentials. This parameter is only offered when createAnonymousSession is available, so pass it whenever you have called that tool.",minLength:1}}}};export{n as EXECUTE_BASE_ANNOTATIONS,r as EXECUTE_TOOL_SCHEMA};
1
+ import{EXECUTE_TOOL_NAME as e}from"../constants.js";import{buildExecuteDescription as t}from"../prompts.js";const r={name:e,title:"Execute code",description:t(!1),schema:{type:"object",required:["code","description"],additionalProperties:!1,properties:{code:{type:"string",description:"JavaScript to run in the sandbox. Return a value to receive it as the result.",minLength:1},description:{type:"string",description:"A short, human-readable summary of what this code does.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required when an API needs credentials. This parameter is only offered when createAnonymousSession is available, so pass it whenever you have called that tool.",minLength:1}}},outputSchema:{type:"object",properties:{result:{description:"The value the code returned, as JSON"},error:{type:"string",description:"Why the code failed. Set only on a failed call"}}}};export{r as EXECUTE_TOOL_SCHEMA};
@@ -1 +1 @@
1
- import{runInSandbox as C}from"../../../../sandbox/sandbox.js";import{SANDBOX_FAILURE as E}from"../../../../sandbox/constants/sandbox.js";import{returnLastExpression as y}from"../../../../sandbox/utils/return-last-expression.js";import{isMcpToolAvailable as g}from"../../workers/execute-mcp-tool.js";import{importMcpToolHandlers as A}from"../../workers/import-mcp-tool-handlers.js";import{isFetchCapable as M}from"../../gateway-mcp/callable-apis.js";import{buildExecuteHostFetch as S}from"../../gateway-mcp/fetch-capability.js";import{withAuthRequiredMeta as k}from"../../gateway-mcp/upstream-auth.js";import{CODE_MODE_TOOL_NAMES as v,EXECUTE_TOOL_NAME as x}from"../constants.js";import{sanitizeToolName as F}from"../build-tool-type-declarations.js";import{invokeTool as H}from"../invoke-tool.js";import{createSandboxSetup as R}from"../sandbox/create-sandbox-setup.js";import{createToolInputValidator as D}from"../sandbox/tool-input-validator.js";import{describeGuestError as j}from"../sandbox/unknown-tool-hint.js";import{auditCode as B}from"../code-audit.js";import{reportCodeExecuted as I}from"../code-execution-telemetry.js";import{logCodeExecutionAudit as L}from"../code-execution-audit.js";import{ToolResultError as N}from"./tool-errors.js";const P=async(e,t,n)=>{const i=new Map,s=await U(t,n,i),a=[],o=M(t)?await S(t,h=>a.push(h)):void 0;let f=0;const m=o?h=>(f+=1,o(h).catch(_=>{throw i.set(O(_),{reason:"runtime_error"}),_})):void 0,c=B(e.code);L({audit:c,code:e.code,description:e.description,sessionHandle:e.sessionHandle,toolNames:Object.keys(s),fetchBudget:t.gatewayFetchBudgetPerExec,fetchCapable:o!==void 0});const l=Date.now(),d=y(e.code),{result:u,error:r,failure:b,errorPosition:T}=await C(d,{setupContext:R(s,m),authoredCode:e.code}),w=r===void 0?void 0:j(d,r,T,Object.keys(s)),p=r!==void 0&&b===E.HostError?i.get(r):void 0;return I({audit:c,fetchCount:f,outcome:r===void 0?"ok":"error",durationMs:Date.now()-l,failureReason:V(b,p,w?.unknownTool),failedTool:p?.tool,client:t.client,clientVersion:t.clientVersion}),k({content:[{type:"text",text:JSON.stringify({result:u,error:w?.message})}],isError:r!==void 0,...p?.tool!==void 0?{errorClass:"tool_error"}:{}},a)};function V(e,t,n){return e===E.HostError?t?.reason??"runtime_error":e===E.RuntimeError?n===void 0?e:"unknown_tool":e}async function U(e,t,n){const i=Object.keys(await A()).filter(o=>!v.has(o)),s=await Promise.all(i.map(o=>g({toolName:o,context:e}).catch(()=>!1))),a={};return i.forEach((o,f)=>{if(!s[f])return;const m=F(o),c=e.sandboxToolSchemas?.[o],l=c?D(m,c):void 0;a[m]=d=>{const u=l?.(d);return u?(n.set(u,{tool:o,reason:"inner_tool_error"}),Promise.reject(new Error(u))):H(o,d,e,t).catch(r=>{throw n.set(O(r),{tool:o,reason:r instanceof N?"inner_tool_error":"server_error"}),r})}}),a}function O(e){return e instanceof Error?e.message:String(e)}var ae={[x]:P};export{ae as default};
1
+ import{runInSandbox as y}from"../../../../sandbox/sandbox.js";import{SANDBOX_FAILURE as b}from"../../../../sandbox/constants/sandbox.js";import{returnLastExpression as g}from"../../../../sandbox/utils/return-last-expression.js";import{isMcpToolAvailable as A}from"../../workers/execute-mcp-tool.js";import{importMcpToolHandlers as M}from"../../workers/import-mcp-tool-handlers.js";import{isFetchCapable as S}from"../../gateway-mcp/callable-apis.js";import{buildExecuteHostFetch as k}from"../../gateway-mcp/fetch-capability.js";import{withAuthRequiredMeta as v}from"../../gateway-mcp/upstream-auth.js";import{CODE_MODE_TOOL_NAMES as x,EXECUTE_TOOL_NAME as F}from"../constants.js";import{sanitizeToolName as H}from"../build-tool-type-declarations.js";import{invokeTool as R}from"../invoke-tool.js";import{createSandboxSetup as D}from"../sandbox/create-sandbox-setup.js";import{createToolInputValidator as j}from"../sandbox/tool-input-validator.js";import{describeGuestError as B}from"../sandbox/unknown-tool-hint.js";import{auditCode as I}from"../code-audit.js";import{reportCodeExecuted as L}from"../code-execution-telemetry.js";import{logCodeExecutionAudit as N}from"../code-execution-audit.js";import{ToolResultError as P}from"./tool-errors.js";const V=async(e,t,n)=>{const i=new Map,s=await X(t,n,i),a=[],o=S(t)?await k(t,E=>a.push(E)):void 0;let f=0;const m=o?E=>(f+=1,o(E).catch(O=>{throw i.set(T(O),{reason:"runtime_error"}),O})):void 0,c=I(e.code);N({audit:c,code:e.code,description:e.description,sessionHandle:e.sessionHandle,toolNames:Object.keys(s),fetchBudget:t.gatewayFetchBudgetPerExec,fetchCapable:o!==void 0});const l=Date.now(),d=g(e.code),{result:u,error:r,failure:w,errorPosition:C}=await y(d,{setupContext:D(s,m),authoredCode:e.code}),p=r===void 0?void 0:B(d,r,C,Object.keys(s)),h=r!==void 0&&w===b.HostError?i.get(r):void 0;L({audit:c,fetchCount:f,outcome:r===void 0?"ok":"error",durationMs:Date.now()-l,failureReason:U(w,h,p?.unknownTool),failedTool:h?.tool,client:t.client,clientVersion:t.clientVersion});const _=p===void 0?{result:u}:{error:p.message};return v({content:[{type:"text",text:JSON.stringify(_)}],structuredContent:_,isError:r!==void 0,...h?.tool!==void 0?{errorClass:"tool_error"}:{}},a)};function U(e,t,n){return e===b.HostError?t?.reason??"runtime_error":e===b.RuntimeError?n===void 0?e:"unknown_tool":e}async function X(e,t,n){const i=Object.keys(await M()).filter(o=>!x.has(o)),s=await Promise.all(i.map(o=>A({toolName:o,context:e}).catch(()=>!1))),a={};return i.forEach((o,f)=>{if(!s[f])return;const m=H(o),c=e.sandboxToolSchemas?.[o],l=c?j(m,c):void 0;a[m]=d=>{const u=l?.(d);return u?(n.set(u,{tool:o,reason:"inner_tool_error"}),Promise.reject(new Error(u))):R(o,d,e,t).catch(r=>{throw n.set(T(r),{tool:o,reason:r instanceof P?"inner_tool_error":"server_error"}),r})}}),a}function T(e){return e instanceof Error?e.message:String(e)}var ce={[F]:V};export{ce as default};
@@ -1,2 +1,2 @@
1
- import{MANAGE_API_CREDENTIALS_TOOL_NAME as P}from"../../constants.js";import{buildUpstreamCredentialFallback as b,buildUpstreamCredentialInputRequired as I,dedupeElicitation as S,isCredentialElicitationDeclined as k,isStatelessRequest as E,schemeLabelOf as w,supportsUrlElicitation as C}from"../../gateway-mcp/elicitation.js";import{getCallableApis as u,isFetchCapable as j}from"../../gateway-mcp/callable-apis.js";import{secretStore as T}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as q,resolveSecretSubject as $}from"../../gateway-mcp/subject.js";import{upstreamAuthKey as R}from"../../gateway-mcp/upstream-auth.js";import{resolveUpstreamApiAuth as L}from"../../gateway-mcp/upstream-auth-scheme.js";import{apiTitleKey as N,countApiTitles as O}from"../../docs-mcp/utils/api-titles.js";function W(e){return!!e.elicitationEnabled&&j(e)}function z(e){const s=u(e),t=s.map(r=>r.relativePath),a=[...t,...U(s)];return{name:P,title:"Manage API credentials",description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","The link covers every credential the API accepts, so scheme is optional - pass it only when a tool result names the scheme that was rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
2
- `),annotations:{readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for: its \`filePath\` or the \`name\` from \`listApis\`.${t.length>0?` The file paths are: ${t.join(", ")}.`:""}`,minLength:1,...a.length>0?{enum:a}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it to let the link cover every scheme the API accepts.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function U(e){const s=O(e);return e.map(t=>t.name).filter(t=>s.get(N(t))===1)}function J(e,s){return async(t,a)=>{const r=$(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(r.kind==="invalid")return h(r.message);if(r.kind==="anonymous")return h(q);const{subject:m}=r,p=typeof t.api=="string"?t.api:"",n=u(e).find(c=>c.relativePath===p||c.name===p);if(!n){const c=u(e).map(v=>v.relativePath).join(", ");return h(`Unknown api "${p}". Credentials can only be managed for the APIs this server can call: ${c||"none"}.`)}if(k(a.mcpReq.inputResponses))return{content:[{type:"text",text:`The user declined the credentials prompt for the "${n.name}" API, so this call minted no link.`}],isError:!1};const i=await L(e,n.relativePath),l=typeof t.scheme=="string"?t.scheme:void 0,y=Object.keys(i.schemes);if(l&&!i.schemes[l])return h(`Unknown scheme "${l}" for api "${n.relativePath}". The security schemes this server can store credentials for are: ${y.join(", ")}.`);const o=l??i.defaultScheme,d=w(i.schemes,o),f=await s.getKv(),A={subject:m,baseUrl:e.baseUrl??"",target:{api:n.relativePath,name:n.name,scheme:o,...d?{schemeLabel:d}:{}},auth:i.schemes[o],apiAuth:i,kv:f,hasExistingSecret:await T.hasSecret(f,m,n.relativePath,o),reason:{kind:"requested"}};if(E(a.mcpReq)&&C(s.getClientCapabilities()))return await I(A);const g=R({api:n.relativePath,scheme:o});return{content:[{type:"text",text:await S(`${m}:${g}`,()=>b(A))}],isError:!1}}}function h(e){return{content:[{type:"text",text:e}],isError:!0}}export{W as isManageApiCredentialsToolAvailable,J as manageApiCredentials,z as manageApiCredentialsSchema};
1
+ import{MANAGE_API_CREDENTIALS_TOOL_NAME as P}from"../../constants.js";import{buildUpstreamCredentialFallback as b,buildUpstreamCredentialInputRequired as I,dedupeElicitation as S,isCredentialElicitationDeclined as k,isStatelessRequest as E,schemeLabelOf as w,supportsUrlElicitation as C}from"../../gateway-mcp/elicitation.js";import{getCallableApis as u,isFetchCapable as j}from"../../gateway-mcp/callable-apis.js";import{secretStore as T}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as q,resolveSecretSubject as $}from"../../gateway-mcp/subject.js";import{upstreamAuthKey as H}from"../../gateway-mcp/upstream-auth.js";import{resolveUpstreamApiAuth as R}from"../../gateway-mcp/upstream-auth-scheme.js";import{apiTitleKey as L,countApiTitles as N}from"../../docs-mcp/utils/api-titles.js";function W(e){return!!e.elicitationEnabled&&j(e)}function z(e){const s=u(e),t=s.map(i=>i.relativePath),a=[...t,...O(s)];return{name:P,title:"Manage API credentials",description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","The link covers every credential the API accepts, so scheme is optional - pass it only when a tool result names the scheme that was rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
2
+ `),annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for: its \`filePath\` or the \`name\` from \`listApis\`.${t.length>0?` The file paths are: ${t.join(", ")}.`:""}`,minLength:1,...a.length>0?{enum:a}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it to let the link cover every scheme the API accepts.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function O(e){const s=N(e);return e.map(t=>t.name).filter(t=>s.get(L(t))===1)}function J(e,s){return async(t,a)=>{const i=$(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(i.kind==="invalid")return h(i.message);if(i.kind==="anonymous")return h(q);const{subject:m}=i,p=typeof t.api=="string"?t.api:"",n=u(e).find(c=>c.relativePath===p||c.name===p);if(!n){const c=u(e).map(g=>g.relativePath).join(", ");return h(`Unknown api "${p}". Credentials can only be managed for the APIs this server can call: ${c||"none"}.`)}if(k(a.mcpReq.inputResponses))return{content:[{type:"text",text:`The user declined the credentials prompt for the "${n.name}" API, so this call minted no link.`}],isError:!1};const r=await R(e,n.relativePath),l=typeof t.scheme=="string"?t.scheme:void 0,y=Object.keys(r.schemes);if(l&&!r.schemes[l])return h(`Unknown scheme "${l}" for api "${n.relativePath}". The security schemes this server can store credentials for are: ${y.join(", ")}.`);const o=l??r.defaultScheme,d=w(r.schemes,o),f=await s.getKv(),A={subject:m,baseUrl:e.baseUrl??"",target:{api:n.relativePath,name:n.name,scheme:o,...d?{schemeLabel:d}:{}},auth:r.schemes[o],apiAuth:r,kv:f,hasExistingSecret:await T.hasSecret(f,m,n.relativePath,o),reason:{kind:"requested"}};if(E(a.mcpReq)&&C(s.getClientCapabilities()))return await I(A);const v=H({api:n.relativePath,scheme:o});return{content:[{type:"text",text:await S(`${m}:${v}`,()=>b(A))}],isError:!1}}}function h(e){return{content:[{type:"text",text:e}],isError:!0}}export{W as isManageApiCredentialsToolAvailable,J as manageApiCredentials,z as manageApiCredentialsSchema};
@@ -1 +1 @@
1
- import{SEMANTIC_SEARCH_API_URL as y}from"../../../constants/common.js";import{shouldHandleMcpAuth as f}from"../../../utils/rbac/should-handle-mcp-auth.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as g,SUBMIT_FEEDBACK_TOOL_NAME as b}from"../constants.js";import{GRAPHQL_TYPE_KINDS as l,GRAPHQL_LIST_SCOPES as v}from"./tools/graphql/constants.js";import{OPERATION_METHODS as c}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},s={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},a={codemodeType:"OpenApiServer[]"},o={codemodeType:"OpenApiSecurityRequirement[]"},u={codemodeType:"Record<string, OpenApiSecurityScheme>"},P={codemodeType:"OpenApiDocument"},A={codemodeType:"OpenApiOperation & { path: string; method: string }"},n={filePath:{type:"string",description:"API definition file path"},otherMatches:{type:"array",description:"Other API definitions the lookup also matched. Present only when several matched; pass `filePath` to pick another one",items:{type:"object",required:["filePath"],properties:{version:{type:"string"},filePath:{type:"string"}}}}},r="When several definitions match the name, the highest version or the shortest file path is used and the rest are listed in `otherMatches`; tell the user which version or file the answer is based on.",S={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"Matches part of an API name, or of a description when no name matches",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},p={type:"object",required:[],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it. Omit when `filePath` is given.",minLength:1,nullable:!0},apiName:{type:"string",description:"Alias of `name`",nullable:!0},api:{type:"string",description:"Alias of `name`",nullable:!0},version:{type:"string",description:"API version. When omitted and several versions exist, the highest one is used",nullable:!0},filePath:{type:"string",description:"API definition file path, as returned by `listApis`",nullable:!0}}},H={type:"object",required:["path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it. Omit when `filePath` is given.",minLength:1,nullable:!0},apiName:{type:"string",description:"Alias of `name`",nullable:!0},api:{type:"string",description:"Alias of `name`",nullable:!0},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...c.map(e=>e.toUpperCase()),...c],minLength:1},version:{type:"string",description:"API version. When omitted and several versions exist, the highest one is used",nullable:!0},filePath:{type:"string",description:"API definition file path, as returned by `listApis`",nullable:!0}}},L={type:"object",required:[],additionalProperties:!1,properties:{}},q={type:"object",required:[],additionalProperties:!1,properties:{}},O={type:"object",required:["path","summary"],additionalProperties:!1,properties:{path:{type:"string",description:"Path or URL of the documentation page the feedback is about (e.g. /guides/auth)",minLength:1,maxLength:1e3},summary:{type:"string",description:"One sentence that names what is wrong with the page",minLength:1,maxLength:500},details:{type:"string",description:"Expected versus actual behavior, missing steps, or the snippet that failed",maxLength:4e3,nullable:!0},targetFeature:{type:"string",description:"The API or feature the page documents",maxLength:500,nullable:!0}}};function T(e){const t=[{name:"list-apis",title:"List APIs",description:"Lists the APIs documented in this project",schema:S,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:a,filePath:{type:"string",description:"API definition file path"},ambiguousTitle:{type:"boolean",description:"true = another API shares this title; a lookup picks the highest version, or the shortest file path at the same version, and lists the rest in `otherMatches`. Pass `filePath` to pick another one"},...e.hasCallableApis?{callable:{type:"boolean",description:"true = the documented hosts of this API are reachable via `fetch`; false = documentation only"}}:{}}}},...s}}},{name:"get-endpoints",title:"Get endpoints",description:`Get all endpoints for a specific API. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,servers:a,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:o}}}}}},{name:"get-endpoint-info",title:"Get endpoint info",description:`Get comprehensive information about specific endpoint including parameters, security, and examples. ${r}`,schema:H,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,servers:a,endpoint:A,globalSecurity:o,securitySchemes:u}}},{name:"get-security-schemes",title:"Get security schemes",description:`Get the security schemes for a specific API. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{name:i,version:i,...n,securitySchemes:u,security:o}}},{name:"get-full-api-description",title:"Get full API description",description:`Get the complete OpenAPI description. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,definition:P}}},...y?[d(e.products,e.locales)]:[]];return f(e.requiresLogin,e.rbac)&&t.push({name:"whoami",title:"Who am I",description:"Get information about the currently authenticated user",schema:L,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.feedbackHidden||t.push({name:b,title:"Submit feedback",description:"Report a documentation page that is wrong or incomplete",schema:O,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["status","page","message"],properties:{status:{type:"string"},page:{type:"string"},message:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...E()),e.elicitationEnabled&&e.hasCallableApis&&t.push({name:g,title:"Create anonymous session",description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:q,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const G={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},w={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...l,null],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...v,null],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},I={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1,nullable:!0},typeName:{type:"string",description:"Alias of `type`",nullable:!0},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},j={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function E(){return[{name:"list-graphql-apis",title:"List GraphQL APIs",description:"Lists available GraphQL schemas with their context and purpose",schema:G,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...s}}},{name:"get-graphql-type-list",title:"Get GraphQL type list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:w,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:l},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...s}}},{name:"get-graphql-type",title:"Get GraphQL type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:I,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",title:"Get GraphQL schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:j,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function d(e,t){const h=e&&e.length>0,m=t&&t.length>0;return{name:"search",title:"Search documentation",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...h?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:[...e,null],nullable:!0}}:{},...m?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:[...t,null],nullable:!0}}:{}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}const x=new Set([...T({requiresLogin:!0,hasGraphqlApis:!0,elicitationEnabled:!0,hasCallableApis:!0}).map(e=>e.name),d().name]);export{x as ALL_DEFAULT_TOOL_NAMES,T as getDefaultToolSchemas};
1
+ import{SEMANTIC_SEARCH_API_URL as y}from"../../../constants/common.js";import{shouldHandleMcpAuth as f}from"../../../utils/rbac/should-handle-mcp-auth.js";import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as g,SUBMIT_FEEDBACK_TOOL_NAME as b}from"../constants.js";import{GRAPHQL_TYPE_KINDS as l,GRAPHQL_LIST_SCOPES as v}from"./tools/graphql/constants.js";import{OPERATION_METHODS as c}from"./tools/openapi/constants.js";const i={type:"string",nullable:!0},s={limit:{type:"number"},total:{type:"number"},page:{type:"number"},totalPages:{type:"number"}},a={codemodeType:"OpenApiServer[]"},o={codemodeType:"OpenApiSecurityRequirement[]"},u={codemodeType:"Record<string, OpenApiSecurityScheme>"},A={codemodeType:"OpenApiDocument"},P={codemodeType:"OpenApiOperation & { path: string; method: string }"},S={codemodeType:"OpenApiComponents",description:"Components the endpoint references. A `$ref` in `endpoint` resolves against the root of the result."},n={filePath:{type:"string",description:"API definition file path"},otherMatches:{type:"array",description:"Other API definitions the lookup also matched. Present only when several matched; pass `filePath` to pick another one",items:{type:"object",required:["filePath"],properties:{version:{type:"string"},filePath:{type:"string"}}}}},r="When several definitions match the name, the highest version or the shortest file path is used and the rest are listed in `otherMatches`; tell the user which version or file the answer is based on.",H="A `$ref` in `endpoint` points into the `components` key at the root of the result: read `#/components/<section>/<name>` there.",L={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"Matches part of an API name, or of a description when no name matches",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}},p={type:"object",required:[],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it. Omit when `filePath` is given.",minLength:1,nullable:!0},apiName:{type:"string",description:"Alias of `name`",nullable:!0},api:{type:"string",description:"Alias of `name`",nullable:!0},version:{type:"string",description:"API version. When omitted and several versions exist, the highest one is used",nullable:!0},filePath:{type:"string",description:"API definition file path, as returned by `listApis`",nullable:!0}}},O={type:"object",required:["path","method"],additionalProperties:!1,properties:{name:{type:"string",description:"API name, or a unique part of it. Omit when `filePath` is given.",minLength:1,nullable:!0},apiName:{type:"string",description:"Alias of `name`",nullable:!0},api:{type:"string",description:"Alias of `name`",nullable:!0},path:{type:"string",description:"Endpoint path (e.g. /api/v1/users)",minLength:1},method:{type:"string",description:"HTTP method (GET, POST, PUT, DELETE, etc.)",enum:[...c.map(e=>e.toUpperCase()),...c],minLength:1},version:{type:"string",description:"API version. When omitted and several versions exist, the highest one is used",nullable:!0},filePath:{type:"string",description:"API definition file path, as returned by `listApis`",nullable:!0}}},T={type:"object",required:[],additionalProperties:!1,properties:{}},q={type:"object",required:[],additionalProperties:!1,properties:{}},G={type:"object",required:["path","summary"],additionalProperties:!1,properties:{path:{type:"string",description:"Path or URL of the documentation page the feedback is about (e.g. /guides/auth)",minLength:1,maxLength:1e3},summary:{type:"string",description:"One sentence that names what is wrong with the page",minLength:1,maxLength:500},details:{type:"string",description:"Expected versus actual behavior, missing steps, or the snippet that failed",maxLength:4e3,nullable:!0},targetFeature:{type:"string",description:"The API or feature the page documents",maxLength:500,nullable:!0}}};function I(e){const t=[{name:"list-apis",title:"List APIs",description:"Lists the APIs documented in this project",schema:L,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i,version:i,servers:a,filePath:{type:"string",description:"API definition file path"},ambiguousTitle:{type:"boolean",description:"true = another API shares this title; a lookup picks the highest version, or the shortest file path at the same version, and lists the rest in `otherMatches`. Pass `filePath` to pick another one"},...e.hasCallableApis?{callable:{type:"boolean",description:"true = the documented hosts of this API are reachable via `fetch`; false = documentation only"}}:{}}}},...s}}},{name:"get-endpoints",title:"Get endpoints",description:`Get all endpoints for a specific API. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,servers:a,endpoints:{type:"array",items:{type:"object",properties:{path:{type:"string"},method:{type:"string"},summary:i,description:i,security:o}}}}}},{name:"get-endpoint-info",title:"Get endpoint info",description:`Get comprehensive information about specific endpoint including parameters, security, and examples. ${H} ${r}`,schema:O,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,servers:a,endpoint:P,globalSecurity:o,securitySchemes:u,components:S}}},{name:"get-security-schemes",title:"Get security schemes",description:`Get the security schemes for a specific API. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{name:i,version:i,...n,securitySchemes:u,security:o}}},{name:"get-full-api-description",title:"Get full API description",description:`Get the complete OpenAPI description. ${r}`,schema:p,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",required:["filePath"],properties:{api:{type:"string"},version:{type:"string"},...n,definition:A}}},...y?[d(e.products,e.locales)]:[]];return f(e.requiresLogin,e.rbac)&&t.push({name:"whoami",title:"Who am I",description:"Get information about the currently authenticated user",schema:T,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{email:{type:"string"},name:{type:"string"},subject:{type:"string"},clientId:{type:"string"},scope:{type:"string"},issuedAt:{type:"string"},expiresAt:{type:"string"}}}}),e.feedbackHidden||t.push({name:b,title:"Submit feedback",description:"Report a documentation page that is wrong or incomplete",schema:G,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["status","page","message"],properties:{status:{type:"string"},page:{type:"string"},message:{type:"string"}}}}),e.hasGraphqlApis&&t.push(...k()),e.elicitationEnabled&&e.hasCallableApis&&t.push({name:g,title:"Create anonymous session",description:"Create an anonymous session handle for storing upstream API credentials. Pass the returned sessionHandle to execute when an API needs credentials and the caller is not signed in. Treat it as a secret and reuse it across calls.",schema:q,annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"object",required:["sessionHandle"],properties:{sessionHandle:{type:"string"}}}}),t}const w={type:"object",required:[],additionalProperties:!1,properties:{filter:{type:"string",description:"GraphQL schema name (or part of it)",minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},j={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},kind:{type:"string",description:'Optional: return only a single category of schema members. With scope "fields", restricts to the fields of that parent category ("objects", "interfaces", or "inputs" only).',enum:[...l,null],nullable:!0},scope:{type:"string",description:'Optional: what each list item is. "members" (default) lists top-level types and operations. "fields" flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions \u2014 combine with `filter` to search nested fields anywhere in the schema.',enum:[...v,null],nullable:!0},filter:{type:"string",description:'Optional: only members whose name or description contains this substring, case-insensitive (e.g. "custom tags")',minLength:1,nullable:!0},referencesType:{type:"string",description:'Optional: only members that reference this type directly or transitively (via return, field, argument, union-member or input-field types). Finds cross-cutting members that never name it \u2014 e.g. referencesType "Book" surfaces a search query returning a union that includes Book.',minLength:1,nullable:!0},page:{type:"number",description:"Page number",minimum:1,default:1,nullable:!0},limit:{type:"number",description:"Number of results per page. Default is 300",minimum:1,default:300,nullable:!0}}},E={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1},type:{type:"string",description:"Name of the type, operation, or directive",minLength:1,nullable:!0},typeName:{type:"string",description:"Alias of `type`",nullable:!0},includeReferencedTypes:{type:"boolean",description:"Also include the SDL of every type referenced transitively. Gives the whole subgraph at once, but can be large for highly-connected types.",default:!1,nullable:!0}}},N={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"GraphQL schema name",minLength:1}}};function k(){return[{name:"list-graphql-apis",title:"List GraphQL APIs",description:"Lists available GraphQL schemas with their context and purpose",schema:w,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{items:{type:"array",items:{type:"object",properties:{name:{type:"string"},description:i}}},...s}}},{name:"get-graphql-type-list",title:"Get GraphQL type list",description:'List all operations and types in a GraphQL schema as a flat, paginated list. Each item is tagged with its kind (query, object, enum, \u2026) and carries a one-line description; operations also include their return type and argument signature \u2014 use it to decide which members to fetch in detail. Narrow the result with `kind` (one category), `filter` (case-insensitive substring of the member name or description, e.g. "book"), or `referencesType` (only members that use a given type, directly or transitively) to explore a domain cheaply instead of pulling the full schema. To search nested fields \u2014 not just type names \u2014 set scope "fields": it flattens every field of every object/interface/input type into "ParentType.fieldName" entries with their descriptions, so `filter` finds things like an input field whose docstring mentions your keyword.',schema:j,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"object",properties:{name:{type:"string"},items:{type:"array",items:{type:"object",properties:{kind:{type:"string",enum:l},name:{type:"string"},description:i,type:{type:"string"},args:{type:"string"},directives:{type:"string"}}}},...s}}},{name:"get-graphql-type",title:"Get GraphQL type",description:'Get the SDL for a single GraphQL type, operation (query/mutation/subscription field), or directive. Without includeReferencedTypes, the SDL is followed by a one-level manifest of every directly referenced type\'s field names (e.g. "input ActionOperationInput { addTags, removeTags }"), so you can see what nested types contain before fetching them. Set includeReferencedTypes to instead pull in the full SDL of every type it references transitively \u2014 the whole structural subgraph in one call, ideal for complete exploration of an entity (e.g. Book and all the types it touches). That traversal only follows what the target depends on; to go the other direction and find the members that use a type, call get-graphql-type-list with `referencesType`.',schema:E,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}},{name:"get-graphql-schema",title:"Get GraphQL schema",description:"Get the complete GraphQL schema as SDL \u2014 the whole type graph in one call. Returns everything, so it can be very large and token-expensive for big schemas; prefer get-graphql-type-list (filter/paginate) plus targeted get-graphql-type calls, and reach for this only for small schemas or when you truly need the full SDL.",schema:N,annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},outputSchema:{type:"string"}}]}function d(e,t){const h=e&&e.length>0,m=t&&t.length>0;return{name:"search",title:"Search documentation",description:"Search across the documentation to fetch relevant content for a given query",schema:{type:"object",required:["query"],additionalProperties:!1,properties:{query:{type:"string",description:"Search query. Should be a single word or that phrase that is presented in a documentation.",minLength:1},...h?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:[...e,null],nullable:!0}}:{},...m?{locale:{type:"string",description:"Optional locale code to filter search results by documentation language (e.g. 'en', 'ja'). Use the locale matching the language of the user's request. Defaults to the project's default locale.",enum:[...t,null],nullable:!0}}:{}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!1,openWorldHint:!1},outputSchema:{type:"string"}}}const Q=new Set([...I({requiresLogin:!0,hasGraphqlApis:!0,elicitationEnabled:!0,hasCallableApis:!0}).map(e=>e.name),d().name]);export{Q as ALL_DEFAULT_TOOL_NAMES,I as getDefaultToolSchemas};
@@ -1 +1 @@
1
- import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as o}from"../../../constants.js";import{isFetchCapable as i}from"../../../gateway-mcp/callable-apis.js";import{createSessionHandle as t}from"../../../gateway-mcp/subject.js";const r=async()=>({content:[{type:"text",text:JSON.stringify({sessionHandle:t()})}],isError:!1});function s(e){return!!e.elicitationEnabled&&e.mcpMode==="code"&&i(e)&&!e.user?.isAuthenticated}var c={[o]:{execute:r,isAvailable:s}};export{c as default,s as isAnonymousSessionToolAvailable};
1
+ import{CREATE_ANONYMOUS_SESSION_TOOL_NAME as t}from"../../../constants.js";import{isFetchCapable as o}from"../../../gateway-mcp/callable-apis.js";import{createSessionHandle as n}from"../../../gateway-mcp/subject.js";const i=async()=>{const e={sessionHandle:n()};return{content:[{type:"text",text:JSON.stringify(e)}],structuredContent:e}};function s(e){return!!e.elicitationEnabled&&e.mcpMode==="code"&&o(e)&&!e.user?.isAuthenticated}var u={[t]:{execute:i,isAvailable:s}};export{u as default,s as isAnonymousSessionToolAvailable};
@@ -1,3 +1,5 @@
1
1
  export declare const OPERATION_METHODS: readonly ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace', 'query', 'x-query'];
2
2
  export declare const HTTP_METHODS: Set<string>;
3
+ export declare const MAX_ENDPOINT_INFO_BYTES: number;
4
+ export declare const MAX_INLINED_ENDPOINT_BYTES: number;
3
5
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- const e=["get","put","post","delete","options","head","patch","trace","query","x-query"],t=new Set(e);export{t as HTTP_METHODS,e as OPERATION_METHODS};
1
+ const t=["get","put","post","delete","options","head","patch","trace","query","x-query"],e=new Set(t),o=8*1024*1024,T=1024*1024;export{e as HTTP_METHODS,o as MAX_ENDPOINT_INFO_BYTES,T as MAX_INLINED_ENDPOINT_BYTES,t as OPERATION_METHODS};
@@ -4,6 +4,12 @@ import type { McpMode } from '../../../types.js';
4
4
  export declare function visibleMethodsAt(definition: OpenAPIDefinition, path: string): string[];
5
5
  /** Paths with at least one visible operation. */
6
6
  export declare function visiblePaths(definition: OpenAPIDefinition): string[];
7
+ /** The tools that recover a lookup miss. The sandbox calls the same tools by their JS names. */
8
+ export declare function recoveryToolNames(mcpMode?: McpMode): {
9
+ endpoints: string;
10
+ search: string;
11
+ listApis: string;
12
+ };
7
13
  /** Message for a path the definition does not document, or documents only behind `x-mcp`. */
8
14
  export declare function describePathMiss(definition: OpenAPIDefinition, apiTitle: string, path: string, mcpMode?: McpMode): string;
9
15
  /** Message for a visible path that does not document the requested method, or hides it. */
@@ -1 +1 @@
1
- import{isEndpointHidden as s}from"../../../utils/xmcp-utils.js";import{formatCandidates as i}from"../../utils/api-name-matching.js";import{HTTP_METHODS as p}from"./constants.js";import{isMcpEndpoint as a}from"./utils.js";function d(t,r){const e=t.paths?.[r];return!e||s(e,"docs")?[]:Object.entries(e).filter(([n,o])=>p.has(n.toLowerCase())&&a(o)&&!s(o,"docs")).map(([n])=>n.toUpperCase()).sort()}function f(t){return Object.keys(t.paths??{}).filter(r=>d(t,r).length>0).sort()}function $(t,r,e,n){const o=f(t);if(o.length===0)return"Endpoint not found";const h=n==="code"?"Call tools.getEndpoints for the full list, or tools.search with a keyword from the request.":"Call get-endpoints for the full list, or search with a keyword from the request.";return`No path "${e}" in "${r}". Documented paths: ${i(o)}. ${h}`}function b(t,r,e,n){const o=d(t,e);return o.length===0?"Endpoint not found":`"${e}" in "${r}" documents no ${n.toUpperCase()}. Available methods: ${i(o)}.`}export{b as describeMethodMiss,$ as describePathMiss,d as visibleMethodsAt,f as visiblePaths};
1
+ import{isEndpointHidden as r}from"../../../utils/xmcp-utils.js";import{formatCandidates as i}from"../../utils/api-name-matching.js";import{HTTP_METHODS as c}from"./constants.js";import{isMcpEndpoint as h}from"./utils.js";function p(t,s){const e=t.paths?.[s];return!e||r(e,"docs")?[]:Object.entries(e).filter(([n,o])=>c.has(n.toLowerCase())&&h(o)&&!r(o,"docs")).map(([n])=>n.toUpperCase()).sort()}function l(t){return Object.keys(t.paths??{}).filter(s=>p(t,s).length>0).sort()}function f(t){return t==="code"?{endpoints:"tools.getEndpoints",search:"tools.search",listApis:"tools.listApis"}:{endpoints:"get-endpoints",search:"search",listApis:"list-apis"}}function E(t,s,e,n){const o=l(t);if(o.length===0)return"Endpoint not found";const{endpoints:d,search:a}=f(n);return`No path "${e}" in "${s}". Documented paths: ${i(o)}. Call ${d} for the full list, or ${a} with a keyword from the request.`}function M(t,s,e,n){const o=p(t,e);return o.length===0?"Endpoint not found":`"${e}" in "${s}" documents no ${n.toUpperCase()}. Available methods: ${i(o)}.`}export{M as describeMethodMiss,E as describePathMiss,f as recoveryToolNames,p as visibleMethodsAt,l as visiblePaths};
@@ -1 +1 @@
1
- import{resolveParameters as v,resolveRequestBody as E,resolveResponses as M}from"../../utils/resolve-refs.js";import{isMcpEndpoint as P}from"./utils.js";import{describeMethodMiss as g,describePathMiss as b,visibleMethodsAt as q}from"./endpoint-suggestions.js";import{loadApiDescription as B}from"./load-api-description.js";import{checkEndpointAndDeleteXMcp as S}from"../../../utils/xmcp-utils.js";const k=async(c,d)=>{const{path:r,method:o}=c;if(!r||!o)return{content:[{type:"text",text:'Both "path" and "method" are required (e.g. path: "/tickets", method: "GET").'}],isError:!0};let h;try{h=await B(c,d)}catch(p){return{content:[{type:"text",text:p.message}],isError:!0}}const{description:t,filePath:l,otherMatches:n}=h,m=n?` Other definitions share this title: ${n.map(p=>`"${p.filePath}"`).join(", ")}. Pass "filePath" to check them.`:"",s=r.startsWith("/")?r:`/${r}`,{title:i=""}=t.info||{},a=t.paths?.[s],f=o.toLowerCase();if(!a||q(t,s).length===0)return{content:[{type:"text",text:b(t,i,s,d.mcpMode)+m}],isError:!0};const e=a[f];if(!P(e)||!S(e,"docs"))return{content:[{type:"text",text:g(t,i,s,o)+m}],isError:!0};const u=a?.parameters||[],y=e.parameters||[],x={...e,parameters:v({pathParams:u,opParams:y,definition:t}),requestBody:E(e.requestBody,t),responses:M(e.responses,t)};return{content:[{type:"text",text:JSON.stringify({api:i,version:t.info?.version||"",filePath:l,otherMatches:n,servers:t.servers||[],endpoint:{path:r,method:o.toUpperCase(),...x},globalSecurity:t.security||[],securitySchemes:t.components?.securitySchemes||{}},null,2)}]}};var D={"get-endpoint-info":k};export{D as default};
1
+ import{inlineOperation as g,resolveOperation as v}from"../../utils/resolve-refs.js";import{isMcpEndpoint as N,matchDocumentedMethod as D,matchDocumentedPath as I}from"./utils.js";import{MAX_ENDPOINT_INFO_BYTES as y,MAX_INLINED_ENDPOINT_BYTES as O}from"./constants.js";import{describeMethodMiss as T,describePathMiss as b,visibleMethodsAt as S}from"./endpoint-suggestions.js";import{loadApiDescription as _}from"./load-api-description.js";import{checkEndpointAndDeleteXMcp as $}from"../../../utils/xmcp-utils.js";const A=async(h,n)=>{const{path:d,method:o}=h;if(!d||!o)return{content:[{type:"text",text:'Both "path" and "method" are required (e.g. path: "/tickets", method: "GET").'}],isError:!0};let m;try{m=await _(h,n)}catch(a){return{content:[{type:"text",text:a.message}],isError:!0}}const{description:t,filePath:E,otherMatches:i}=m,l=i?` Other definitions share this title: ${i.map(a=>`"${a.filePath}"`).join(", ")}. Pass "filePath" to check them.`:"",e=I(t,d),{title:s=""}=t.info||{},r=t.paths?.[e];if(!r||S(t,e).length===0)return{content:[{type:"text",text:b(t,s,e,n.mcpMode)+l}],isError:!0};const c=r[D(r,o)];if(!N(c)||!$(c,"docs"))return{content:[{type:"text",text:T(t,s,e,o)+l}],isError:!0};const M=r?.parameters||[],f={operation:c,pathParams:M,definition:t},x=n.mcpMode==="code"?g(f,O):void 0,{operation:P,components:u}=x??v(f),p=JSON.stringify({api:s,version:t.info?.version||"",filePath:E,otherMatches:i,servers:t.servers||[],endpoint:{path:e,method:o.toUpperCase(),...P},globalSecurity:t.security||[],securitySchemes:t.components?.securitySchemes||{},...u?{components:u}:{}},null,2);return p.length>y?{content:[{type:"text",text:`${o.toUpperCase()} ${e} is ${p.length} bytes, over the ${y} byte limit.`}],isError:!0}:{content:[{type:"text",text:p}]}};var j={"get-endpoint-info":A};export{j as default};
@@ -13,6 +13,8 @@ type ApiLookup = {
13
13
  name?: string;
14
14
  version?: string;
15
15
  filePath?: string;
16
+ path?: string;
17
+ method?: string;
16
18
  };
17
19
  export declare function loadApiDescription(args: ApiLookup & {
18
20
  apiName?: string;
@@ -1 +1 @@
1
- import{describeAvailableApis as u,findApiDescriptionByNameAndVersion as d}from"../../utils/api-name-matching.js";import{getApiDescriptionFromFs as m}from"./utils.js";function P({name:i,version:t,filePath:c},o){const e=Object.values(o);if(c)return e.filter(s=>s.relativePath===c);const r=e.find(s=>s.relativePath===i);if(r)return[r];const a=d(o,i??"",t);return a?[a.description,...a.otherMatches??[]]:[]}function A({version:i,relativePath:t}){return{...i?{version:i}:{},filePath:t}}async function w(i,t){const{version:c,filePath:o}=i,e=i.name??i.apiName??i.api;if(!o&&!e)throw new Error("Pass `name` (the API title, or a unique part of it) or `filePath` from list-apis.");const r=P({name:e,version:c,filePath:o},t.apiDescriptionsMap);if(r.length===0){if(o)throw new Error(`No API found with filePath "${o}". File paths are returned by the list-apis tool.`);const n=u(t.apiDescriptionsMap,e??"");throw new Error(n?`No API found matching "${e}". Available APIs: ${n}.`:`No API found matching "${e}".`)}const a=!!t?.config?.access?.requiresLogin,s=t?.config?.access?.rbac||{};for(const n of r){const f=await m({relativePath:n.relativePath,outdir:t.outdir||"",user:t.user,rbac:s,requiresLogin:a});if(!f)continue;const h=r.filter(p=>p!==n);return{description:f,filePath:n.relativePath,otherMatches:h.length>0?h.map(A):void 0}}const l=r.map(({relativePath:n})=>`"${n}"`).join(", ");throw new Error(r.length>1?`No API found matching "${e}". None of the matching definitions could be loaded: ${l}.`:`No API found matching "${o||e}".`)}export{w as loadApiDescription};
1
+ import{describeAvailableApis as l,findApiDescriptionByNameAndVersion as b,formatCandidates as d}from"../../utils/api-name-matching.js";import{apiTitleKey as A,compareApiDescriptions as P}from"../../utils/api-titles.js";import{recoveryToolNames as f,visibleMethodsAt as g}from"./endpoint-suggestions.js";import{absolutePath as u,getApiDescriptionFromFs as I,matchDocumentedPath as M}from"./utils.js";function m(i){return`Pass \`name\` (the API title, or a unique part of it) or \`filePath\` from ${f(i).listApis}.`}const D=10;function N({name:i,version:e,filePath:n},t){const o=Object.values(t);if(n)return o.filter(a=>a.relativePath===n);const r=o.find(a=>a.relativePath===i);if(r)return[r];const s=b(t,i??"",e);return s?[s.description,...s.otherMatches??[]]:[]}function w({version:i,relativePath:e}){return{...i?{version:i}:{},filePath:e}}function h({relativePath:i},e){return I({relativePath:i,outdir:e.outdir||"",user:e.user,rbac:e?.config?.access?.rbac||{},requiresLogin:!!e?.config?.access?.requiresLogin})}async function S(i,e){const{version:n,filePath:t}=i,o=i.name??i.apiName??i.api;if(!t&&!o)return y(i,e);const r=N({name:o,version:n,filePath:t},e.apiDescriptionsMap);return F(r,{name:o,filePath:t},e)}async function y({path:i,method:e,version:n},t){const o=Object.values(t.apiDescriptionsMap);if(o.length===0)throw new Error("This project documents no OpenAPI description.");if(o.length===1){const[p]=o;if(n&&p.version!==n)throw new Error(`"${p.name}" has no version "${n}". Available versions: ${p.version||"(no version)"}.`);const $=await h(p,t);if(!$)throw new Error(`Could not read the API definition "${p.relativePath}". ${m(t.mcpMode)}`);return{description:$,filePath:p.relativePath}}if(!i||!e||o.length>D)throw new Error(`${m(t.mcpMode)} Available APIs: ${l(t.apiDescriptionsMap,"")}.`);const r=await k(o,i,e,t),{documenting:s,otherMethods:a}=r;if(s.length===0)throw C(t,a,i,e);if(!n)return v(s,r.best,i,e,t);const c=s.filter(p=>p.version===n);if(c.length===0)throw j(s,i,n);return v(c,r.best,i,e,t)}async function v(i,e,n,t,o){const r=new Set(i.map(({name:p})=>A(p)));if(r.size>1)throw E(i,r,n,t,o.mcpMode);const[s,...a]=i,c=s===e?.api?e.description:await h(s,o);if(!c)throw new Error(`Could not read the API definition "${s.relativePath}". ${m(o.mcpMode)}`);return{description:c,filePath:s.relativePath,otherMatches:a.length>0?a.map(w):void 0}}function E(i,e,n,t,o){const r=[...e].map(s=>{const[a]=i.filter(c=>A(c.name)===s);return`"${a.name}" (${a.relativePath})`});return new Error(`${t.toUpperCase()} ${u(n)} is documented by ${e.size} APIs: ${d(r)}. Pass \`name\` or \`filePath\` from ${f(o).listApis} to pick one.`)}function C(i,e,n,t){if(e.length>0){const s=e.map(({api:a,methods:c})=>`"${a.name}" (${a.relativePath}) documents ${d(c)}.`).join(" ");return new Error(`No API documents ${t.toUpperCase()} ${u(n)}. ${s}`)}const{endpoints:o,search:r}=f(i.mcpMode);return new Error(`No API in this project documents ${u(n)}. Available APIs: ${l(i.apiDescriptionsMap,"")}. Call ${o} with one of them, or ${r} with a keyword from the request.`)}function j(i,e,n){const t=[...new Set(i.map(({version:o})=>o||"(no version)"))];return new Error(`No API documenting "${u(e)}" has version "${n}". Available versions: ${d(t)}.`)}async function k(i,e,n,t){const o=[],r=[];let s;for(const a of i){const c=await h(a,t);if(!c)continue;const p=g(c,M(c,e));p.includes(n.toUpperCase())?(o.push(a),(!s||P(a,s.api)<0)&&(s={api:a,description:c})):p.length>0&&r.push({api:a,methods:p})}return{documenting:o.sort(P),best:s,otherMethods:r}}async function F(i,{name:e,filePath:n},t){if(i.length===0){if(n)throw new Error(`No API found with filePath "${n}". File paths are returned by the ${f(t.mcpMode).listApis} tool.`);const r=l(t.apiDescriptionsMap,e??"");throw new Error(r?`No API found matching "${e}". Available APIs: ${r}.`:`No API found matching "${e}".`)}for(const r of i){const s=await h(r,t);if(!s)continue;const a=i.filter(c=>c!==r);return{description:s,filePath:r.relativePath,otherMatches:a.length>0?a.map(w):void 0}}const o=i.map(({relativePath:r})=>`"${r}"`).join(", ");throw new Error(i.length>1?`No API found matching "${n||e}". None of the matching definitions could be loaded: ${o}.`:`No API found matching "${n||e}".`)}export{S as loadApiDescription};