Skip to content

Plugin-sourced HTTP MCP servers with OAuth never trigger authorization prompt #1967

@tmech

Description

@tmech

Summary

HTTP MCP servers defined in marketplace plugins silently fail to authenticate. The OAuth authorization flow (RFC 9728 discovery → browser prompt) never triggers. The same server added manually to ~/.copilot/mcp-config.json works correctly and prompts for authorization.

Reproduction

  1. Create a marketplace plugin with an HTTP MCP server requiring OAuth:

    .github/plugin/marketplace.json:

    {
      "plugins": [{
        "name": "my-plugin",
        "source": "plugins/my-plugin",
        "mcpServers": {
          "my-server": {
            "type": "http",
            "url": "https://my-oauth-protected-mcp-server.example.com",
            "tools": ["*"]
          }
        }
      }]
    }

    The MCP server implements RFC 9728 (/.well-known/oauth-protected-resource) and RFC 8414 (/.well-known/oauth-authorization-server), returning 401 for unauthenticated requests.

  2. Install the plugin via the CLI marketplace.

  3. Start a new session — the plugin's skill loads, but no OAuth prompt appears and no MCP tools are available.

  4. Add the identical server config to ~/.copilot/mcp-config.json — the CLI correctly discovers OAuth metadata and prompts for authorization.

Note: Using an incorrect server URL does produce an error, confirming the plugin MCP config is being read.

Root Cause

In the bundled CLI code (app.js v1.0.4-0), the oj class (MCP host) accepts an onOAuthRequired callback as its 5th constructor parameter. This callback is what triggers the browser-based OAuth flow when a server returns 401.

Workspace MCP host (manual config) — receives all parameters:

new oj(ee, {mcpServers: this.mcpServers ?? {}}, this.excludedTools, this.envValueMode, void 0, this.runtimeSettings)

Plugin MCP host (getOrCreateHost in Qot class) — only passes 2 parameters:

// In Qot.getOrCreateHost():
let o = {mcpServers: n};
let s = new oj(this.logger, o);   // onOAuthRequired is never passed!

Because onOAuthRequired is undefined, the getAuthProvider method returns early:

async getAuthProvider(e, n) {
  if (!this.onOAuthRequired) return;  // ← always exits here for plugin servers
  // ... OAuth flow never reached
}

The 401 is silently swallowed and the server fails to connect without any user-visible error.

Expected Behavior

Plugin-sourced HTTP MCP servers should trigger the same OAuth authorization flow as manually-configured ones. The Qot.getOrCreateHost() method should forward the onOAuthRequired callback to the oj constructor.

Workaround

Add the MCP server manually to ~/.copilot/mcp-config.json alongside the plugin installation:

{
  "mcpServers": {
    "my-server": {
      "type": "http",
      "url": "https://my-oauth-protected-mcp-server.example.com"
    }
  }
}

Authorize it there; the plugin's skills can then use the MCP tools.

Environment

  • CLI Version: 1.0.4-0
  • OS: Windows 11 (ARM64)
  • Plugin format: Marketplace plugin with mcpServers in marketplace.json
  • MCP server transport: HTTP with RFC 9728 OAuth
  • Not affected: stdio MCP servers in plugins (e.g., workiq), manually configured HTTP MCP servers

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions