v0.5.9 — what initialize declares, the engine now does
What initialize declares, the engine now does.
The handshake every MCP host reads first is a set of promises about verbs, and two of them had never been kept — for as long as they existed, and without a single test going red, because nothing in the repository ever called the verbs the promises were about.
resources: { subscribe: true }was declared from the first release. A host that took it at its word and sentresources/subscribegot-32601 Method not found— on every legacy protocol version the SDK negotiates, which is the wire every shipping host speaks today. The engine already pushesnotifications/resources/updatedto everyone on the connection for every app-plane write (a larger promise than "subscribe"); what was missing was a handler that accepts the request. Bothresources/subscribeandresources/unsubscribenow answer. The bit stays declared, because on the 2026-07-28 wire it is what makes asubscriptions/listenfilter naming our URIs honourable — and that verb the SDK serves itself.tools.listChangedwas written as a conditional meant to sayfalseunless the per-app openers are on. It saidtruein every mode: an absent key is notfalseto the SDK, which fills the bit in with?? truethe moment a tool is registered. Measured in all three settings. It is now written out unconditionally —truewithOMA_DYNAMIC_TOOLS=1,falsewithout.
A new suite, test/capabilities.mjs, starts the server over real stdio, reads what was declared, and then calls each declared verb — on the legacy wire and, separately, on 2026-07-28. Run against the code as it was, three assertions went red by name.
Added
- A bug-report form on the public repository whose first four fields are the tuple nearly every defect in this project's history has turned out to be a property of: host × host version × channel × surface. One template, blank issues still enabled, three contact links (
KNOWN-ISSUES.mdfirst, Discussions for questions and app wishes, the private advisory channelSECURITY.mdnames). No "app request" template on purpose — this project's answer to I want an app is to have your AI build it. - One sentence when a person runs the server by hand. Pasting
npx -y @2nd1st/open-mcp-appsinto a terminal used to end in a cursor that stopped: zero bytes on stdout, because a stdio MCP server prints nothing until a host speaks. Now, when — and only when —stdinis a terminal, one line on stderr says what this process is and where the command belongs (your host's MCP config). Hosts spawning over pipes never see it. Both READMEs' fact table renamed the row that invited the paste from Run it to Command. - A net under every tool call in the smoke suite, reading each result the way the model reads it, so
undefined,NaNand[object Object]cannot reach the model as prose again. 0.5.7 fixed one such row; this pins the species. A row deliberately broken elsewhere was caught by name; the tree as shipped scans clean.
Fixed
- A comment at the top of
src/server.mjssaid the repository must not advertisenpx, and the README's first install path isnpx. The line predates the scoped package. The rule is scoped only — barenpx open-mcp-appsstill runs a stranger's package — and the comment now says exactly that.
The tool surface did not move — 44,911 B, byte-identical to 0.5.8 — because none of this rides tools/list.
Verified before this note was written: the public CI ran on the snapshot commit itself (6f6d2fd) — test (22) and test (24) both green, no MCP host present: https://github.com/2nd1st/open-mcp-apps/actions/runs/31930937332
Full detail: CHANGELOG.md