Skip to content

v0.5.9 — what initialize declares, the engine now does

Choose a tag to compare

@2nd1st 2nd1st released this 16 Aug 06:16
· 1 commit to main since this release

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 sent resources/subscribe got -32601 Method not found — on every legacy protocol version the SDK negotiates, which is the wire every shipping host speaks today. The engine already pushes notifications/resources/updated to everyone on the connection for every app-plane write (a larger promise than "subscribe"); what was missing was a handler that accepts the request. Both resources/subscribe and resources/unsubscribe now answer. The bit stays declared, because on the 2026-07-28 wire it is what makes a subscriptions/listen filter naming our URIs honourable — and that verb the SDK serves itself.
  • tools.listChanged was written as a conditional meant to say false unless the per-app openers are on. It said true in every mode: an absent key is not false to the SDK, which fills the bit in with ?? true the moment a tool is registered. Measured in all three settings. It is now written out unconditionally — true with OMA_DYNAMIC_TOOLS=1, false without.

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.md first, Discussions for questions and app wishes, the private advisory channel SECURITY.md names). 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-apps into 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 — stdin is 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, NaN and [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.mjs said the repository must not advertise npx, and the README's first install path is npx. The line predates the scoped package. The rule is scoped only — bare npx open-mcp-apps still 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