github/github-mcp-server · error
App not connected
Error message
App not connected
What it means
HTTP 401 'Unauthorized' emitted by the ExtractUserToken middleware (pkg/http/middleware/token.go). It fires when the incoming request has no Authorization header at all (utils.ErrMissingAuthorizationHeader); malformed or unsupported header formats instead yield 400. Per RFC 6750/RFC 9728 and the MCP spec, the response carries 'WWW-Authenticate: Bearer resource_metadata="<url>"' pointing at /.well-known/oauth-protected-resource so OAuth-capable MCP clients can bootstrap token acquisition. This middleware only extracts the token; it does not validate the token's worth, so a present-but-invalid token does not produce this error here.
Source
Thrown at ui/src/hooks/useMcpApp.ts:96
setToolResult(null);
onToolInput?.(args);
};
app.onhostcontextchanged = (params) => {
setHostContext((prev) => ({ ...(prev ?? {}), ...params }));
};
app.onerror = console.error;
},
});
useEffect(() => {
if (!app) return;
const initial = app.getHostContext();
if (initial) setHostContext(initial);
}, [app]);
const callTool = useCallback(
async (name: string, args: Record<string, unknown>) => {
if (!app) throw new Error("App not connected");
return app.callServerTool({ name, arguments: args });
},
[app]
);
const setModelContext = useCallback<UseMcpAppReturn["setModelContext"]>(
async (params) => {
if (!app) return;
await app.updateModelContext(params);
},
[app]
);
const openLink = useCallback<UseMcpAppReturn["openLink"]>(
async (url) => {
if (!app) {
window.open(url, "_blank", "noopener,noreferrer");
return;View on GitHub (pinned to 0ea1f775a7)
Solutions
- If your MCP client supports OAuth, simply let it follow the challenge: read the WWW-Authenticate resource_metadata URL, fetch the protected-resource metadata, and complete the authorization-code flow - the 401 is the expected first step.
- If using a PAT, configure the client to send 'Authorization: Bearer <token>' (or the token type your server accepts) on every request to the MCP endpoint.
- Verify no intermediary strips the header: hit the endpoint with curl -H 'Authorization: Bearer <token>' directly against the server, then repeat through the proxy to isolate where it disappears.
- If the header is present but you still see 401, confirm it reaches this middleware unmodified (check token info is not already in context in remote setups) and that the scheme parses via utils.ParseAuthorizationHeader; unsupported formats produce 400, not this 401.
Example fix
// before (client request with no auth header)
curl -i https://mcp.example.com/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}'
// -> HTTP/1.1 401 Unauthorized
// -> WWW-Authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource/mcp"
// after (supply the bearer token explicitly)
curl -i https://mcp.example.com/mcp \
-H 'Authorization: Bearer ghp_xxx' \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"initialize","id":1}' Defensive patterns
Strategy: validation
Validate before calling
// Go client: attach the bearer header before every MCP request
func withAuth(req *http.Request, token string) error {
if token == "" {
return fmt.Errorf("no token configured: set it before calling the MCP endpoint")
}
req.Header.Set("Authorization", "Bearer "+token)
return nil
}
if err := withAuth(req, cfg.GitHubToken); err != nil {
return err // fail before sending instead of collecting a 401
} Try / catch
// Treat 401 as a discoverable challenge, not a hard failure
resp, err := client.Do(req)
if err != nil { return err }
if resp.StatusCode == http.StatusUnauthorized {
if rm := resp.Header.Get("WWW-Authenticate"); strings.Contains(rm, "resource_metadata=") {
// follow RFC 9728 discovery: GET the resource_metadata URL,
// then the authorization server metadata, then (re)authenticate
}
}
// any other status: handle normally Prevention
- Always configure the token (PAT or OAuth) in the MCP client before the first request; the initial handshake 401 should only ever be seen by OAuth-capable clients.
- Send credentials only via the Authorization header - query-parameter or cookie auth is not recognized by this middleware.
- When deploying behind a proxy, explicitly forward the Authorization header (e.g. proxy_set_header Authorization $http_authorization) and verify with a direct-to-origin curl.
- In integration tests, assert on the WWW-Authenticate resource_metadata URL of the 401 to confirm OAuth discovery wiring instead of treating it as an unexpected failure.
When it happens
Trigger: Any HTTP-mode request to the MCP endpoint that lacks an Authorization header: curl without a header, an MCP client (Claude, IDE plugin, custom SDK session) that has not completed the OAuth flow or was not given a PAT, a client that sends the token in a query param or cookie instead of the header, or a reverse proxy (nginx/ALB) that strips the Authorization header before forwarding to the server.
Common situations: First request of an MCP OAuth handshake (this 401 is by design, not a bug); forgetting to configure the PAT/token in the MCP client; testing the endpoint with plain curl; proxies or service meshes stripping Authorization; a client library that only supports query-parameter auth; local stdio-mode habits carried over to HTTP mode where no env token is attached automatically.
Related errors
- %w: missing required Authorization header
- %w: Authorization header is badly formatted
- Forbidden: insufficient scopes
- bad request: Authorization header is badly formatted
- bad request: unsupported Authorization header
AI-assisted analysis of github/github-mcp-server@0ea1f775a7 (2026-08-15).
Data as JSON: /api/errors/e2c870f0405a606a.
Report an issue: GitHub.