@redocly/redoc 0.137.0-next.3 → 0.137.0-next.5
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 +64 -0
- package/dist/cli/stats/collectors/openapi/index.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/json-schema.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-code-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-example.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/openapi-response-sample.js +1 -1
- package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.d.ts +45 -0
- package/dist/client/app/markdoc/custom-components/openapi/use-markdown-adapter.js +1 -0
- package/dist/client/utils/catalog/get-next-page-param.d.ts +1 -1
- package/dist/client/utils/catalog/get-next-page-param.js +1 -1
- package/dist/compiled/svgo/svgo-node.js +32 -32
- 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/search.d.ts +3 -0
- package/dist/server/config/env-schemas/search.js +1 -1
- package/dist/server/constants/plugins/search.d.ts +0 -2
- package/dist/server/constants/plugins/search.js +1 -1
- package/dist/server/plugins/markdown/utils/sanitize-malformed-md-content.js +2 -2
- package/dist/server/plugins/mcp/codemode/tools/manage-credentials.js +2 -2
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.d.ts +2 -1
- package/dist/server/plugins/mcp/gateway-mcp/elicitation.js +4 -4
- package/dist/server/plugins/mcp/gateway-mcp/secret-store.d.ts +8 -0
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.d.ts +2 -0
- package/dist/server/plugins/mcp/gateway-mcp/upstream-auth-scheme.js +2 -2
- package/dist/server/plugins/mcp/handlers/mcp-credentials-handler.js +1 -144
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.d.ts +6 -0
- package/dist/server/plugins/mcp/handlers/mcp-credentials-page.js +217 -0
- package/dist/server/plugins/mcp/index.js +1 -1
- package/dist/server/plugins/mcp/servers/mcp-server.js +3 -3
- package/dist/server/plugins/mcp/utils/find-gated-tool-names.d.ts +13 -0
- package/dist/server/plugins/mcp/utils/find-gated-tool-names.js +1 -0
- package/dist/server/plugins/search/engines/typesense/index.js +1 -1
- package/dist/server/plugins/search/index.js +1 -1
- package/dist/server/plugins/search/utils.d.ts +2 -1
- package/dist/server/plugins/search/utils.js +2 -2
- package/dist/server/store.d.ts +1 -0
- package/dist/server/store.js +1 -1
- package/dist/server/types/plugins/common.d.ts +3 -0
- package/dist/server/web-server/routes/mcp-routes/mcp-server-card.js +1 -1
- package/package.json +8 -8
|
@@ -56,6 +56,7 @@ declare function loadEnvConfig(): {
|
|
|
56
56
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: boolean | undefined;
|
|
57
57
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
58
58
|
SEARCH_DEV_DEBUG: boolean;
|
|
59
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: boolean;
|
|
59
60
|
TYPESENSE_API_URL?: string | undefined;
|
|
60
61
|
TYPESENSE_API_KEY?: string | undefined;
|
|
61
62
|
MAIN_API_URL?: string | undefined;
|
|
@@ -137,6 +138,7 @@ export declare const envConfig: {
|
|
|
137
138
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: boolean | undefined;
|
|
138
139
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
139
140
|
SEARCH_DEV_DEBUG: boolean;
|
|
141
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: boolean;
|
|
140
142
|
TYPESENSE_API_URL?: string | undefined;
|
|
141
143
|
TYPESENSE_API_KEY?: string | undefined;
|
|
142
144
|
MAIN_API_URL?: string | undefined;
|
|
@@ -61,6 +61,7 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
61
61
|
} & {
|
|
62
62
|
SEARCH_DEV_REINIT: z.ZodOptional<z.ZodString>;
|
|
63
63
|
SEARCH_DEV_DEBUG: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false"]>>, boolean, "false" | "true" | undefined>;
|
|
64
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false"]>>, boolean, "false" | "true" | undefined>;
|
|
64
65
|
TYPESENSE_API_URL: z.ZodOptional<z.ZodString>;
|
|
65
66
|
TYPESENSE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
66
67
|
} & {
|
|
@@ -138,6 +139,7 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
138
139
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: boolean | undefined;
|
|
139
140
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
140
141
|
SEARCH_DEV_DEBUG: boolean;
|
|
142
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: boolean;
|
|
141
143
|
TYPESENSE_API_URL?: string | undefined;
|
|
142
144
|
TYPESENSE_API_KEY?: string | undefined;
|
|
143
145
|
MAIN_API_URL?: string | undefined;
|
|
@@ -209,6 +211,7 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
209
211
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: "false" | "true" | undefined;
|
|
210
212
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
211
213
|
SEARCH_DEV_DEBUG?: "false" | "true" | undefined;
|
|
214
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING?: "false" | "true" | undefined;
|
|
212
215
|
TYPESENSE_API_URL?: string | undefined;
|
|
213
216
|
TYPESENSE_API_KEY?: string | undefined;
|
|
214
217
|
MAIN_API_URL?: string | undefined;
|
|
@@ -280,6 +283,7 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
280
283
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: boolean | undefined;
|
|
281
284
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
282
285
|
SEARCH_DEV_DEBUG: boolean;
|
|
286
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: boolean;
|
|
283
287
|
TYPESENSE_API_URL?: string | undefined;
|
|
284
288
|
TYPESENSE_API_KEY?: string | undefined;
|
|
285
289
|
MAIN_API_URL?: string | undefined;
|
|
@@ -351,6 +355,7 @@ export declare const envSchema: z.ZodEffects<z.ZodObject<{
|
|
|
351
355
|
REDOCLY_MCP_ENFORCE_TOKEN_AUDIENCE?: "false" | "true" | undefined;
|
|
352
356
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
353
357
|
SEARCH_DEV_DEBUG?: "false" | "true" | undefined;
|
|
358
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING?: "false" | "true" | undefined;
|
|
354
359
|
TYPESENSE_API_URL?: string | undefined;
|
|
355
360
|
TYPESENSE_API_KEY?: string | undefined;
|
|
356
361
|
MAIN_API_URL?: string | undefined;
|
|
@@ -5,16 +5,19 @@ import { z } from 'zod';
|
|
|
5
5
|
export declare const searchSchema: z.ZodObject<{
|
|
6
6
|
SEARCH_DEV_REINIT: z.ZodOptional<z.ZodString>;
|
|
7
7
|
SEARCH_DEV_DEBUG: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false"]>>, boolean, "false" | "true" | undefined>;
|
|
8
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: z.ZodEffects<z.ZodDefault<z.ZodEnum<["true", "false"]>>, boolean, "false" | "true" | undefined>;
|
|
8
9
|
TYPESENSE_API_URL: z.ZodOptional<z.ZodString>;
|
|
9
10
|
TYPESENSE_API_KEY: z.ZodOptional<z.ZodString>;
|
|
10
11
|
}, "strip", z.ZodTypeAny, {
|
|
11
12
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
12
13
|
SEARCH_DEV_DEBUG: boolean;
|
|
14
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING: boolean;
|
|
13
15
|
TYPESENSE_API_URL?: string | undefined;
|
|
14
16
|
TYPESENSE_API_KEY?: string | undefined;
|
|
15
17
|
}, {
|
|
16
18
|
SEARCH_DEV_REINIT?: string | undefined;
|
|
17
19
|
SEARCH_DEV_DEBUG?: "false" | "true" | undefined;
|
|
20
|
+
REDOCLY_SKIP_TYPESENSE_INDEXING?: "false" | "true" | undefined;
|
|
18
21
|
TYPESENSE_API_URL?: string | undefined;
|
|
19
22
|
TYPESENSE_API_KEY?: string | undefined;
|
|
20
23
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{z as
|
|
1
|
+
import{z as E}from"zod";import{envBooleanStringDefaultFalse as o}from"../../utils/envs/env-boolean-string.js";const S=E.object({SEARCH_DEV_REINIT:E.string().optional(),SEARCH_DEV_DEBUG:o(),REDOCLY_SKIP_TYPESENSE_INDEXING:o(),TYPESENSE_API_URL:E.string().url().optional(),TYPESENSE_API_KEY:E.string().optional()});export{S as searchSchema};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { SearchDocument } from '../../types';
|
|
2
1
|
export declare const SEARCH_INDEX_FIELDS: string[];
|
|
3
2
|
export declare const SEARCH_GROUP_NAME_DOCUMENTATION = "Documentation";
|
|
4
3
|
export declare const SEARCH_GROUP_NAME_API = "API Reference";
|
|
@@ -12,7 +11,6 @@ export declare const AI_INDEX_EXPORT_FOLDER = "ai-search";
|
|
|
12
11
|
export declare const AI_SEARCH_CHUNK_SIZE = 50;
|
|
13
12
|
export declare const AI_SEARCH_DOCUMENT_CHUNK_SIZE = 250;
|
|
14
13
|
export declare const SEARCH_DOCUMENT_METADATA_KEY: unique symbol;
|
|
15
|
-
export declare const BASE_SEARCH_DOCUMENT: SearchDocument;
|
|
16
14
|
export declare const DEFAULT_AI_SEARCH_ENABLED = true;
|
|
17
15
|
export declare const DISABLE_DEEP_LINK_IF_FIELDS_EXIST: string[];
|
|
18
16
|
//# sourceMappingURL=search.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
const t=["title","text","path","httpPath","parameters[]:name","parameters[]:description","parameters[]:place","parameters[]:path"],_="Documentation",E="API Reference",e="search",o=100,A=10,r=25,p=100,I=150,s="ai-search",c=50,R=250,S=Symbol("metadata"),a=!0,n=["title"];export{s as AI_INDEX_EXPORT_FOLDER,c as AI_SEARCH_CHUNK_SIZE,R as AI_SEARCH_DOCUMENT_CHUNK_SIZE,a as DEFAULT_AI_SEARCH_ENABLED,n as DISABLE_DEEP_LINK_IF_FIELDS_EXIST,I as HIGHLIGHTED_TEXT_MAX_LENGTH,e as SEARCH_DATA_EXPORT_FOLDER,S as SEARCH_DOCUMENT_METADATA_KEY,A as SEARCH_GROUP_LIMIT,E as SEARCH_GROUP_NAME_API,_ as SEARCH_GROUP_NAME_DOCUMENTATION,t as SEARCH_INDEX_FIELDS,o as SEARCH_LIMIT,r as SEARCH_MAX_FACET_VALUES,p as SEARCH_MAX_INMEMORY_DOCUMENTS_COUNT};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function
|
|
2
|
-
${
|
|
1
|
+
function c(n){return n.replace(/\[([^\]]*<[^>]*>[^\]]*)\]\(([^)]+)\)/g,(r,t,s)=>`[${t.replace(/(`+)[^`]+\1|[<>]/g,e=>e==="<"?"<":e===">"?">":e)}](${s})`).replace(/({%\s*(\w+)(?:\s+[^%]*)?%})(\s*)({%\s*\/\2\s*%})/g,(r,t,s,l,e)=>`${t}
|
|
2
|
+
${e}`)}export{c as sanitizeMalformedMdContent};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{MANAGE_API_CREDENTIALS_TOOL_NAME as
|
|
2
|
-
`),annotations:{title:"Manage API credentials",readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it
|
|
1
|
+
import{MANAGE_API_CREDENTIALS_TOOL_NAME as v}from"../../constants.js";import{buildUpstreamCredentialFallback as y,buildUpstreamCredentialInputRequired as b,schemeLabelOf as P}from"../../gateway-mcp/elicitation.js";import{getCallableApis as m,isFetchCapable as S}from"../../gateway-mcp/fetch-capability.js";import{secretStore as I}from"../../gateway-mcp/secret-store.js";import{MISSING_SESSION_HANDLE_MESSAGE as w,resolveSecretSubject as k}from"../../gateway-mcp/subject.js";import{resolveUpstreamApiAuth as j}from"../../gateway-mcp/upstream-auth-scheme.js";function H(e){return!!e.elicitationEnabled&&S(e)}function U(e){const t=m(e).map(n=>n.relativePath);return{name:v,description:["Get a secure link where the user sets, replaces, or removes the credentials this server sends to a documented API.","Call it when the user wants to change or clear an API key, or when a tool result tells you the stored credentials were rejected.","The link covers every credential the API accepts, so scheme is optional - pass it only when a tool result names the scheme that was rejected.","It returns a link for the user to open - never ask for the secret in the conversation and never pass it as an argument."].join(`
|
|
2
|
+
`),annotations:{title:"Manage API credentials",readOnlyHint:!1,openWorldHint:!1},schema:{type:"object",required:["api"],additionalProperties:!1,properties:{api:{type:"string",description:`The API to manage credentials for.${t.length>0?` One of: ${t.join(", ")}.`:""}`,minLength:1,...t.length>0?{enum:t}:{}},scheme:{type:"string",description:"Name of the security scheme to manage the credential for, as named in the API description (a tool result that reports a rejected credential names it). Omit it to let the link cover every scheme the API accepts.",minLength:1},sessionHandle:{type:"string",description:"Handle returned by createAnonymousSession. Required while the caller is anonymous, so the credentials are stored against the same subject the API calls use.",minLength:1}}}}}function _(e,c){return async t=>{const n=k(e.user,typeof t.sessionHandle=="string"?t.sessionHandle:void 0);if(n.kind==="invalid")return l(n.message);if(n.kind==="anonymous")return l(w);const{subject:u}=n,h=typeof t.api=="string"?t.api:"",s=m(e).find(o=>o.relativePath===h||o.name===h);if(!s){const o=m(e).map(g=>g.relativePath).join(", ");return l(`Unknown api "${h}". Credentials can only be managed for the APIs this server can call: ${o||"none"}.`)}const r=await j(e,s.relativePath),a=typeof t.scheme=="string"?t.scheme:void 0,A=Object.keys(r.schemes);if(a&&!r.schemes[a])return l(`Unknown scheme "${a}" for api "${s.relativePath}". The security schemes this server can store credentials for are: ${A.join(", ")}.`);const i=a??r.defaultScheme,p=P(r.schemes,i),d=await c.getKv(),f={subject:u,baseUrl:e.baseUrl??"",target:{api:s.relativePath,name:s.name,scheme:i,...p?{schemeLabel:p}:{}},auth:r.schemes[i],apiAuth:r,kv:d,hasExistingSecret:await I.hasSecret(d,u,s.relativePath,i),reason:{kind:"requested"}};return c.getClientCapabilities()?.elicitation?.url?await b(f):{content:[{type:"text",text:await y(f)}],isError:!1}}}function l(e){return{content:[{type:"text",text:e}],isError:!0}}export{H as isManageApiCredentialsToolAvailable,_ as manageApiCredentials,U as manageApiCredentialsSchema};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InputRequiredResult } from '@modelcontextprotocol/server';
|
|
2
2
|
import type { UpstreamAuthRequiredEvent } from './upstream-auth.js';
|
|
3
|
-
import type { SchemeChoice, UpstreamAuthScheme } from './upstream-auth-scheme.js';
|
|
3
|
+
import type { SchemeChoice, UpstreamApiAuth, UpstreamAuthScheme } from './upstream-auth-scheme.js';
|
|
4
4
|
import type { KvService } from '@redocly/config';
|
|
5
5
|
export declare const CREDENTIALS_INPUT_REQUEST_KEY = "credentials";
|
|
6
6
|
export type CredentialTarget = {
|
|
@@ -25,6 +25,7 @@ type CredentialElicitationRequest = {
|
|
|
25
25
|
baseUrl: string;
|
|
26
26
|
target: CredentialTarget;
|
|
27
27
|
auth: UpstreamAuthScheme;
|
|
28
|
+
apiAuth?: UpstreamApiAuth;
|
|
28
29
|
kv: KvService;
|
|
29
30
|
hasExistingSecret: boolean;
|
|
30
31
|
reason: CredentialElicitationReason;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{inputRequired as
|
|
2
|
-
`)}function
|
|
3
|
-
`)}async function
|
|
4
|
-
`)}export{
|
|
1
|
+
import{inputRequired as h,inputResponse as T}from"@modelcontextprotocol/server";import{MANAGE_API_CREDENTIALS_TOOL_NAME as l}from"../constants.js";import{secretStore as d,ELICITATION_TTL_MS as w}from"./secret-store.js";import{operationsForScheme as y,orderedSchemesOf as I}from"./upstream-auth-scheme.js";import{ANONYMOUS_SUBJECT_PREFIX as u}from"./subject.js";const m="credentials";function N(e){const t=T(e,m);return t.kind==="elicit"&&(t.action==="decline"||t.action==="cancel")}function j(e,t){return Object.keys(e).length>1?t:void 0}async function x(e){const{url:t}=await p(e);return h({inputRequests:{[m]:h.elicitUrl({message:P(e.target,e.hasExistingSecret),url:t})}})}async function R(e){const{url:t}=await p(e);return S(e,t,A(e.subject))}function U(e,t){const r=e.status===401?`The stored credentials for "${e.name}" were rejected (HTTP 401).`:`The "${e.name}" API rejected the request with HTTP ${e.status}.`,n=t?`api: "${e.api}" and scheme: "${t}"`:`api: "${e.api}"`;return[r,"",`Do not retry the same call. Call \`${l}\` with ${n} to get a link the user has to open in a browser to replace or remove the stored credentials, then retry once they confirm they saved them.`].join(`
|
|
2
|
+
`)}function F(e,t){const r=t.map(({name:n,label:o,description:a})=>`- ${n} \u2014 ${o}${a?`: ${a}`:""}`);return[`The "${e.name}" API rejected the request with HTTP ${e.status} and this operation accepts more than one kind of credential:`,"",...r,"","Ask the user which one of these they have, and wait for the answer. Never guess and never reuse a value meant for another kind.",`Then call \`${l}\` with api: "${e.api}" and scheme set to the name they picked to get a link where they enter it.`].join(`
|
|
3
|
+
`)}async function p({kv:e,subject:t,baseUrl:r,target:n,auth:o,apiAuth:a,hasExistingSecret:i}){const s=a?await b(e,t,n.api,a):[],c=await d.createElicitation(e,{subject:t,api:n.api,name:n.name,scheme:n.scheme,...n.schemeLabel?{schemeLabel:n.schemeLabel}:{},auth:o,hasExistingSecret:i,...s.length>1?{schemes:s}:{}});return{token:c,url:E(r,c)}}async function b(e,t,r,n){const a=(await d.getSecretsForSubject(e,t))[r]??{};return I(n.schemes).map(i=>{const s=y(n,i),c=n.descriptions?.[i];return{name:i,...c?{label:c}:{},auth:n.schemes[i],hasExistingSecret:!!a[i],...s.length>0?{operations:s}:{}}})}function A(e){return e.startsWith(u)?e.slice(u.length):void 0}function P(e,t){const r=`The link works for ${f()} minutes.`,n=g(e);return t?`Open this page in a browser to replace or remove the stored credentials for ${n}. They go straight to the API and never enter the chat. ${r}`:`${k(n)} needs credentials. Open this page in a browser to paste them - they go straight to the API and never enter the chat. ${r}`}function g({name:e,schemeLabel:t}){return t?`the "${t}" scheme of the "${e}" API`:`the "${e}" API`}function k(e){return e.charAt(0).toUpperCase()+e.slice(1)}function f(){return Math.round(w/6e4)}function E(e,t){if(!e)return`/mcp/credentials?token=${t}`;try{const r=new URL("/mcp/credentials",e);return r.searchParams.set("token",t),r.toString()}catch{return`${e}/mcp/credentials?token=${t}`}}function S({target:e,reason:t,hasExistingSecret:r},n,o){const i=[`**Stop and show the user this link. They have to open it in a browser to ${r?`replace or remove the stored credentials for the "${e.name}" API`:`add credentials for the "${e.name}" API`} - nothing continues until they do.**`,"",n,"",`The link is valid ${f()} minutes and opens a form on this site. Credentials go straight to the API from there: never ask for them in the conversation and never pass them as a tool argument.`,"",t.kind==="authFailure"?`The upstream API "${e.name}" returned ${t.status} and needs credentials.`:r?`Credentials are already stored for the "${e.name}" API; the link lets the user replace or remove them.`:`The "${e.name}" API needs credentials.`,...e.schemeLabel?[`The operation authenticates with its "${e.schemeLabel}" security scheme.`]:[],o?`Once the user confirms they saved them, call this tool again with sessionHandle: "${o}" - the credentials are stored against that handle, applied server-side, and never appear in the conversation.`:"Once the user confirms they saved them, call this tool again - the credentials are applied server-side and never appear in the conversation."],s=t.kind==="authFailure"?t.otherApiNames??[]:[];if(s.length>0){const c=s.map($=>`"${$}"`).join(", ");i.push("",`The remaining APIs (${c}) will prompt for credentials on subsequent calls.`)}return i.join(`
|
|
4
|
+
`)}export{m as CREDENTIALS_INPUT_REQUEST_KEY,U as buildCredentialsToolHint,F as buildSchemeChoiceHint,R as buildUpstreamCredentialFallback,x as buildUpstreamCredentialInputRequired,N as isCredentialElicitationDeclined,j as schemeLabelOf};
|
|
@@ -7,6 +7,13 @@ export type StoredSecret = {
|
|
|
7
7
|
savedAt?: number;
|
|
8
8
|
};
|
|
9
9
|
export type UpstreamSecrets = Record<string, Record<string, StoredSecret>>;
|
|
10
|
+
export type ElicitationScheme = {
|
|
11
|
+
name: string;
|
|
12
|
+
label?: string;
|
|
13
|
+
auth: UpstreamAuthScheme;
|
|
14
|
+
hasExistingSecret: boolean;
|
|
15
|
+
operations?: string[];
|
|
16
|
+
};
|
|
10
17
|
export type ElicitationTarget = {
|
|
11
18
|
subject: string;
|
|
12
19
|
api: string;
|
|
@@ -15,6 +22,7 @@ export type ElicitationTarget = {
|
|
|
15
22
|
schemeLabel?: string;
|
|
16
23
|
auth: UpstreamAuthScheme;
|
|
17
24
|
hasExistingSecret?: boolean;
|
|
25
|
+
schemes?: ElicitationScheme[];
|
|
18
26
|
};
|
|
19
27
|
export declare class SecretStore {
|
|
20
28
|
#private;
|
|
@@ -17,6 +17,7 @@ export type UpstreamAuthScheme = {
|
|
|
17
17
|
};
|
|
18
18
|
export type UpstreamAuthRoute = {
|
|
19
19
|
pattern: RegExp;
|
|
20
|
+
path: string;
|
|
20
21
|
method: string;
|
|
21
22
|
schemes: string[];
|
|
22
23
|
};
|
|
@@ -37,5 +38,6 @@ export declare function resolveUpstreamApiAuth(context: McpToolContext, apiRelat
|
|
|
37
38
|
export declare function schemeChoicesOf(auth: UpstreamApiAuth, names: readonly string[]): SchemeChoice[];
|
|
38
39
|
export declare function credentialHeadersOf(schemes: Record<string, UpstreamAuthScheme>): string[];
|
|
39
40
|
export declare function orderedSchemesOf(schemes: Record<string, UpstreamAuthScheme>): string[];
|
|
41
|
+
export declare function operationsForScheme(auth: UpstreamApiAuth, scheme: string): string[];
|
|
40
42
|
export {};
|
|
41
43
|
//# sourceMappingURL=upstream-auth-scheme.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{getApiDescriptionFromFs as O}from"../docs-mcp/tools/openapi/utils.js";import{OPERATION_METHODS as
|
|
2
|
-
`)[0].trim();s&&(n[r]=s.slice(0,
|
|
1
|
+
import{getApiDescriptionFromFs as O}from"../docs-mcp/tools/openapi/utils.js";import{OPERATION_METHODS as S}from"../docs-mcp/tools/openapi/constants.js";const A=120,l="default",g={kind:"raw"},h=["oauth2ClientCredentials","bearer","apiKey","cookie","raw"],w=new Set(S),C=/\{[^}]*\}/g;function c(){return{schemes:{[l]:g},defaultScheme:l,routes:[]}}async function L(e,t,n=O){const r=Object.values(e.apiDescriptionsMap??{}).find(i=>i.relativePath===t);if(!r)return c();try{const i=await n({relativePath:r.relativePath,outdir:e.outdir||"",user:e.user,rbac:e.config?.access?.rbac||{},requiresLogin:!!e.config?.access?.requiresLogin});return i?E(i,r.servers??[]):c()}catch{return c()}}function E(e,t){const n=$(e),r=Object.keys(n);if(r.length===0)return c();const i=m(e.security,n)[0]??a(r,n)[0],o=x(e,r);return{schemes:n,defaultScheme:i,routes:v(e,n,t),...Object.keys(o).length>0?{descriptions:o}:{}}}function N(e,t){return t.flatMap(n=>{const r=e.schemes[n];if(!r)return[];const i=e.descriptions?.[n];return[{name:n,label:j(r),...i?{description:i}:{}}]})}function U(e){return[...new Set(Object.values(e).map(P))]}function q(e){return a(Object.keys(e),e)}function z(e,t){const n=new Set;for(const r of e.routes)r.schemes.includes(t)&&n.add(`${r.method} ${r.path}`);return[...n]}function P(e){return e.kind==="apiKey"?e.header:e.kind==="cookie"?"Cookie":"Authorization"}function j(e){switch(e.kind){case"bearer":return"API key or bearer token";case"cookie":return`browser session cookie (${e.name})`;case"apiKey":return`API key in the ${e.header} header`;case"oauth2ClientCredentials":return"OAuth2 client credentials";default:return"Authorization header value"}}function x(e,t){const n={};for(const r of t){const i=e.components?.securitySchemes?.[r],o=k(i)?i.description:void 0;if(typeof o!="string")continue;const s=o.split(`
|
|
2
|
+
`)[0].trim();s&&(n[r]=s.slice(0,A))}return n}function $(e){const t={};for(const[n,r]of Object.entries(e.components?.securitySchemes??{})){if(!k(r))continue;const i=T(r);i&&(t[n]=i)}return t}function T(e){if(e.type==="oauth2"){const t=e.flows?.clientCredentials;return t?{kind:"oauth2ClientCredentials",tokenUrl:t.tokenUrl,scopes:Object.keys(t.scopes??{})}:{kind:"bearer"}}if(e.type==="http"&&e.scheme==="bearer")return{kind:"bearer"};if(e.type==="apiKey"&&e.in==="header")return{kind:"apiKey",header:e.name||"X-Api-Key"};if(e.type==="apiKey"&&e.in==="cookie"&&e.name)return{kind:"cookie",name:e.name}}function a(e,t){return[...e].sort((n,r)=>h.indexOf(t[n].kind)-h.indexOf(t[r].kind)||e.indexOf(n)-e.indexOf(r))}function m(e,t){const n=[];for(const r of e??[])for(const i of Object.keys(r))t[i]&&!n.includes(i)&&n.push(i);return n}function v(e,t,n){const r=I(n),i=[];for(const[o,s]of Object.entries(e.paths??{})){if(!s||typeof s!="object")continue;const f=H(r,o);if(f)for(const[p,b]of Object.entries(s)){if(!w.has(p.toLowerCase()))continue;const u=b?.security??e.security;if(u===void 0)continue;const d=m(u,t);d.length===0&&u.length>0||i.push({pattern:f,path:o,method:p.toUpperCase(),schemes:a(d,t)})}}return i}function H(e,t){if(t.startsWith("/"))return new RegExp(`^${e}${y(t)}/?$`)}function y(e){return e.split(C).map(_).join("[^/]+").replace(/\/+$/,"")}function I(e){const t=[...new Set(e.map(n=>R(n.url)).filter(Boolean).map(y))];return t.length>0?`(?:${t.join("|")})?`:""}function R(e){if(!e)return"";const t=e.replace(/^[a-z][a-z0-9+.-]*:\/\//i,""),n=t.indexOf("/");return n===-1?"":t.slice(n).split(/[?#]/)[0].replace(/\/+$/,"")}function _(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function k(e){return typeof e=="object"&&e!==null&&"type"in e}export{l as DEFAULT_SCHEME_NAME,U as credentialHeadersOf,z as operationsForScheme,q as orderedSchemesOf,L as resolveUpstreamApiAuth,N as schemeChoicesOf};
|
|
@@ -1,144 +1 @@
|
|
|
1
|
-
import{secretStore as
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<meta name="robots" content="noindex">
|
|
7
|
-
<title>API credentials</title>
|
|
8
|
-
<style>
|
|
9
|
-
:root{color-scheme:light dark}
|
|
10
|
-
*{box-sizing:border-box}
|
|
11
|
-
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;margin:0;min-height:100vh;display:flex;align-items:center;justify-content:center;background:#f3f4f6;color:#111827;padding:1.5rem}
|
|
12
|
-
.card{width:100%;max-width:26rem;background:#ffffff;border-radius:.75rem;box-shadow:0 10px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);padding:2rem}
|
|
13
|
-
h1{font-size:1.25rem;margin:0 0 .5rem}
|
|
14
|
-
.section{margin-top:1.5rem;padding-top:1.5rem;border-top:1px solid #e5e7eb}
|
|
15
|
-
.section h2{font-size:1rem;margin:0 0 .5rem}
|
|
16
|
-
p.hint{color:#6b7280;font-size:.875rem;line-height:1.4}
|
|
17
|
-
p.error{margin:1rem 0 0;padding:.75rem;border:1px solid #fecaca;border-radius:.5rem;background:#fef2f2;color:#991b1b;font-size:.875rem;line-height:1.4}
|
|
18
|
-
label{display:block;margin:1rem 0 .25rem;font-weight:600;font-size:.875rem}
|
|
19
|
-
input[type=password],input[type=text]{width:100%;padding:.625rem .75rem;font-size:1rem;border:1px solid #d1d5db;border-radius:.5rem;background:#fff;color:#111827}
|
|
20
|
-
input:focus{outline:none;border-color:#4f46e5;box-shadow:0 0 0 3px rgba(79,70,229,.2)}
|
|
21
|
-
button{margin-top:1.25rem;padding:.625rem 1.5rem;font-size:1rem;font-weight:600;color:#fff;background:#4f46e5;border:none;border-radius:.5rem;cursor:pointer}
|
|
22
|
-
button:hover{background:#4338ca}
|
|
23
|
-
button:focus-visible{outline:2px solid #4f46e5;outline-offset:2px}
|
|
24
|
-
button.danger{background:#b91c1c}
|
|
25
|
-
button.danger:hover{background:#991b1b}
|
|
26
|
-
button.danger:focus-visible{outline-color:#b91c1c}
|
|
27
|
-
code{background:#f3f4f6;padding:.125rem .375rem;border-radius:.25rem;font-size:.85em}
|
|
28
|
-
footer{margin-top:1.5rem;padding-top:1rem;border-top:1px solid #e5e7eb;font-size:.75rem;color:#9ca3af;text-align:center}
|
|
29
|
-
@media (prefers-color-scheme:dark){
|
|
30
|
-
body{background:#0b0f19;color:#e5e7eb}
|
|
31
|
-
.card{background:#111827;box-shadow:0 10px 25px -5px rgba(0,0,0,.4)}
|
|
32
|
-
.section{border-top-color:#1f2937}
|
|
33
|
-
p.hint{color:#9ca3af}
|
|
34
|
-
p.error{border-color:#7f1d1d;background:#2a1215;color:#fca5a5}
|
|
35
|
-
input[type=password],input[type=text]{background:#0b0f19;border-color:#374151;color:#e5e7eb}
|
|
36
|
-
code{background:#1f2937}
|
|
37
|
-
footer{border-top-color:#1f2937;color:#6b7280}
|
|
38
|
-
}
|
|
39
|
-
</style>
|
|
40
|
-
</head>
|
|
41
|
-
<body><div class="card">${e}</div></body>
|
|
42
|
-
</html>`}function l(e){return`<h1>\u{1F512} ${i(e)}</h1>`}function u(){return"<footer>Secured by Redocly · credentials never enter the AI conversation</footer>"}function b(){return`<p class="hint">This link expires ${Math.round(C/6e4)} minutes after it was issued. If it stops working, ask your AI client for a new one.</p>`}function S(e,t,n){const o=e.hasExistingSecret?N(t):"",r=n?`<p class="error">${i(n)}</p>`:"",s=k(e);switch(e.auth.kind){case"bearer":return W(s,t,o,r);case"apiKey":return X(s,t,e.auth.header,o,r);case"cookie":return Y(s,t,e.auth.name,o,r);case"oauth2ClientCredentials":return J(s,t,e.auth.scopes,o,r);default:return Q(s,t,o,r)}}function k({name:e,schemeLabel:t}){return t?`${e} (${t})`:e}function N(e){return`
|
|
43
|
-
<div class="section">
|
|
44
|
-
<h2>Remove stored credentials</h2>
|
|
45
|
-
<p class="hint">This API already has credentials stored. Save above to replace them, or remove them to make calls anonymous again.</p>
|
|
46
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
47
|
-
<input type="hidden" name="token" value="${i(e)}">
|
|
48
|
-
<input type="hidden" name="mode" value="clear">
|
|
49
|
-
<button class="danger" type="submit">Remove credentials</button>
|
|
50
|
-
</form>
|
|
51
|
-
</div>`}function W(e,t,n,o){return d(`
|
|
52
|
-
${l(`Credentials for ${e}`)}
|
|
53
|
-
${o}
|
|
54
|
-
<p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
|
|
55
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
56
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
57
|
-
<input type="hidden" name="mode" value="token">
|
|
58
|
-
<label for="secret">API token</label>
|
|
59
|
-
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
60
|
-
<p class="hint">Paste the raw token or one already prefixed with <code>Bearer</code> — we add the prefix automatically if it is missing.</p>
|
|
61
|
-
<button type="submit">Save credentials</button>
|
|
62
|
-
</form>
|
|
63
|
-
${n}
|
|
64
|
-
${b()}
|
|
65
|
-
${u()}`)}function X(e,t,n,o,r){return d(`
|
|
66
|
-
${l(`Credentials for ${e}`)}
|
|
67
|
-
${r}
|
|
68
|
-
<p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${i(n)}</code> header. It never passes through the AI conversation.</p>
|
|
69
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
70
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
71
|
-
<input type="hidden" name="mode" value="token">
|
|
72
|
-
<label for="secret">${i(n)} value</label>
|
|
73
|
-
<input type="password" id="secret" name="secret" placeholder="value" required autofocus>
|
|
74
|
-
<button type="submit">Save credentials</button>
|
|
75
|
-
</form>
|
|
76
|
-
${o}
|
|
77
|
-
${b()}
|
|
78
|
-
${u()}`)}function Y(e,t,n,o,r){return d(`
|
|
79
|
-
${l(`Credentials for ${e}`)}
|
|
80
|
-
${r}
|
|
81
|
-
<p class="hint">Stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and sent to the API as the <code>${i(n)}</code> cookie. It never passes through the AI conversation.</p>
|
|
82
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
83
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
84
|
-
<input type="hidden" name="mode" value="token">
|
|
85
|
-
<label for="secret">${i(n)} cookie value</label>
|
|
86
|
-
<input type="password" id="secret" name="secret" placeholder="value" required autofocus>
|
|
87
|
-
<p class="hint">Paste only the value — we send it as <code>${i(n)}=<value></code>.</p>
|
|
88
|
-
<button type="submit">Save credentials</button>
|
|
89
|
-
</form>
|
|
90
|
-
${o}
|
|
91
|
-
${b()}
|
|
92
|
-
${u()}`)}function J(e,t,n,o,r){const s=n.length>0?n.map(i).join(", "):"default scopes";return d(`
|
|
93
|
-
${l(`Credentials for ${e}`)}
|
|
94
|
-
${r}
|
|
95
|
-
<p class="hint">Paste a token you already have, or let us exchange client credentials for one via OAuth2 client_credentials.</p>
|
|
96
|
-
<div class="section">
|
|
97
|
-
<h2>I have a token</h2>
|
|
98
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
99
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
100
|
-
<input type="hidden" name="mode" value="token">
|
|
101
|
-
<label for="secret">API token</label>
|
|
102
|
-
<input type="password" id="secret" name="secret" placeholder="your API token" required autofocus>
|
|
103
|
-
<p class="hint">We add <code>Bearer</code> automatically if it is missing.</p>
|
|
104
|
-
<button type="submit">Save token</button>
|
|
105
|
-
</form>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="section">
|
|
108
|
-
<h2>Exchange client credentials</h2>
|
|
109
|
-
<p class="hint">We request a token from the API on your behalf. Scopes: ${s}.</p>
|
|
110
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
111
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
112
|
-
<input type="hidden" name="mode" value="client-credentials">
|
|
113
|
-
<label for="client_id">Client ID</label>
|
|
114
|
-
<input type="text" id="client_id" name="client_id" required>
|
|
115
|
-
<label for="client_secret">Client secret</label>
|
|
116
|
-
<input type="password" id="client_secret" name="client_secret" required>
|
|
117
|
-
<button type="submit">Exchange & save</button>
|
|
118
|
-
</form>
|
|
119
|
-
</div>
|
|
120
|
-
${o}
|
|
121
|
-
${b()}
|
|
122
|
-
${u()}`)}function Q(e,t,n,o){return d(`
|
|
123
|
-
${l(`Credentials for ${e}`)}
|
|
124
|
-
${o}
|
|
125
|
-
<p class="hint">The value is stored encrypted for you only (24 hours for anonymous callers, 30 days when signed in) and is sent to the API as the <code>Authorization</code> header. It never passes through the AI conversation.</p>
|
|
126
|
-
<form method="post" action="/mcp/credentials" autocomplete="off">
|
|
127
|
-
<input type="hidden" name="token" value="${i(t)}">
|
|
128
|
-
<input type="hidden" name="mode" value="token">
|
|
129
|
-
<label for="secret">Authorization header value</label>
|
|
130
|
-
<input type="password" id="secret" name="secret" placeholder="Bearer <your token>" required autofocus>
|
|
131
|
-
<button type="submit">Save credentials</button>
|
|
132
|
-
</form>
|
|
133
|
-
${n}
|
|
134
|
-
${b()}
|
|
135
|
-
${u()}`)}function V(e){return d(`
|
|
136
|
-
${l("Credentials removed")}
|
|
137
|
-
<p>The stored credentials for ${i(k(e))} are gone. Calls to that API are anonymous again until you add new ones.</p>
|
|
138
|
-
${u()}`)}function E(e){return d(`
|
|
139
|
-
${l("Credentials saved")}
|
|
140
|
-
<p>Credentials for ${i(k(e))} are saved. Go back to your AI client and ask it to retry - or confirm the prompt it left open, if there is one. You can close this tab.</p>
|
|
141
|
-
${u()}`)}function y(e){return new Response(d(`
|
|
142
|
-
${l("Exchange failed")}
|
|
143
|
-
<p>We could not exchange your client credentials for a token for ${i(e)}. This link has been used up - go back to your AI client and request a new link, then try again.</p>
|
|
144
|
-
${u()}`),{status:502,headers:p})}function m(){return new Response(d(`${l("Link expired")}<p>This credentials link is invalid, already used, or older than ${Math.round(C/6e4)} minutes. Retry the request in your AI client to get a fresh link.</p>${u()}`),{status:404,headers:p})}var ae=H;export{ae as default,re as setCredentialsFetchImpl};
|
|
1
|
+
import{secretStore as u}from"../gateway-mcp/secret-store.js";import{rejectCredential as k}from"../gateway-mcp/credential-validation.js";import{buildAllowedHostMatchers as P}from"../gateway-mcp/fetch/allowed-hosts.js";import{assertUrlAllowed as A}from"../gateway-mcp/fetch/ssrf-guard.js";import{readCappedBody as R,getDefaultAgent as j}from"../gateway-mcp/fetch/fetch-bridge.js";import{logger as O}from"../../../tools/notifiers/logger.js";import{clearedPage as v,formPage as C,notFoundPage as N,successPage as y}from"./mcp-credentials-page.js";const m={"Content-Type":"text/html; charset=utf-8","Cache-Control":"no-store","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer","Content-Security-Policy":"default-src 'none'; style-src 'unsafe-inline'; form-action 'self'"},I=3e4,D=64*1024;let x=fetch;function Q(e){x=e}const F=async(e,r)=>{const t=await r.getKv();if(e.method==="GET"){const n=new URL(e.url).searchParams.get("token")??"",o=await u.peekElicitation(t,n);return o?new Response(C(o,n),{status:200,headers:m}):f()}if(e.method==="POST"){const n=await e.formData(),o=d(n,"token"),s=d(n,"mode")||"token";return s==="multi"?B(t,n,o):s==="client-credentials"?G(t,n,o):s==="clear"?U(t,o):M(t,n,o)}return new Response("Method Not Allowed",{status:405,headers:{Allow:"GET, POST"}})};async function M(e,r,t){const n=d(r,"secret").trim(),o=await u.peekElicitation(e,t);if(!o)return f();const s=k(o.auth,n);if(s)return p(o,t,s);const l=await u.consumeElicitation(e,t);return l?(await u.setSecret(e,l.subject,l.api,l.scheme,S(l.auth,n)),new Response(y(l),{status:200,headers:m})):f()}async function U(e,r){const t=await u.consumeElicitation(e,r);return t?(await u.deleteSecret(e,t.subject,t.api,t.scheme),new Response(v(t),{status:200,headers:m})):f()}async function B(e,r,t){const n=await u.peekElicitation(e,t);if(!n?.schemes||n.schemes.length<=1)return f();const o=n.schemes,s=[];for(let a=0;a<o.length;a++){const i=o[a];if(i.hasExistingSecret&&d(r,`remove__${a}`)){s.push({kind:"remove",scheme:i.name});continue}const h=d(r,`secret__${a}`).trim();if(i.auth.kind==="oauth2ClientCredentials"){const E=d(r,`client_id__${a}`).trim(),_=d(r,`client_secret__${a}`).trim();if(h){const b=k(i.auth,h);if(b)return p(n,t,`${i.name}: ${b}`);s.push({kind:"set",scheme:i.name,secret:S(i.auth,h)})}else if(E&&_)s.push({kind:"exchange",scheme:i.name,auth:i.auth,clientId:E,clientSecret:_});else if(E||_)return p(n,t,`${i.name}: enter both the client ID and the client secret, or leave both blank.`);continue}if(!h)continue;const w=k(i.auth,h);if(w)return p(n,t,`${i.name}: ${w}`);s.push({kind:"set",scheme:i.name,secret:S(i.auth,h)})}if(s.length===0)return p(n,t,"Fill in at least one credential, or check a box to remove one, before saving.");const l=[];for(const a of s){if(a.kind!=="exchange"){l.push(a);continue}const i=await $(a.auth,a.clientId,a.clientSecret);if(!i.ok)return T(n,t,i.reason,a.scheme);l.push({kind:"set",scheme:a.scheme,secret:i.secret})}const c=await u.consumeElicitation(e,t);if(!c)return f();for(const a of l)a.kind==="remove"?await u.deleteSecret(e,c.subject,c.api,a.scheme):await u.setSecret(e,c.subject,c.api,a.scheme,a.secret);return new Response(y(c),{status:200,headers:m})}async function G(e,r,t){const n=d(r,"client_id").trim(),o=d(r,"client_secret").trim(),s=await u.peekElicitation(e,t);if(!s||s.auth.kind!=="oauth2ClientCredentials")return f();if(!n||!o)return p(s,t,"Enter both the client ID and the client secret before saving.");const l=await $(s.auth,n,o);if(!l.ok)return T(s,t,l.reason);const c=await u.consumeElicitation(e,t);return c?(await u.setSecret(e,c.subject,c.api,c.scheme,l.secret),new Response(y(c),{status:200,headers:m})):f()}async function $(e,r,t){try{const n=P([{url:e.tokenUrl}]),o=A(e.tokenUrl,n),s=new AbortController,l=setTimeout(()=>s.abort(),I);let c;try{c=await x(o,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"client_credentials",client_id:r,client_secret:t}),redirect:"manual",signal:s.signal,dispatcher:j()})}finally{clearTimeout(l)}if(!c.ok)return g(`token endpoint returned ${c.status}`);let a;try{a=await R(c,D)}catch{return g("token response exceeded the size cap")}let i;try{i=JSON.parse(a)}catch{return g("token response was not valid JSON")}const h=typeof i=="object"&&i!==null&&"access_token"in i?i.access_token:void 0,w=typeof h=="string"?h.trim():"";return w?{ok:!0,secret:{header:"Authorization",value:`Bearer ${w}`}}:g("token response had no access_token")}catch(n){return g(n instanceof Error?n.message:String(n))}}function g(e){return{ok:!1,reason:e}}function T(e,r,t,n){return O.warn(`gateway oauth2 client_credentials exchange failed for ${e.api}: ${t}`),p(e,r,`${n?`${n}: we`:"We"} could not exchange those client credentials for a token. Check the client ID and secret, then try again.`)}function d(e,r){const t=e.get(r);return typeof t=="string"?t:""}function p(e,r,t){return new Response(C(e,r,t),{status:400,headers:m})}function f(){return new Response(N(),{status:404,headers:m})}function S(e,r){return e.kind==="apiKey"?{header:e.header,value:r}:e.kind==="cookie"?{header:"Cookie",value:`${e.name}=${r}`}:e.kind==="bearer"||e.kind==="oauth2ClientCredentials"?{header:"Authorization",value:H(r)}:{header:"Authorization",value:r}}function H(e){const r=e.trim();return r.toLowerCase().startsWith("bearer")?r:`Bearer ${r}`}var V=F;export{V as default,Q as setCredentialsFetchImpl};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ElicitationTarget } from '../gateway-mcp/secret-store.js';
|
|
2
|
+
export declare function formPage(target: ElicitationTarget, token: string, error?: string): string;
|
|
3
|
+
export declare function clearedPage(target: ElicitationTarget): string;
|
|
4
|
+
export declare function successPage(target: ElicitationTarget): string;
|
|
5
|
+
export declare function notFoundPage(): string;
|
|
6
|
+
//# sourceMappingURL=mcp-credentials-page.d.ts.map
|