{"record":{"id":"a81ffa0edbc6784c","repo":"micro/go-micro","slug":"missing-token","errorCode":null,"errorMessage":"missing token","messagePattern":"missing token","errorType":"exception","errorClass":null,"httpStatus":401,"severity":"error","filePath":"gateway/mcp/mcp.go","lineNumber":786,"sourceCode":"\t\t\treturn nil, \"\", false, errResponseWritten\n\t\t}\n\t}\n\n\t// Generate trace ID for this call\n\ttraceID := uuid.New().String()\n\n\t// Start OTel span (noop if TraceProvider is nil)\n\tctx, span := s.startToolSpan(r.Context(), toolName, \"http\", traceID)\n\tdefer span.End()\n\n\t// Authenticate and authorize\n\tvar account *auth.Account\n\tif s.opts.Auth != nil {\n\t\ttoken := r.Header.Get(\"Authorization\")\n\t\ttoken = strings.TrimPrefix(token, \"Bearer \")\n\t\tif token == \"\" {\n\t\t\tspan.SetAttributes(attribute.Bool(AttrAuthAllowed, false), attribute.String(AttrAuthDeniedReason, \"missing token\"))\n\t\t\tsetSpanError(span, fmt.Errorf(\"missing token\"))\n\t\t\ts.audit(AuditRecord{TraceID: traceID, Timestamp: time.Now(), Tool: toolName, Allowed: false, DeniedReason: \"missing token\"})\n\t\t\treturn nil, traceID, false, &toolError{status: http.StatusUnauthorized, message: \"Unauthorized\"}\n\t\t}\n\t\tacc, err := s.opts.Auth.Inspect(token)\n\t\tif err != nil {\n\t\t\tspan.SetAttributes(attribute.Bool(AttrAuthAllowed, false), attribute.String(AttrAuthDeniedReason, \"invalid token\"))\n\t\t\tsetSpanError(span, fmt.Errorf(\"invalid token\"))\n\t\t\ts.audit(AuditRecord{TraceID: traceID, Timestamp: time.Now(), Tool: toolName, Allowed: false, DeniedReason: \"invalid token\"})\n\t\t\treturn nil, traceID, false, &toolError{status: http.StatusUnauthorized, message: \"Unauthorized\"}\n\t\t}\n\t\taccount = acc\n\t\tspan.SetAttributes(attribute.String(AttrAccountID, account.ID))\n\n\t\t// Check per-tool scopes\n\t\tif len(tool.Scopes) > 0 {\n\t\t\tspan.SetAttributes(attribute.StringSlice(AttrScopesRequired, tool.Scopes))\n\t\t\tif !hasScope(account.Scopes, tool.Scopes) {\n\t\t\t\tspan.SetAttributes(attribute.Bool(AttrAuthAllowed, false), attribute.String(AttrAuthDeniedReason, \"insufficient scopes\"))","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/gateway/mcp/mcp.go#L768-L804","documentation":"When Options.Auth is configured, invokeTool requires every incoming HTTP request to carry a Bearer token in the Authorization header. A request with no token is rejected with HTTP 401 Unauthorized, an OTel span attribute AttrAuthDeniedReason=\"missing token\", and an audit record with Allowed=false.","triggerScenarios":"A client calls an MCP tool over the SSE/HTTP transport while Auth is set, but sends no Authorization header or sends \"Authorization: Bearer\" with an empty token — invokeTool returns the 401 toolError before Inspect is called.","commonSituations":"Client not configured to attach credentials; a proxy or gateway strips the Authorization header; frontend calls the MCP endpoint without logging in; curl tests omit -H \"Authorization: Bearer <token>\"; requests made from server-side code that never forwarded the user token.","solutions":["Send \"Authorization: Bearer <valid-token>\" on every request to the MCP endpoint.","Verify no reverse proxy/ingress strips the Authorization header before it reaches the gateway.","Obtain a token first from your auth provider (login/service account) and refresh it when expired.","If the endpoint should be public, construct the server with Auth nil so token enforcement is disabled."],"exampleFix":"// before\nreq, _ := http.NewRequest(\"POST\", mcpURL, body)\n// after\nreq, _ := http.NewRequest(\"POST\", mcpURL, body)\nreq.Header.Set(\"Authorization\", \"Bearer \"+token)","handlingStrategy":"try-catch","validationCode":"token := strings.TrimPrefix(req.Header.Get(\"Authorization\"), \"Bearer \")\nif token == \"\" {\n    return errors.New(\"request must carry an Authorization: Bearer <token> header when Auth is configured\")\n}","typeGuard":"func hasBearerToken(h http.Header) (string, bool) {\n    const p = \"Bearer \"\n    v := h.Get(\"Authorization\")\n    if len(v) > len(p) && v[:len(p)] == p {\n        return v[len(p):], true\n    }\n    return \"\", false\n}","tryCatchPattern":"resp, err := http.DefaultClient.Do(req)\nif err != nil || resp.StatusCode == http.StatusUnauthorized {\n    // refresh/obtain token, re-attach Authorization header, retry once\n}","preventionTips":["Configure HTTP clients/middleware to always attach the Bearer token for MCP endpoints.","Check that proxies/ingress don't strip the Authorization header.","Implement token refresh before expiry so requests never go out tokenless.","Set Auth nil only if the endpoint is intentionally public."],"tags":["mcp","auth","http","bearer-token"],"backgroundTag":"missing-auth-token","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}