Skip to content

Land the rest of the agent-first stack on main - #15

Merged
Mathieu2301 merged 5 commits into
mainfrom
mathieu/agent-mcp
Sep 14, 2026
Merged

Mathieu2301 merged 5 commits into
mainfrom
mathieu/agent-mcp

Conversation

@Mathieu2301

Copy link
Copy Markdown
Member

#10 landed on main. #11, #12, #13 and #14 were merged while GitHub had not yet
retargeted them off the stack, so each landed in its own base branch instead of
main: the guide into mathieu/agent-cli, discover into mathieu/agent-guide,
the MCP server into mathieu/agent-discover, and agent-pack into
mathieu/agent-mcp. No content was lost — mathieu/agent-mcp now holds all four.

This carries them to main in one commit. The diff is exactly the union of the
four already-reviewed pull requests:

#11 docs/agent-guide.md — the guide an agent reads before building a home
#12 miakapp discover — read an existing Node-RED house before designing one
#13 miakapp mcp — the CLI as MCP tools, with confirm: true on physical acts
#14 miakapp agent-pack — put the guide where the agent actually looks

Verified on the union with the pinned toolchain (bun 1.2.23), from a clean
bun install --frozen-lockfile: bun run check exit 0, bun run check:packages
exit 0 — cli, component, template and the publishable artifact all green.

Mathieu2301 and others added 5 commits September 14, 2026 01:30
The toolchain shipped before the document that explains it. An agent handed
someone's house could read the SDK reference and the template, but nothing
stated the judgment neither of them carries: that the coordinator is the only
place authorization happens, that component requirements and coordinator grants
intersect silently so a mismatch renders a hole instead of raising, and that a
call whose outcome is unknown must never be retried because the lamp may
already have moved.

Everything here is taken from the code it describes: the UI vocabulary and
limits from packages/component, the command surface and the exit-code table
from packages/cli, the ordering and authorization rules from templates/home.
The two CI lessons are the ones this repository paid for on #10 — a check that
exists is not a check that runs, and a local run under the wrong Bun proves
nothing about the runner.

Closes the second deliverable of workstream F.
The guide tells an agent to characterize an existing installation before
writing anything, and then leaves it to do that by hand. Most houses arriving
at V4 already run Node-RED with the v3 MiakAPI nodes, and their flows export
already answers most of the questions: which brokers exist, which topics the
devices actually use, which variables were committed, which actions users
could invoke and who was allowed to invoke them.

`miakapp discover --flows <path>` reads that export offline and turns it into
an inventory. It needs no project file and no Home Key, because the house it
reads has no V4 project yet.

Three things it will not do. It never opens a socket or writes back into the
export. It never drops a node silently: every type it does not model is
counted and reported, so the reader knows what the inventory missed. And it
never guesses which actions are physically consequential -- it lists them all
and leaves that judgement to the reader.

Four findings decide work that is otherwise discovered late:

- the v3 initMiakapi node declares coordSecret in its defaults rather than its
  credentials, so Node-RED stores that secret in cleartext in flows.json
  instead of the encrypted flows_cred.json. If it is there, it is leaked.
- the v3 handler allows an action outright when its node lists no group, so an
  empty allowedGroups is a grant to every signed-in user, not a deny.
- a v3 name that V4 would reject has to be renamed while nobody depends on it.
- a topic holding # or + is a subscription pattern, not one device.

Field names come from the two real schemas: Node-RED core mqtt in, mqtt out
and mqtt-broker, and the node-red-contrib-MiakAPI v3 node definitions. No real
export was available to test against, so the parser recognizes nodes by type
and reads only fields it knows.
An agent that speaks MCP natively had to spawn a shell, quote arguments and
parse output to publish a component. `miakapp mcp` serves the same commands as
tools over newline-delimited JSON-RPC on stdio.

The server is a translation layer and nothing else: a tool call becomes the
exact argv a person would have typed, then runs the same dispatch. There is no
second implementation of an option, a default or a validation rule, and a test
proves the tool surface exposes every CLI option and invents none.

Two deliberate departures from the command line:

- publish, activate and rollback refuse to run without `confirm: true`. The
  guard is checked before anything else and never reaches the argv, so a model
  that hallucinated a publication spends the mistake on an argument check
  rather than on a generation;
- a command failure comes back as a tool result carrying `isError`, not as a
  JSON-RPC error. A protocol error means the call never happened; a publication
  that reached the control plane and failed did happen, and only `kind` tells
  the caller whether to reconcile.

Nothing but framed JSON-RPC reaches stdout: the dispatch runs against a host
that captures its own rendering, because one stray line would desynchronize the
stream for the rest of the session.

Zero new dependencies. 37 tests, including a live handshake shape, a message
split across chunks, and a guarded publish proving the control plane was never
contacted.
The toolchain, the guide and the contract all existed, and none of them
were where a coding agent actually looks. An agent opening the owner's
home repository reads AGENTS.md or CLAUDE.md, and finds whatever the
owner wrote there — which is not this.

`miakapp agent-pack` puts the knowledge in the repository: the guide as
a file under .miakapp/, a pointer to it in both instruction files, and
the MCP server registered in .mcp.json so the tools are wired rather
than described.

The CLI's rule that it never rewrites what it did not generate is what
shapes every merge. The guide is a file the command owns outright. The
instruction files are edited only between markers it wrote, so prose
above and below survives byte for byte and a second run rewrites the
block in place instead of stacking another copy. .mcp.json is merged as
a structure, one key by name: every other server survives, and a file
that does not parse is refused rather than replaced with a valid one.
Two new FileSystem methods carry this — `replace` is deliberately
separate from `write`, so overwriting stays something a command asks
for rather than something it falls into.

The guide ships as a package asset, and a test asserts it is byte-equal
to docs/agent-guide.md. Editing the doc without copying it across is a
red test, not a pack that teaches an agent rules the CLI no longer has.

Verified against the built binary, not only through injected tests: the
pack installed into a repository with its own AGENTS.md and its own
.mcp.json kept both, `claude mcp get miakapp` reads the generated entry
back as a project-scope stdio server, a second run reported every file
unchanged, and miakapp_agent_pack installed a pack over real MCP stdio.
Put the guide where the agent actually looks: miakapp agent-pack
@Mathieu2301
Mathieu2301 merged commit 9eb9df9 into main Sep 14, 2026
1 check passed
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.

1 participant