@redocly/reef 0.132.0-next.6 → 0.132.0-next.7

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 (93) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli/telemetry/index.js +1 -1
  3. package/dist/client/App.js +1 -1
  4. package/dist/client/app/hooks/utils/match-banner-target.d.ts +23 -1
  5. package/dist/client/app/hooks/utils/match-banner-target.js +1 -1
  6. package/dist/client/mcp-tool-handlers-entry.d.ts +3 -0
  7. package/dist/client/mcp-tool-handlers-entry.js +1 -0
  8. package/dist/client/templates/asyncapi-docs/helpers.d.ts +1 -1
  9. package/dist/client/types/ai-search.d.ts +5 -3
  10. package/dist/constants/l10n/langs/ar.js +1 -1
  11. package/dist/constants/l10n/langs/de.js +1 -1
  12. package/dist/constants/l10n/langs/en.js +1 -1
  13. package/dist/constants/l10n/langs/es.js +1 -1
  14. package/dist/constants/l10n/langs/fr.js +1 -1
  15. package/dist/constants/l10n/langs/hi.js +1 -1
  16. package/dist/constants/l10n/langs/it.js +1 -1
  17. package/dist/constants/l10n/langs/ja.js +1 -1
  18. package/dist/constants/l10n/langs/ko.js +1 -1
  19. package/dist/constants/l10n/langs/pl.js +1 -1
  20. package/dist/constants/l10n/langs/pt-BR.js +1 -1
  21. package/dist/constants/l10n/langs/pt.js +1 -1
  22. package/dist/constants/l10n/langs/ru.js +1 -1
  23. package/dist/constants/l10n/langs/uk.js +1 -1
  24. package/dist/constants/l10n/langs/zh.js +1 -1
  25. package/dist/markdoc/nodes/fence/index.js +1 -1
  26. package/dist/markdoc/nodes/index.d.ts +1 -1
  27. package/dist/markdoc/nodes/index.js +1 -1
  28. package/dist/server/api-routes/run-api-routes-worker.js +1 -1
  29. package/dist/server/config/env-schema.d.ts +3 -3
  30. package/dist/server/config/env-schemas/api-urls.d.ts +3 -3
  31. package/dist/server/config/env-schemas/api-urls.js +1 -1
  32. package/dist/server/esbuild/esbuild.js +3 -3
  33. package/dist/server/esbuild/plugins/codegen/index.d.ts +1 -0
  34. package/dist/server/esbuild/plugins/codegen/index.js +7 -3
  35. package/dist/server/esbuild/plugins/mcp-tool-handlers-resolver.d.ts +7 -0
  36. package/dist/server/esbuild/plugins/mcp-tool-handlers-resolver.js +1 -0
  37. package/dist/server/plugins/config-parser/loaders/content-slugs-loader.js +1 -1
  38. package/dist/server/plugins/default-theme/resolve-products-config.js +1 -1
  39. package/dist/server/plugins/markdown/attribute-resolvers/index.js +1 -1
  40. package/dist/server/plugins/markdown/attribute-resolvers/resolve-diagram-from-file.d.ts +4 -0
  41. package/dist/server/plugins/markdown/attribute-resolvers/resolve-diagram-from-file.js +1 -0
  42. package/dist/server/plugins/markdown/attribute-resolvers/resolve-native-md-link.js +1 -1
  43. package/dist/server/plugins/markdown/markdoc/markdoc-options.d.ts +1 -1
  44. package/dist/server/plugins/markdown/markdoc/plugins/render-diagrams.d.ts +16 -0
  45. package/dist/server/plugins/markdown/markdoc/plugins/render-diagrams.js +1 -0
  46. package/dist/server/plugins/markdown/runtime-transform.js +1 -1
  47. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.d.ts +7 -0
  48. package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -0
  49. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoint-info.d.ts +5 -9
  50. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoint-info.js +1 -1
  51. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoints.d.ts +5 -9
  52. package/dist/server/plugins/mcp/docs-mcp/tools/get-endpoints.js +1 -1
  53. package/dist/server/plugins/mcp/docs-mcp/tools/get-full-api-description.d.ts +5 -9
  54. package/dist/server/plugins/mcp/docs-mcp/tools/get-full-api-description.js +1 -1
  55. package/dist/server/plugins/mcp/docs-mcp/tools/get-security-schemes.d.ts +5 -9
  56. package/dist/server/plugins/mcp/docs-mcp/tools/get-security-schemes.js +1 -1
  57. package/dist/server/plugins/mcp/docs-mcp/tools/helpers/load-api-description.d.ts +4 -0
  58. package/dist/server/plugins/mcp/docs-mcp/tools/helpers/load-api-description.js +1 -0
  59. package/dist/server/plugins/mcp/docs-mcp/tools/index.d.ts +10 -7
  60. package/dist/server/plugins/mcp/docs-mcp/tools/index.js +1 -1
  61. package/dist/server/plugins/mcp/docs-mcp/tools/list-apis.d.ts +5 -9
  62. package/dist/server/plugins/mcp/docs-mcp/tools/list-apis.js +1 -1
  63. package/dist/server/plugins/mcp/docs-mcp/tools/search.d.ts +5 -10
  64. package/dist/server/plugins/mcp/docs-mcp/tools/search.js +3 -3
  65. package/dist/server/plugins/mcp/docs-mcp/tools/utils.d.ts +6 -3
  66. package/dist/server/plugins/mcp/docs-mcp/tools/utils.js +6 -6
  67. package/dist/server/plugins/mcp/docs-mcp/tools/whoami.d.ts +5 -9
  68. package/dist/server/plugins/mcp/docs-mcp/tools/whoami.js +1 -1
  69. package/dist/server/plugins/mcp/handlers/docs-mcp-handler.js +1 -1
  70. package/dist/server/plugins/mcp/handlers/handle-mcp-request.js +1 -1
  71. package/dist/server/plugins/mcp/index.js +1 -1
  72. package/dist/server/plugins/mcp/servers/docs-server.d.ts +13 -22
  73. package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
  74. package/dist/server/plugins/mcp/types.d.ts +24 -56
  75. package/dist/server/plugins/mcp/workers/execute-mcp-tool.d.ts +2 -2
  76. package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
  77. package/dist/server/store.d.ts +4 -1
  78. package/dist/server/store.js +1 -1
  79. package/dist/server/telemetry/index.js +1 -1
  80. package/dist/server/types/plugins/common.d.ts +10 -0
  81. package/dist/server/utils/rbac.js +1 -1
  82. package/dist/server/web-server/routes/otel/otel.js +1 -1
  83. package/dist/server/workers/types.d.ts +2 -2
  84. package/dist/server/workers/worker-pool.js +1 -1
  85. package/package.json +8 -8
  86. package/dist/server/plugins/markdown/markdoc/plugins/render-mermaid.d.ts +0 -4
  87. package/dist/server/plugins/markdown/markdoc/plugins/render-mermaid.js +0 -1
  88. package/dist/server/plugins/mcp/docs-mcp/tools/docs-mcp-tool.d.ts +0 -55
  89. package/dist/server/plugins/mcp/docs-mcp/tools/docs-mcp-tool.js +0 -1
  90. package/dist/server/plugins/mcp/handlers/mcp-request-handler.d.ts +0 -11
  91. package/dist/server/plugins/mcp/handlers/mcp-request-handler.js +0 -1
  92. package/dist/server/plugins/mcp/servers/base-server.d.ts +0 -17
  93. package/dist/server/plugins/mcp/servers/base-server.js +0 -1
@@ -1,10 +1,6 @@
1
- import type { McpToolWorkerParams, McpToolWorkerResponse, ToolArgsMap } from '../../types.js';
2
- import { DocsMcpTool, type ContextKey } from './docs-mcp-tool.js';
3
- export declare class GetSecuritySchemesTool extends DocsMcpTool<'get-security-schemes'> {
4
- readonly name = "get-security-schemes";
5
- readonly description = "Get the security schemes for a specific API";
6
- readonly requiredContext: readonly ContextKey[];
7
- constructor();
8
- protected executeAction(args: ToolArgsMap['get-security-schemes'], context: McpToolWorkerParams['context']): Promise<McpToolWorkerResponse>;
9
- }
1
+ import type { McpToolHandler } from '../../types.js';
2
+ declare const _default: {
3
+ 'get-security-schemes': McpToolHandler;
4
+ };
5
+ export default _default;
10
6
  //# sourceMappingURL=get-security-schemes.d.ts.map
