Skip to content

feat: opfor setup wizard has no prompt for custom HTTP headers on url-transport MCP targets #166

Description

@arunSunnyKVS

Area

CLI

Problem or motivation

opfor setup's interactive wizard has no prompt for custom HTTP headers (auth tokens, device IDs, etc.) when configuring a url-transport MCP target — even though the underlying config schema already supports this via target.urlHeaders (documented in docs/cli.md).

Looking at collectMcpTarget() in runners/cli/src/commands/setup.ts, the url transport branch only asks for the server URL:

if (transport === "url") {
  const url = await input({ message: "Server URL", ... });
  return { kind: "mcp", name: name.trim(), transport, url: url.trim() };
}

There's no equivalent to the hasEnv/env prompt that the stdio branch gets a few lines later — url targets have no way to collect headers through the wizard at all.

In practice, this means anyone running opfor setup --mcp against a remote MCP server that requires bearer-token or custom-header auth (which is common — many hosted MCP servers gate access this way) gets a config with no urlHeaders field. The first opfor run then fails with an opaque transport-level error (in my case, SSE error: Non-200 status code (401) — the MCP SDK's EventSource client swallows the server's actual JSON error body, so there's no hint in the CLI output that the fix is "add an Authorization header").

The user has no way to know this without already knowing to open docs/cli.md, manually edit the generated JSON, and add urlHeaders by hand — which defeats the point of using the wizard.

Proposed solution

Add a header-collection step to collectMcpTarget()'s url branch, mirroring the existing stdio branch's env-var prompt:

if (transport === "url") {
  const url = await input({ message: "Server URL", ... });

  const hasHeaders = await confirm({
    message: "Set custom HTTP headers (auth tokens, API keys, etc.)?",
    default: false,
  });
  let urlHeaders: Record<string, string> | undefined;
  if (hasHeaders) {
    const headersRaw = await input({
      message: "KEY=VALUE pairs, comma-separated (e.g. Authorization=Bearer ${TOKEN},x-device-id=${DEVICE_ID})",
    });
    urlHeaders = /* same parsing as the existing env-var block */;
  }

  return { kind: "mcp", name: name.trim(), transport, url: url.trim(), urlHeaders };
}

Since ${VAR} expansion is already supported for urlHeaders at run time, the prompt should nudge users toward env-var placeholders rather than raw secrets, the same way the existing docs do.

Separately (smaller, optional): the underlying 401 transport error could be more actionable if the CLI surfaced the server's actual JSON error body instead of just "Non-200 status code (401)" from the MCP SDK's EventSource wrapper — right now there's no signal at all pointing the user toward "add an auth header." Happy to split this into its own issue if preferred; flagging here since it compounds the same discoverability problem.

Alternatives considered

  • Leaving it as documentation-only (already the case in docs/cli.md) — insufficient, since the wizard is the primary onboarding path and most users won't cross-reference the CLI reference doc before their first run.
  • Requiring users to hand-edit the generated config — works, but silently produces a broken config with no indication anything is missing, which is the actual problem being reported.

Would you like to contribute this?

Yes — but I'd need guidance

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions