MCP server connects successfully but its tools are never exposed to the model
MCP server connects successfully but its tools are never exposed to the model
Summary
A remote HTTP MCP server (Google's Gmail MCP endpoint) completes OAuth, reports as connected in /mcp, and serves a valid tools/list — but none of its tools are ever made available to the model. The model's tool catalog contains no mcp__gmail__* entries, so the server is effectively dead weight despite every part of the setup being correct.
Environment
- Claude Code VS Code extension
2.1.214(anthropic.claude-code-2.1.214-darwin-arm64) - macOS 26.5.2, arm64
- Model: Opus 4.8
- MCP server:
https://gmailmcp.googleapis.com/mcp/v1,type: http, OAuth
Configuration
Project-scoped in ~/.claude.json under /Users/ceo-rimmer/Desktop/VSC projekter:
"gmail": {
"type": "http",
"url": "https://gmailmcp.googleapis.com/mcp/v1",
"oauth": {
"clientId": "…apps.googleusercontent.com",
"callbackPort": 45289
}
}
Expected
After a successful connection and OAuth, the server's 13 tools appear in the model's tool catalog as mcp__gmail__* and are callable.
Actual
/mcp reports 1 connected, 0 not connected, 0 disabled. The model has no Gmail tools. ToolSearch returns no matches for either direct name lookup (select:mcp__gmail__search_threads,…) or broad keyword search.
What was ruled out
This is not a misconfiguration. Each of the following was verified:
- The endpoint is real and healthy. A direct
curlPOST of an MCPinitializereturns HTTP 200 withprotocolVersion: 2024-11-05andcapabilities.tools. Atools/listcall returns 13 tools:create_draft,list_drafts,get_thread,get_message,search_threads,label_thread,unlabel_thread,apply_sensitive_thread_label,list_labels,label_message,unlabel_message,apply_sensitive_message_label,create_label.
- OAuth is complete and the token is valid. The macOS keychain entry
Claude Code-credentialscontainsmcpOAuth/gmail|<id>with a populatedaccessToken,refreshToken, and anexpiresAtin the future. Granted scopes:gmail.readonly,gmail.modify,gmail.compose,gmail.metadata,https://mail.google.com/.~/.claude/mcp-needs-auth-cache.jsonis empty ({}), i.e. nothing is flagged as needing auth.
- Restarting does not help. Tried both
Developer: Reload Windowand a fullCmd+Qrelaunch. Tools remain absent after each.
- The token works against Google's API. Using the same keychain access token to call
https://gmail.googleapis.com/gmail/v1/users/me/messagesdirectly returns real inbox data. So the credential the MCP server obtained is functional — only the path through the MCP integration is broken.
Impact
The failure is silent and actively misleading. /mcp reporting "connected" suggests the integration is working, when in fact the connection exists at the transport layer while no tools ever reach the model. There is no error, no warning, and nothing in the UI distinguishing this state from a healthy one — the only symptom is the assistant saying it has no access, which reads like a model problem rather than an integration problem.
This cost a fair amount of user time chasing wrong hypotheses (stale session, incomplete OAuth), each of which required a restart to disprove.
Suggested fix
At minimum, surface the discrepancy: if a server is connected but contributed zero tools to the catalog, /mcp should say so rather than showing a bare "connected". A per-server tool count in /mcp output would have made this diagnosable in seconds.
Workaround
Bypass MCP entirely — read the access token from the keychain and call the Gmail REST API directly with a Bearer header. Functional, but loses the server's guardrails (notably its separate handling of sensitive labels) and requires handling token refresh manually.
3 Comments
This usually happens when the server's ools/list handler either returns an empty array or errors out silently. The connection succeeds (the initialize handshake works) but the tool listing step fails.
A quick way to diagnose this is to wrap the server command with a debug proxy to see the actual JSON-RPC exchange:
mcp-debug --out debug_session.json -- python your_server.py
mcp-debug --report debug_session.json
This will show you exactly what the server sent back (or didn't) during ools/list. Common causes:
Hope this helps narrow it down!
@in4mer Good detail on the enterprise setup. Since you're on Linux with Claude Code 2.1.216, try this quick diagnostic:
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | your-server-command
pip install mcp-debug-proxy
mcp-debug --out diagnose.json -- your-server-command
Then connect Claude Code to the proxy instead of directly to your server. Check diagnose.json after a connection attempt — you'll see exactly where the handshake breaks (likely during ools/list or esources/list).
Common enterprise issues:
I had commented here earlier about enterprise-provisioned MCP services (Slack, Atlassian, Gmail, Google Calendar, Google Drive, etc.) being unavailable on recent Claude Code versions (2.1.216, 2.1.217) on Linux. Turned out to be PEBKAC; reauthenticating fixed it. Not connected to the original bug report, so I've removed the earlier comments to avoid polluting search results.