alibaba/open-code-review · error

remote MCP server %q returned HTTP 403 Forbidden — your cred

Error message

remote MCP server %q returned HTTP 403 Forbidden — your credentials may lack required permissions

What it means

The headerTransport wrapping the remote MCP HTTP client converts HTTP 403 responses into this descriptive error (draining and closing the body). Unlike a generic status error, it signals that authentication itself worked but the credential is not permitted to access the MCP server. Raised in RoundTrip for every request routed through the transport.

Source

Thrown at internal/mcp/client.go:149

func (t *headerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	cloned := req.Clone(req.Context())
	for k, v := range t.headers {
		cloned.Header.Set(k, v)
	}
	resp, err := t.base.RoundTrip(cloned)
	if err != nil {
		return nil, err
	}
	switch resp.StatusCode {
	case http.StatusUnauthorized:
		io.Copy(io.Discard, resp.Body)
		resp.Body.Close()
		return nil, fmt.Errorf("remote MCP server %q returned HTTP 401 Unauthorized — check your token/header configuration", t.serverName)
	case http.StatusForbidden:
		io.Copy(io.Discard, resp.Body)
		resp.Body.Close()
		return nil, fmt.Errorf("remote MCP server %q returned HTTP 403 Forbidden — your credentials may lack required permissions", t.serverName)
	}
	return resp, nil
}

func (c *Client) Name() string       { return c.name }
func (c *Client) Tools() []*mcp.Tool { return c.tools }

// CallTool invokes a tool on the MCP server and returns the text result.
func (c *Client) CallTool(ctx context.Context, name string, args map[string]any) (string, error) {
	params := &mcp.CallToolParams{
		Name:      name,
		Arguments: args,
	}

	result, err := c.session.CallTool(ctx, params)
	if err != nil {
		return "", fmt.Errorf("call MCP tool %q: %w", name, err)
	}

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Verify the credential has the scopes/permissions required by the MCP server (regenerate the token with the necessary scopes)
  2. Confirm you are hitting the intended server/environment URL and the account is allowed to use it
  3. Check server-side allowlists (IP allowlist, org membership, plan limits) with the MCP provider
  4. If auth is via custom headers, confirm header names/values match what the server expects — a valid-but-wrong principal can yield 403 rather than 401

Example fix

// before
headers = {"Authorization": "Bearer <staging-token>"}
// after — token regenerated with required scopes for this server
headers = {"Authorization": "Bearer <prod-token-with-scopes>"}
Defensive patterns

Strategy: try-catch

Try / catch

_, err := client.CallTool(ctx, name, args)
if err != nil {
    var httpErr interface{ Error() string }
    if strings.Contains(err.Error(), "HTTP 403 Forbidden") {
        // credential authenticated but lacks permissions: prompt user to
        // regenerate the MCP token with required scopes; do not retry blindly
    }
}

Prevention

When it happens

Trigger: Calling a remote MCP server (ListTools during Connect, or any session request) whose server responds 403 Forbidden — e.g. the configured token/header authenticates but the account lacks scopes/permissions for that server or tool.

Common situations: MCP token issued without the required scopes; server-side allowlist denying the user or IP; expired/downgraded plan on the MCP provider; token for the wrong environment (staging token against prod server).

Understand the failure class

Related errors


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/b30254e3e2363da5. Report an issue: GitHub.