@@ -1 +1 @@
1
- import{DocsMcpTool as c}from"./docs-mcp-tool.js";const o={type:"object",required:["name"],additionalProperties:!1,properties:{name:{type:"string",description:"API name",minLength:1}}};class p extends c{name="get-security-schemes";description="Get the security schemes for a specific API";requiredContext=["outdir","accessInfo"];constructor(){super(o)}async executeAction(s,i){const{name:n}=s,t=await this.getApiDefinition(n,i);if(!t.success)return t.response;const{definition:e}=t;return{content:[{type:"text",text:JSON.stringify({name:e.info?.title,version:e.info?.version,securitySchemes:e.components?.securitySchemes||[],security:e.security||[]},null,2)}]}}}export{p as GetSecuritySchemesTool};
1
+ import{loadApiDescription as n}from"./helpers/load-api-description.js";const c=async(t,r)=>{const{name:s}=t;let e;try{e=await n(s,r)}catch(i){return{content:[{type:"text",text:i.message}],isError:!0}}return{content:[{type:"text",text:JSON.stringify({name:e.info?.title,version:e.info?.version,securitySchemes:e.components?.securitySchemes||[],security:e.security||[]},null,2)}]}};var u={"get-security-schemes":c};export{u as default};
@@ -0,0 +1,4 @@
1
+ import type { OpenAPIDefinition } from '@redocly/openapi-docs';
2
+ import type { McpToolContext } from '../../../types.js';
3
+ export declare function loadApiDescription(name: string, context: McpToolContext): Promise<OpenAPIDefinition>;
4
+ //# sourceMappingURL=load-api-description.d.ts.map
@@ -0,0 +1 @@
1
+ import{findApiDescriptionByName as c}from"../../utils.js";import{getApiDescriptionFromFs as t}from"../utils.js";async function f(r,i){const o=c(i.apiDescriptionsMap,r);if(!o)throw new Error(`No API found matching "${r}".`);const n=!!i?.config?.access?.requiresLogin,e=i?.config?.access?.rbac||{},s=await t({relativePath:o.relativePath||"",outdir:i.outdir||"",user:i.user,rbac:e,requiresLogin:n});if(!s)throw new Error(`No API found matching "${r}".`);return s}export{f as loadApiDescription};
@@ -1,8 +1,11 @@
1
- import type { ToolArgsMap } from '../../types.js';
2
- import type { DocsMcpTool, DocsMcpToolRegistrationOptions } from './docs-mcp-tool.js';
3
- export declare const docsTools: DocsMcpTool<keyof ToolArgsMap>[];
4
- export declare function registerDocsTools(options: DocsMcpToolRegistrationOptions): void;
5
- export declare function getDocsTool<T extends keyof ToolArgsMap>(name: T): DocsMcpTool<T> | undefined;
6
- export { DocsMcpTool } from './docs-mcp-tool.js';
7
- export type { DocsMcpToolRegistrationOptions } from './docs-mcp-tool.js';
1
+ declare const _default: {
2
+ whoami: import("../../types.js").McpToolHandler;
3
+ search: import("../../types.js").McpToolHandler;
4
+ 'get-full-api-description': import("../../types.js").McpToolHandler;
5
+ 'get-security-schemes': import("../../types.js").McpToolHandler;
6
+ 'get-endpoint-info': import("../../types.js").McpToolHandler;
7
+ 'get-endpoints': import("../../types.js").McpToolHandler;
8
+ 'list-apis': import("../../types.js").McpToolHandler;
9
+ };
10
+ export default _default;
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- import{GetEndpointInfoTool as t}from"./get-endpoint-info.js";import{GetEndpointsTool as n}from"./get-endpoints.js";import{GetFullApiDescriptionTool as i}from"./get-full-api-description.js";import{GetSecuritySchemesTool as m}from"./get-security-schemes.js";import{ListApisTool as s}from"./list-apis.js";import{SearchTool as c}from"./search.js";import{WhoAmITool as p}from"./whoami.js";import{shouldHandleMcpAuth as f}from"../../auth/auth-handlers.js";const e=[new s,new n,new t,new m,new i,new c,new p],l=e.reduce((o,r)=>(o[r.name]=r,o),{});function A(o){e.forEach(r=>{r.name==="whoami"&&!f(o.accessInfo.requiresLogin,o.accessInfo.rbac)||r.register(o)})}function D(o){return l[o]}import{DocsMcpTool as E}from"./docs-mcp-tool.js";export{E as DocsMcpTool,e as docsTools,D as getDocsTool,A as registerDocsTools};
1
+ import o from"./list-apis.js";import r from"./get-endpoints.js";import t from"./get-endpoint-info.js";import i from"./get-security-schemes.js";import m from"./get-full-api-description.js";import p from"./search.js";import e from"./whoami.js";var d={...o,...r,...t,...i,...m,...p,...e};export{d as default};
@@ -1,10 +1,6 @@
1
- import type { McpToolWorkerParams, McpToolWorkerResponse, ToolArgsMap } from '../../types.js';
2
- import { DocsMcpTool, type ContextKey } from './docs-mcp-tool.js';
3
- export declare class ListApisTool extends DocsMcpTool<'list-apis'> {
4
- readonly name = "list-apis";
5
- readonly description = "Lists available APIs with their context and purpose";
6
- readonly requiredContext: readonly ContextKey[];
7
- constructor();
8
- protected executeAction(args: ToolArgsMap['list-apis'], context: McpToolWorkerParams['context']): Promise<McpToolWorkerResponse>;
9
- }
1
+ import type { McpToolHandler } from '../../types.js';
2
+ declare const _default: {
3
+ 'list-apis': McpToolHandler;
4
+ };
5
+ export default _default;
10
6
  //# sourceMappingURL=list-apis.d.ts.map
@@ -1 +1 @@
1
- import{filterApiDescriptionsByName as u}from"../utils.js";import{DocsMcpTool as m}from"./docs-mcp-tool.js";const d={type:"object",additionalProperties:!1,required:[],properties:{filter:{type:"string",description:"API 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 APIs per page. Default is 300",minimum:1,default:300,nullable:!0}}};class y extends m{name="list-apis";description="Lists available APIs with their context and purpose";requiredContext=[];constructor(){super(d)}async executeAction(s,o){const{filter:i,page:n=1,limit:e=300}=s;let t=Object.values(o.apiDescriptionsMap);i&&(t=u(t,i));const r=(n-1)*e,a=r+e,l=Math.ceil(t.length/e),p=t.length;return t=t.slice(r,a),t.length===0?{content:[{type:"text",text:"No APIs available"}]}:{content:[{type:"text",text:JSON.stringify({items:t.map(({relativePath:b,...c})=>c),limit:e,total:p,page:n,totalPages:l})}]}}}export{y as ListApisTool};
1
+ import{filterApiDescriptionsByName as f}from"../utils.js";const x=async(a,l)=>{const{filter:i,page:n=1,limit:e=300}=a;let t=Object.values(l.apiDescriptionsMap);i&&(t=f(t,i));const s=(n-1)*e,o=s+e,r=Math.ceil(t.length/e),c=t.length;return t=t.slice(s,o),t.length===0?{content:[{type:"text",text:"No APIs available"}]}:{content:[{type:"text",text:JSON.stringify({items:t.map(({relativePath:g,...p})=>p),limit:e,total:c,page:n,totalPages:r})}]}};var m={"list-apis":x};export{m as default};
@@ -1,11 +1,6 @@
1
- import type { McpToolWorkerParams, McpToolWorkerResponse, ToolArgsMap } from '../../types.js';
2
- import { DocsMcpTool, type DocsMcpToolRegistrationOptions, type ContextKey } from './docs-mcp-tool.js';
3
- export declare class SearchTool extends DocsMcpTool<'search'> {
4
- readonly name = "search";
5
- readonly description = "Search across the documentation to fetch relevant content for a given query";
6
- readonly requiredContext: readonly ContextKey[];
7
- constructor(products?: string[]);
8
- register(options: DocsMcpToolRegistrationOptions): void;
9
- protected executeAction(args: ToolArgsMap['search'], context: McpToolWorkerParams['context']): Promise<McpToolWorkerResponse>;
10
- }
1
+ import type { McpToolHandler } from '../../types.js';
2
+ declare const _default: {
3
+ search: McpToolHandler;
4
+ };
5
+ export default _default;
11
6
  //# sourceMappingURL=search.d.ts.map
