MCP server connects successfully but its tools are never exposed to the model

Status Open
Reported on v2.1.214
Maintainer reply None cached
Activity 3 comments · opened Jul 18, 2026

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:

  1. The endpoint is real and healthy. A direct curl POST of an MCP initialize returns HTTP 200 with protocolVersion: 2024-11-05 and capabilities.tools. A tools/list call 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.
  1. OAuth is complete and the token is valid. The macOS keychain entry Claude Code-credentials contains mcpOAuth/gmail|<id> with a populated accessToken, refreshToken, and an expiresAt in the future. Granted scopes: gmail.readonly, gmail.modify, gmail.compose, gmail.metadata, https://mail.google.com/. ~/.claude/mcp-needs-auth-cache.json is empty ({}), i.e. nothing is flagged as needing auth.
  1. Restarting does not help. Tried both Developer: Reload Window and a full Cmd+Q relaunch. Tools remain absent after each.
  1. The token works against Google's API. Using the same keychain access token to call https://gmail.googleapis.com/gmail/v1/users/me/messages directly 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.

View original on GitHub ↗

3 Comments

ZachDreamZ · 1 month ago

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:

  • Missing eturn statement in the handler
  • Async handler that throws before returning
  • Handler returns None instead of ToolsResult

Hope this helps narrow it down!

ZachDreamZ · 1 month ago

@in4mer Good detail on the enterprise setup. Since you're on Linux with Claude Code 2.1.216, try this quick diagnostic:

  1. Run your MCP server standalone to check if it initializes:


echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | your-server-command

  1. If that works, wrap it with a debug proxy to see the full exchange:


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:

  • Proxy/VPN blocking the server process from spawning
  • SELinux or AppArmor preventing subprocess execution
  • The server binary is in a path Claude Code can't resolve
in4mer · 1 month ago

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.