Skip to content

Logged-in browser flow: four fixes from getting the bridge working inside ChatGPT's browser #3

Description

@webmyc

Respira for WordPress vendors this plugin as its WebMCP bridge, and on 2026-08-26 it powered what is very likely the first WordPress site with working WebMCP site tools inside ChatGPT's browser. Getting there required four fixes to the bridge, because the logged-in browser flow could not work as shipped: discovery registered zero tools and execution never reached its handler. Each bug masked the next, which is why they are reported together. All four are running in production now; patched source is public at https://github.com/respira-press/webmcp-for-wordpress (GPL-2.0, bridge/ mirrors this plugin's layout), and i am happy to turn any or all of this into a PR here.

1. Tool discovery runs as an anonymous visitor

webmcp-abilities.ts fetches the tools endpoint with credentials: 'same-origin' and no X-WP-Nonce. WordPress deliberately ignores cookie authentication on REST requests without a wp_rest-action nonce, so the request executes as user 0. With wmcp_discovery_public off (the default), tools_permission_check() returns 401 and the bridge registers nothing, on every default-configured site.

Fix: localize a wp_rest nonce alongside the existing execute nonce and send it as X-WP-Nonce on the discovery fetch (and refresh it via the tools response and the nonce endpoint).

2. Execution dies at core's door on the nonce header

The execute call sends the plugin's wmcp_execute nonce in the X-WP-Nonce header, and class-rest-api.php reads x_wp_nonce to verify it. But core reserves that header for wp_rest-action nonces: rest_cookie_check_errors() hard-rejects any request carrying a different nonce there (rest_cookie_invalid_nonce, 403) before the route callback runs. The handler's own check at the wmcp_execute verification is unreachable for cookie-authenticated browsers.

Fix: two nonces. The wp_rest nonce rides X-WP-Nonce on every bridge fetch and authenticates the cookie; the wmcp_execute CSRF token moves to its own X-WMCP-Nonce header, with the old header kept as a fallback read for non-cookie contexts.

3. ChatGPT's modelContext is frozen and implements only registerTool

The bridge registers everything through a single batch provideContext({ tools }) call. ChatGPT's in-app browser (site tools shipped 2026-08-25) exposes a frozen document.modelContext implementing only registerTool; assigning a provideContext polyfill onto the frozen object silently no-ops in non-strict code, so the batch call throws and zero tools register even though discovery succeeded.

Fix: feature-detect registerTool and register tools one at a time through it, falling back to provideContext where that is what exists.

4. Ability names with a slash 404 on stock Apache

Ability names are namespaced (vendor/ability-name) and the execute URL is built with encodeURIComponent, producing %2F in the path. Stock Apache rejects encoded slashes before WordPress runs (AllowEncodedSlashes Off), and a raw slash does not match the route pattern [a-zA-Z0-9_%\-]+, so execution 404s on every Apache host either way.

Fix: the bridge sends / as __ and the route's sanitize_callback maps it back with str_replace before rawurldecode's result is used. No registered ability name contains a double underscore.

One operational note, not a bug

Agent browsers enforce a per-page tool budget. Registering the full ability catalog (296 tools in my case) made ChatGPT disable WebMCP for the document entirely ("configuration exceeds supported limits"). A curated allowlist via wmcp_exposed_tools (~30 page-scoped tools) is the shape that works; may be worth a note in the README so the first real-world user does not hit the wall at full catalog size.

Verified end to end inside ChatGPT's desktop browser: discovery 200, 30 tools registered, agent-invoked execution returning 200 with real data. Thank you for building this bridge; it is the reason a WordPress site could show up on day two of agent browsers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions