Connected with zero tools has a specific meaning
A connected state proves only that the client reached the server. It does not prove the server completed capability negotiation or returned a non-empty tool list. Claude Code's official MCP documentation explicitly distinguishes a server that advertises the tools capability but exposes no tools, and its /mcp panel shows the tool count beside each server.
Use MCP Inspector as the client-independent boundary. Its Tools tab lists tool names, descriptions, and schemas and can invoke them with test inputs. If that view is empty, fix the server implementation or registration. If it is populated, move to the target client's filters, scope, authentication, cache, and session behavior.
Test initialize before blaming tools/list
MCP tool discovery happens after the protocol initialization exchange. Verify the negotiated protocol version, the server's declared capabilities, and the initialized notification before investigating tool schemas. A transport that opens and then fails initialization can look like an empty tool catalog in a client UI.
For local stdio servers, run the exact configured command with the same arguments and environment. Use absolute paths where the working directory may differ, and write diagnostics to stderr because stdout is reserved for protocol messages. For Streamable HTTP, inspect authentication, response status, session headers, and server-side logs.
Separate discovery, loading, and invocation
Discovery asks whether tools/list returns valid tool definitions. Loading asks whether the client makes those definitions available in the current workspace, agent, and session. Invocation asks whether the model selects an available tool and receives permission to call it. Each layer has different proof and a different fix.
Claude Code and GitHub Copilot can load tools on demand to reduce context use. That means a tool may not be expanded into the model context until a matching task triggers tool search. Check the client status and tool policy before treating deferred loading as server failure.
Client-specific checks after Inspector passes
In Claude Code, use /mcp to inspect connection state, authentication, and tool count. Project-scoped configuration may require approval, duplicate names can resolve by scope precedence, and tool search can defer definitions. In GitHub Copilot CLI, inspect /mcp or copilot mcp get, then check enabled-tool filters and any custom-agent tool policy.
In Cursor or another MCP client, use the MCP settings and logs exposed by the installed version, then compare that client view with the same server in Inspector. Product interfaces and policy controls change, so retain the client version and a sanitized status capture in the bug report instead of assuming one universal menu path.
What evidence to collect before filing a bug
Record the client and server versions, transport, operating system, active config scope, exact symptom, expected tool names, initialization result, tools/list result, enabled-tool policy, and sanitized logs. Remove credentials, tokens, private URLs, and sensitive payloads before sharing any record.
The downloadable worksheet on this page follows the same eight-layer order as the debugger. Attach the first failing check and its observable output; a report that says only 'tools are missing' forces maintainers to repeat every layer.
Recommended play
- Use MCP Inspector first when the server connects but exposes zero tools; it separates server discovery from client behavior.
- Keep discovery, client loading, and model invocation as three different states with separate pass conditions.
- Change only the first failing layer, then rerun the same proof before modifying permissions or unrelated configuration.
MCP missing-tools symptom map
Start at the row that matches the observable symptom, then move only after its pass condition is proven.
| Area | Likely layer | First proof | Do not confuse with |
|---|---|---|---|
| Server absent from status | Config load or scope | Server name appears in the active client status view | A tools/list implementation bug |
| Configured but disconnected | Process, transport, auth, or initialization | Exact command stays alive or HTTP connection initializes | A model tool-selection problem |
| Connected with 0 tools | Capability negotiation or tools/list | Inspector Tools tab returns expected definitions | Write permission or prompt wording |
| Inspector passes, client empty | Client scope, authentication, or tool filter | Tool is enabled for the current workspace and agent | A server reinstall |
| Tool visible but unused | Deferred loading, description, schema, prompt, or approval | Explicit matching request selects and invokes the tool | A discovery failure |
Execution steps
Capture the exact state
Record whether the server is absent, disconnected, connected with zero tools, populated only in Inspector, stale after a change, or visible but never called.
Prove process and initialization
Reproduce the exact transport, inspect initialization and declared capabilities, and eliminate stdout noise for local stdio servers.
Inspect tools/list independently
Connect MCP Inspector to the same server and verify expected names, descriptions, input schemas, and one safe test invocation.
Audit client policy
Check authentication, configuration precedence, workspace scope, enabled tools, allowlists, denylists, custom-agent policy, and on-demand loading.
Retest one changed layer
Reconnect or reload as required, repeat the original proof, and retain sanitized evidence for the server and client versions tested.
Common pitfalls
Reinstalling before finding the failing layer
A reinstall changes many variables without proving the cause. Test config, transport, initialize, and tools/list in order.
Writing debug logs to stdout
For stdio servers, keep stdout exclusively for protocol traffic and send diagnostic logs to stderr.
Treating a tool that is not called as a missing tool
First prove the tool is visible. Then check deferred loading, prompt fit, description, schema, permission, and invocation separately.
Widening every tool permission
Enable only the expected tool in the required scope. A broad allow-all rule hides policy mistakes and increases risk.
Implementation checklist
- Record the exact missing-tools symptom before changing configuration.
- Confirm the active config source, scope, server name, command or URL, and authentication state.
- Run the exact stdio command or inspect the Streamable HTTP connection and server logs.
- Verify initialization, protocol version, declared tools capability, and initialized notification.
- Inspect tools/list in MCP Inspector and validate names, descriptions, and input schemas.
- Check client allowlists, denylists, enabled-tool filters, workspace scope, and custom-agent policy.
- Reconnect or reload after changes and verify dynamic list-change behavior where applicable.
- Test one explicit, safe request that clearly matches the expected tool.
- Redact credentials, tokens, private URLs, and sensitive payloads from shared evidence.
Questions this guide answers
Why is my MCP server connected but showing zero tools?
A connected state proves the transport reached the server, not that initialization completed or tools/list returned definitions. Open the same server in MCP Inspector. If its Tools tab is empty, fix capability negotiation, tool registration, tools/list, or invalid schemas. If Inspector lists tools, check the target client's scope, authentication, enabled-tool filters, and refresh behavior.
How do I test MCP tools/list?
Connect the server with the official MCP Inspector and open its Tools tab. Confirm the expected names, descriptions, and input schemas appear, then run one safe test call. This gives client-independent proof before you troubleshoot Claude Code, Cursor, GitHub Copilot, or another client's policy and loading behavior.
Why is an MCP tool visible but never called?
That is usually a selection, loading, permission, or schema problem rather than discovery failure. Use an explicit request that clearly requires the tool, check whether the client defers tools through tool search, review the description and required schema fields, and confirm the invocation is allowed in the current agent and workspace.
Should an MCP stdio server write logs to stdout?
No. For a stdio MCP server, stdout carries protocol messages, so debug output can corrupt JSON-RPC framing. Send diagnostic logs to stderr, run the exact configured command with the same environment, and use absolute paths when the client may launch from a different working directory.