@@ -1,6 +1,6 @@
1
- import{withPathPrefix as f}from"@redocly/theme/core/utils";import{ServerRoutes as y}from"../../../../../constants/common.js";import{DocsMcpTool as m}from"./docs-mcp-tool.js";class b extends m{name="search";description="Search across the documentation to fetch relevant content for a given query";requiredContext=["baseUrl","headers","products"];constructor(t){super(h(t))}register(t){this.schema=h(t.products),super.register(t)}async executeAction(t,r){const{query:p,product:l}=t;if(!r.baseUrl)throw new Error("Missing required context: baseUrl");const d=JSON.stringify({query:p,product:l});let n=`${r.baseUrl}${f(y.SEMANTIC_SEARCH)}`;n.startsWith("http://")&&(n=n.replace(/^http:\/\//,"https://"));const i=r.headers?.authorization,a=i?`authorization=${String(i).replace(/^Bearer /,"")}`:"";try{const c=await fetch(n,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...a?{Cookie:a}:{}},body:d});if(!c.ok)return{content:[{type:"text",text:"Error retrieving search results."}],isError:!0};const o=await c.json();if(!o||o.length===0)return{content:[{type:"text",text:"No results found."}]};const u=o.map(s=>`### [${s.title}](${new URL(s.url,r.baseUrl).toString()})
1
+ import{withPathPrefix as l}from"@redocly/theme/core/utils";import{ServerRoutes as h}from"../../../../../constants/common.js";const d=async(c,t)=>{const{query:a,product:u}=c,p=JSON.stringify({query:a,product:u});let e=`${t.baseUrl}${l(h.SEMANTIC_SEARCH)}`;e.startsWith("http://")&&(e=e.replace(/^http:\/\//,"https://"));const o=t.user.idpAccessToken?`authorization=${String(t.user.idpAccessToken).replace(/^Bearer /,"")}`:"";try{const s=await fetch(e,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...o?{Cookie:o}:{}},body:p});if(!s.ok)return{content:[{type:"text",text:"Error retrieving search results."}],isError:!0};const r=await s.json();if(!r||r.length===0)return{content:[{type:"text",text:"No results found."}]};const i=r.map(n=>`### [${n.title}](${new URL(n.url,t.baseUrl).toString()})
2
2
 
3
- ${s.content}
3
+ ${n.content}
4
4
  `).join(`
5
5
 
6
- `).trim();return{content:[{type:"text",text:u.length?u:"No results found."}]}}catch{return{content:[{type:"text",text:"Error retrieving search results."}],isError:!0}}}}function h(e){const t=e&&e.length>0;return{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},...t?{product:{type:"string",description:"Optional product name to filter search results by specific product.",enum:e,nullable:!0}}:{}}}}export{b as SearchTool};
6
+ `).trim();return{content:[{type:"text",text:i.length?i:"No results found."}]}}catch{return{content:[{type:"text",text:"Error retrieving search results."}],isError:!0}}};var m={search:d};export{m as default};
@@ -1,6 +1,7 @@
1
1
  import type { OpenAPIDefinition, OpenAPIOperation } from '@redocly/openapi-docs';
2
2
  import type { SearchItemData } from '@redocly/theme/core/types';
3
- import type { AccessInfo, McpEndpoint } from '../../types';
3
+ import type { ApiFunctionsUser, RbacConfig } from '@redocly/config';
4
+ import type { McpEndpoint } from '../../types';
4
5
  export declare function isMcpEndpoint(value: unknown): value is McpEndpoint;
5
6
  type EndpointInfo = Pick<OpenAPIOperation, 'summary' | 'description' | 'security'> & {
6
7
  method: string;
@@ -8,10 +9,12 @@ type EndpointInfo = Pick<OpenAPIOperation, 'summary' | 'description' | 'security
8
9
  };
9
10
  export declare function getEndpointsFromPaths(def: OpenAPIDefinition): EndpointInfo[];
10
11
  export declare function processDocuments(documents: Record<string, SearchItemData[]>, outdir: string): string;
11
- export declare function getApiDescriptionFromFs({ relativePath, outdir, accessInfo: { isAuthenticated, email, teams, rbac, requiresLogin }, }: {
12
+ export declare function getApiDescriptionFromFs({ relativePath, outdir, user, rbac, requiresLogin, }: {
12
13
  relativePath: string;
13
14
  outdir: string;
14
- accessInfo: AccessInfo;
15
+ user: ApiFunctionsUser;
16
+ rbac?: RbacConfig;
17
+ requiresLogin?: boolean;
15
18
  }): Promise<OpenAPIDefinition | undefined>;
16
19
  export {};
17
20
  //# sourceMappingURL=utils.d.ts.map
@@ -1,11 +1,11 @@
1
- import y from"node:fs";import u from"node:path";import{existsSync as j}from"node:fs";import{readFile as A}from"node:fs/promises";import{replaceFileExtension as O}from"../../../../plugins/openapi-docs/store-definition-bundles";import{PUBLIC_API_DEFINITIONS_FOLDER as b}from"../../../../constants/common";import{filterDataByAccessDeep as g}from"../../../../utils/rbac";import{checkEndpointAndDeleteXMcp as E}from"../../utils/xmcp-utils.js";import{MAX_DOCUMENTS_PER_CATEGORY as F}from"../../constants.js";function N(e){return typeof e=="object"&&e!==null&&"responses"in e&&typeof e.responses=="object"}function T(e){const{paths:i={}}=e,s=[];for(const[c,r]of Object.entries(i)){const p=!E(r,"docs");for(const[a,t]of Object.entries(r))p||!E(t,"docs")||s.push({path:c,method:a.toUpperCase(),summary:t.summary,description:t.description,security:t.security})}return s}function R(e,i){return Object.entries(e).map(([c,r])=>{if(!r||r.length===0)return"";const p=r.slice(0,F).map(a=>{const{document:t,highlight:m}=a,d=m?.title||(Array.isArray(t.title)?t.title[0]:t.title);let o=`Document: ${t.title}`;o+=`### [${d}](${t.url})
1
+ import y from"node:fs";import d from"node:path";import{existsSync as D}from"node:fs";import{readFile as j}from"node:fs/promises";import{replaceFileExtension as O}from"../../../../plugins/openapi-docs/store-definition-bundles";import{PUBLIC_API_DEFINITIONS_FOLDER as b}from"../../../../constants/common";import{filterDataByAccessDeep as g}from"../../../../utils/rbac";import{checkEndpointAndDeleteXMcp as A}from"../../utils/xmcp-utils.js";import{MAX_DOCUMENTS_PER_CATEGORY as F}from"../../constants.js";function N(e){return typeof e=="object"&&e!==null&&"responses"in e&&typeof e.responses=="object"}function T(e){const{paths:s={}}=e,o=[];for(const[c,i]of Object.entries(s)){const r=!A(i,"docs");for(const[p,t]of Object.entries(i))r||!A(t,"docs")||o.push({path:c,method:p.toUpperCase(),summary:t.summary,description:t.description,security:t.security})}return o}function R(e,s){return Object.entries(e).map(([c,i])=>{if(!i||i.length===0)return"";const r=i.slice(0,F).map(p=>{const{document:t,highlight:f}=p,h=f?.title||(Array.isArray(t.title)?t.title[0]:t.title);let a=`Document: ${t.title}`;a+=`### [${h}](${t.url})
2
2
 
3
- `;let f;if(t.url)try{let n=t.url.startsWith("/")?t.url.slice(1):t.url;const l=n.indexOf("#");l!==-1&&(n=n.substring(0,l));const h=u.extname(n);h&&(n=n.slice(0,-h.length));let D=n+".md";const x=u.join(i,D);y.existsSync(x)&&(f=y.readFileSync(x,"utf8"))}catch{}return f||(f=m?.text||(Array.isArray(t.text)?t.text[0]:t.text)),o+=f,t.facets&&(o+=`
3
+ `;let m;if(t.url)try{let n=t.url.startsWith("/")?t.url.slice(1):t.url;const l=n.indexOf("#");l!==-1&&(n=n.substring(0,l));const u=d.extname(n);u&&(n=n.slice(0,-u.length));let E=n+".md";const x=d.join(s,E);y.existsSync(x)&&(m=y.readFileSync(x,"utf8"))}catch{}return m||(m=f?.text||(Array.isArray(t.text)?t.text[0]:t.text)),a+=m,t.facets&&(a+=`
4
4
 
5
5
  **Categories:**
6
- `,Object.entries(t.facets).forEach(([n,l])=>{o+=`- ${n}: ${l}
7
- `})),o});return`## ${c}
6
+ `,Object.entries(t.facets).forEach(([n,l])=>{a+=`- ${n}: ${l}
7
+ `})),a});return`## ${c}
8
8
 
9
- ${p}`}).join(`
9
+ ${r}`}).join(`
10
10
 
11
- `).trim()}async function U({relativePath:e,outdir:i,accessInfo:{isAuthenticated:s,email:c,teams:r,rbac:p={},requiresLogin:a=!1}}){const t=u.join(i||"",b,O(e,".json"));if(!j(t))return;const d=await A(t,"utf-8"),o=JSON.parse(d);return g(o,{isAuthenticated:s,email:c,teams:r},p,a)}export{U as getApiDescriptionFromFs,T as getEndpointsFromPaths,N as isMcpEndpoint,R as processDocuments};
11
+ `).trim()}async function U({relativePath:e,outdir:s,user:o,rbac:c={},requiresLogin:i=!1}){const r=d.join(s||"",b,O(e,".json"));if(!D(r))return;const t=await j(r,"utf-8"),f=JSON.parse(t);return g(f,{isAuthenticated:o?.isAuthenticated,email:o?.email,teams:o?.teams},c,i)}export{U as getApiDescriptionFromFs,T as getEndpointsFromPaths,N as isMcpEndpoint,R as processDocuments};
@@ -1,10 +1,6 @@
1
- import type { McpToolWorkerParams, McpToolWorkerResponse, ToolArgsMap } from '../../types.js';
2
- import { DocsMcpTool, type ContextKey } from './docs-mcp-tool.js';
3
- export declare class WhoAmITool extends DocsMcpTool<'whoami'> {
4
- readonly name = "whoami";
5
- readonly description = "Get information about the currently authenticated user";
6
- readonly requiredContext: readonly ContextKey[];
7
- constructor();
8
- protected executeAction(_args: ToolArgsMap['whoami'], context: McpToolWorkerParams['context']): Promise<McpToolWorkerResponse>;
9
- }
1
+ import type { McpToolHandler } from '../../types.js';
2
+ declare const _default: {
3
+ whoami: McpToolHandler;
4
+ };
5
+ export default _default;
10
6
  //# sourceMappingURL=whoami.d.ts.map
@@ -1 +1 @@
1
- import{getUserInfoFromHeaders as r}from"../../utils/jwt.js";import{DocsMcpTool as o}from"./docs-mcp-tool.js";const n={type:"object",required:[],additionalProperties:!1,properties:{}};class u extends o{name="whoami";description="Get information about the currently authenticated user";requiredContext=["headers"];constructor(){super(n)}async executeAction(s,t){const e=r(t.headers||{});return e?{content:[{type:"text",text:JSON.stringify({email:e.email,name:e.name,subject:e.sub,clientId:e.client_id,scope:e.scope,issuedAt:e.iat?new Date(e.iat*1e3).toISOString():void 0,expiresAt:e.exp?new Date(e.exp*1e3).toISOString():void 0})}],isError:!1}:{content:[{type:"text",text:JSON.stringify({error:"Not authenticated",message:"No valid authentication token found. Please authenticate first."})}],isError:!0}}}export{u as WhoAmITool};
1
+ const s=async(i,e)=>e.user?.isAuthenticated?{content:[{type:"text",text:JSON.stringify({email:e.user.email,name:e.user.claims?.name,subject:e.user.claims?.sub,clientId:e.user.claims?.client_id,scope:e.user.claims?.scope,issuedAt:e.user.claims?.iat?new Date(Number(e.user.claims.iat)*1e3).toISOString():void 0,expiresAt:e.user.claims?.exp?new Date(Number(e.user.claims.exp)*1e3).toISOString():void 0})}],isError:!1}:{content:[{type:"text",text:JSON.stringify({error:"Not authenticated",message:"No valid authentication token found. Please authenticate first."})}],isError:!0};var r={whoami:s};export{r as default};
@@ -1 +1 @@
1
- import{createMcpRequestHandler as y}from"./mcp-request-handler.js";import{createDocsMcpServer as A}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as S}from"../utils.js";import{createInternalServerError as I}from"./errors.js";import{McpServerType as f}from"../constants.js";import{telemetry as d}from"../../../telemetry/index.js";import{constructInvalidTokenResponse as L,constructUnauthorizedResponse as R,handleMcpAuth as w,shouldHandleMcpAuth as T}from"../auth/auth-handlers.js";async function U(r,s,t,n){try{d.initialize();const e=s,a=e?.props?.config?.apiDescriptionsMap||{},i=e?.props?.outdir||"",c=e?.props?.config?.mcpDocsServerName||"Docs MCP server",l=new URL(n.url).origin,{user:o,config:{access:p,mcp:g={}}}=r,m=p?.rbac||{},u=p?.requiresLogin||!1,v=S(a,o,m,u),M=g.docs?.name||c,D=r.config.products?Object.values(r.config.products).map(b=>b?.name):[],h={rbac:m,email:o?.email,teams:o?.teams,isAuthenticated:!!o?.isAuthenticated,requiresLogin:u};return await A({name:M,baseUrl:l,headers:t,apiDescriptionsMap:v,outdir:i,accessInfo:h,products:D})}catch(e){throw d.sendMcpErrorMessage([{object:"mcp_server",server_type:f.Docs,message:e?.message||"",stack:e?.stack||""}]),I(e?.message||"Internal server error mcp docs")}}const k=y({createServerInstance:U,serverType:f.Docs}),H=async(r,s,t)=>{const n=!!s?.config?.access?.requiresLogin,e=s?.config?.access?.rbac;if(T(n,e)){const{isAuthenticated:a,isTokenValid:i,currentUser:c}=await w(r,s);if(!a)return R(new URL(r.url).origin);if(!i)return L();c&&(s.user=c)}return k(r,s,t)};var B=H;export{B as default};
1
+ import{toFetchResponse as R,toReqRes as w}from"fetch-to-node";import{createDocsMcpServer as y}from"../servers/docs-server.js";import{filterApiDescriptionsByRbac as C}from"../utils.js";import{createMethodNotAllowedError as D,withErrorHandling as v}from"./errors.js";import{McpServerType as A}from"../constants.js";import{constructInvalidTokenResponse as S,constructUnauthorizedResponse as T,handleMcpAuth as U,shouldHandleMcpAuth as b}from"../auth/auth-handlers.js";const L=async(o,e,p)=>{const t=!!e?.config?.access?.requiresLogin,a=e?.config?.access?.rbac||{};if(b(t,a)){const{isAuthenticated:s,isTokenValid:i,currentUser:n}=await U(o,e);if(!s)return T(new URL(o.url).origin);if(!i)return S();n&&(e.user=n)}let r;const d=async()=>{r&&(await r.cleanup(),r=void 0)};return await v(async()=>{if(o.method==="GET")return new Response(JSON.stringify({error:"Method Not Allowed",message:`In order to use this MCP server, you need register it in your MCP Client (VS Code, Cursor, Claude Code, etc.) using that URL: ${o.url}`}),{status:405,headers:{"Content-Type":"application/json"}});if(o.method!=="POST")return D();const s=p,i=s?.props?.config?.apiDescriptionsMap||{},n=s?.props?.config?.mcpDocsServerName||"Docs MCP server",{config:{mcp:u={}}}=e,l=C(i,e.user,a,t),f=e.config.products?Object.values(e.config.products).map(M=>M?.name):[],m=u.docs?.name||n;r=await y({name:m,tools:s?.props?.tools||[],context:{...e,outdir:e.outdir||"",baseUrl:e.baseUrl||new URL(o.url).origin,apiDescriptionsMap:l,products:f}});const g=await o.json(),{req:h,res:c}=w(o);return await r.transport.handleRequest(h,c,g),R(c)},A.Docs,d)};var O=L;export{O as default};
@@ -1 +1 @@
1
- import{withPathPrefix as d}from"@redocly/theme/core/utils";import{importApiRoutesHandler as f}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as h}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as A}from"../../../telemetry/index.js";import{KvService as D}from"../../../persistence/kv/services/kv-service.js";import{envConfig as o}from"../../../config/env-config.js";async function y(a,s,t){const e=s.get("auth"),i=await t.resolveRouteStaticData(a)||{},r=d(a.slug),{requestHandlers:c}=await f(t.serverOutDir),p=c[a.requestHandlerId],m=(await p()).default,u=async()=>await D.getInstance({baseDbDir:t.serverOutDir,sqldRemoteDatabaseUrl:o.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:o.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),l=h({honoCtx:s,ctx:{user:{teams:e.teams,email:e.claims.email,claims:e.claims,idpAccessToken:e.idpAccessToken,idpId:e.claims.idpId,isAuthenticated:e.isAuthenticated},config:t.config},telemetry:A,getKv:u}),n=await m(s.req.raw,l,{...i,props:{...i.props,routeSlug:r,outdir:t.outdir}});return n instanceof Response?n:typeof n=="string"?new Response(n,{headers:{"Content-Type":"text/plain"}}):new Response(JSON.stringify(n),{headers:{"Content-Type":"application/json"}})}export{y as handleMcpRequest};
1
+ import{withPathPrefix as d}from"@redocly/theme/core/utils";import{importApiRoutesHandler as f}from"../../../api-routes/import-api-routes-handlers.js";import{enhanceContext as h}from"../../../api-routes/helpers/enhance-context.js";import{telemetry as A}from"../../../telemetry/index.js";import{KvService as D}from"../../../persistence/kv/services/kv-service.js";import{envConfig as s}from"../../../config/env-config.js";async function v(i,a,e){const t=a.get("auth"),r=await e.resolveRouteStaticData(i)||{},o=d(i.slug),{requestHandlers:c}=await f(e.serverOutDir),p=c[i.requestHandlerId],u=(await p()).default,l=async()=>await D.getInstance({baseDbDir:e.serverOutDir,sqldRemoteDatabaseUrl:s.SQLD_REMOTE_DATABASE_URL,sqldRemoteDatabaseAuthToken:s.SQLD_REMOTE_DATABASE_AUTH_TOKEN}),m=h({honoCtx:a,ctx:{user:{teams:t.teams,email:t.claims.email,claims:t.claims,idpAccessToken:t.idpAccessToken,idpId:t.claims.idpId,isAuthenticated:t.isAuthenticated},config:e.config,outdir:e.outdir,baseUrl:new URL(a.req.url).origin},telemetry:A,getKv:l}),n=await u(a.req.raw,m,{...r,props:{...r.props,routeSlug:o,outdir:e.outdir}});return n instanceof Response?n:typeof n=="string"?new Response(n,{headers:{"Content-Type":"text/plain"}}):new Response(JSON.stringify(n),{headers:{"Content-Type":"application/json"}})}export{v as handleMcpRequest};
@@ -1 +1 @@
1
- import{fileURLToPath as g}from"node:url";import{dirname as u,join as D}from"node:path";import{logger as a}from"../../tools/notifiers/logger.js";import{reporter as h}from"../../tools/notifiers/reporter.js";import{filterIgnoredApiDescriptions as v,getCleanedUpApiDescriptions as C,isMcpInRedirects as y}from"./utils.js";import{telemetryTraceStep as P}from"../../../cli/telemetry/helpers/trace-step.js";const p="mcp-docs-server-handler",M=u(g(import.meta.url));async function R(){let o=!1;return{id:"mcp",async processContent(e,i){o||await P("build.plugin.mcp",async n=>{const s=await e.loadOpenApiDefinitions(i),d=await i.getConfig(),{mcp:r,redirects:f}=d;if(n?.setAttribute("config",JSON.stringify(r||{})),!(r?.hide||r?.docs?.hide)&&(y(f)&&await h.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),!r?.docs?.hide)){a.info("Configuring MCP servers...");const m=D(M,"./handlers/docs-mcp-handler.js"),l=C(v(s,r?.docs?.ignore||[])),c=new Map;for(const t of l)c.set(t.definition.info?.title?.toLowerCase()||"",{name:t.definition.info?.title||"",description:t.definition.info?.description||"",version:t.definition.info?.version||"",servers:t.definition.servers||[],relativePath:t.relativePath});await S({actions:e,handlerPath:m,apiDescriptionsMap:Object.fromEntries(c),mcpConfig:r}),o=!0}})},async afterRoutesCreated(e,i){const n=await i.getConfig(),{mcp:s}=n;e.setGlobalData({mcpData:{docs:{enabled:!s?.hide&&!s?.docs?.hide,name:s?.docs?.name}}})}}}var I=R;async function S({actions:o,handlerPath:e,apiDescriptionsMap:i,mcpConfig:n}){o.createRequestHandler(p,e),o.addApiRoute({slug:"/mcp",requestHandlerId:p,httpMethod:"all",getStaticData:async()=>({props:{config:{apiDescriptionsMap:i,mcpDocsServerName:n?.docs?.name||"Docs MCP Server"}}})}),a.info("Registered Docs MCP Server endpoint at /mcp")}export{p as MCP_DOCS_SERVER_HANDLER_ID,I as default,R as mcpServerPlugin};
1
+ import{fileURLToPath as M}from"node:url";import{dirname as P,join as p}from"node:path";import{logger as d}from"../../tools/notifiers/logger.js";import{reporter as y}from"../../tools/notifiers/reporter.js";import{isDefined as S}from"../../../utils/guards/is-defined.js";import{filterIgnoredApiDescriptions as b,getCleanedUpApiDescriptions as R,isMcpInRedirects as w}from"./utils.js";import{getDefaultToolSchemas as O}from"./docs-mcp/tool-schemas.js";import{telemetryTraceStep as A}from"../../../cli/telemetry/helpers/trace-step.js";const f="mcp-docs-server-handler",m=P(M(import.meta.url));async function T(){let i=!1;return{id:"mcp",async processContent(r,o){i||await A("build.plugin.mcp",async c=>{const t=await r.loadOpenApiDefinitions(o),n=await o.getConfig(),{mcp:s,redirects:l}=n;if(c?.setAttribute("config",JSON.stringify(s||{})),!(s?.hide||s?.docs?.hide)&&(w(l)&&await y.panicOnBuildContentError('The reserved route "/mcp" cannot be used in redirect configuration. Please remove any redirects involving "/mcp" from your redocly.yaml config.'),!s?.docs?.hide)){d.info("Configuring MCP servers...");const u=p(m,"./handlers/docs-mcp-handler.js"),g=R(b(t,s?.docs?.ignore||[])),a=new Map;for(const e of g)a.set(e.definition.info?.title?.toLowerCase()||"",{name:e.definition.info?.title||"",description:e.definition.info?.description||"",version:e.definition.info?.version||"",servers:e.definition.servers||[],relativePath:e.relativePath});const D=Object.values(n.products||{}).map(e=>e?.name).filter(S),h=!!n?.access?.requiresLogin,v=n?.access?.rbac||{},C=O({products:D,requiresLogin:h,rbac:v});r.addMcpTools(p(m,"./docs-mcp/tools/index.js"),C),await j({actions:r,handlerPath:u,apiDescriptionsMap:Object.fromEntries(a),mcpConfig:s}),i=!0}})},async afterRoutesCreated(r,o){const c=await o.getConfig(),{mcp:t}=c;r.setGlobalData({mcpData:{docs:{enabled:!t?.hide&&!t?.docs?.hide,name:t?.docs?.name}}})}}}var U=T;async function j({actions:i,handlerPath:r,apiDescriptionsMap:o,mcpConfig:c}){i.createRequestHandler(f,r),i.addApiRoute({slug:"/mcp",requestHandlerId:f,httpMethod:"all",getStaticData:async()=>({props:{config:{apiDescriptionsMap:o,mcpDocsServerName:c?.docs?.name||"Docs MCP Server"},serverOutDir:i.serverOutDir,tools:i.getMcpTools().map(({importPath:t,...n})=>n)}})}),d.info("Registered Docs MCP Server endpoint at /mcp")}export{f as MCP_DOCS_SERVER_HANDLER_ID,U as default,T as mcpServerPlugin};
@@ -1,24 +1,15 @@
1
- import type { AccessInfo, ApiDescriptionInfo, DocsServerDependencies, McpServerType } from '../types.js';
2
- import { BaseMcpServer } from './base-server.js';
3
- export declare class DocsMcpServer extends BaseMcpServer {
4
- private readonly dependencies;
5
- constructor(dependencies: DocsServerDependencies & {
6
- baseUrl: string;
7
- headers: Record<string, string | string[] | undefined>;
8
- outdir: string;
9
- accessInfo: AccessInfo;
10
- products?: string[];
11
- });
12
- protected registerTools(): void;
13
- protected getServerType(): McpServerType;
14
- }
15
- export declare function createDocsMcpServer({ name, baseUrl, headers, apiDescriptionsMap, outdir, accessInfo, products, }: {
1
+ import { McpServer } from '@redocly/mcp-typescript-sdk/server/mcp.js';
2
+ import { StreamableHTTPServerTransport } from '@redocly/mcp-typescript-sdk/server/streamableHttp.js';
3
+ import type { McpToolSchema } from '../../../types';
4
+ import type { McpToolContext } from '../types.js';
5
+ export type DocsMcpServerInstance = {
6
+ server: McpServer;
7
+ transport: StreamableHTTPServerTransport;
8
+ cleanup: () => Promise<void>;
9
+ };
10
+ export declare function createDocsMcpServer({ name, tools, context, }: {
16
11
  name: string;
17
- baseUrl: string;
18
- headers: Record<string, string | string[] | undefined>;
19
- apiDescriptionsMap: Record<string, ApiDescriptionInfo>;
20
- outdir: string;
21
- accessInfo: AccessInfo;
22
- products: string[];
23
- }): Promise<import("../types.js").McpServerInstance>;
12
+ tools: McpToolSchema[];
13
+ context: McpToolContext;
14
+ }): Promise<DocsMcpServerInstance>;
24
15
  //# sourceMappingURL=docs-server.d.ts.map
@@ -1 +1 @@
1
- import{BaseMcpServer as o}from"./base-server.js";import{registerDocsTools as p}from"../docs-mcp/tools/index.js";import{McpServerType as a}from"../constants.js";class d extends o{dependencies;constructor(e){super({name:e.name,version:new Date().toISOString().slice(0,10)}),this.dependencies=e}registerTools(){p({server:this.server,apiDescriptionsMap:this.dependencies.apiDescriptionsMap,baseUrl:this.dependencies.baseUrl,outdir:this.dependencies.outdir,headers:this.dependencies.headers,accessInfo:this.dependencies.accessInfo,products:this.dependencies.products})}getServerType(){return a.Docs}}async function S({name:s,baseUrl:e,headers:r,apiDescriptionsMap:i,outdir:t,accessInfo:n,products:c}){return await new d({name:s,baseUrl:e,headers:r,apiDescriptionsMap:i,outdir:t,accessInfo:n,products:c}).initialize()}export{d as DocsMcpServer,S as createDocsMcpServer};
1
+ import{McpServer as p}from"@redocly/mcp-typescript-sdk/server/mcp.js";import{StreamableHTTPServerTransport as l}from"@redocly/mcp-typescript-sdk/server/streamableHttp.js";import{logger as m}from"../../../tools/notifiers/logger.js";import{mcpToolWorkers as f,MCP_TOOL_WORKER_KEY as d}from"../../../workers/mcp-tool-worker-pool.js";async function v({name:e,tools:a,context:n}){const o=new p({name:e,version:new Date().toISOString().slice(0,10)},{capabilities:{logging:{}}}),s=new l({sessionIdGenerator:void 0});for(const r of a){const t=async(i,c)=>{m.info(`MCP tool called: ${r.name}`);const u={toolName:r.name,args:i,context:g(n),extra:I(c)};return await f.exec(d,[u],{timeout:6e4})};o.tool(r.name,r.description,r.schema,t)}return await o.connect(s),{server:o,transport:s,cleanup:async()=>{s.close()}}}function I(e){return{sessionId:e.sessionId,authInfo:e.authInfo,requestId:e.requestId,requestInfo:e.requestInfo,_meta:e._meta}}function g(e){return{user:e.user,config:e.config,outdir:e.outdir,baseUrl:e.baseUrl,params:e.params,query:e.query,cookies:e.cookies,apiDescriptionsMap:e.apiDescriptionsMap,products:e.products}}export{v as createDocsMcpServer};
@@ -1,20 +1,13 @@
1
1
  import type { OpenAPIPath } from '@redocly/openapi-docs/src/types';
2
- import type { McpServer } from '@redocly/mcp-typescript-sdk/server/mcp.js';
3
- import type { StreamableHTTPServerTransport } from '@redocly/mcp-typescript-sdk/server/streamableHttp.js';
4
- import type { CallToolResult } from '@redocly/mcp-typescript-sdk/types.js';
2
+ import type { ApiFunctionsContext, ApiFunctionsContextMethods, PageStaticData } from '@redocly/config';
3
+ import type { CallToolResult, RequestId, RequestInfo, RequestMeta } from '@redocly/mcp-typescript-sdk/types.js';
4
+ import type { AuthInfo } from '@redocly/mcp-typescript-sdk/server/types.js';
5
5
  import type { OpenAPIServer } from '@redocly/openapi-docs/lib/types/open-api.js';
6
6
  import type { McpErrorCodes, McpServerType } from './constants';
7
- import type { RbacConfig, PageStaticData } from '@redocly/config';
7
+ import type { McpToolSchema } from '../../types';
8
8
  type ObjectValues<T> = T[keyof T];
9
9
  export type McpServerType = ObjectValues<typeof McpServerType>;
10
10
  export type McpErrorCodeTypes = ObjectValues<typeof McpErrorCodes>;
11
- export type AccessInfo = {
12
- isAuthenticated: boolean;
13
- email?: string;
14
- teams?: string[];
15
- rbac: RbacConfig;
16
- requiresLogin: boolean;
17
- };
18
11
  export type ApiDescriptionInfo = {
19
12
  name: string;
20
13
  relativePath: string;
@@ -29,6 +22,8 @@ export type McpDocsStaticDataPropsConfig = {
29
22
  export type McpDocsStaticData = PageStaticData & {
30
23
  props: {
31
24
  config: McpDocsStaticDataPropsConfig;
25
+ serverOutDir?: string;
26
+ tools?: McpToolSchema[];
32
27
  };
33
28
  };
34
29
  export type McpPath = {
@@ -43,11 +38,6 @@ export type McpEndpoint = {
43
38
  'x-mcp'?: XMcpConfig;
44
39
  [key: string]: any;
45
40
  };
46
- export type McpServerInstance = {
47
- readonly server: McpServer;
48
- readonly transport: StreamableHTTPServerTransport;
49
- cleanup(): Promise<void>;
50
- };
51
41
  export type McpPluginConfig = {
52
42
  readonly hide: boolean;
53
43
  readonly docs: {
@@ -71,51 +61,29 @@ export type McpUserInfo = {
71
61
  exp?: number;
72
62
  [key: string]: any;
73
63
  };
74
- export type ToolArgsMap = {
75
- 'list-apis': {
76
- filter?: string;
77
- page?: number;
78
- limit?: number;
79
- };
80
- 'get-endpoints': {
81
- name: string;
82
- };
83
- 'get-endpoint-info': {
84
- name: string;
85
- path: string;
86
- method: string;
87
- };
88
- 'get-security-schemes': {
89
- name: string;
90
- };
91
- 'get-full-api-description': {
92
- name: string;
93
- };
94
- search: {
95
- query: string;
96
- product?: string;
97
- };
98
- whoami: Record<string, never>;
99
- };
100
- export type McpToolWorkerParams = {
101
- [K in keyof ToolArgsMap]: {
102
- toolName: K;
103
- args: ToolArgsMap[K];
104
- context: {
105
- apiDescriptionsMap: Record<string, ApiDescriptionInfo>;
106
- outdir?: string;
107
- accessInfo?: AccessInfo;
108
- baseUrl?: string;
109
- headers?: Record<string, string | string[] | undefined>;
110
- products?: string[];
111
- };
112
- };
113
- }[keyof ToolArgsMap];
114
64
  export type SemanticSearchResult = {
115
65
  title: string;
116
66
  url: string;
117
67
  content: string;
118
68
  };
119
69
  export type McpToolWorkerResponse = CallToolResult;
70
+ export type McpToolExtra = {
71
+ sessionId?: string;
72
+ authInfo?: AuthInfo;
73
+ requestId: RequestId;
74
+ requestInfo?: RequestInfo;
75
+ _meta?: RequestMeta;
76
+ };
77
+ export type McpToolContext = Omit<ApiFunctionsContext, 'telemetry' | 'getKv' | keyof ApiFunctionsContextMethods> & {
78
+ apiDescriptionsMap: Record<string, ApiDescriptionInfo>;
79
+ products?: string[];
80
+ };
81
+ export type McpToolHandler<TArgs extends Record<string, unknown> = Record<string, unknown>> = (args: TArgs, context: McpToolContext, extra: McpToolExtra) => Promise<McpToolWorkerResponse>;
82
+ export type McpToolExecutionParams = {
83
+ toolName: string;
84
+ args: Record<string, unknown>;
85
+ context: McpToolContext;
86
+ extra: McpToolExtra;
87
+ };
120
88
  export {};
121
89
  //# sourceMappingURL=types.d.ts.map
@@ -1,3 +1,3 @@
1
- import type { McpToolWorkerParams, McpToolWorkerResponse } from '../types.js';
2
- export declare function executeMcpTool(params: McpToolWorkerParams): Promise<McpToolWorkerResponse>;
1
+ import type { McpToolExecutionParams, McpToolWorkerResponse } from '../types.js';
2
+ export declare function executeMcpTool(params: McpToolExecutionParams): Promise<McpToolWorkerResponse>;
3
3
  //# sourceMappingURL=execute-mcp-tool.d.ts.map
@@ -1 +1 @@
1
- import{getDocsTool as c}from"../docs-mcp/tools/index.js";async function l(e){const{toolName:o,args:n,context:r}=e,t=c(o);if(!t)throw new Error(`Unknown tool: ${o}`);return await t.execute(n,r)}export{l as executeMcpTool};
1
+ import{telemetry as s}from"../../../telemetry/index.js";import{mcpToolHandlers as p}from"../../../../client/mcp-tool-handlers-entry.js";async function w(o){s.initialize();const{toolName:e,args:c,context:a,extra:l}=o;try{const r=p[e];if(!r)throw new Error(`Unknown MCP tool: ${e}`);const{default:i}=await r(),n=i[e];if(!n)throw new Error(`MCP tool module does not export a handler for "${e}". Expected \`export default { '${e}': handler }\`.`);const t=await n(c,a,l);if(!f(t))throw new Error(`MCP tool "${e}" returned an invalid result.`);return t.isError?s.sendMcpErrorMessage([{object:"mcp_server",server_type:"docs",tool:e,message:`${t.content.map(({text:d})=>d).join(" ")}`,stack:""}]):s.sendMcpToolCalledMessage([{object:"mcp_server",server_type:"docs",tool:e}]),t}catch(r){throw s.sendMcpErrorMessage([{object:"mcp_server",server_type:"docs",tool:e,message:r instanceof Error?r.message:String(r),stack:r instanceof Error&&r.stack||""}]),r}}function f(o){return!o||typeof o!="object"?!1:Array.isArray(o.content)}export{w as executeMcpTool};
@@ -1,7 +1,7 @@
1
1
  import type { Node } from '@markdoc/markdoc';
2
2
  import type { JSX } from 'react';
3
3
  import type { CommonError, GlobalData } from '../types/index.js';
4
- import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, RedirectOptions } from './types';
4
+ import type { MiddlewareDetails, PageRouteInit, PageRouteDetails, RouteDetails, LifecycleContext, ApiRoute, ParseMarkdocOpts, WildcardRedirectsTree, RedirectOptions, McpToolRegistration, McpToolSchema } from './types';
5
5
  import type { RedoclyConfig, RedirectConfig, CompilationError, PageStaticData } from '@redocly/config';
6
6
  import type { SearchFacet } from '@redocly/theme/core/types';
7
7
  import type { BundledDefinition as OpenApiBundledDefinition } from './plugins/openapi-docs/load-definition.js';
@@ -55,6 +55,7 @@ export declare class Store {
55
55
  templates: Map<string, string>;
56
56
  browserPlugins: Set<string>;
57
57
  apiRoutesRequestHandlers: Map<string, string>;
58
+ mcpToolHandlers: Map<string, McpToolRegistration>;
58
59
  serverPropsGetters: Map<string, string>;
59
60
  pagePropsGetters: Map<string, string>;
60
61
  listeners: Map<string, Set<(...args: any[]) => void>>;
@@ -149,6 +150,8 @@ export declare class Store {
149
150
  addRoute: (route: PageRouteInit) => void;
150
151
  addRouteSharedDataToAllLocales: (slug: string, dataKey: string, dataId: string) => void;
151
152
  addApiRoute: (route: ApiRoute) => void;
153
+ addMcpTools: (importPath: string, tools: McpToolSchema[]) => void;
154
+ getMcpTools: () => McpToolRegistration[];
152
155
  addMiddleware: (middleware: MiddlewareDetails) => void;
153
156
  getRouteByFsPath: (relativePath: string) => PageRouteDetails | undefined;
154
157
  getRouteBySlug: (slug: string, opts?: {
@@ -1 +1 @@
1
- import A from"@markdoc/markdoc";import{getPathnameForLocale as C}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as p}from"../constants/common.js";import{DEFAULT_TITLE as O}from"./constants/common.js";import{GATED_MARKDOC_TAGS as D}from"./constants/entitlements.js";import{isObject as T}from"../utils/guards/is-object.js";import{mapObject as M}from"../utils/object/map-object.js";import{getValueDeep as S}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as y}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as g}from"./tools/notifiers/logger.js";import{sha1 as k}from"./utils/crypto/sha1.js";import{writeEnvVariable as _}from"./utils/envs/write-env-variable.js";import{envConfig as G}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{writeSharedData as B}from"./utils/index.js";import{renderComponents as I}from"./ssr/render.js";import{readStaticData as N,writeStaticData as V}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as H}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as R}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveMetadataGlobs as U}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as q}from"./utils/redirects/find-redirect.js";import{followRedirectChain as x}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as W}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as $}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map"},m="markdown/partials",z="markdown/partials-deps",E="PLAN_GATES",Y=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Ot="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#s=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#r;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#a;userCodeReady;#o=Promise.resolve();#i;#n=Promise.resolve();#c;#e=new Map;constructor({outdir:t,contentDir:s,serverMode:e=!1,serverOutDir:a}){this.#r=s,this.outdir=t,this.serverMode=e,this.serverOutDir=a,this.userCodeReady=new Promise(r=>{this.#a=r})}on(t,s){const e=this.listeners.get(t);e?e.add(s):this.listeners.set(t,new Set([s]))}queueEvent=(t,s,...e)=>{this.#e.set(t+String(s),[t,s,...e])};runListeners=(t,s,...e)=>{for(const a of this.listeners.get(t)||new Set)s?a(s,...e):a(...e)};startPluginsRun(){this.clear(),this.#o=new Promise(t=>{this.#i=t})}waitForPluginsLifecycle(){return Promise.all([this.#o,this.#n])}finishPluginsRun(){this.#i?.();for(const t of this.#e.values())this.runListeners(...t);this.#e.clear()}startEsbuildRun(){this.#n=new Promise(t=>{this.#c=t})}finishEsbuildRun(){this.#c?.()}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#r}markUserCodeReady(){this.#a?.(!0)}async reloadMarkdocOptions(){await $("build.reload_markdoc_options",async()=>{const t=R.instance(),s=await H(this.serverOutDir),e=Object.fromEntries(Object.entries(s.tags).filter(([a])=>D[a]!=null?t.canAccessFeature(D[a]):!0));this.#s={...s,tags:e}})}get markdocOptions(){return{...this.#s,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=t=>{const s=this.globalData,e={...this.globalData,...t};this.globalData=e,JSON.stringify(e)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,e)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:t,context:s,deps:e,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(t,this.markdocOptions,{actions:this,context:s},a);for(const i of r.sharedDataDeps||[]){for(const n of e?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of e?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of e?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of e?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(e?.routeSlugs&&r.partials?.length)for(const i of e.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadOpenApiDefinitions(t){return(await t.cache.load(".","load-oas-docs")).data}async loadAsyncApiDefinitions(t){return(await t.cache.load(".","asyncapi-docs")).data}setSearchEngine(t){this.searchEngine=t}setSearchFacets=t=>{this.searchFacets=t};setGlobalConfig=t=>{const s=Object.keys(t);for(const o of s)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=S(t,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:e,unsetEnvVars:a,replacedValues:r}=J(t);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,e)};getConfig=()=>this.config;getGlobalConfig=t=>this.config[t];getSearchFacets=()=>this.searchFacets;addRedirect=(t,s,e={})=>{if(!R.instance().canAccessFeature("redirects")&&t!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(t).toLowerCase();this.config.redirects[o]=s;const{trackOriginalSource:l=!0}=e,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&W(this.config.wildcardRedirectsTree,o)};getRedirect=t=>{const s=h(t).toLowerCase(),e=q(s,this.config.redirects,this.config.wildcardRedirectsTree);if(!e)return null;if(y(e.to)){const a=h(e.to).toLowerCase();if(!a.endsWith("*")&&x(a,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:e.to,type:e.type}};createSharedData=async(t,s,e)=>{if(e&&this.#t[t]===e)return t;const a=JSON.stringify(s),r=e??k(a);return this.#t[t]===r||(this.#t[t]=r,await B(t,a,this.outdir),this.queueEvent("shared-data-updated",t)),t};addRouteSharedData=(t,s,e)=>{const a=L(t),r=this.routesSharedData.get(a)||{};r[s]=e,this.routesSharedData.set(a,r),g.verbose(`Adding shared data to ${t}, ${s}, ${e}`)};getRouteSharedDataByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=t=>{const s=this.getGlobalConfig(m)||{},e=this.routesPartials.get(t);if(!e||e.length===0)return{};const a=this.getGlobalConfig(z)||{},r=new Set(e),o=Array.from(e);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)s[i]&&(l[i]=s[i]);return l};addRoute=t=>{const e={...U(t.fsPath,this.config.metadataGlobs),...t.metadata||{}};this.newRoutes.push({...t,metadata:e}),g.verbose("Created route %s",t.slug)};addRouteSharedDataToAllLocales=(t,s,e)=>{const a=[p,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=C(t,p,r.code,a);this.addRouteSharedData(o,s,e)}};addApiRoute=t=>{this.apiRoutes.push(t),g.verbose("Created API route %s",t.slug)};addMiddleware=t=>{this.middleware.push(t),g.verbose("Created middleware %s",t.id)};getRouteByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(t,s={})=>{const{followRedirect:e=!0}=s,a=this.getRedirect(t);return e&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(t)};slugHasRouteOrRedirect=t=>{if(this.routesBySlug.has(t))return!0;const s=this.getRedirect(t);if(!s)return!1;if(!y(s.to))return!0;const e=h(s.to);return this.routesBySlug.has(e)};getRoutesByTemplateId=t=>this.newRoutes.filter(s=>s.templateId===t);getAllRoutesForLocale=(t=p)=>{const s=Array.from(this.routesBySlug.values()),e=t.toLowerCase();return s.filter(a=>t===p?!K(a.fsPath):a.slug.startsWith(`/${e}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=t=>this.templates.get(t);getRequestHandler=t=>this.apiRoutesRequestHandlers.get(t);createTemplate=(t,s)=>(this.templates.set(t,s),t);addBrowserPlugin=t=>{this.browserPlugins.add(t)};createRequestHandler=(t,s)=>(this.apiRoutesRequestHandlers.set(t,s),t);registerServerPropsGetter=(t,s)=>(this.serverPropsGetters.set(t,s),t);registerPagePropsGetter=(t,s)=>{this.pagePropsGetters.set(t,s)};async writeRouteStaticData(t,s){const e=await this.resolveRouteStaticData(t,s,!1);e&&V(t.slug,e,this.outdir)}async resolveRouteStaticData(t,s,e){if(this.serverMode)return N(t.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:u})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[t.slug]},resource:u})},r=await t.getStaticData?.(t,a)||{},o=new Set(this.routesDynamicComponents.get(t.slug)),l=this.routesSharedData.get(t.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(f=>o.add(f));const u=this.sharedDataDeps.get(c);u&&u.forEach(f=>this.addRouteSharedData(t.slug,f,f))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await t.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...t.metadata},seo:{title:O,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:e||!t.fsPath?null:await this.lifecycleContext?.fs.getLastModified(t.fsPath)}}addSsrComponents(t,s){if(!t?.length)return;const e=typeof t[0]=="string"?t.join(""):I(t);e&&(s==="head"?this.ssr.headTags.push(e):s==="preBody"?this.ssr.preBodyTags.push(e):this.ssr.postBodyTags.push(e))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const t=[];for(const[e,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[e].entries());t.push([e,r]);break;case"object":e==="config"&&t.push([e,await this.getConfigWithEnvPlaceholders()]),t.push([e,this[e]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(t);return s[E]=G.PLAN_GATES,s}static fromJson(t,s){const e=new P(s);for(const[r,o]of Object.entries(v))switch(o){case"map":e[r]=new Map(t[r]);break;case"object":if(r==="config"){e.setGlobalConfig(t[r]);break}e[r]=t[r];break;default:throw new Error("Invalid format")}e.config[m]=Z(e.config[m]||{});const a=t[E];return a&&_("PLAN_GATES",a),e}async getConfigWithEnvPlaceholders(){const t=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:e}=this.replacedEnvVars[s],a=s.split(":"),r=a.pop(),{error:o,value:l}=S(t,a);if(o||!T(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[r]=e}return t}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const t=Array.from(this.unsetEnvVars).filter(e=>!Y.includes(e));if(t.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${t.join(", ")}`;await w.panicOnBuildContentError(s)}}function Z(b){return M(b,t=>A.Ast.fromJSON(JSON.stringify(t)))}export{m as MARKDOC_PARTIALS_DATA_KEY,z as MARKDOC_PARTIALS_DEPS_KEY,P as Store,Ot as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
1
+ import A from"@markdoc/markdoc";import{getPathnameForLocale as C}from"@redocly/theme/core/utils";import{DEFAULT_LOCALE_PLACEHOLDER as g}from"../constants/common.js";import{DEFAULT_TITLE as T}from"./constants/common.js";import{GATED_MARKDOC_TAGS as D}from"./constants/entitlements.js";import{isObject as O}from"../utils/guards/is-object.js";import{mapObject as M}from"../utils/object/map-object.js";import{getValueDeep as y}from"../utils/object/get-value-deep.js";import{removeTrailingSlash as L}from"../utils/url/remove-trailing-slash.js";import{normalizeRouteSlug as h}from"../utils/path/normalize-route-slug.js";import{isLocalLink as S}from"../utils/path/is-local-link.js";import{reporter as w}from"./tools/notifiers/reporter.js";import{logger as u}from"./tools/notifiers/logger.js";import{sha1 as k}from"./utils/crypto/sha1.js";import{writeEnvVariable as _}from"./utils/envs/write-env-variable.js";import{envConfig as G}from"./config/env-config.js";import{KvService as F}from"./persistence/kv/services/kv-service.js";import{writeSharedData as B}from"./utils/index.js";import{renderComponents as I}from"./ssr/render.js";import{readStaticData as N,writeStaticData as V}from"./utils/static-data.js";import{parseAndResolveMarkdoc as j}from"./plugins/markdown/compiler.js";import{getMarkdocOptions as H}from"./plugins/markdown/markdoc/markdoc-options.js";import{EntitlementsProvider as R}from"./entitlements/entitlements-provider.js";import{isL10nPath as K}from"./fs/utils/is-l10n-path.js";import{resolveMetadataGlobs as U}from"./utils/globs.js";import{replaceEnvVariablesDeep as J}from"./utils/envs/replace-env-variables-deep.js";import{findRedirect as q}from"./utils/redirects/find-redirect.js";import{followRedirectChain as x}from"./utils/redirects/follow-redirect-chain.js";import{addWildcardRedirectToTree as W}from"./utils/redirects/add-wildcard-redirect-to-tree.js";import{telemetryTraceStep as $}from"../cli/telemetry/helpers/trace-step.js";const v={routesBySlug:"map",apiRoutes:"object",middleware:"object",routesByFsPath:"map",routesSharedData:"map",globalData:"object",config:"object",ssr:"object",searchFacets:"map",routesPartials:"map",mcpToolHandlers:"map"},m="markdown/partials",z="markdown/partials-deps",E="PLAN_GATES",Y=["OAUTH_CLIENT_ID","OAUTH_CLIENT_SECRET","ORGANIZATION_SLUG","ORGANIZATION_ID","ORG_ID"],Tt="userDefinedApiFunctions";class P{routesBySlug=new Map;replacedEnvVars={};unsetEnvVars=new Set;lifecycleContext;newRoutes=[];#t={};routesByFsPath=new Map;apiRoutes=[];middleware=[];routesSharedData=new Map;sharedDataDeps=new Map;sharedDataMarkdocComponents=new Map;routesDynamicComponents=new Map;routesPartials=new Map;ssr={preBodyTags:[],postBodyTags:[],headTags:[]};searchFacets=new Map;searchEngine;templates=new Map;browserPlugins=new Set;apiRoutesRequestHandlers=new Map;mcpToolHandlers=new Map;serverPropsGetters=new Map;pagePropsGetters=new Map;listeners=new Map;globalData={};#s=void 0;config={configFilePath:"",redirects:{},wildcardRedirectsTree:{},access:{rbac:{},requiresLogin:!1},directoryPermissions:{},devLogin:!1,ssoDirect:{}};#r;serverMode;serverOutDir;outdir;buildRevision=0;hasSitemap=!1;compilationErrors=[];#a;userCodeReady;#o=Promise.resolve();#i;#n=Promise.resolve();#c;#e=new Map;constructor({outdir:t,contentDir:s,serverMode:e=!1,serverOutDir:a}){this.#r=s,this.outdir=t,this.serverMode=e,this.serverOutDir=a,this.userCodeReady=new Promise(r=>{this.#a=r})}on(t,s){const e=this.listeners.get(t);e?e.add(s):this.listeners.set(t,new Set([s]))}queueEvent=(t,s,...e)=>{this.#e.set(t+String(s),[t,s,...e])};runListeners=(t,s,...e)=>{for(const a of this.listeners.get(t)||new Set)s?a(s,...e):a(...e)};startPluginsRun(){this.clear(),this.#o=new Promise(t=>{this.#i=t})}waitForPluginsLifecycle(){return Promise.all([this.#o,this.#n])}finishPluginsRun(){this.#i?.();for(const t of this.#e.values())this.runListeners(...t);this.#e.clear()}startEsbuildRun(){this.#n=new Promise(t=>{this.#c=t})}finishEsbuildRun(){this.#c?.()}get contentDir(){if(this.serverMode)throw new Error("contentDir should not be used in server mode");return this.#r}markUserCodeReady(){this.#a?.(!0)}async reloadMarkdocOptions(){await $("build.reload_markdoc_options",async()=>{const t=R.instance(),s=await H(this.serverOutDir),e=Object.fromEntries(Object.entries(s.tags).filter(([a])=>D[a]!=null?t.canAccessFeature(D[a]):!0));this.#s={...s,tags:e}})}get markdocOptions(){return{...this.#s,partials:this.getGlobalConfig(m),themeConfig:this.config.markdown}}setGlobalData=t=>{const s=this.globalData,e={...this.globalData,...t};this.globalData=e,JSON.stringify(e)!==JSON.stringify(s)&&this.queueEvent("global-data-updated",void 0,e)};getGlobalData=()=>this.globalData;getKv=async()=>F.getInstance({baseDbDir:this.serverOutDir});parseMarkdoc=async({input:t,context:s,deps:e,resource:a})=>{const{data:{info:r,ast:o},compoundHash:l}=await j(t,this.markdocOptions,{actions:this,context:s},a);for(const i of r.sharedDataDeps||[]){for(const n of e?.routeSlugs||[])this.addRouteSharedData(n,i,i);for(const n of e?.sharedDataIds||[]){const c=this.sharedDataDeps.get(n)||new Set;c.add(i),this.sharedDataDeps.set(n,c)}}for(const i of r.dynamicMarkdocComponents||[]){for(const n of e?.routeSlugs||[]){const c=this.routesDynamicComponents.get(n)||new Set;c.add(i),this.routesDynamicComponents.set(n,c)}for(const n of e?.sharedDataIds||[]){const c=this.sharedDataMarkdocComponents.get(n)||new Set;c.add(i),this.sharedDataMarkdocComponents.set(n,c)}}if(e?.routeSlugs&&r.partials?.length)for(const i of e.routeSlugs){const n=this.routesPartials.get(i)||[];for(const c of r.partials)n.includes(c)||n.push(c);this.routesPartials.set(i,n)}return{info:r,ast:o,compoundHash:l}};async loadOpenApiDefinitions(t){return(await t.cache.load(".","load-oas-docs")).data}async loadAsyncApiDefinitions(t){return(await t.cache.load(".","asyncapi-docs")).data}setSearchEngine(t){this.searchEngine=t}setSearchFacets=t=>{this.searchFacets=t};setGlobalConfig=t=>{const s=Object.keys(t);for(const o of s)for(const l in this.replacedEnvVars)if(l===o||l.startsWith(`${o}:`)){const i=l.split(":"),{error:n,value:c}=y(t,i);(n||c!==this.replacedEnvVars[l].replaced)&&delete this.replacedEnvVars[l]}const{resolvedObj:e,unsetEnvVars:a,replacedValues:r}=J(t);for(const o of a)this.unsetEnvVars.add(o);Object.assign(this.replacedEnvVars,r),Object.assign(this.config,e)};getConfig=()=>this.config;getGlobalConfig=t=>this.config[t];getSearchFacets=()=>this.searchFacets;addRedirect=(t,s,e={})=>{if(!R.instance().canAccessFeature("redirects")&&t!=="/")return;this.config.redirects||(this.config.redirects={});const o=h(t).toLowerCase();this.config.redirects[o]=s;const{trackOriginalSource:l=!0}=e,i=this.getGlobalConfig("originalRedirectSources");l&&Array.isArray(i)&&!i.includes(o)&&i.push(o),o.endsWith("*")&&W(this.config.wildcardRedirectsTree,o)};getRedirect=t=>{const s=h(t).toLowerCase(),e=q(s,this.config.redirects,this.config.wildcardRedirectsTree);if(!e)return null;if(S(e.to)){const a=h(e.to).toLowerCase();if(!a.endsWith("*")&&x(a,[s],this.config.redirects,this.config.wildcardRedirectsTree).type==="cycle")return null}return{to:e.to,type:e.type}};createSharedData=async(t,s,e)=>{if(e&&this.#t[t]===e)return t;const a=JSON.stringify(s),r=e??k(a);return this.#t[t]===r||(this.#t[t]=r,await B(t,a,this.outdir),this.queueEvent("shared-data-updated",t)),t};addRouteSharedData=(t,s,e)=>{const a=L(t),r=this.routesSharedData.get(a)||{};r[s]=e,this.routesSharedData.set(a,r),u.verbose(`Adding shared data to ${t}, ${s}, ${e}`)};getRouteSharedDataByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.routesSharedData.get(s)||{}:{}};getPartialsForRoute=t=>{const s=this.getGlobalConfig(m)||{},e=this.routesPartials.get(t);if(!e||e.length===0)return{};const a=this.getGlobalConfig(z)||{},r=new Set(e),o=Array.from(e);for(let i=0;i<o.length;i++){const n=o[i],c=a[n]?.partials??[];for(const d of c)r.has(d)||(r.add(d),o.push(d))}const l={};for(const i of r)s[i]&&(l[i]=s[i]);return l};addRoute=t=>{const e={...U(t.fsPath,this.config.metadataGlobs),...t.metadata||{}};this.newRoutes.push({...t,metadata:e}),u.verbose("Created route %s",t.slug)};addRouteSharedDataToAllLocales=(t,s,e)=>{const a=[g,...this.lifecycleContext?.fs.localeFolders||[]].map(r=>({code:r,name:r}));for(const r of a){const o=C(t,g,r.code,a);this.addRouteSharedData(o,s,e)}};addApiRoute=t=>{this.apiRoutes.push(t),u.verbose("Created API route %s",t.slug)};addMcpTools=(t,s)=>{for(const e of s)this.mcpToolHandlers.set(e.name,{...e,importPath:t}),u.verbose("Created MCP tool %s",e.name)};getMcpTools=()=>Array.from(this.mcpToolHandlers.values());addMiddleware=t=>{this.middleware.push(t),u.verbose("Created middleware %s",t.id)};getRouteByFsPath=t=>{const s=this.routesByFsPath.get(t);return s?this.getRouteBySlug(s):void 0};getRouteBySlug=(t,s={})=>{const{followRedirect:e=!0}=s,a=this.getRedirect(t);return e&&a?this.routesBySlug.get(h(a.to)):this.routesBySlug.get(t)};slugHasRouteOrRedirect=t=>{if(this.routesBySlug.has(t))return!0;const s=this.getRedirect(t);if(!s)return!1;if(!S(s.to))return!0;const e=h(s.to);return this.routesBySlug.has(e)};getRoutesByTemplateId=t=>this.newRoutes.filter(s=>s.templateId===t);getAllRoutesForLocale=(t=g)=>{const s=Array.from(this.routesBySlug.values()),e=t.toLowerCase();return s.filter(a=>t===g?!K(a.fsPath):a.slug.startsWith(`/${e}`))};getAllRoutes=()=>Array.from(this.routesBySlug.values());getAllApiRoutes=()=>this.apiRoutes;getAllMiddleware=()=>this.middleware;getTemplate=t=>this.templates.get(t);getRequestHandler=t=>this.apiRoutesRequestHandlers.get(t);createTemplate=(t,s)=>(this.templates.set(t,s),t);addBrowserPlugin=t=>{this.browserPlugins.add(t)};createRequestHandler=(t,s)=>(this.apiRoutesRequestHandlers.set(t,s),t);registerServerPropsGetter=(t,s)=>(this.serverPropsGetters.set(t,s),t);registerPagePropsGetter=(t,s)=>{this.pagePropsGetters.set(t,s)};async writeRouteStaticData(t,s){const e=await this.resolveRouteStaticData(t,s,!1);e&&V(t.slug,e,this.outdir)}async resolveRouteStaticData(t,s,e){if(this.serverMode)return N(t.slug,this.outdir);const a={...this,contentDir:this.contentDir,parseMarkdoc:({input:c,context:d,resource:f})=>this.parseMarkdoc({input:c,context:d,deps:{routeSlugs:[t.slug]},resource:f})},r=await t.getStaticData?.(t,a)||{},o=new Set(this.routesDynamicComponents.get(t.slug)),l=this.routesSharedData.get(t.slug)||{};for(const c of Object.values(l)){const d=this.sharedDataMarkdocComponents.get(c);d&&d.forEach(p=>o.add(p));const f=this.sharedDataDeps.get(c);f&&f.forEach(p=>this.addRouteSharedData(t.slug,p,p))}const i=this.getGlobalConfig("seo"),n=r?.frontmatter||{};return{...r,frontmatter:{...n,seo:{...n?.seo,title:n?.seo?.title||await t.getNavText?.()}},props:{...r.props,dynamicMarkdocComponents:Array.from(o),metadata:{...r?.props?.metadata,...t.metadata},seo:{title:T,...i,...r.props?.seo},compilationErrors:this.compilationErrors},lastModified:e||!t.fsPath?null:await this.lifecycleContext?.fs.getLastModified(t.fsPath)}}addSsrComponents(t,s){if(!t?.length)return;const e=typeof t[0]=="string"?t.join(""):I(t);e&&(s==="head"?this.ssr.headTags.push(e):s==="preBody"?this.ssr.preBodyTags.push(e):this.ssr.postBodyTags.push(e))}clear=()=>{this.routesByFsPath.clear(),this.templates.clear(),this.newRoutes=[],this.routesBySlug.clear(),this.apiRoutes=[],this.middleware=[],this.routesSharedData.clear(),this.sharedDataDeps.clear(),this.sharedDataMarkdocComponents.clear(),this.routesDynamicComponents.clear(),this.routesPartials.clear(),this.config.redirects={},this.config.wildcardRedirectsTree={},this.config.directoryPermissions={},this.ssr={preBodyTags:[],postBodyTags:[],headTags:[]}};async toJson(){const t=[];for(const[e,a]of Object.entries(v))switch(a){case"map":const r=Array.from(this[e].entries());t.push([e,r]);break;case"object":e==="config"&&t.push([e,await this.getConfigWithEnvPlaceholders()]),t.push([e,this[e]]);break;default:throw new Error("Invalid format")}const s=Object.fromEntries(t);return s[E]=G.PLAN_GATES,s}static fromJson(t,s){const e=new P(s);for(const[r,o]of Object.entries(v))switch(o){case"map":e[r]=new Map(t[r]);break;case"object":if(r==="config"){e.setGlobalConfig(t[r]);break}e[r]=t[r];break;default:throw new Error("Invalid format")}e.config[m]=Z(e.config[m]||{});const a=t[E];return a&&_("PLAN_GATES",a),e}async getConfigWithEnvPlaceholders(){const t=JSON.parse(JSON.stringify(this.config));for(const s in this.replacedEnvVars){const{original:e}=this.replacedEnvVars[s],a=s.split(":"),r=a.pop(),{error:o,value:l}=y(t,a);if(o||!O(l)&&!Array.isArray(l)){await w.panicOnBuild(`Failed to replace env var with env name for ${s}`);continue}l[r]=e}return t}async reportUnsetEnvVars(){if(this.unsetEnvVars.size===0)return;const t=Array.from(this.unsetEnvVars).filter(e=>!Y.includes(e));if(t.length===0)return;const s=`Failed to resolve config. The following environment variables are not set: ${t.join(", ")}`;await w.panicOnBuildContentError(s)}}function Z(b){return M(b,t=>A.Ast.fromJSON(JSON.stringify(t)))}export{m as MARKDOC_PARTIALS_DATA_KEY,z as MARKDOC_PARTIALS_DEPS_KEY,P as Store,Tt as USER_DEFINED_API_FUNCTIONS_COUNTER_KEY};
@@ -1 +1 @@
1
- import{AsyncApiRealmAPI as o}from"@redocly/realm-asyncapi-sdk";import{envConfig as e}from"../config/env-config.js";import{PACKAGE_NAME as t}from"../../config/product-gates.js";import{OTEL_TRACES_DEV_URL as i,TELEMETRY_ENABLED as s}from"../constants/common.js";class l extends o.Telemetry{constructor(){super(),this.updateCloudEventData(()=>({organizationId:e.ORGANIZATION_ID||"",organizationSlug:e.ORGANIZATION_SLUG||"",projectId:e.PROJECT_ID||"",projectSlug:"",origin:"realmApi",actor:{id:"Anonymous",object:"user",uri:""},source:"server"}))}initialize(r=!1){this.init({otel:{serviceName:"realm-server",serviceVersion:`${t}@${e.REDOCLY_PORTAL_VERSION||""}`,collectorTraceUrl:r?i:e.OTEL_TRACES_URL||"https://otel.cloud.redocly.com/v1/traces",isProd:e.isProductionEnv,version:"1.0",tracerName:"server-telemetry",delayMillis:100},disabled:r?e.TELEMETRY_DEV_DEBUG!=="true":!s})}}const A=new l;export{A as telemetry};
1
+ import{AsyncApiRealmAPI as o}from"@redocly/realm-asyncapi-sdk";import{envConfig as e}from"../config/env-config.js";import{PACKAGE_NAME as t}from"../../config/product-gates.js";import{OTEL_TRACES_DEV_URL as i,TELEMETRY_ENABLED as s}from"../constants/common.js";class l extends o.Telemetry{constructor(){super(),this.updateCloudEventData(()=>({organizationId:e.ORGANIZATION_ID||"",organizationSlug:e.ORGANIZATION_SLUG||"",projectId:e.PROJECT_ID||"",projectSlug:e.PROJECT_SLUG||"",origin:"realmApi",actor:{id:"Anonymous",object:"user",uri:""},source:"server",env:e.redoclyEnv}))}initialize(r=!1){this.init({otel:{serviceName:"realm-server",serviceVersion:`${t}@${e.REDOCLY_PORTAL_VERSION||""}`,collectorTraceUrl:r?i:e.OTEL_TRACES_URL||"https://otel.cloud.redocly.com/v1/traces",isProd:e.isProductionEnv,version:"1.0",tracerName:"server-telemetry",delayMillis:100},disabled:r?e.TELEMETRY_DEV_DEBUG!=="true":!s})}}const A=new l;export{A as telemetry};
@@ -115,6 +115,14 @@ export type ApiRoute = RouteDetails & {
115
115
  httpMethod: ApiRouteMethod;
116
116
  requestHandlerId: string;
117
117
  };
118
+ export type McpToolSchema = {
119
+ name: string;
120
+ description: string;
121
+ schema: Record<string, unknown>;
122
+ };
123
+ export type McpToolRegistration = McpToolSchema & {
124
+ importPath: string;
125
+ };
118
126
  export type MiddlewareDetails = {
119
127
  id: string;
120
128
  importPath: string;
@@ -144,6 +152,8 @@ export type ProcessContentActions = {
144
152
  getSearchFacets: () => Map<string, SearchFacet>;
145
153
  addRoute: (route: PageRouteInit) => void;
146
154
  addApiRoute: (route: ApiRoute) => void;
155
+ addMcpTools: (importPath: string, tools: McpToolSchema[]) => void;
156
+ getMcpTools: () => McpToolRegistration[];
147
157
  createRequestHandler: (id: string, importPath: string) => void;
148
158
  addRedirect: (from: string, to: RedirectConfig, options?: RedirectOptions) => void;
149
159
  createTemplate: (id: string, importPath: string) => string;