fix(mcp): surface catalog failures instead of serving an empty tool list - #40
fix(mcp): surface catalog failures instead of serving an empty tool list#40sheagreco wants to merge 2 commits into
Conversation
|
Pushed one more doc commit ( Electron-based MCP clients spawn servers with a curated environment that drops Reproduced and confirmed by elimination — spawning And end to end through the client's own config, with and without the fix: Worth documenting because nothing in the failure points at ssh — there's no message anywhere to search for. This is also the one case where an 🤖 Generated with Claude Code |
|
Thanks for this — really good catch. The cache-poisoning half is the subtle bit: The README additions are genuinely useful. The Keep To your process note: this is fine as a PR. CONTRIBUTING's "issue first" is for behavior changes, and this is a bug fix where the current behavior is "silently lie about a healthy connection." No issue needed. One small ask before merge: Strip the Claude trailers from both commit messages. Both commits carry Paste-ready:
Once that's in, I'll verify on the dev box (I can reproduce the failure path with a bad Really appreciate this. Welcome to the repo. |
When the FreePBX ajax endpoint is unreachable, get_mcp_tools() returned an
empty array. That had two consequences:
- tools/list answered {"tools": []} with no error, so the client showed a
connected server with zero tools and no indication of why.
- [] is not null, so $toolCache was populated with the failure and every
later tools/list served the empty catalog for the life of the process,
even after the backend recovered.
get_mcp_tools() now returns ['tools' => [...]] or ['error' => string], and
tools/list sends a JSON-RPC -32603 naming the URL it tried. Failures are left
uncached so the next request retries.
Also fixes mcp-config.example.json: FROGMAN_FREEPBX_URL was set in an "env"
block, which applies to the local ssh process and is never forwarded to the
remote PHP unless sshd is configured with AcceptEnv. Moved inline into the
remote command so it actually takes effect.
Electron-based MCP clients spawn servers with a curated environment that omits ProgramData. Windows OpenSSH resolves its system config through a __PROGRAMDATA__ token, so ssh.exe aborts during startup without it — exit 255 in under 20ms with no stderr and no log output. The client surfaces only "Server transport closed unexpectedly", which points at Frogman rather than at ssh. Documents the env-block workaround, and notes why this is the one case where an env block is correct (it sets the local ssh process environment, not the remote one).
96311a3 to
3c43b24
Compare
|
Trailers stripped from both commits and force-pushed — messages only, the diff is unchanged and authors/dates are preserved. New SHAs: Agreed on One note for anyone else following the rebase steps: in my clone |
What
tools/listreported success with an empty tool array whenever the FreePBX ajax endpoint was unreachable. It now returns a JSON-RPC error naming the URL it tried.Why
get_mcp_tools()returned[]on any backend failure. Two things followed from that:tools/listanswered{"tools": []}, so the MCP client showed a happily-connectedfrogmanserver with zero tools and no reason given. Diagnosing it meant reading the server's stderr, which most MCP clients bury.[]is notnull, so$toolCachegot populated with the empty result on the first call. Every latertools/listshort-circuited on that cache and kept serving an empty catalog — even after the backend came back.I hit this on a stock Sangoma-layout FreePBX 17 box. There, port 80 is the Let's Encrypt vhost with
DocumentRoot /invalid/folder/nameand the admin control panel is on 8080, so the defaulthttp://localhost/admin/ajax.phpgets Apache's 403 HTML page. The MCP server connected fine and offered zero tools with no explanation.Changes
mcp-server.php—get_mcp_tools()now returns['tools' => [...]]or['error' => string].tools/listsends-32603with the attempted URL and the underlying error, and leaves$toolCacheatnullso the next request retries. Also guards a catalog response that has notoolskey, which previously hit an uncheckedforeach.mcp-config.example.json—FROGMAN_FREEPBX_URLwas set in anenvblock. Over the SSH transport that sets the variable on the localsshprocess;sshdnever forwards it to the remote PHP unless the server is configured withAcceptEnv. It silently did nothing and the server always fell back to its built-in default. Moved inline into the remote command.README.md— documents both traps in the MCP section, with a one-liner to check which port serves the ACP.I also added
-o BatchMode=yesto the example ssh args so a missing or locked key fails fast instead of blocking the stdio pipe on a password prompt. Happy to drop that if you'd rather keep the example minimal — it's the one change here that isn't strictly the bug.No version bump, per CONTRIBUTING.
Testing
Real FreePBX 17 box: Frogman 2.8.1, PHP 8.2.32, Debian 12. The patched server was run from
/tmpso the installed module stayed untouched.Failure path — two consecutive
tools/listagainst the wrong port:id: 3erroring as well is the part that matters — before this change the second call returned the cached{"tools": []}.Success path — same probe plus a
tools/call, against port 8080:Catalog endpoint directly, for reference:
Note on process
CONTRIBUTING asks for an issue first on behavior changes. I read this one as an obvious bug fix — the current path caches a failed request and reports it as success — but if you'd rather see it as an issue first, say the word and I'll move it over.
🤖 Generated with Claude Code
https://claude.ai/code/session_01CWe9ds1uAffXsVvE3LZUXM