Skip to content

[BUG]: API token last_used not updated for MCP Streamable HTTP requests #4027

@kimsehwan96

Description

@kimsehwan96

🐞 Bug Summary

API token last_used timestamp is not updated when accessing Virtual Servers via MCP Streamable HTTP transport (/servers/{id}/mcp). The same API token correctly updates last_used when used for regular REST API calls (e.g., GET /tools, GET /resources). Additionally, TokenUsageMiddleware does not log MCP requests made with API tokens because auth_method is hardcoded to "jwt" in the MCP auth path

Root cause: _StreamableHttpAuthHandler._auth_jwt() in streamablehttp_transport.py performs its own JWT verification via verify_credentials() but does not call _update_api_token_last_used_sync() and always sets auth_method to "jwt" regardless of whether the token is an API token or a session token.


🧩 Affected Component

Select the area of the project impacted:

  • mcpgateway - API
  • mcpgateway - UI (admin panel)
  • mcpgateway.wrapper - stdio wrapper
  • Federation or Transports
  • CLI, Makefiles, or shell scripts
  • Container setup (Docker/Podman/Compose)
  • Other (explain below)

🔁 Steps to Reproduce

  1. Create an API token via Admin UI or POST /tokens
  2. Use the API token to access a Virtual Server: POST /servers/{id}/mcp with Authorization: Bearer <api_token>
  3. Check the token's last_used field via Admin UI → Tokens page
  4. Observe that last_used remains null
  5. Use the same token for a regular API call: GET /tools
  6. Check last_used again — it is now updated

🤔 Expected Behavior

last_used should be updated for all authenticated requests made with an API token, including MCP Streamable HTTP requests to Virtual Servers. Token usage should also be logged by TokenUsageMiddleware.


📓 Logs / Error Output

No errors in logs. The MCP request succeeds (auth passes via streamable_http_auth()), but the last_used update is silently skipped because the MCP-specific auth path lacks this functionality.


🧠 Environment Info

You can retrieve most of this from the /version endpoint.

Key Value
Version or commit main@860ef3f
Runtime Python 3.11+, Gunicorn
Platform / OS Any (reproduced on macOS + Docker Compose)
Container Docker Compose (default stack)

🧩 Additional Context (optional)

The MCP transport uses a dedicated auth handler (_StreamableHttpAuthHandler) instead of the standard AuthContextMiddleware because BaseHTTPMiddleware has known issues with SSE streaming responses. This dedicated handler duplicates most auth functionality (JWT verification, revocation checks, team
membership, auth cache) but omits last_used tracking and correct auth_method classification for API tokens.

The outer AuthContextMiddleware theoretically also processes these requests, but may silently fail for streaming responses and catches all exceptions without propagating auth state.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriageIssues / Features awaiting triage

Type

No type
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