🐞 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:
🔁 Steps to Reproduce
- Create an API token via Admin UI or POST /tokens
- Use the API token to access a Virtual Server: POST /servers/{id}/mcp with Authorization: Bearer <api_token>
- Check the token's last_used field via Admin UI → Tokens page
- Observe that last_used remains null
- Use the same token for a regular API call: GET /tools
- 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.
🐞 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- APImcpgateway- UI (admin panel)mcpgateway.wrapper- stdio wrapper🔁 Steps to Reproduce
🤔 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
/versionendpoint.main@860ef3fPython 3.11+, GunicornAny (reproduced on macOS + Docker Compose)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.