Skip to content

fix: record MCP health on the transport every zip actually ships (#115) - #116

Merged
ifahimreza merged 1 commit into
mainfrom
fix/115-health-on-builtin-transport
Aug 17, 2026
Merged

fix: record MCP health on the transport every zip actually ships (#115)#116
ifahimreza merged 1 commit into
mainfrom
fix/115-health-on-builtin-transport

Conversation

@ifahimreza

Copy link
Copy Markdown
Contributor

Closes #115

What

Saddle_MCP_Diagnostics::record_health() now runs on the built-in JSON-RPC transport, not only on the adapter path. Both branches of the Client traffic card's headline are reworded to say what the number actually is.

Why

The record had exactly one call site: Saddle_MCP::record_server_health(), reached only from register_adapter_server(). Since bff1a99 the vendored library is in no zip, so unless a site separately installs the official MCP Adapter plugin it runs Saddle_MCP's own transport — where nothing wrote it.

McpDiagnostics.jsx then took its health.registered === undefined branch and printed "No app has connected yet, so there's nothing to report." on every build we ship, whatever had actually happened.

That sentence is the first line of the one screen support points a customer at when their app connects and sees no tools. It reads as "this card is broken", so the Record the next hour button underneath — which works, and which is the thing that answers the question — never gets pressed. This is live right now in a customer conversation.

The other branch was wrong the same way: %1$d of %2$d tools are available to connected apps reported a construction count (tools built vs. names handed over) as an availability count. Availability is tier-filtered per credential at dispatch and is already reported accurately, per request, as "N tools sent" in the trace.

How

  • Recorded from list_tools(), not at route registration: abilities register lazily and rest_api_init fires on requests that have nothing to do with MCP, so this is the first moment the number is real. Cost is one non-autoloaded get_option on an actual tools/list, and record_health() already no-ops on an unchanged value.
  • Counted before the tier filter. Reading it off the filtered payload would report a read-tier site as having lost the tools it is merely not offering — turning a working guardrail into a fault report. There is a test for exactly that.
  • Nothing can fail to convert on this path (every resolved ability becomes a tool), so expected and registered are the same list and assess() flags the case that does happen in the field: an empty one.

Testing

  • composer test596 tests (was 594), 1993 assertions, 1 pre-existing skip, green
  • Both new tests verified red without the fixstash the PHP change and they fail on "A tools/list must leave a health record on the built-in transport."
  • composer lint — 0 errors (3 pre-existing warnings, none in the changed lines)
  • npm run lint:js — clean
  • npm run build — bundle rebuilt and committed
  • languages/saddle.pot regenerated — diff is exactly the two changed strings plus the version stamp

…hips

record_health() had one call site, inside register_adapter_server(). No
zip carries the vendored library, so unless a site separately installs
the official MCP Adapter plugin it runs the JSON-RPC transport, nothing
ever wrote the record, and the Client traffic card opened with "No app
has connected yet, so there's nothing to report" no matter what had
happened. That is the first line of the one screen a support
conversation points a customer at, and it reads as "this card is
broken" — so the Record button underneath it, which does work and does
answer the question, never gets pressed.

Recorded from list_tools() rather than at route registration: abilities
register lazily and rest_api_init fires on requests that have nothing to
do with MCP, so this is the first moment the number is real. Counted
before the tier filter, because the record answers "did the tools load?"
— a property of the site — while "how many is this credential offered?"
is per-request and already in the trace row.

The copy was wrong in the same way on its other branch: it read the
construction count as an availability count. It now says which it is and
points at the per-request number for the other.

Closes #115
@ifahimreza
ifahimreza merged commit 4df5deb into main Aug 17, 2026
1 of 7 checks passed
@ifahimreza
ifahimreza deleted the fix/115-health-on-builtin-transport branch August 17, 2026 19:38
ifahimreza added a commit that referenced this pull request Aug 17, 2026
Carries the two fixes that rc5 missed. rc5 was built at 14:14 UTC on
2026-08-17 and #112 merged at 18:30 the same day, so the shim that exists
specifically so ChatGPT can discover tools is on main but has never been
in a zip — verified absent in dist/saddle-1.0.0-rc5.zip.

Also fixes the version task itself. It wrote four of the five places the
version lives and skipped package-lock.json entirely, so the lockfile sat
at the previous version after every bump. Handled structurally rather
than by text substitution: every dependency in that file carries a
"version" field too, and a global regex for one would rewrite them all.

Changelog entries for #112 and #116 written against 1.0.0 — Stable tag
does not move for a release candidate.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Client traffic card claims no app has connected on every build we ship

1 participant