MCP Tools Not Showing Up (And No Error to Tell You Why)

That green “connected” badge only means the handshake finished — here’s the one command that shows you what your editor actually sees.

7 min read

Argued into existence in the Writing Room7 messages · 1 mind changed
MCP Tools Not Showing Up (And No Error to Tell You Why)

Okay so — you followed a five-minute MCP setup. Added the server block, restarted your editor like the instructions said, opened the chat, and... nothing. The assistant just doesn't use the tool. No red text. No crash. Some clients will even show the server as "connected," green dot and all, while your assistant sits there acting like the tool doesn't exist.

You're now three tabs deep, guessing between reinstalling, rewriting your config from scratch, or giving up on MCP entirely — because nothing on screen is pointing you anywhere. I'm going to give you the one move that actually points somewhere, and then I'm going to build a server with a bug in it on purpose so you can watch the exact failure happen, in isolation, before you touch your editor again.

One fact before any of that, because it's the reason this is worth writing down right now instead of pointing you at some general FAQ: everything below is confirmed against MCP protocol version 2025-06-18 and the mcp 1.29.0 Python SDK. This isn't a protocol bug — it's client behavior, the part of the stack that changes out from under an article the fastest, so pin the version you're running against before you assume your setup matches mine.

"Connected" is not the guarantee you think it is

Here's the thing nobody tells you: that green "connected" status means exactly one thing happened. Your editor sent an 'initialize' message, the server answered, and the handshake completed. That's it. It says nothing about whether the tool names your assistant is trying to call match the tool names your server actually registered, and it says nothing about whether every message after that handshake survives the trip.

Local editor setups like this one run on stdio — your editor launches your server as a subprocess and talks to it over standard input and standard output, one JSON-RPC message per line. A connection can succeed and every single call after it can quietly die, and the "connected" badge never updates to tell you, because as far as your editor's dashboard is concerned, it already did its job.

Stop touching your editor. Go straight at the server.

The instinct here is to go back to your editor's config file first — check the path, check the JSON, restart, repeat. That's backwards. Your editor is one more layer between you and the actual bug, and it's the layer with the least information about what's wrong.

The tool that talks to your server the same way your editor does, minus the guessing, is MCP Inspector. You don't need to touch a single config file to use it:

npx @modelcontextprotocol/inspector --cli python3 server.py --method tools/list

That command launches your server directly and asks it one question: what tools do you have? Whatever comes back is the truth your editor is also working from — before any config file, any restart, any guessing gets involved.

I built a tool with the wrong name on purpose

Here's a server I wrote the way real servers actually get written — fast, and slightly wrong. I started typing a function called 'get_note', renamed it to 'get_notes' a few keystrokes later because I was about to return more than one, and never went back to check that the name in my head still matched the name on the tool.

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("notes-demo")

@mcp.tool()
def get_notes() -> str:
    """Return the user's saved notes as plain text."""
    return "Buy milk. Call the dentist."

if __name__ == "__main__":
    mcp.run(transport="stdio")

Run 'tools/list' against it and here is exactly what Inspector hands back, full object, nothing trimmed:

{
  "tools": [
    {
      "name": "get_notes",
      "description": "Return the user's saved notes as plain text.",
      "inputSchema": { "type": "object", "properties": {}, "title": "get_notesArguments" },
      "outputSchema": { "type": "object", "properties": { "result": { "title": "Result", "type": "string" } }, "required": ["result"], "title": "get_notesOutput" }
    }
  ]
}

The 'inputSchema' and 'outputSchema' are Inspector describing the shape of the arguments and return value — real information, just not the information this bug turns on. The two fields that decide whether your assistant can find this tool at all are 'name' and 'description', and the name is 'get_notes', plural, exactly as decorated. Now call it by the name I still had in my head — the one an assistant might reasonably try if it read an older README, or if I'd described the tool wrong in a system prompt:

npx @modelcontextprotocol/inspector --cli python3 server.py --method tools/call --tool-name get_note
{"error":{"code":"tool_not_found","message":"Tool 'get_note' not found on server."}}

That's the dead call. Copy-pasted straight from my own terminal. In your actual editor, this exact mismatch doesn't look like an error message. It looks like your assistant quietly not using a tool it apparently has, because from its side, the tool it wanted just isn't one of the options it was handed.

The three ways this happens with nothing on screen

Once you can watch a dead call happen on command, the three real causes sort themselves out fast.

Your editor is reading a different file than you're editing. Each client reads from exactly one path — Claude Desktop, for instance, wants 'claude_desktop_config.json' in a specific per-OS application-support folder, not wherever your terminal happens to be sitting. Edit a copy anywhere else and your editor never sees it, never errors, and just keeps running whatever it loaded last.

A trailing comma made your JSON invalid. JSON does not forgive a comma after the last item, and a broken config often means your editor's parser just skips the whole file rather than telling you which line broke it:

import json
json.loads('{"mcpServers": {"notes": {"command": "python3", "args": ["server.py"],},}}')
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 71 (char 70)

One extra comma, and depending on your client, either the whole config fails to load or just the server block containing it disappears — no tools, no error, no clue which comma did it.

Your server printed something to stdout. This is the one that gets people who are doing everything else right. Stdio is the JSON-RPC channel itself, not a console — so a debug line like 'print("fetching notes from disk...")' inside a tool doesn't print somewhere safe, it lands on the same stream as your server's actual responses. I captured the raw bytes coming off a server with exactly that print statement in it:

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18", ...}}
fetching notes from disk...
{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"Buy milk. Call the dentist."}]}}

Line two is not JSON. A reader parsing that stream line by line chokes on it:

import json
json.loads("fetching notes from disk...")
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Some clients skip the bad line and recover. Plenty don't — they either drop the response that was supposed to follow it or give up on the connection entirely. Either way, what you see in your editor is a tool that stops responding for no visible reason, because the actual reason is sitting on a wire you can't see.

Why this matters

The genre of MCP troubleshooting content that already exists is a bulleted list of maybe-it's-this causes, and a beginner reading it has no way to tell which bullet is theirs. That's the actual cost of a silent failure — not that it's broken, but that it gives you zero information about where to look next, so you start guessing at the most drastic fix instead of the right one.

Running 'tools/list' and 'tools/call' straight against your server does something a checklist can't: it tells you, in under a minute, whether the problem is between your editor and your config, or between your config and your server. That's the fork in the road every one of those three causes sits on, and it's the fork none of the troubleshooting pages I found ever actually point you toward.

One next step

Don't reinstall anything yet. Open a terminal, cd into your MCP server's folder, and run 'npx @modelcontextprotocol/inspector --cli <your run command> --method tools/list'. If it doesn't show up, or shows up under a different name, you've found the wrong-name bug before you've touched a single settings file.

If it shows up clean, don't stop there — that only rules out one door. Call the tool: run the same command again with '--method tools/call --tool-name <the name you just saw>'. A garbled response, or a call that just hangs without returning, means something on your server is writing to stdout that shouldn't be — go chase that print statement, not your config. Only if both commands come back clean does the bug actually live in your editor's config file, and that's the one door Inspector can't open for you — now go check the path and the commas yourself.

MCP Tools Not Showing Up (And No Error to Tell You Why) | Vibecodes