import { Duration, Effect, Exit, Layer, Match, Option, Predicate, Result, Scope, Schema, ScopedCache,} from "effect";import type { HttpClient } from "effect/unstable/http"; import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js";import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js";import * as z from "zod/v4"; import { type CredentialBindingRef, type CredentialBindingValue, ScopeId, SourceDetectionResult, ToolResult, authToolFailure, defaultSourceInstallScopeId, definePlugin, tool, resolveSecretBackedMap as resolveSharedSecretBackedMap, type PluginCtx, type StaticToolSchema, type StorageFailure, StorageError, type ToolAnnotations,} from "@executor-js/sdk/core";import { compileHttpNamedCredentialMap, OAuth2SourceConfig, httpCredentialInputToBindingValue, type HttpConfiguredValueInput,} from "@executor-js/sdk/http-source"; import { makeMcpStore, mcpSchema, type McpBindingStore, type McpStoredSource,} from "./binding-store";import { createMcpConnector, type ConnectorInput, type McpConnection } from "./connection";import { discoverTools } from "./discover";import { McpAuthRequiredError, McpConnectionError, McpInvocationError, McpToolDiscoveryError,} from "./errors";import { invokeMcpTool } from "./invoke";import { deriveMcpNamespace, type McpToolManifest, type McpToolManifestEntry } from "./manifest";import { mcpPresets } from "./presets";import { probeMcpEndpointShape, type McpShapeProbeResult } from "./probe-shape";import { MCP_OAUTH_CLIENT_ID_SLOT, MCP_OAUTH_CLIENT_SECRET_SLOT, MCP_OAUTH_CONNECTION_SLOT, McpConnectionAuthInput, McpConfiguredValueInput, McpCredentialInput, McpRemoteTransport, McpToolBinding, mcpHeaderSlot, mcpQueryParamSlot, type McpConnectionAuth, type McpConfiguredValueInput as McpConfiguredValueInputType, SecretBackedValue, type McpStoredSourceData, type ConfiguredMcpCredentialValue,} from "./types"; import { type ConfigFileSink, type ConfigHeaderValue, type McpAuthConfig, type McpRemoteSourceConfig as McpRemoteConfigEntry, type McpStdioSourceConfig as McpStdioConfigEntry, type SourceConfig, headerToConfigValue,} from "@executor-js/config"; // ---------------------------------------------------------------------------// Plugin config — discriminated union on transport// --------------------------------------------------------------------------- /** * Executor scope id that owns a newly-added MCP source row. Must be one * of the executor's configured scopes. Admins adding a shared server at * org scope pin here; per-user stdio sources can pin at the inner * scope. */type McpSourceScopeField = { readonly scope: string }; export interface McpRemoteSourceConfig extends McpSourceScopeField { readonly transport: "remote"; readonly name: string; readonly endpoint: string; readonly remoteTransport?: "streamable-http" | "sse" | "auto"; readonly queryParams?: Record<string, McpConfiguredValueInputType>; readonly headers?: Record<string, McpConfiguredValueInputType>; readonly namespace?: string; readonly oauth2?: OAuth2SourceConfig; readonly credentials?: McpInitialCredentialsInput;} export interface McpStdioSourceConfig extends McpSourceScopeField { readonly transport: "stdio"; readonly name: string; readonly command: string; readonly args?: string[]; readonly env?: Record<string, string>; readonly cwd?: string; readonly namespace?: string;} export type McpSourceConfig = McpRemoteSourceConfig | McpStdioSourceConfig;type McpConfigFileRemoteSourceConfig = Omit< McpRemoteSourceConfig, "headers" | "queryParams" | "oauth2"> & { readonly headers?: Record<string, McpCredentialInput | McpConfiguredValueInputType>; readonly queryParams?: Record<string, McpCredentialInput | McpConfiguredValueInputType>; readonly auth?: McpConnectionAuthInput;};type McpConfigFileSourceConfig = McpConfigFileRemoteSourceConfig | McpStdioSourceConfig; // ---------------------------------------------------------------------------// Extension types// --------------------------------------------------------------------------- // OAuth start/complete/callback moved to the shared// `/scopes/:scopeId/oauth/*` surface in `@executor-js/api` — no// plugin-specific types needed here. export interface McpProbeResult { readonly connected: boolean; readonly requiresOAuth: boolean; readonly supportsDynamicRegistration: boolean; readonly name: string; readonly namespace: string; readonly toolCount: number | null; readonly serverName: string | null;} const McpConfigureSourcePayloadSchema = Schema.Struct({ name: Schema.optional(Schema.String), endpoint: Schema.optional(Schema.String), headers: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), queryParams: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), auth: Schema.optional(McpConnectionAuthInput),});const McpConfigureSourceInputSchema = Schema.Struct({ scope: Schema.String, ...McpConfigureSourcePayloadSchema.fields,});export type McpConfigureSourceInput = typeof McpConfigureSourceInputSchema.Type; const McpInitialCredentialsInputSchema = Schema.Struct({ scope: Schema.String, headers: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), queryParams: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), auth: Schema.optional(McpConnectionAuthInput),});type McpInitialCredentialsInput = typeof McpInitialCredentialsInputSchema.Type; const McpRemoteAddSourceInputSchema = Schema.Struct({ transport: Schema.Literal("remote"), name: Schema.String, endpoint: Schema.String, remoteTransport: Schema.optional(McpRemoteTransport), queryParams: Schema.optional(Schema.Record(Schema.String, McpConfiguredValueInput)), headers: Schema.optional(Schema.Record(Schema.String, McpConfiguredValueInput)), namespace: Schema.optional(Schema.String), oauth2: Schema.optional(OAuth2SourceConfig), credentials: Schema.optional(McpInitialCredentialsInputSchema),}); const McpStdioAddSourceInputSchema = Schema.Struct({ transport: Schema.Literal("stdio"), name: Schema.String, command: Schema.String, args: Schema.optional(Schema.Array(Schema.String)), env: Schema.optional(Schema.Record(Schema.String, Schema.String)), cwd: Schema.optional(Schema.String), namespace: Schema.optional(Schema.String),}); const McpAddSourceInputSchema = Schema.Union([ McpRemoteAddSourceInputSchema, McpStdioAddSourceInputSchema,]); const McpAddSourceOutputSchema = Schema.Struct({ namespace: Schema.String, source: Schema.Struct({ id: Schema.String, scope: Schema.String, }), toolCount: Schema.Number, discovery: Schema.optional( Schema.Struct({ status: Schema.Literals(["ok", "failed"]), message: Schema.optional(Schema.String), stage: Schema.optional(Schema.String), }), ),}); const McpProbeEndpointInputSchema = Schema.Struct({ endpoint: Schema.String, headers: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)), queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)),}); const McpProbeEndpointOutputSchema = Schema.Struct({ connected: Schema.Boolean, requiresOAuth: Schema.Boolean, supportsDynamicRegistration: Schema.Boolean, name: Schema.String, namespace: Schema.String, toolCount: Schema.NullOr(Schema.Number), serverName: Schema.NullOr(Schema.String),}); const McpGetSourceInputSchema = Schema.Struct({ namespace: Schema.String, scope: Schema.String,}); const McpGetSourceOutputSchema = Schema.Struct({ source: Schema.NullOr(Schema.Unknown),}); const McpStaticConfigureSourceInputSchema = Schema.Struct({ source: Schema.Struct({ id: Schema.String, scope: Schema.String, }), scope: Schema.String, ...McpConfigureSourcePayloadSchema.fields,}); const McpStaticConfigureSourceOutputSchema = Schema.Struct({ configured: Schema.Boolean,}); const schemaToStaticToolSchema = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> => Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema< A, I >; const mcpToolFailure = (code: string, message: string, details?: unknown) => ToolResult.fail({ code, message, ...(details === undefined ? {} : { details }), }); const mcpAuthToolFailure = (failure: McpAuthRequiredError) => authToolFailure({ code: failure.code, message: failure.message, source: { id: failure.sourceId, scope: failure.sourceScope }, credential: { kind: failure.credentialKind, ...(failure.credentialLabel ? { label: failure.credentialLabel } : {}), ...(failure.slotKey ? { slotKey: failure.slotKey } : {}), ...(failure.secretId ? { secretId: failure.secretId } : {}), ...(failure.connectionId ? { connectionId: failure.connectionId } : {}), }, ...(failure.status !== undefined ? { status: failure.status } : {}), ...(failure.details !== undefined ? { upstream: { ...(failure.status !== undefined ? { status: failure.status } : {}), details: failure.details, }, } : {}), recovery: { configureSourceTool: "executor.mcp.configureSource" }, }); const McpAddSourceInputStandardSchema = schemaToStaticToolSchema(McpAddSourceInputSchema);const McpAddSourceOutputStandardSchema = schemaToStaticToolSchema(McpAddSourceOutputSchema);const McpProbeEndpointInputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointInputSchema);const McpProbeEndpointOutputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointOutputSchema);const McpGetSourceInputStandardSchema = schemaToStaticToolSchema(McpGetSourceInputSchema);const McpGetSourceOutputStandardSchema = schemaToStaticToolSchema(McpGetSourceOutputSchema);const McpStaticConfigureSourceInputStandardSchema = schemaToStaticToolSchema( McpStaticConfigureSourceInputSchema,);const McpStaticConfigureSourceOutputStandardSchema = schemaToStaticToolSchema( McpStaticConfigureSourceOutputSchema,); export type McpProbeEndpointInput = typeof McpProbeEndpointInputSchema.Type; const resolveStaticScopeInput = ( ctx: { readonly scopes: readonly { readonly id: ScopeId; readonly name: string }[] }, value: string,): string => String( ctx.scopes.find((scope) => scope.name === value || String(scope.id) === value)?.id ?? value, ); // ---------------------------------------------------------------------------// Helpers// --------------------------------------------------------------------------- const toStoredSourceData = ( config: McpSourceConfig, remoteCredentials?: { readonly headers: Record<string, ConfiguredMcpCredentialValue>; readonly queryParams: Record<string, ConfiguredMcpCredentialValue>; readonly auth: McpConnectionAuth; },): McpStoredSourceData => { if (config.transport === "stdio") { return { transport: "stdio", command: config.command, args: config.args, env: config.env, cwd: config.cwd, }; } return { transport: "remote", endpoint: config.endpoint, remoteTransport: config.remoteTransport ?? "auto", queryParams: remoteCredentials?.queryParams, headers: remoteCredentials?.headers, auth: remoteCredentials?.auth ?? { kind: "none" }, };}; const normalizeNamespace = (config: McpSourceConfig): string => config.namespace ?? deriveMcpNamespace({ name: config.name, endpoint: config.transport === "remote" ? config.endpoint : undefined, command: config.transport === "stdio" ? config.command : undefined, }); type JsonSchemaObject = Record<string, unknown> & { readonly properties?: Record<string, unknown>;}; const McpCallToolResultJsonSchema = z.toJSONSchema(CallToolResultSchema) as JsonSchemaObject; const mcpCallToolResultOutputSchema = (structuredContentSchema?: unknown): JsonSchemaObject => { const defaultStructuredContentSchema = McpCallToolResultJsonSchema.properties?.structuredContent ?? {}; return { ...McpCallToolResultJsonSchema, properties: { ...McpCallToolResultJsonSchema.properties, structuredContent: structuredContentSchema === undefined ? defaultStructuredContentSchema : structuredContentSchema, isError: { const: false }, }, required: structuredContentSchema === undefined ? ["content"] : ["content", "structuredContent"], };}; const toBinding = (entry: McpToolManifestEntry): McpToolBinding => McpToolBinding.make({ toolId: entry.toolId, toolName: entry.toolName, description: entry.description, inputSchema: entry.inputSchema, outputSchema: entry.outputSchema, annotations: entry.annotations, }); const MCP_PLUGIN_ID = "mcp";const McpTextContent = Schema.Struct({ type: Schema.Literal("text"), text: Schema.String });const McpToolCallEnvelope = Schema.Struct({ isError: Schema.optional(Schema.Boolean), content: Schema.optional(Schema.Array(Schema.Unknown)),}); const decodeMcpTextContent = Schema.decodeUnknownOption(McpTextContent);const decodeMcpToolCallEnvelope = Schema.decodeUnknownOption(McpToolCallEnvelope); const extractMcpErrorMessage = (content: unknown): string => { if (Array.isArray(content)) { for (const item of content) { const decoded = Option.getOrUndefined(decodeMcpTextContent(item)); if (decoded !== undefined && decoded.text.length > 0) return decoded.text; } } return "MCP tool returned an error";}; /** Match `token` as a separator-bounded run inside a URL hostname or path, * used as a low-confidence detection hint when wire-shape detection fails. * Boundary chars are everything non-alphanumeric, so `/api/mcp`, * `mcp.example.com`, `mcp-server`, and `mcp_v1` all match while * `mcphost.com` and `/mcpstore` do not. */const urlMatchesToken = (url: URL, token: string): boolean => { const re = new RegExp(`(?:^|[^a-z0-9])${token}(?:$|[^a-z0-9])`, "i"); return re.test(url.hostname) || re.test(url.pathname);}; /** Translate a non-MCP probe outcome into a message a user can act on. * The technical `reason` (`401 without Bearer WWW-Authenticate — not an * MCP auth challenge`, etc.) stays in telemetry via the probe span; the * user gets a sentence pointing at their next step. Exported for tests. */export const userFacingProbeMessage = ( shape: Extract<McpShapeProbeResult, { kind: "not-mcp" } | { kind: "unreachable" }>,): string => { if (shape.kind === "unreachable") { return "Couldn't reach this URL. Check the address, your network, and that the server is running."; } return Match.value(shape.category).pipe( Match.when( "auth-required", () => "This server requires authentication. Add credentials (Authorization header, query parameter, or API key) below and retry.", ), Match.when( "wrong-shape", () => "This URL doesn't appear to host an MCP server. Double-check the address, including the path.", ), Match.exhaustive, );}; const scopeRanks = (ctx: PluginCtx<McpBindingStore>): ReadonlyMap<string, number> => new Map(ctx.scopes.map((scope, index) => [String(scope.id), index])); const scopeRank = (ranks: ReadonlyMap<string, number>, scopeId: string): number => ranks.get(scopeId) ?? Infinity; const resolveMcpSourceBinding = ( ctx: PluginCtx<McpBindingStore>, sourceId: string, sourceScope: string, slot: string,): Effect.Effect<CredentialBindingRef | null, StorageFailure> => Effect.gen(function* () { const ranks = scopeRanks(ctx); const sourceSourceRank = scopeRank(ranks, sourceScope); if (sourceSourceRank === Infinity) return null; const bindings = yield* ctx.credentialBindings.listForSource({ pluginId: MCP_PLUGIN_ID, sourceId, sourceScope: ScopeId.make(sourceScope), }); const binding = bindings .filter( (candidate) => candidate.slotKey === slot && scopeRank(ranks, candidate.scopeId) <= sourceSourceRank, ) .sort((a, b) => scopeRank(ranks, a.scopeId) - scopeRank(ranks, b.scopeId))[0]; return binding ?? null; }); const validateMcpBindingTarget = ( ctx: PluginCtx<McpBindingStore>, input: { readonly sourceScope: string; readonly targetScope: string; readonly sourceId: string; },): Effect.Effect<void, StorageFailure> => Effect.gen(function* () { const ranks = scopeRanks(ctx); const sourceSourceRank = scopeRank(ranks, input.sourceScope); const targetRank = scopeRank(ranks, input.targetScope); const scopeList = `[${ctx.scopes.map((s) => s.id).join(", ")}]`; if (sourceSourceRank === Infinity) { return yield* new StorageError({ message: `MCP source binding references source scope "${input.sourceScope}" ` + `which is not in the executor's scope stack ${scopeList}.`, cause: undefined, }); } if (targetRank === Infinity) { return yield* new StorageError({ message: `MCP source binding targets scope "${input.targetScope}" which is not ` + `in the executor's scope stack ${scopeList}.`, cause: undefined,Lines 1–500 of 2358. Load more
import { Duration, Effect, Exit, Layer, Match, Option, Predicate, Result, Scope, Schema, ScopedCache, } from "effect"; import type { HttpClient } from "effect/unstable/http"; import type { OAuthClientProvider } from "@modelcontextprotocol/sdk/client/auth.js"; import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js"; import * as z from "zod/v4"; import { type CredentialBindingRef, type CredentialBindingValue, ScopeId, SourceDetectionResult, ToolResult, authToolFailure, defaultSourceInstallScopeId, definePlugin, tool, resolveSecretBackedMap as resolveSharedSecretBackedMap, type PluginCtx, type StaticToolSchema, type StorageFailure, StorageError, type ToolAnnotations, } from "@executor-js/sdk/core"; import { compileHttpNamedCredentialMap, OAuth2SourceConfig, httpCredentialInputToBindingValue, type HttpConfiguredValueInput, } from "@executor-js/sdk/http-source"; import { makeMcpStore, mcpSchema, type McpBindingStore, type McpStoredSource, } from "./binding-store"; import { createMcpConnector, type ConnectorInput, type McpConnection } from "./connection"; import { discoverTools } from "./discover"; import { McpAuthRequiredError, McpConnectionError, McpInvocationError, McpToolDiscoveryError, } from "./errors"; import { invokeMcpTool } from "./invoke"; import { deriveMcpNamespace, type McpToolManifest, type McpToolManifestEntry } from "./manifest"; import { mcpPresets } from "./presets"; import { probeMcpEndpointShape, type McpShapeProbeResult } from "./probe-shape"; import { MCP_OAUTH_CLIENT_ID_SLOT, MCP_OAUTH_CLIENT_SECRET_SLOT, MCP_OAUTH_CONNECTION_SLOT, McpConnectionAuthInput, McpConfiguredValueInput, McpCredentialInput, McpRemoteTransport, McpToolBinding, mcpHeaderSlot, mcpQueryParamSlot, type McpConnectionAuth, type McpConfiguredValueInput as McpConfiguredValueInputType, SecretBackedValue, type McpStoredSourceData, type ConfiguredMcpCredentialValue, } from "./types"; import { type ConfigFileSink, type ConfigHeaderValue, type McpAuthConfig, type McpRemoteSourceConfig as McpRemoteConfigEntry, type McpStdioSourceConfig as McpStdioConfigEntry, type SourceConfig, headerToConfigValue, } from "@executor-js/config"; // --------------------------------------------------------------------------- // Plugin config — discriminated union on transport // --------------------------------------------------------------------------- /** * Executor scope id that owns a newly-added MCP source row. Must be one * of the executor's configured scopes. Admins adding a shared server at * org scope pin here; per-user stdio sources can pin at the inner * scope. */ type McpSourceScopeField = { readonly scope: string }; export interface McpRemoteSourceConfig extends McpSourceScopeField { readonly transport: "remote"; readonly name: string; readonly endpoint: string; readonly remoteTransport?: "streamable-http" | "sse" | "auto"; readonly queryParams?: Record<string, McpConfiguredValueInputType>; readonly headers?: Record<string, McpConfiguredValueInputType>; readonly namespace?: string; readonly oauth2?: OAuth2SourceConfig; readonly credentials?: McpInitialCredentialsInput; } export interface McpStdioSourceConfig extends McpSourceScopeField { readonly transport: "stdio"; readonly name: string; readonly command: string; readonly args?: string[]; readonly env?: Record<string, string>; readonly cwd?: string; readonly namespace?: string; } export type McpSourceConfig = McpRemoteSourceConfig | McpStdioSourceConfig; type McpConfigFileRemoteSourceConfig = Omit< McpRemoteSourceConfig, "headers" | "queryParams" | "oauth2" > & { readonly headers?: Record<string, McpCredentialInput | McpConfiguredValueInputType>; readonly queryParams?: Record<string, McpCredentialInput | McpConfiguredValueInputType>; readonly auth?: McpConnectionAuthInput; }; type McpConfigFileSourceConfig = McpConfigFileRemoteSourceConfig | McpStdioSourceConfig; // --------------------------------------------------------------------------- // Extension types // --------------------------------------------------------------------------- // OAuth start/complete/callback moved to the shared // `/scopes/:scopeId/oauth/*` surface in `@executor-js/api` — no // plugin-specific types needed here. export interface McpProbeResult { readonly connected: boolean; readonly requiresOAuth: boolean; readonly supportsDynamicRegistration: boolean; readonly name: string; readonly namespace: string; readonly toolCount: number | null; readonly serverName: string | null; } const McpConfigureSourcePayloadSchema = Schema.Struct({ name: Schema.optional(Schema.String), endpoint: Schema.optional(Schema.String), headers: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), queryParams: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), auth: Schema.optional(McpConnectionAuthInput), }); const McpConfigureSourceInputSchema = Schema.Struct({ scope: Schema.String, ...McpConfigureSourcePayloadSchema.fields, }); export type McpConfigureSourceInput = typeof McpConfigureSourceInputSchema.Type; const McpInitialCredentialsInputSchema = Schema.Struct({ scope: Schema.String, headers: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), queryParams: Schema.optional(Schema.Record(Schema.String, McpCredentialInput)), auth: Schema.optional(McpConnectionAuthInput), }); type McpInitialCredentialsInput = typeof McpInitialCredentialsInputSchema.Type; const McpRemoteAddSourceInputSchema = Schema.Struct({ transport: Schema.Literal("remote"), name: Schema.String, endpoint: Schema.String, remoteTransport: Schema.optional(McpRemoteTransport), queryParams: Schema.optional(Schema.Record(Schema.String, McpConfiguredValueInput)), headers: Schema.optional(Schema.Record(Schema.String, McpConfiguredValueInput)), namespace: Schema.optional(Schema.String), oauth2: Schema.optional(OAuth2SourceConfig), credentials: Schema.optional(McpInitialCredentialsInputSchema), }); const McpStdioAddSourceInputSchema = Schema.Struct({ transport: Schema.Literal("stdio"), name: Schema.String, command: Schema.String, args: Schema.optional(Schema.Array(Schema.String)), env: Schema.optional(Schema.Record(Schema.String, Schema.String)), cwd: Schema.optional(Schema.String), namespace: Schema.optional(Schema.String), }); const McpAddSourceInputSchema = Schema.Union([ McpRemoteAddSourceInputSchema, McpStdioAddSourceInputSchema, ]); const McpAddSourceOutputSchema = Schema.Struct({ namespace: Schema.String, source: Schema.Struct({ id: Schema.String, scope: Schema.String, }), toolCount: Schema.Number, discovery: Schema.optional( Schema.Struct({ status: Schema.Literals(["ok", "failed"]), message: Schema.optional(Schema.String), stage: Schema.optional(Schema.String), }), ), }); const McpProbeEndpointInputSchema = Schema.Struct({ endpoint: Schema.String, headers: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)), queryParams: Schema.optional(Schema.Record(Schema.String, SecretBackedValue)), }); const McpProbeEndpointOutputSchema = Schema.Struct({ connected: Schema.Boolean, requiresOAuth: Schema.Boolean, supportsDynamicRegistration: Schema.Boolean, name: Schema.String, namespace: Schema.String, toolCount: Schema.NullOr(Schema.Number), serverName: Schema.NullOr(Schema.String), }); const McpGetSourceInputSchema = Schema.Struct({ namespace: Schema.String, scope: Schema.String, }); const McpGetSourceOutputSchema = Schema.Struct({ source: Schema.NullOr(Schema.Unknown), }); const McpStaticConfigureSourceInputSchema = Schema.Struct({ source: Schema.Struct({ id: Schema.String, scope: Schema.String, }), scope: Schema.String, ...McpConfigureSourcePayloadSchema.fields, }); const McpStaticConfigureSourceOutputSchema = Schema.Struct({ configured: Schema.Boolean, }); const schemaToStaticToolSchema = <A, I>(schema: Schema.Decoder<A, I>): StaticToolSchema<A, I> => Schema.toStandardSchemaV1(Schema.toStandardJSONSchemaV1(schema) as never) as StaticToolSchema< A, I >; const mcpToolFailure = (code: string, message: string, details?: unknown) => ToolResult.fail({ code, message, ...(details === undefined ? {} : { details }), }); const mcpAuthToolFailure = (failure: McpAuthRequiredError) => authToolFailure({ code: failure.code, message: failure.message, source: { id: failure.sourceId, scope: failure.sourceScope }, credential: { kind: failure.credentialKind, ...(failure.credentialLabel ? { label: failure.credentialLabel } : {}), ...(failure.slotKey ? { slotKey: failure.slotKey } : {}), ...(failure.secretId ? { secretId: failure.secretId } : {}), ...(failure.connectionId ? { connectionId: failure.connectionId } : {}), }, ...(failure.status !== undefined ? { status: failure.status } : {}), ...(failure.details !== undefined ? { upstream: { ...(failure.status !== undefined ? { status: failure.status } : {}), details: failure.details, }, } : {}), recovery: { configureSourceTool: "executor.mcp.configureSource" }, }); const McpAddSourceInputStandardSchema = schemaToStaticToolSchema(McpAddSourceInputSchema); const McpAddSourceOutputStandardSchema = schemaToStaticToolSchema(McpAddSourceOutputSchema); const McpProbeEndpointInputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointInputSchema); const McpProbeEndpointOutputStandardSchema = schemaToStaticToolSchema(McpProbeEndpointOutputSchema); const McpGetSourceInputStandardSchema = schemaToStaticToolSchema(McpGetSourceInputSchema); const McpGetSourceOutputStandardSchema = schemaToStaticToolSchema(McpGetSourceOutputSchema); const McpStaticConfigureSourceInputStandardSchema = schemaToStaticToolSchema( McpStaticConfigureSourceInputSchema, ); const McpStaticConfigureSourceOutputStandardSchema = schemaToStaticToolSchema( McpStaticConfigureSourceOutputSchema, ); export type McpProbeEndpointInput = typeof McpProbeEndpointInputSchema.Type; const resolveStaticScopeInput = ( ctx: { readonly scopes: readonly { readonly id: ScopeId; readonly name: string }[] }, value: string, ): string => String( ctx.scopes.find((scope) => scope.name === value || String(scope.id) === value)?.id ?? value, ); // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const toStoredSourceData = ( config: McpSourceConfig, remoteCredentials?: { readonly headers: Record<string, ConfiguredMcpCredentialValue>; readonly queryParams: Record<string, ConfiguredMcpCredentialValue>; readonly auth: McpConnectionAuth; }, ): McpStoredSourceData => { if (config.transport === "stdio") { return { transport: "stdio", command: config.command, args: config.args, env: config.env, cwd: config.cwd, }; } return { transport: "remote", endpoint: config.endpoint, remoteTransport: config.remoteTransport ?? "auto", queryParams: remoteCredentials?.queryParams, headers: remoteCredentials?.headers, auth: remoteCredentials?.auth ?? { kind: "none" }, }; }; const normalizeNamespace = (config: McpSourceConfig): string => config.namespace ?? deriveMcpNamespace({ name: config.name, endpoint: config.transport === "remote" ? config.endpoint : undefined, command: config.transport === "stdio" ? config.command : undefined, }); type JsonSchemaObject = Record<string, unknown> & { readonly properties?: Record<string, unknown>; }; const McpCallToolResultJsonSchema = z.toJSONSchema(CallToolResultSchema) as JsonSchemaObject; const mcpCallToolResultOutputSchema = (structuredContentSchema?: unknown): JsonSchemaObject => { const defaultStructuredContentSchema = McpCallToolResultJsonSchema.properties?.structuredContent ?? {}; return { ...McpCallToolResultJsonSchema, properties: { ...McpCallToolResultJsonSchema.properties, structuredContent: structuredContentSchema === undefined ? defaultStructuredContentSchema : structuredContentSchema, isError: { const: false }, }, required: structuredContentSchema === undefined ? ["content"] : ["content", "structuredContent"], }; }; const toBinding = (entry: McpToolManifestEntry): McpToolBinding => McpToolBinding.make({ toolId: entry.toolId, toolName: entry.toolName, description: entry.description, inputSchema: entry.inputSchema, outputSchema: entry.outputSchema, annotations: entry.annotations, }); const MCP_PLUGIN_ID = "mcp"; const McpTextContent = Schema.Struct({ type: Schema.Literal("text"), text: Schema.String }); const McpToolCallEnvelope = Schema.Struct({ isError: Schema.optional(Schema.Boolean), content: Schema.optional(Schema.Array(Schema.Unknown)), }); const decodeMcpTextContent = Schema.decodeUnknownOption(McpTextContent); const decodeMcpToolCallEnvelope = Schema.decodeUnknownOption(McpToolCallEnvelope); const extractMcpErrorMessage = (content: unknown): string => { if (Array.isArray(content)) { for (const item of content) { const decoded = Option.getOrUndefined(decodeMcpTextContent(item)); if (decoded !== undefined && decoded.text.length > 0) return decoded.text; } } return "MCP tool returned an error"; }; /** Match `token` as a separator-bounded run inside a URL hostname or path, * used as a low-confidence detection hint when wire-shape detection fails. * Boundary chars are everything non-alphanumeric, so `/api/mcp`, * `mcp.example.com`, `mcp-server`, and `mcp_v1` all match while * `mcphost.com` and `/mcpstore` do not. */ const urlMatchesToken = (url: URL, token: string): boolean => { const re = new RegExp(`(?:^|[^a-z0-9])${token}(?:$|[^a-z0-9])`, "i"); return re.test(url.hostname) || re.test(url.pathname); }; /** Translate a non-MCP probe outcome into a message a user can act on. * The technical `reason` (`401 without Bearer WWW-Authenticate — not an * MCP auth challenge`, etc.) stays in telemetry via the probe span; the * user gets a sentence pointing at their next step. Exported for tests. */ export const userFacingProbeMessage = ( shape: Extract<McpShapeProbeResult, { kind: "not-mcp" } | { kind: "unreachable" }>, ): string => { if (shape.kind === "unreachable") { return "Couldn't reach this URL. Check the address, your network, and that the server is running."; } return Match.value(shape.category).pipe( Match.when( "auth-required", () => "This server requires authentication. Add credentials (Authorization header, query parameter, or API key) below and retry.", ), Match.when( "wrong-shape", () => "This URL doesn't appear to host an MCP server. Double-check the address, including the path.", ), Match.exhaustive, ); }; const scopeRanks = (ctx: PluginCtx<McpBindingStore>): ReadonlyMap<string, number> => new Map(ctx.scopes.map((scope, index) => [String(scope.id), index])); const scopeRank = (ranks: ReadonlyMap<string, number>, scopeId: string): number => ranks.get(scopeId) ?? Infinity; const resolveMcpSourceBinding = ( ctx: PluginCtx<McpBindingStore>, sourceId: string, sourceScope: string, slot: string, ): Effect.Effect<CredentialBindingRef | null, StorageFailure> => Effect.gen(function* () { const ranks = scopeRanks(ctx); const sourceSourceRank = scopeRank(ranks, sourceScope); if (sourceSourceRank === Infinity) return null; const bindings = yield* ctx.credentialBindings.listForSource({ pluginId: MCP_PLUGIN_ID, sourceId, sourceScope: ScopeId.make(sourceScope), }); const binding = bindings .filter( (candidate) => candidate.slotKey === slot && scopeRank(ranks, candidate.scopeId) <= sourceSourceRank, ) .sort((a, b) => scopeRank(ranks, a.scopeId) - scopeRank(ranks, b.scopeId))[0]; return binding ?? null; }); const validateMcpBindingTarget = ( ctx: PluginCtx<McpBindingStore>, input: { readonly sourceScope: string; readonly targetScope: string; readonly sourceId: string; }, ): Effect.Effect<void, StorageFailure> => Effect.gen(function* () { const ranks = scopeRanks(ctx); const sourceSourceRank = scopeRank(ranks, input.sourceScope); const targetRank = scopeRank(ranks, input.targetScope); const scopeList = `[${ctx.scopes.map((s) => s.id).join(", ")}]`; if (sourceSourceRank === Infinity) { return yield* new StorageError({ message: `MCP source binding references source scope "${input.sourceScope}" ` + `which is not in the executor's scope stack ${scopeList}.`, cause: undefined, }); } if (targetRank === Infinity) { return yield* new StorageError({ message: `MCP source binding targets scope "${input.targetScope}" which is not ` + `in the executor's scope stack ${scopeList}.`, cause: undefined,