@redocly/realm 0.137.0-next.9 → 0.137.1
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.
- package/CHANGELOG.md +219 -0
- package/dist/cli/build/index.js +1 -1
- package/dist/client/app/Sidebar/useActions.js +1 -1
- package/dist/client/app/Sidebar/useSidebarItems.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogClassic.js +1 -1
- package/dist/client/app/hooks/catalog/useCatalogFilter.js +1 -1
- package/dist/client/app/search/useFacets.js +1 -1
- package/dist/client/app/seo/SeoTags.js +1 -1
- package/dist/client/app/utils/loadAndNavigate.js +1 -1
- package/dist/server/config/env-config.d.ts +2 -0
- package/dist/server/config/env-schema.d.ts +5 -0
- package/dist/server/config/env-schemas/feature-flags.d.ts +4 -0
- package/dist/server/config/env-schemas/feature-flags.js +1 -1
- package/dist/server/constants/common.d.ts +1 -0
- package/dist/server/constants/common.js +1 -1
- package/dist/server/constants/plugins/search.js +1 -1
- package/dist/server/esbuild/esbuild-logger.d.ts +2 -2
- package/dist/server/esbuild/esbuild-logger.js +3 -3
- package/dist/server/plugins/api-docs/options.js +1 -1
- package/dist/server/plugins/api-docs/search/create-indexer.d.ts +5 -0
- package/dist/server/plugins/api-docs/search/create-indexer.js +1 -0
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.d.ts +1 -1
- package/dist/server/plugins/api-docs/search/get-ai-search-documents.js +41 -41
- package/dist/server/plugins/api-docs/search/search-resolver.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/revision-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/common/version-repository.js +1 -1
- package/dist/server/plugins/catalog-entities/database/repositories/utils/normalize-revision-flags.js +1 -1
- package/dist/server/plugins/config-parser/loaders/content-slugs-loader.js +1 -1
- package/dist/server/plugins/config-parser/loaders/versions-config-loader.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDescriptionDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditAppDialog.js +1 -1
- package/dist/server/plugins/dev-onboarding/template/components/EditCallbackUrlDialog.js +1 -1
- package/dist/server/plugins/markdown/is-partial.js +1 -1
- package/dist/server/plugins/mcp/codemode/capabilities/fetch/index.js +5 -4
- package/dist/server/plugins/mcp/codemode/capabilities/types.d.ts +5 -2
- package/dist/server/plugins/mcp/codemode/capabilities/utils.js +4 -4
- package/dist/server/plugins/mcp/codemode/code-execution-audit.js +1 -1
- package/dist/server/plugins/mcp/codemode/prompts.d.ts +1 -1
- package/dist/server/plugins/mcp/codemode/prompts.js +7 -6
- package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.d.ts +2 -2
- package/dist/server/plugins/mcp/codemode/sandbox/create-sandbox-setup.js +1 -11
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/console.d.ts +2 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/console.js +7 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.d.ts +1 -0
- package/dist/server/plugins/mcp/codemode/sandbox/polyfills/index.js +2 -2
- package/dist/server/plugins/mcp/codemode/sandbox/tool-input-validator.d.ts +5 -0
- package/dist/server/plugins/mcp/codemode/sandbox/tool-input-validator.js +1 -0
- package/dist/server/plugins/mcp/codemode/sandbox/unknown-tool-hint.d.ts +8 -0
- package/dist/server/plugins/mcp/codemode/sandbox/unknown-tool-hint.js +2 -0
- package/dist/server/plugins/mcp/codemode/tools/describe-tools.js +3 -1
- package/dist/server/plugins/mcp/codemode/tools/execute.js +1 -1
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.d.ts +2 -2
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
- package/dist/server/plugins/mcp/docs-mcp/tool-schemas.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/core/create-anonymous-session.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/core/search.js +2 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/core/types.d.ts +2 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/graphql/get-graphql-type.js +5 -5
- package/dist/server/plugins/mcp/docs-mcp/tools/graphql/types.d.ts +2 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/list-apis.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.d.ts +4 -2
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/load-api-description.js +1 -1
- package/dist/server/plugins/mcp/docs-mcp/tools/openapi/types.d.ts +6 -2
- package/dist/server/plugins/mcp/docs-mcp/utils/api-titles.d.ts +6 -0
- package/dist/server/plugins/mcp/docs-mcp/utils/api-titles.js +1 -0
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +11 -0
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +5 -5
- package/dist/server/plugins/mcp/gateway-mcp/fetch/ssrf-guard.js +1 -1
- package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.d.ts +5 -0
- package/dist/server/plugins/mcp/gateway-mcp/fetch/url-placeholders.js +1 -0
- package/dist/server/plugins/mcp/servers/docs-server.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
- package/dist/server/plugins/mcp/types.d.ts +1 -0
- package/dist/server/plugins/mcp/utils/available-sandbox-tools.js +1 -1
- package/dist/server/plugins/mcp/workers/execute-mcp-tool.js +1 -1
- package/dist/server/plugins/search/ai-indexer/prepare-semantic-documents.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/index.js +1 -1
- package/dist/server/plugins/search/engines/flexsearch/search-index.d.ts +8 -4
- package/dist/server/plugins/search/engines/flexsearch/search-index.js +1 -1
- package/dist/server/plugins/search/llmstxt/create-llms-txt-generator.js +1 -1
- package/dist/server/plugins/search/llmstxt/index.d.ts +3 -1
- package/dist/server/plugins/search/llmstxt/index.js +9 -9
- package/dist/server/sandbox/sandbox.js +2 -2
- package/dist/server/sandbox/types.d.ts +9 -0
- package/dist/server/sandbox/utils/execution-budget.js +1 -1
- package/dist/server/sandbox/utils/return-last-expression.d.ts +6 -0
- package/dist/server/sandbox/utils/return-last-expression.js +1 -0
- package/dist/server/sandbox/utils/returns-from-top-level.d.ts +1 -2
- package/dist/server/sandbox/utils/returns-from-top-level.js +1 -1
- package/dist/server/sandbox/utils/setup-host-functions.d.ts +1 -0
- package/dist/server/sandbox/utils/setup-host-functions.js +2 -2
- package/dist/server/ssr/render.js +1 -1
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/search.d.ts +7 -2
- package/dist/server/utils/globs.d.ts +4 -0
- package/dist/server/utils/globs.js +1 -1
- package/dist/server/utils/search/highlight-text-for-search.js +1 -1
- package/dist/server/utils/static-data.js +1 -1
- package/dist/server/web-server/auth.d.ts +9 -1
- package/dist/server/web-server/auth.js +4 -4
- package/dist/server/web-server/jwt/jwt.js +1 -1
- package/dist/server/web-server/jwt/token-types.d.ts +11 -0
- package/dist/server/web-server/jwt/token-types.js +1 -0
- package/dist/server/web-server/middleware/ensureSearchData.js +1 -1
- package/dist/server/web-server/routes/auth.js +1 -1
- package/dist/server/web-server/routes/dynamic-route.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-oauth.js +1 -1
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/dist/server/web-server/routes/semantic-search.d.ts +1 -1
- package/dist/server/web-server/routes/semantic-search.js +1 -1
- package/dist/server/web-server/routes/static-content.js +1 -1
- package/dist/{server/plugins/catalog-entities/utils/version-compare.d.ts → utils/version/compare-versions.d.ts} +1 -16
- package/dist/utils/version/compare-versions.js +1 -0
- package/package.json +11 -10
- package/dist/server/plugins/api-docs/search/search-indexer.d.ts +0 -9
- package/dist/server/plugins/api-docs/search/search-indexer.js +0 -1
- package/dist/server/plugins/catalog-entities/utils/version-compare.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import m from"path";import{REDOCLY_ROUTE_RBAC as
|
|
1
|
+
import m from"path";import{realpath as N}from"fs/promises";import{REDOCLY_ROUTE_RBAC as Y,REDOCLY_TEAMS_RBAC as z}from"@redocly/config";import{combineUrls as $,withoutPathPrefix as ne,withPathPrefix as T}from"@redocly/theme/core/utils";import{CACHE_CONTROL_NO_STORE_HEADER_VALUE as k,CACHE_CONTROL_PUBLIC_HTML as oe,DEFAULT_IMMUTABLE_CACHE_MAX_AGE as re,STATIC_DATA_FOLDER as ie,X_REDOCLY_CACHE_CONTROL_HEADER as ae,X_REDOCLY_CACHE_CONTROL_PUBLIC_HTML as se}from"../../constants/common.js";import{removeTrailingSlash as ce}from"../../../utils/url/remove-trailing-slash.js";import{findInIterable as le}from"../../../utils/collection/find-in-iterable.js";import{sanitizeRedirectPathname as me}from"../../../utils/url/sanitize-redirect-pathname.js";import{envConfig as ue}from"../../config/env-config.js";import{sanitizePath as de}from"../../../utils/path/sanitize-path.js";import{normalizeRouteSlug as fe}from"../../../utils/path/normalize-route-slug.js";import{isPathInFolder as G}from"../../../utils/path/is-path-in-folder.js";import{getLlmsTxtMdPathBySlug as ge}from"../../utils/llmstxt/get-llms-txt-md-path-by-slug.js";import{resolveLlmsTxtMd as j}from"../../utils/llmstxt/resolve-llms-txt-md.js";import{removeLeadingSlash as pe}from"../../../utils/url/remove-leading-slash.js";import{AI_INDEX_EXPORT_FOLDER as he,SEARCH_DATA_EXPORT_FOLDER as Re}from"../../constants/plugins/search.js";import{processRedirects as Ce}from"./helpers/process-redirects.js";import{renderPage as Ae,getServerProps as Ee}from"../../ssr/index.js";import{canAccessAsset as Le,canAccessResource as V}from"../../utils/rbac.js";import{handleErrorPageRender as w,handleUnauthorized as W,handleUnauthorizedAsset as Z}from"../utils.js";import{DEFAULT_MAX_AGE_FOR_MIME_TYPE as _e,MIME_TYPES as Te}from"../mime-types.js";import{fileExistsAsync as we}from"../../utils/index.js";import{isAiAgentRequest as Oe}from"../../utils/ai-agent-detection.js";import{getRedirectRoute as Pe}from"../utils/legacy-openapi-redirects.js";import{getContentTypeHeaderValue as x}from"../utils/content-type.js";import{telemetry as ye}from"../../../cli/telemetry/index.js";import{telemetry as Ue}from"../../telemetry/index.js";const Me=[Re,he,ie];async function be(e,p){if(m.extname(e)===".md")return!1;try{const[h,u]=await Promise.all([N(e),N(p)]);return Me.some(t=>G(h,m.join(u,t)))}catch{return!1}}function at(e,p,h,u){return async t=>{const O=t.get("logger"),r=t.req,c=new URL(r.url),i=ne(de(decodeURIComponent(c.pathname))),P=m.parse(i).ext===".md",y=Oe({accept:r.header("accept"),signatureAgent:r.header("signature-agent"),signature:r.header("signature"),signatureInput:r.header("signature-input"),userAgent:r.header("user-agent")}),a=fe(i),o=(r.method==="GET"||r.method==="HEAD")&&!P?e.getRouteBySlug(a,{followRedirect:!1})||le(e.routesBySlug?.values(),n=>n.hasClientRoutes&&(i===n.slug||i.startsWith(n.slug+"/"))):void 0,U=e.getRedirect(a);if(U){const n=Ce({redirect:U,reqUrlSearch:c.search});return Ue.sendRedirectMessage([{id:a,object:"redirect",uri:`urn:redocly:realm:api:redirect:${a}`,from:a,templateId:n.type.toString()}]),t.newResponse(null,n.type,{Location:n.location})}const R=ue.isProductionEnv?301:302;if(o?.metadata?.type==="openapi"){const n=Pe(c.pathname);if(n)return O.info("Legacy OpenAPI docs redirect from "+c.pathname),t.newResponse(null,R,{Location:encodeURI(n)});if(c.pathname.match(/[A-Z]/))return O.warn("Redirect to lowercase route to avoid 404 error"),t.newResponse(null,R,{Location:encodeURI(c.pathname.toLowerCase())})}if(i.endsWith("/")&&i!=="/"){const n=me(new URL(a||"/",t.req.url).pathname);return t.newResponse(null,R,{Location:encodeURI(T((n==="/"?"/":ce(n))+c.search))})}const{isAuthenticated:d,teams:C,claims:{email:A}}=t.get("auth");if(o&&!V(o,{isAuthenticated:d,email:A,teams:C},e.config.access?.rbac,e.config.access?.requiresLogin))return d?w(t,e,{slug:o.slug,[z]:o[z],[Y]:o[Y]},403):W(t,e,o.slug);const f=o&&y?ge(o.slug):void 0,M=f?await j(f,e,u):void 0,b=M?.exists??!1;if(o&&(!y||!b)){const n=await p(o),K=await Ee(o,t,n,e),{html:Q,statusCode:q,error:ee}=await Ae(o,K,t,e,ye),v=e.config.access?.rbac,B=!e.config.access?.requiresLogin&&(!v||!Object.keys(v).length),X=q>=400||!!ee||!!e.compilationErrors?.length,te=B&&!X;let g;return X?g=k:B?g=oe:g="private",t.html(Q,q,{"Cache-Control":g,...te&&{[ae]:se}})}const E=f&&b?f:i,J=pe(E),s=m.resolve(e.outdir,J);if(!G(s,e.outdir))return Z(t);if(await be(s,e.outdir))return w(t,e,{slug:a},404);let l,D;if(P){const n=await j(i,e,u);if(l=n.route,D=n.content,l&&!V(l,{isAuthenticated:d,email:A,teams:C},e.config.access?.rbac,e.config.access?.requiresLogin))return W(t,e,i)}const I=e.getGlobalConfig("access");if(!Le(E,I?.rbac||{},I?.requiresLogin||!1,e.getGlobalConfig("directoryPaths"),{isAuthenticated:d,email:A,teams:C}))return Z(t);const L=Te[m.extname(s)]||"text/plain",_=s.match(/assets\/.*\.[a-f0-9]{8,}\..+/)||s.match(/runtime\/chunks\/.*/)?re:_e[L],S=_?{"Cache-Control":`public, max-age=${_}, immutable`,Expires:new Date(Date.now()+_*1e3).toUTCString()}:{"Cache-Control":k},F=r.query("download")!=null,H=M?.content??D;if(H!==void 0)return t.newResponse(H,200,{"Content-Type":x(L),"Access-Control-Allow-Origin":"*",...S,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${$(e.config.seo.siteUrl,T(l.slug))}>; rel="canonical"`}},...F&&{"Content-Disposition":`attachment; filename="${m.basename(E)}"`}});if(await we(s)){const n=await h(s);return t.newResponse(n,200,{"Content-Type":x(L),"Access-Control-Allow-Origin":"*",...S,...l&&{"X-Robots-Tag":"noindex, follow",...e.config.seo?.siteUrl&&{Link:`<${$(e.config.seo.siteUrl,T(l.slug))}>; rel="canonical"`}},...F&&{"Content-Disposition":`attachment; filename="${m.basename(s)}"`}})}else return w(t,e,{slug:a},404)}}export{at as dynamicRouteHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{getCookie as A}from"hono/cookie";import{createHash as K,timingSafeEqual as J}from"node:crypto";import{ulid as V}from"ulid";import{isMcpContentProtected as P}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as E}from"../../../plugins/mcp/constants.js";import{isRedoclySso as Y,isOidcProviderConfig as v}from"../../auth.js";import{AUTH_URL as Z,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as b,MCP_REFRESH_SESSION_LIFETIME_SEC as W}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as R}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as M}from"../../../telemetry/index.js";import{envConfig as U}from"../../../config/env-config.js";import{createMcpAuthorizationCode as x,verifyMcpAuthorizationCode as Q,createMcpRefreshToken as j,verifyMcpRefreshToken as X,createMcpSessionResource as y,oidcJwksCache as z,oidcRefreshTokenGrant as ee,getOidcMetadata as re,discoverJwks as te}from"../../auth.js";import*as S from"../../jwt/jwt.js";import{AlgorithmTypes as D}from"../../jwt/types.js";import{getRequestOrigin as q}from"../../utils/get-request-origin.js";import{encryptClaimValue as L}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as ne}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as F}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as oe,isClientMetadataDocumentUrl as H,isRedirectUriRegistered as ie}from"./mcp-client-metadata.js";const s=(r,e,t=200,i)=>r.json(e,t,{"Content-Type":"application/json",...i??{}}),N=new Set(["S256"]),$=["authorization_code","refresh_token"],se="https://claude.ai/oauth/claude-code-client-metadata";function ce(r,e,t){if(!N.has(e||""))return!1;const i=K("sha256").update(t).digest("base64url"),c=Buffer.from(i),o=Buffer.from(r);return c.length!==o.length?!1:J(c,o)}async function de(r){const e=Math.floor(Date.now()/1e3);return S.sign({type:"mcp_context",...r,iat:e,exp:e+600},m,D.HS256)}async function ae(r){await S.verify(r,m,D.HS256);const{payload:e}=S.decode(r);if(e.type!=="mcp_context")throw new Error("Invalid context token type");return e}function Oe(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=q(e);return s(e,{resource:`${t}${I("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...E],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function Ue(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=q(e);return s(e,{issuer:t,authorization_endpoint:`${t}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...E],registration_endpoint:`${t}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...$],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function De(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:U.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...$],response_types:["code"],scope:E.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function G(r){const e=r||{},t=Object.keys(e),i=t.find(o=>v(e[o])&&Y(e[o])),c=t.find(o=>v(e[o]));return i||c||t[0]}function qe(r){return async e=>{if(!P(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:i}=t,c=i.get("redirect_uri"),o=i.get("client_id"),a=i.get("code_challenge"),n=i.get("code_challenge_method"),l=V();if(a&&!N.has(n||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...N].join(", ")}`},400);if(H(o)&&!a)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(H(o)&&c)try{const d=await oe(o),p=o===se;if(!ie(c,d.redirect_uris,{ignorePort:p}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const p=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${p}`},400)}M.sendMcpAuthorizationStartedMessage([{...y(l),redirect_uri:c||null}]);const f=q(e),_={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:o,mcpState:i.get("state"),mcpSessionId:l,mcpCodeChallenge:a,mcpCodeChallengeMethod:a?n:null,timestamp:Date.now()};try{const d=await de(_),p=new URL(I(T.IDP_LOGIN),f);p.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const k=G(r.getConfig().ssoDirect);return k?(p.searchParams.set("idpId",k),e.redirect(p.toString())):s(e,{error:"not_found"},404)}catch(d){const p=d instanceof Error?d.message:String(d),k=d instanceof Error?d.stack:String(d);M.sendMcpAuthorizationFailedMessage([{...y(l),error:p,error_details:k}]);const C=new URL(I(`${Z}/oauth2/auth`));return C.search=i.toString(),e.redirect(C.toString())}}}async function B(r){const e=Math.floor(Date.now()/1e3),t=await S.sign({...r.claims,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,D.HS256,r.kid),i=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):b);return{accessToken:t,expiresIn:i}}async function le(r,e,t){const i=g=>s(r,{error:"invalid_grant",error_description:g},400),c=r.get("logger"),o=t.get("refresh_token"),a=t.get("client_id")||void 0;if(!o)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let n;try{n=await X(o)}catch(g){return c?.warn("MCP refresh token rejected: %s",g instanceof Error?g.message:String(g)),i("Invalid refresh token")}if(a&&a!==n.client_id)return i("Client mismatch");const l=n.idp_id?e.getConfig().ssoDirect?.[n.idp_id]:void 0;let f,_,d=n.idp_access_token_enc,p=n.idp_refresh_token_enc;if(n.idp_refresh_token_enc){let g;try{g=ne(n.idp_refresh_token_enc,m)}catch{return i("Invalid refresh token")}if(!n.idp_id||!v(l))return i("Unknown identity provider");let u;try{const h=await re(n.idp_id,l);if(!h?.token_endpoint)return i("Identity provider has no token endpoint");u=await ee(h.token_endpoint,g,l)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),i("Refresh rejected by identity provider");if(u.refresh_token&&(p=F(u.refresh_token,m)),u.access_token&&(d=L(u.access_token,m)),u.id_token)try{const h=S.decode(u.id_token);f=h.payload,_=h.header.kid}catch{return i("Invalid token response from identity provider")}}const k=v(l)&&l.tokenExpirationTime||b,C=f??n.claims,w=await B({claims:C,kid:_,idpId:n.idp_id,idpAccessTokenEnc:d,ttlSec:f?void 0:k}),O=await j({clientId:n.client_id,idpId:n.idp_id,claims:C,idpAccessTokenEnc:d,idpRefreshTokenEnc:p,sessionExp:n.session_exp});return s(r,{access_token:w.accessToken,token_type:"Bearer",expires_in:w.expiresIn,refresh_token:O,scope:E.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Le(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),i=t.get("grant_type"),c=t.get("code"),o=t.get("redirect_uri")||void 0,a=t.get("code_verifier")||void 0;if(!$.includes(i))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(i==="refresh_token")return await le(e,r,t);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const n=await Q(c);if(o&&o!==n.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(n.code_challenge){if(!a)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!ce(n.code_challenge,n.code_challenge_method,a))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(U.OAUTH_CLIENT_ID&&n.client_id&&!H(n.client_id)&&n.client_id!==U.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const l=n.id_token;if(typeof l!="string"||l.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const f=n.idp_access_token_enc??(n.idp_access_token?L(n.idp_access_token,m):void 0),_=S.decode(l),{accessToken:d,expiresIn:p}=await B({claims:_.payload,kid:_.header.kid,idpId:n.idp_id,idpAccessTokenEnc:f}),C=Math.floor(Date.now()/1e3)+W,w=await j({clientId:n.client_id,idpId:n.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:n.idp_refresh_token_enc,sessionExp:C}),O={access_token:d,token_type:"Bearer",expires_in:p,refresh_token:w,scope:E.join(" "),id_token:l};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(n){return e.get("logger")?.warn("MCP token exchange failed: %s",n instanceof Error?n.message:String(n)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const i=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:i},500)}}}async function pe(r,e){try{const{payload:t,header:i}=S.decode(r);if(typeof t.idpId=="string"&&t.idpId)return t.idpId;if(i.kid){z.jwks[i.kid]==null&&await te(e||{});const c=z.jwks[i.kid];if(c?.idpId)return c.idpId}}catch{}return G(e)}function He(r){return async e=>{const t=new URL(e.req.url);let i=t.searchParams.get("context");if(!i&&t.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const o=t.pathname.split("/");i=o[o.length-1]}if(!i)return M.sendMcpAuthorizationFailedMessage([{...y(null),error:"Missing context parameter",error_details:null}]),e.text("Missing context parameter",400);let c=null;try{const o=await ae(i);if(c=o.mcpSessionId||null,!o.isMcpFlow||!o.originalRedirectUri)throw new Error("Invalid MCP context");const a=A(e,R.IDP_ID_TOKEN)||A(e,R.AUTHORIZATION),n=A(e,R.IDP_ACCESS_TOKEN),l=A(e,R.IDP_REFRESH_TOKEN);if(!a)return M.sendMcpAuthorizationFailedMessage([{...y(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const f=await x({idToken:a,idpId:await pe(a,r.getConfig().ssoDirect),idpAccessTokenEnc:n?L(n,m):void 0,idpRefreshTokenEnc:l?F(l,m):void 0,clientId:o.mcpClientId||"",redirectUri:o.originalRedirectUri,codeChallenge:o.mcpCodeChallenge||void 0,codeChallengeMethod:o.mcpCodeChallengeMethod||void 0,ttlSec:600}),_=new URL(o.originalRedirectUri);return _.searchParams.set("code",f),o.mcpState&&_.searchParams.set("state",o.mcpState),M.sendMcpAuthorizationCompletedMessage([{...y(c),redirect_uri:o.originalRedirectUri||null}]),e.redirect(_.toString())}catch(o){const a=o instanceof Error?o.message:String(o),n=o instanceof Error?o.stack:String(o);return M.sendMcpAuthorizationFailedMessage([{...y(c),error:a,error_details:n}]),e.text(`Invalid MCP callback: ${a}`,400)}}}export{$ as SUPPORTED_MCP_GRANT_TYPES,de as createMcpContextToken,qe as mcpAuthorizationHandler,He as mcpCallbackHandler,De as mcpDynamicClientRegistrationHandler,Ue as mcpOAuthAuthorizationServerHandler,Oe as mcpOAuthProtectedResourceHandler,Le as mcpTokenPortalHandler,G as resolveMcpIdpId,ae as verifyAndParseMcpContextToken,ce as verifyPkce};
|
|
1
|
+
import{getCookie as U}from"hono/cookie";import{createHash as V,timingSafeEqual as Z}from"node:crypto";import{ulid as Y}from"ulid";import{isMcpContentProtected as w}from"../../../plugins/mcp/auth/auth-handlers.js";import{SUPPORTED_MCP_SCOPES as S}from"../../../plugins/mcp/constants.js";import{isRedoclySso as W,isOidcProviderConfig as P}from"../../auth.js";import{AUTH_URL as X,JWT_SECRET_KEY as m,MCP_ACCESS_TOKEN_TTL_SEC as z,MCP_REFRESH_SESSION_LIFETIME_SEC as Q}from"../../../constants/common.js";import{ServerRoutes as T}from"../../../../constants/api.js";import{AuthCookieNames as v}from"../../../../constants/common.js";import{withPathPrefix as I}from"@redocly/theme/core/utils";import{telemetry as y}from"../../../telemetry/index.js";import{envConfig as D}from"../../../config/env-config.js";import{createMcpAuthorizationCode as x,verifyMcpAuthorizationCode as ee,createMcpRefreshToken as j,verifyMcpRefreshToken as re,createMcpSessionResource as A,oidcRefreshTokenGrant as te,getOidcMetadata as ne,getUserParamsFromCookies as oe}from"../../auth.js";import*as M from"../../jwt/jwt.js";import{InternalTokenTypes as F,McpAccessTokenMarker as R}from"../../jwt/token-types.js";import{AlgorithmTypes as L,JwtAlgorithmNotImplemented as ie,JwtTokenExpired as se,JwtTokenInvalid as ce,JwtTokenNotBefore as de,JwtTokenSignatureMismatched as ae}from"../../jwt/types.js";import{getRequestOrigin as N}from"../../utils/get-request-origin.js";import{encryptClaimValue as q}from"../../../utils/crypto/encrypt-claim-value.js";import{decryptAesGcm as le}from"../../../utils/crypto/decrypt-aes-gcm.js";import{encryptAesGcm as G}from"../../../utils/crypto/encrypt-aes-gcm.js";import{fetchClientMetadataDocument as pe,isClientMetadataDocumentUrl as H,isRedirectUriRegistered as _e}from"./mcp-client-metadata.js";const s=(r,e,t=200,i)=>r.json(e,t,{"Content-Type":"application/json",...i??{}}),b=new Set(["S256"]),$=["authorization_code","refresh_token"],ue="https://claude.ai/oauth/claude-code-client-metadata";function fe(r,e,t){if(!b.has(e||""))return!1;const i=V("sha256").update(t).digest("base64url"),c=Buffer.from(i),o=Buffer.from(r);return c.length!==o.length?!1:Z(c,o)}async function he(r){const e=Math.floor(Date.now()/1e3);return M.sign({type:F.MCP_CONTEXT,...r,iat:e,exp:e+600},m,L.HS256)}async function me(r){await M.verify(r,m,L.HS256);const{payload:e}=M.decode(r);if(e.type!==F.MCP_CONTEXT)throw new Error("Invalid context token type");return e}function $e(r){return async e=>{if(!w(r))return s(e,{error:"not_found"},404);if(e.req.method!=="GET")return s(e,{error:"Method not allowed"},405,{Allow:"GET"});const t=N(e);return s(e,{resource:`${t}${I("/mcp")}`,authorization_servers:[t],bearer_methods_supported:["header"],resource_documentation:`${t}${T.MCP_OAUTH_AUTHORIZATION_SERVER}`,scopes_supported:[...S],bearer_token_types_supported:["Bearer"]},200,{"Access-Control-Allow-Origin":"*"})}}function ze(r){return async e=>{if(!w(r))return s(e,{error:"not_found"},404);const t=N(e);return s(e,{issuer:t,authorization_endpoint:`${t}${I(T.MCP_AUTHORIZATION)}`,token_endpoint:`${t}${I(T.MCP_TOKEN_PORTAL)}`,scopes_supported:[...S],registration_endpoint:`${t}${I(T.MCP_DYNAMIC_CLIENT_REGISTRATION)}`,response_types_supported:["code"],grant_types_supported:[...$],subject_types_supported:["public"],id_token_signing_alg_values_supported:["RS256"],code_challenge_methods_supported:["S256"],token_endpoint_auth_methods_supported:["none"],client_id_metadata_document_supported:!0},200,{"Access-Control-Allow-Origin":"*"})}}function je(r){return async e=>{if(!w(r))return s(e,{error:"not_found"},404);if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{return s(e,{client_id:D.OAUTH_CLIENT_ID||"",client_name:"MCP Client",redirect_uris:[],grant_types:[...$],response_types:["code"],scope:S.join(" "),subject_type:"public",token_endpoint_auth_method:"none",created_at:new Date().toISOString(),updated_at:new Date().toISOString()},201,{"Access-Control-Allow-Origin":"*"})}catch(t){return s(e,{error:"invalid_request",error_description:t?.message||"Unable to register client"},500,{"Access-Control-Allow-Origin":"*"})}}}function B(r){const e=r||{},t=Object.keys(e),i=t.find(o=>P(e[o])&&W(e[o])),c=t.find(o=>P(e[o]));return i||c||t[0]}function Fe(r){return async e=>{if(!w(r))return s(e,{error:"not_found"},404);const t=new URL(e.req.url),{searchParams:i}=t,c=i.get("redirect_uri"),o=i.get("client_id"),l=i.get("code_challenge"),n=i.get("code_challenge_method"),p=Y();if(l&&!b.has(n||""))return s(e,{error:"invalid_request",error_description:`Unsupported code_challenge_method. Supported: ${[...b].join(", ")}`},400);if(H(o)&&!l)return s(e,{error:"invalid_request",error_description:"code_challenge is required for client_id_metadata_document clients"},400);if(H(o)&&c)try{const d=await pe(o),a=o===ue;if(!_e(c,d.redirect_uris,{ignorePort:a}))return s(e,{error:"invalid_request",error_description:"redirect_uri is not registered for this client"},400)}catch(d){const a=d instanceof Error?d.message:String(d);return s(e,{error:"invalid_client",error_description:`Unable to resolve client metadata document: ${a}`},400)}y.sendMcpAuthorizationStartedMessage([{...A(p),redirect_uri:c||null}]);const _=N(e),f={isMcpFlow:!0,originalRedirectUri:c,mcpClientId:o,mcpState:i.get("state"),mcpSessionId:p,mcpCodeChallenge:l,mcpCodeChallengeMethod:l?n:null,timestamp:Date.now()};try{const d=await he(f),a=new URL(I(T.IDP_LOGIN),_);a.searchParams.set("redirectTo",`${T.MCP_CALLBACK}/${d}`);const k=B(r.getConfig().ssoDirect);return k?(a.searchParams.set("idpId",k),e.redirect(a.toString())):s(e,{error:"not_found"},404)}catch(d){const a=d instanceof Error?d.message:String(d),k=d instanceof Error?d.stack:String(d);y.sendMcpAuthorizationFailedMessage([{...A(p),error:a,error_details:k}]);const C=new URL(I(`${X}/oauth2/auth`));return C.search=i.toString(),e.redirect(C.toString())}}}async function K(r){const e=Math.floor(Date.now()/1e3),t=await M.sign({...r.claims,[R.CLAIM]:R.VALUE,...r.idpId?{idpId:r.idpId}:{},...r.idpAccessTokenEnc?{idp_access_token:r.idpAccessTokenEnc}:{},...r.ttlSec?{iat:e,exp:e+r.ttlSec}:{}},m,L.HS256,r.kid),i=r.ttlSec??(typeof r.claims.exp=="number"?Math.max(r.claims.exp-e,0):z);return{accessToken:t,expiresIn:i}}async function ge(r,e,t){const i=g=>s(r,{error:"invalid_grant",error_description:g},400),c=r.get("logger"),o=t.get("refresh_token"),l=t.get("client_id")||void 0;if(!o)return s(r,{error:"invalid_request",error_description:"refresh_token is required"},400);let n;try{n=await re(o)}catch(g){return c?.warn("MCP refresh token rejected: %s",g instanceof Error?g.message:String(g)),i("Invalid refresh token")}if(l&&l!==n.client_id)return i("Client mismatch");const p=n.idp_id?e.getConfig().ssoDirect?.[n.idp_id]:void 0;let _,f,d=n.idp_access_token_enc,a=n.idp_refresh_token_enc;if(n.idp_refresh_token_enc){let g;try{g=le(n.idp_refresh_token_enc,m)}catch{return i("Invalid refresh token")}if(!n.idp_id||!P(p))return i("Unknown identity provider");let u;try{const h=await ne(n.idp_id,p);if(!h?.token_endpoint)return i("Identity provider has no token endpoint");u=await te(h.token_endpoint,g,p)}catch(h){return c?.warn("MCP refresh failed to reach the IdP token endpoint: %s",h instanceof Error?h.message:String(h)),s(r,{error:"server_error",error_description:"Failed to refresh session"},500)}if(u.error||!(u.id_token||u.access_token))return c?.warn("MCP refresh rejected by IdP: %s",u.error||"empty token response"),i("Refresh rejected by identity provider");if(u.refresh_token&&(a=G(u.refresh_token,m)),u.access_token&&(d=q(u.access_token,m)),u.id_token)try{const h=M.decode(u.id_token);_=h.payload,f=h.header.kid}catch{return i("Invalid token response from identity provider")}}const k=P(p)&&p.tokenExpirationTime||z,C=_??n.claims,E=await K({claims:C,kid:f,idpId:n.idp_id,idpAccessTokenEnc:d,ttlSec:_?void 0:k}),O=await j({clientId:n.client_id,idpId:n.idp_id,claims:C,idpAccessTokenEnc:d,idpRefreshTokenEnc:a,sessionExp:n.session_exp});return s(r,{access_token:E.accessToken,token_type:"Bearer",expires_in:E.expiresIn,refresh_token:O,scope:S.join(" ")},200,{"Cache-Control":"no-store",Pragma:"no-cache"})}function Ge(r){return async e=>{if(e.req.method!=="POST")return s(e,{error:"Method not allowed"},405);try{const t=await e.req.formData(),i=t.get("grant_type"),c=t.get("code"),o=t.get("redirect_uri")||void 0,l=t.get("code_verifier")||void 0;if(!$.includes(i))return s(e,{error:"invalid_request",error_description:"Invalid grant type"},400);if(i==="refresh_token")return await ge(e,r,t);if(!c)return s(e,{error:"invalid_request",error_description:"Missing authorization code"},400);try{const n=await ee(c);if(o&&o!==n.redirect_uri)return s(e,{error:"invalid_grant",error_description:"redirect_uri mismatch"},400);if(n.code_challenge){if(!l)return s(e,{error:"invalid_grant",error_description:"code_verifier required"},400);if(!fe(n.code_challenge,n.code_challenge_method,l))return s(e,{error:"invalid_grant",error_description:"code_verifier mismatch"},400)}if(D.OAUTH_CLIENT_ID&&n.client_id&&!H(n.client_id)&&n.client_id!==D.OAUTH_CLIENT_ID)return s(e,{error:"invalid_client",error_description:"Client mismatch"},400);const p=n.id_token;if(typeof p!="string"||p.length===0)return s(e,{error:"invalid_grant",error_description:"Missing id_token in authorization code"},400);const _=await J(p,r.getConfig().ssoDirect);if(!_)return s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400);const f=n.idp_access_token_enc??(n.idp_access_token?q(n.idp_access_token,m):void 0),{accessToken:d,expiresIn:a}=await K({claims:_.payload,kid:_.kid,idpId:n.idp_id,idpAccessTokenEnc:f}),C=Math.floor(Date.now()/1e3)+Q,E=await j({clientId:n.client_id,idpId:n.idp_id,claims:_.payload,idpAccessTokenEnc:f,idpRefreshTokenEnc:n.idp_refresh_token_enc,sessionExp:C}),O={access_token:d,token_type:"Bearer",expires_in:a,refresh_token:E,scope:S.join(" ")};return s(e,O,200,{"Cache-Control":"no-store",Pragma:"no-cache"})}catch(n){return e.get("logger")?.warn("MCP token exchange failed: %s",n instanceof Error?n.message:String(n)),s(e,{error:"invalid_grant",error_description:"Invalid authorization code"},400)}}catch(t){const i=t instanceof Error?t.message:String(t);return s(e,{error:"server_error",error_description:"Failed to process token request",error_details:i},500)}}}async function J(r,e){const t=await oe(e||{},{[v.AUTHORIZATION]:r});if(t.isAuthenticated!==!0)return;const{payload:i,header:c}=M.decode(r);if(i[R.CLAIM]!==R.VALUE)return{payload:i,kid:c.kid,idpId:typeof t.idpId=="string"?t.idpId:void 0}}function Be(r){return async e=>{const t=new URL(e.req.url);let i=t.searchParams.get("context");if(!i&&t.pathname.startsWith(I(`${T.MCP_CALLBACK}/`))){const o=t.pathname.split("/");i=o[o.length-1]}if(!i)return e.text("Missing context parameter",400);let c=null;try{const o=await me(i);if(c=o.mcpSessionId||null,!o.isMcpFlow||!o.originalRedirectUri)throw new Error("Invalid MCP context");const l=r.getConfig().ssoDirect,n=U(e,v.AUTHORIZATION),p=n?await J(n,l):void 0,_=U(e,v.IDP_ACCESS_TOKEN),f=U(e,v.IDP_REFRESH_TOKEN);if(!n||!p)return y.sendMcpAuthorizationFailedMessage([{...A(c),error:"Authentication required",error_details:null}]),e.text("Authentication required",401);const d=await x({idToken:n,idpId:p.idpId??B(l),idpAccessTokenEnc:_?q(_,m):void 0,idpRefreshTokenEnc:f?G(f,m):void 0,clientId:o.mcpClientId||"",redirectUri:o.originalRedirectUri,codeChallenge:o.mcpCodeChallenge||void 0,codeChallengeMethod:o.mcpCodeChallengeMethod||void 0,ttlSec:600}),a=new URL(o.originalRedirectUri);return a.searchParams.set("code",d),o.mcpState&&a.searchParams.set("state",o.mcpState),y.sendMcpAuthorizationCompletedMessage([{...A(c),redirect_uri:o.originalRedirectUri||null}]),e.redirect(a.toString())}catch(o){const l=ke(o);return y.sendMcpAuthorizationFailedMessage([{...A(c),...l}]),e.text(`Invalid MCP callback: ${l.error}`,400)}}}function ke(r){return r instanceof Error&&[ie,se,ce,de,ae].some(e=>r instanceof e)?{error:r.name,error_details:null}:r instanceof Error?{error:r.message,error_details:r.stack??null}:{error:String(r),error_details:null}}export{$ as SUPPORTED_MCP_GRANT_TYPES,he as createMcpContextToken,Fe as mcpAuthorizationHandler,Be as mcpCallbackHandler,je as mcpDynamicClientRegistrationHandler,ze as mcpOAuthAuthorizationServerHandler,$e as mcpOAuthProtectedResourceHandler,Ge as mcpTokenPortalHandler,B as resolveMcpIdpId,me as verifyAndParseMcpContextToken,fe as verifyPkce};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withPathPrefix as
|
|
1
|
+
import{combineUrls as b,withPathPrefix as u}from"@redocly/theme/core/utils";import{DEFAULT_DOCS_MCP_SERVER_NAME as S,MCP_PROTOCOL_VERSION as T}from"../../../plugins/mcp/constants.js";import{RESERVED_ROUTES as g}from"../../../plugins/get-reserved-routes.js";import{isMcpCodeModeEnabled as v}from"../../../plugins/mcp/utils/mode.js";import{CODE_MODE_TOOL_NAMES as M,FETCH_TOOL_NAME as O}from"../../../plugins/mcp/codemode/constants.js";import{EXECUTE_TOOL_SCHEMA as R}from"../../../plugins/mcp/codemode/tools/execute-schema.js";import{describeToolsSchema as L}from"../../../plugins/mcp/codemode/tools/describe-tools.js";import{normalizeToolDisplayName as y}from"../../../plugins/mcp/utils/tool-registration.js";import{buildCodeModeInstructions as D,buildExecuteDescription as I}from"../../../plugins/mcp/codemode/prompts.js";import{toToolListEntries as P}from"../../../plugins/mcp/codemode/build-tool-list.js";import{composeExecuteAnnotations as U}from"../../../plugins/mcp/codemode/capabilities/index.js";import{ALL_CAPABILITIES as V}from"../../../plugins/mcp/codemode/capabilities/registry.js";import{isGatewayCapabilityEnabled as H}from"../../../plugins/mcp/gateway-mcp/eligibility.js";import{shouldHandleMcpAuth as x}from"../../../utils/rbac/should-handle-mcp-auth.js";import{resolvePublicBaseUrl as N}from"../../../web-server/utils/resolve-public-base-url.js";import{telemetryTraceStep as w}from"../../../telemetry/helpers/trace-step.js";import{MCP_AUTH_SCHEMES as j}from"./constants.js";const k="https://static.modelcontextprotocol.io/schemas/mcp-server-card/v1.json",q="1.0";function ne(r){const s=v(),C=new Date().toISOString().slice(0,10);return async m=>await w("com.redocly.mcpServerCard.served",async e=>{e?.setAttribute("id","mcpServerCardId"),e?.setAttribute("object","mcpServerCard"),e?.setAttribute("uri","urn:redocly:realm:api:mcpServerCard:mcpServerCardId");const o=r.getConfig(),a=o.mcp?.docs?.name||S,n=r.getMcpTools().filter(t=>!M.has(t.name)&&!t.hasAvailabilityGate),i=H(o.mcp)?[...V]:[],l=i.some(t=>t.id===O),d=P(n,i),c=(s?[{...R,description:I(l,d),annotations:U(i)},L(n,i)]:n).map(y).map(({name:t,title:f,description:h,schema:_,annotations:A})=>({name:t,title:f,description:h,inputSchema:_,annotations:A})),E=o.skills?.hide===!0?[]:r.getSkills(),p={$schema:k,version:q,protocolVersion:T,serverInfo:{name:a,title:a,version:C},description:o.seo?.description,documentationUrl:b(N(r,m),u("/")),transport:{type:"streamable-http",endpoint:u(g.mcp)},capabilities:{logging:{},tools:{listChanged:!0},...E.length>0?{resources:{listChanged:!0},completions:{}}:{}},authentication:x(!!o.access?.requiresLogin,o.access?.rbac)?{required:!0,schemes:j}:void 0,instructions:s?D(l,d):void 0,tools:c.length>0?c:["dynamic"]};return e?.setAttribute("mode",s?"code":"tools"),e?.setAttribute("toolsCount",c.length),e?.setAttribute("authRequired",p.authentication!==void 0),m.json(p,200,{"Access-Control-Allow-Origin":"*","Cache-Control":"public, max-age=3600"})})}export{ne as mcpServerCardHandler};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { Handler } from 'hono';
|
|
2
2
|
import type { Store } from '../../store';
|
|
3
|
-
export declare function semanticSearchHandler(
|
|
3
|
+
export declare function semanticSearchHandler(store: Store): Handler;
|
|
4
4
|
//# sourceMappingURL=semantic-search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{withPathPrefix as p}from"@redocly/theme/core/utils";import{SEMANTIC_SEARCH_API_URL as s}from"../../constants/common.js";import{fetchWithClient as
|
|
1
|
+
import{withPathPrefix as p}from"@redocly/theme/core/utils";import{SEMANTIC_SEARCH_API_URL as s}from"../../constants/common.js";import{fetchWithClient as d}from"../../utils/fetch-with-client.js";import{handleUnauthorizedApiRequest as h}from"../utils.js";function y(i){return async e=>{if(!s)return e.newResponse(null,404);const n=e.get("auth");if(i.getConfig().access?.requiresLogin&&!n.isAuthenticated)return h(e);const o={"Content-Type":"application/json"};n.idpAccessToken&&(o.Cookie=`accessToken=${n.idpAccessToken}`);const a=await e.req.json();try{const t=await d(s,{method:"POST",body:JSON.stringify(a),headers:o});if(t.ok){const u=(await t.json()).documents.map(r=>({...r,url:p(r.url)}));return e.json(u)}const c=await t.text();return e.newResponse(c,t.status,{"Content-Type":"application/json"})}catch(t){return e.json({error:"Failed to fetch semantic search results",details:String(t)},500)}}}export{y as semanticSearchHandler};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import
|
|
1
|
+
import o from"path";import{REDOCLY_ROUTE_RBAC as C}from"@redocly/config";import{PUBLIC_STATIC_FOLDER as r}from"../../constants/common.js";import{isPathInFolder as g}from"../../../utils/path/is-path-in-folder.js";import{fileExistsAsync as T}from"../../utils/index.js";import{handleUnauthorizedAsset as h}from"../utils.js";import{MIME_TYPES as w}from"../mime-types.js";import{getContentTypeHeaderValue as y}from"../utils/content-type.js";import{canAccessResource as E}from"../../utils/rbac.js";const U=async(e,n,s,a)=>{const c=n.req,{isAuthenticated:m,teams:l,claims:{email:u}}=n.get("auth"),i=o.join(r,r);e.startsWith(i)&&(e=e.replace(i,r));const t=o.resolve(s.outdir,e);if(!g(t,o.join(s.outdir,r)))return null;if(await T(t)){const p={[C]:{slug:e,fsPath:t},slug:e};if(!E(p,{isAuthenticated:m,email:u,teams:l},s.config.access?.rbac,s.config.access?.requiresLogin))return h(n);const f=c.query("download")!=null,d=w[o.extname(t)]||"text/plain",A=await a(t);return n.newResponse(A,200,{"Content-Type":y(d),"Access-Control-Allow-Origin":"*",...f&&{"Content-Disposition":`attachment; filename="${o.basename(t)}"`}})}else return null};export{U as staticContentHandler};
|
|
@@ -14,19 +14,4 @@
|
|
|
14
14
|
* ```
|
|
15
15
|
*/
|
|
16
16
|
export declare function compareVersions(versionA: string, versionB: string): number;
|
|
17
|
-
|
|
18
|
-
* Compares two version strings for sorting purposes in descending order (newest first).
|
|
19
|
-
* This is a convenience wrapper around compareVersions for clearer intent.
|
|
20
|
-
*
|
|
21
|
-
* @param versionA - First version string to compare
|
|
22
|
-
* @param versionB - Second version string to compare
|
|
23
|
-
* @returns Positive if versionA < versionB, negative if versionA > versionB, zero if equal
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* ['3.1', '3', '10', 'latest', '2.0'].sort(compareVersionsDescending)
|
|
28
|
-
* // Returns: ['latest', '10', '3.1', '3', '2.0'] (newest to oldest)
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
export declare function compareVersionsDescending(versionA: string, versionB: string): number;
|
|
32
|
-
//# sourceMappingURL=version-compare.d.ts.map
|
|
17
|
+
//# sourceMappingURL=compare-versions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function n(e,r){return e.localeCompare(r,void 0,{numeric:!0})}export{n as compareVersions};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/realm",
|
|
3
|
-
"version": "0.137.
|
|
3
|
+
"version": "0.137.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,19 +33,20 @@
|
|
|
33
33
|
"@opentelemetry/sdk-trace-web": "2.8.0",
|
|
34
34
|
"@opentelemetry/semantic-conventions": "1.40.0",
|
|
35
35
|
"@redocly/ajv": "8.18.0",
|
|
36
|
-
"@redocly/api-docs": "0.2.0
|
|
36
|
+
"@redocly/api-docs": "0.2.0",
|
|
37
37
|
"@redocly/config": "0.56.0",
|
|
38
|
-
"@redocly/openapi-core": "2.
|
|
38
|
+
"@redocly/openapi-core": "2.53.0",
|
|
39
39
|
"@redocly/portal-legacy-ui": "0.19.0",
|
|
40
|
-
"@redocly/portal-plugin-mock-server": "0.22.0
|
|
41
|
-
"@redocly/realm-asyncapi-sdk": "0.15.0
|
|
42
|
-
"@redocly/theme": "0.69.0
|
|
40
|
+
"@redocly/portal-plugin-mock-server": "0.22.0",
|
|
41
|
+
"@redocly/realm-asyncapi-sdk": "0.15.0",
|
|
42
|
+
"@redocly/theme": "0.69.0",
|
|
43
43
|
"@shikijs/transformers": "3.21.0",
|
|
44
44
|
"@tanstack/react-query": "5.62.3",
|
|
45
45
|
"@tanstack/react-table": "8.21.3",
|
|
46
46
|
"@tanstack/react-virtual": "3.13.0",
|
|
47
47
|
"@wojtekmaj/react-datetimerange-picker": "6.0.0",
|
|
48
|
-
"@xmldom/xmldom": "0.9.
|
|
48
|
+
"@xmldom/xmldom": "0.9.12",
|
|
49
|
+
"acorn": "8.17.0",
|
|
49
50
|
"ajv-formats": "^3.0.1",
|
|
50
51
|
"anser": "^2.3.2",
|
|
51
52
|
"babel-plugin-styled-components": "2.1.4",
|
|
@@ -57,14 +58,14 @@
|
|
|
57
58
|
"enquirer": "2.3.6",
|
|
58
59
|
"esbuild": "0.28.1",
|
|
59
60
|
"escape-carriage": "^1.3.1",
|
|
60
|
-
"fflate": "0.7.
|
|
61
|
+
"fflate": "0.7.5",
|
|
61
62
|
"flexsearch": "0.7.43",
|
|
62
63
|
"graphql": "16.14.2",
|
|
63
|
-
"hono": "4.13.
|
|
64
|
+
"hono": "4.13.7",
|
|
64
65
|
"htmlparser2": "8.0.2",
|
|
65
66
|
"i18next": "22.4.15",
|
|
66
67
|
"is-glob": "4.0.3",
|
|
67
|
-
"js-yaml": "4.3.
|
|
68
|
+
"js-yaml": "4.3.2",
|
|
68
69
|
"lru-cache": "11.1.0",
|
|
69
70
|
"minimatch": "10.2.4",
|
|
70
71
|
"mri": "1.2.0",
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ApiItem, SchemaEntry } from '@redocly/api-docs';
|
|
2
|
-
import type { SearchDocument } from '../../../types';
|
|
3
|
-
export declare class ApiDocsSearchIndexer {
|
|
4
|
-
#private;
|
|
5
|
-
constructor(basePath: string, schemaStore?: Record<string, SchemaEntry>, document?: Record<string, unknown>);
|
|
6
|
-
addItem(item: ApiItem): void;
|
|
7
|
-
getResult(): SearchDocument[];
|
|
8
|
-
}
|
|
9
|
-
//# sourceMappingURL=search-indexer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{REDOCLY_TEAMS_RBAC as f}from"@redocly/config";import{extractFullText as w,nodeTypes as h,contentType as A,schemaKind as g}from"@redocly/api-docs";import{toMarkdown as C}from"../../markdown/search/to-markdown.js";const R=10,k=200,I="message payload",P="message headers";function l(u){if(typeof u=="string")return u;const e=Array.isArray(u)?u:[u];return C(e).trim()}function x(u){return u.length>k?`${u.slice(0,k)}\u2026`:u}class L{#a=[];#r;#i;#s;#o;constructor(e,t,n){this.#r=e,this.#i=t,this.#s=n}addItem(e){if(!(!e.link||!e.content))try{switch(e.content.contentType){case A.OVERVIEW:this.#d(e,this.#r);break;case A.GROUP:case A.ITEM:this.#d(e,e.link);break}}catch(t){console.error(`Cannot add item to api-docs search indexer [${e.label}, ${e.link}]:`,t.message)}}#d(e,t){if(!e.content)return;const n=t.startsWith(this.#r+"/")?t.slice(this.#r.length+1):t.split("/").slice(-2).join("/"),r=e.content.children,i=this.#l(r),s=this.#y(r,n),o=e,a={id:t,url:t,title:e.label??"",text:i,path:[],deprecated:e.content.meta?.deprecated,..."httpVerb"in e&&e.httpVerb?{httpMethod:e.httpVerb}:{},..."httpPath"in e&&e.httpPath?{httpPath:e.httpPath}:{},..."badges"in e&&Array.isArray(e.badges)&&e.badges.length?{badges:e.badges}:{},..."isAdditionalOperation"in e&&e.isAdditionalOperation?{isAdditionalOperation:!0}:{},[f]:o[f],...s.length&&{parameters:s}};this.#a.push(a)}#l(e){const t=[],n=r=>{for(const i of r)if(i.nodeType===h.MARKDOC){const s=i;if(this.#h(s.content))continue;const o=w(s.content);o&&t.push(o)}else(i.nodeType===h.CONTAINER||i.nodeType===h.OVERVIEW_SECTION_WRAPPER)&&n(i.children)};return n(e),t.join(" ").trim()}#h(e){let t=!1,n=!1;const r=s=>{s.type==="code"&&s.attributes?.content?t=!0:s.type==="text"&&s.attributes?.content&&(n=!0);for(const o of s.children??[])r(o)},i=Array.isArray(e)?e:[e];for(const s of i)r(s);return t&&!n}#y(e,t){const n={},r=new Set,i=s=>{for(const o of s)if(o.nodeType===h.ITEM){const a=o;if(a[f])continue;this.#m(a,n,t),this.#E(a,n,r,t)}else if(o.nodeType===h.CONTAINER){const a=o;if(a[f])continue;i(a.children)}else o.nodeType===h.OVERVIEW_SECTION_WRAPPER&&i(o.children)};return i(e),Object.values(n)}#m(e,t,n){if(e.parameters?.length)for(const r of e.parameters){if(r[f])continue;const i=this.#A(e.variant),s=this.#O(e.variant),o=r.description?l(r.description):"",a=this.#T(r.schemaId),d={name:r.name,description:o,place:i,mediaType:void 0,type:a,required:r.required??!1,deepLink:this.#c(n,"request",s,void 0,void 0,[r.name]),example:void 0,enum:void 0};t[this.#t(d)]=d}}#t(e){return[...Array.isArray(e.path)?e.path:[],e.name.toString()].join(".")+e.description+e.place}#c(e,t,n,r,i,s){if(!e)return"";const o=s.join("/"),a=[`t=${t}`];return n&&a.push(`in=${n}`),i&&a.push(`c=${i}`),r&&a.push(`ct=${r}`),o&&a.push(`path=${o}`),`/${e}#${e}/${a.join("&")}`.toLowerCase()}#$(e,t){return e?`/${e}#${e}/response&c=${t}`.toLowerCase():""}#S(e,t,n,r){if(!e)return"";let i="messages";t&&(i+=`&m=${t}`),i+=`&t=${n}`;const s=r.join("/");return s&&(i+=`&path=${s}`),`/${e}#${e}/${i}`.toLowerCase()}#O(e){switch(e){case"query":return"query";case"path":return"path";case"headers":return"header";case"cookies":return"cookie";default:return}}#A(e){switch(e){case"query":return"query parameters";case"path":return"path parameters";case"headers":return"header parameters";case"cookies":return"cookie parameters";case"parameters":return"channel parameters";default:return e?`${e} parameters`:"parameters"}}#T(e){const t=this.#i?.[e];if(!t||t.kind!==g.JSON_SCHEMA)return"unknown";const n=t.data,r=this.#u(n);return this.#f(r)}#f(e){return Array.isArray(e.type)?e.type.join(" | "):e.type?e.type:e.properties||e.additionalProperties?"object":e.items?"array":e.enum?"string":"any"}#E(e,t,n,r){const i=this.#P(e);if(e.graphqlSchema&&Array.isArray(e.graphqlSchema)){this.#q(e.graphqlSchema,i,t);return}const s=!!this.#i&&!!this.#s;s&&this.#b(e,i,t,n,r);for(const o of e.responses??[])o[f]||(this.#g(o,t,r),s&&this.#k(o,t,r));if(s)for(const o of e.messages??[])o[f]||this.#I(o,t,r)}#b(e,t,n,r,i){const s=t.startsWith("response");if(e.mediaTypeSchemas){const o=Object.entries(e.mediaTypeSchemas),a=o.find(([,d])=>!d[f]);if(a){const[d,c]=a;c.schemaId&&this.#n(c.schemaId,t,d,o.length>1,s,n,[],r,i)}}else e.schemaId&&this.#n(e.schemaId,t,void 0,!1,s,n,[],r,i)}#g(e,t,n){const r=e.description?l(e.description):"",s=[e.summary?l(e.summary):"",r].filter(Boolean).join(" ").trim();if(!s)return;const o={name:e.code,description:s,place:`response ${e.code}`,mediaType:void 0,type:"unknown",required:!1,deepLink:this.#$(n,e.code),example:void 0,enum:void 0};t[this.#t(o)]=o}#k(e,t,n){const r=`response ${e.code} fields`,i=new Set;if(e.mediaTypeContent){const s=Object.entries(e.mediaTypeContent),o=s.find(([,a])=>!a[f]);if(o){const[a,d]=o;d.schemaId&&this.#n(d.schemaId,r,a,s.length>1,!0,t,[],i,n)}}else e.schemaId&&this.#n(e.schemaId,r,e.mediaType,!1,!0,t,[],i,n)}#I(e,t,n){const r=e.description?l(e.description):"",i=e.summary??"",s={name:e.name,description:(r+i).trim(),place:"message",mediaType:void 0,type:"unknown",required:!1,example:void 0,enum:void 0};t[this.#t(s)]=s,this.#o=e.name;const o=new Set;e.schemaId&&this.#n(e.schemaId,I,e.contentType,!1,!1,t,[],o,n),e.headerSchemaId&&this.#n(e.headerSchemaId,P,void 0,!1,!1,t,[],o,n),this.#o=void 0}#P(e){switch(e.variant){case"body":return"request fields";case"responses":return"response fields";case"graphql-fields":return"fields";case"graphql-args":return"arguments";case"return-type":return"return type fields";default:return e.variant??"fields"}}#q(e,t,n){for(const r of e){const i=r.description?l(r.description):"",s={name:r.name,description:i,place:t,mediaType:void 0,path:[],type:r.type??"unknown",required:!1,example:void 0,enum:void 0};if(n[this.#t(s)]=s,r.args?.length)for(const o of r.args){const a=o.description?l(o.description):"",d={name:o.name,description:a,place:"arguments",mediaType:void 0,path:[r.name],type:o.type??"unknown",required:!1,example:void 0,enum:void 0};n[this.#t(d)]=d}}}#n(e,t,n,r,i,s,o,a,d=""){const c=this.#i?.[e];if(!c||c.kind!==g.JSON_SCHEMA||c[f])return;const p=c.data,y=r?n:void 0;this.#p(p,t,n,y,i,s,o,a,0,d)}#w(e){const t=e.match(/^response (\d+)/);return t?t[1]:void 0}#u(e){if(e.allOf){const t={};for(const n of e.allOf){const r=n.$ref?this.#e(n.$ref):n;if(r){const i=t.properties,s=t.required;Object.assign(t,r),(i||r.properties)&&(t.properties={...i||{},...r.properties||{}}),(s||r.required)&&(t.required=[...s||[],...r.required||[]])}}return delete t.allOf,e.description&&(t.description=e.description),t}if(e.$ref){const t=this.#e(e.$ref);if(t){const n={...t};return e.description&&(n.description=e.description),n}}return e}#e(e){if(!e.startsWith("#/"))return;const t=e.slice(2).split("/");let n=this.#s;for(const r of t){if(n==null||typeof n!="object")return;n=n[r]}return n!=null&&typeof n=="object"?n:void 0}#p(e,t,n,r,i,s,o,a,d,c=""){if(d>R||e[f])return;const p=(y,$,S)=>this.#p(y,t,n,r,i,s,$,a,S,c);if(e.$ref){this.#C(e.$ref,p,o,d,a);return}if(e.allOf){p(this.#R(e,a),o,d);return}if(e.discriminator&&e.properties&&e.oneOf){p({...e,oneOf:void 0,discriminator:void 0},o,d);return}if(e.oneOf||e.anyOf){this.#x(e,p,o,d,a);return}e.properties&&this.#j(e,p,{place:t,mediaType:n,deepLinkContentType:r,isResponse:i,paramsMap:s,path:o,visited:a,depth:d,slug:c}),this.#M(e,p,o,d,a)}#C(e,t,n,r,i){if(i.has(e))return;i.add(e);const s=this.#e(e);s&&t(s,n,r)}#R(e,t){const n={};for(const r of e.allOf??[]){let i=r;if(r.$ref){if(t.has(r.$ref))continue;t.add(r.$ref),i=this.#e(r.$ref)}if(i){const s=n.properties,o=n.required;Object.assign(n,i),(s||i.properties)&&(n.properties={...s||{},...i.properties||{}}),(o||i.required)&&(n.required=[...o||[],...i.required||[]])}}return delete n.allOf,n}#x(e,t,n,r,i){for(const s of e.oneOf||e.anyOf||[]){if(s.$ref){if(i.has(s.$ref))continue;i.add(s.$ref)}const o=s.$ref?this.#e(s.$ref):s;o&&t(o,n,r+1)}}#j(e,t,n){const{isResponse:r,paramsMap:i,path:s,visited:o}=n,a=e.required||[],d=this.#w(n.place),c=r?"response":"request",p=n.place===I?"payload":n.place===P?"headers":void 0,y={requiredList:a,responseCode:d,type:c,asyncSubsection:p,...n};for(const[$,S]of Object.entries(e.properties??{})){if(S[f])continue;const m=this.#D(S,o);if(!m)continue;const q=this.#_(m,o),O=this.#u(m);if(!this.#L(m,O,r))continue;const T=[...s,$],E=this.#V($,m,O,T,y),b=this.#t(E);i[b]==null&&(i[b]=E,q||t(O,T,n.depth+1))}}#D(e,t){if(!e.$ref)return e;if(!t.has(e.$ref))return t.add(e.$ref),this.#e(e.$ref)}#_(e,t){if(!e.allOf)return!1;let n=!1;for(const r of e.allOf)r.$ref?t.has(r.$ref)||(t.add(r.$ref),n=!0):n=!0;return!n}#L(e,t,n){return!(e[f]||t[f]||t.readOnly&&!n||t.writeOnly&&n||e.additionalProperties!==void 0&&!e.properties)}#V(e,t,n,r,i){const s=t.description??n.description,o=s?l(s):"",a=t.example??n.example,d=a!==void 0?x(typeof a=="string"?a:JSON.stringify(a)):void 0,c=Array.isArray(n.enum)&&n.enum.length?n.enum:void 0;return{name:e,description:o,place:i.place,mediaType:i.mediaType,path:i.path,type:this.#f(n),required:i.requiredList.includes(e),deepLink:i.asyncSubsection?this.#S(i.slug,this.#o,i.asyncSubsection,r):this.#c(i.slug,i.type,void 0,i.deepLinkContentType,i.responseCode,r),example:d,enum:c}}#M(e,t,n,r,i){if(!e.items||typeof e.items!="object"||Array.isArray(e.items))return;if(e.items.$ref){if(i.has(e.items.$ref))return;i.add(e.items.$ref)}const s=e.items.$ref?this.#e(e.items.$ref):e.items;s&&t(s,n,r+1)}getResult(){return this.#a}}export{L as ApiDocsSearchIndexer};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
function r(e,n){return e.localeCompare(n,void 0,{numeric:!0})}function o(e,n){return r(n,e)}export{r as compareVersions,o as compareVersionsDescending};
|