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