( config: McpIntegrationConfigType, values: Record<string, string | null>, templateSlug: string | null, allowStdio: boolean, httpClientLayer?: Layer.Layer<HttpClient.HttpClient>, )
| 536 | }; |
| 537 | |
| 538 | const buildConnectorInput = ( |
| 539 | config: McpIntegrationConfigType, |
| 540 | values: Record<string, string | null>, |
| 541 | templateSlug: string | null, |
| 542 | allowStdio: boolean, |
| 543 | httpClientLayer?: Layer.Layer<HttpClient.HttpClient>, |
| 544 | ): Effect.Effect<ConnectorInput, McpConnectionError> => { |
| 545 | if (config.transport === "stdio") { |
| 546 | if (!allowStdio) { |
| 547 | return Effect.fail( |
| 548 | new McpConnectionError({ |
| 549 | transport: "stdio", |
| 550 | message: |
| 551 | "MCP stdio transport is disabled. Enable it by passing `dangerouslyAllowStdioMCP: true` to mcpPlugin() — only safe for trusted local contexts.", |
| 552 | }), |
| 553 | ); |
| 554 | } |
| 555 | // Secret env lives on the connection: render the bound `stdio_env` |
| 556 | // method's vars from the connection's resolved values, layered over any |
| 557 | // static (non-credential / legacy-inline) env in the config. A var that |
| 558 | // resolved to nothing is skipped rather than injected as empty. |
| 559 | const method = selectAuthMethod(config, templateSlug); |
| 560 | const env: Record<string, string> = { ...(config.env ?? {}) }; |
| 561 | if (method?.kind === "stdio_env") { |
| 562 | for (const variable of method.vars) { |
| 563 | const value = values[variable]; |
| 564 | if (value != null) env[variable] = value; |
| 565 | } |
| 566 | } |
| 567 | return Effect.succeed({ |
| 568 | transport: "stdio" as const, |
| 569 | command: config.command, |
| 570 | args: config.args, |
| 571 | env: Object.keys(env).length > 0 ? env : undefined, |
| 572 | cwd: config.cwd, |
| 573 | } satisfies McpStdioIntegrationConfig); |
| 574 | } |
| 575 | |
| 576 | // Credential placements render OVER the integration's static headers / |
| 577 | // query params — a same-named static entry is overwritten. |
| 578 | const headers: Record<string, string> = { ...(config.headers ?? {}) }; |
| 579 | const queryParams: Record<string, string> = { ...(config.queryParams ?? {}) }; |
| 580 | let authProvider: OAuthClientProvider | undefined; |
| 581 | |
| 582 | const auth = selectAuthMethod(config, templateSlug); |
| 583 | if (auth?.kind === "apikey") { |
| 584 | const rendered = renderAuthPlacements(auth.placements, values); |
| 585 | Object.assign(headers, rendered.headers); |
| 586 | Object.assign(queryParams, rendered.queryParams); |
| 587 | } else if (auth?.kind === "oauth2") { |
| 588 | const token = values[TOKEN_VARIABLE]; |
| 589 | if (token != null) authProvider = makeOAuthProvider(token); |
| 590 | } |
| 591 | |
| 592 | return Effect.succeed({ |
| 593 | transport: "remote" as const, |
| 594 | endpoint: config.endpoint, |
| 595 | remoteTransport: config.remoteTransport ?? "auto", |
no test coverage detected