Skip to content

fix: update MCP registry namespace - #1618

Merged
thymikee merged 9 commits into
mainfrom
agent/update-mcp-registry-namespace
Aug 5, 2026
Merged

fix: update MCP registry namespace#1618
thymikee merged 9 commits into
mainfrom
agent/update-mcp-registry-namespace

Conversation

@thymikee

@thymikee thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member

Summary

Publish the MCP server under io.github.callstack/agent-device, matching the GitHub Actions OIDC publisher's authorized callstack namespace.

The previous callstackincubator namespace caused every post-npm-release registry publication to fail with a 403. The generated registry metadata and setup documentation now use the canonical name.

Introduce cross-interface command guidance: a shared semantic description with independent CLI and MCP adapters. The CLI adapter can inject relevant flags while the MCP adapter can inject schema-backed input hints. MCP descriptions no longer contain flags, positional syntax, or terminal command examples; the lowest-scoring Glama tools now use richer MCP-specific guidance.

Validation

pnpm check:affected --run passes. Focused guidance and command-surface tests pass, including a guard against CLI syntax in MCP descriptions. Runtime/device validation does not apply to the metadata, help, and documentation changes.

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.97 MB 1.98 MB +4.1 kB
JS gzip 631.7 kB 633.1 kB +1.4 kB
npm tarball 760.3 kB 761.1 kB +762 B
npm unpacked 2.67 MB 2.67 MB +4.0 kB

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 23.3 ms 23.4 ms +0.1 ms
CLI --help 54.5 ms 54.8 ms +0.3 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/registry.js +5.1 kB +1.7 kB
dist/src/viewport-dimension.js -312 B -70 B
dist/src/screenshot-geometry.js -133 B -56 B
dist/src/interaction.js -66 B -40 B
dist/src/session.js +7 B -5 B

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-05 16:51 UTC

thymikee and others added 3 commits August 5, 2026 14:39
The guidance type carried seven fields, but only three were ever set, and all
twenty call sites used it the same way: to hold a second, hand-written MCP
string next to a near-identical CLI one. That is the drift the abstraction was
meant to remove, so the type no longer offers a per-surface description at all.

A command now has one canonical description plus an optional tail per surface:

  guidance: {
    description: 'Shared body.',
    cliDetail: 'Flags, positional syntax, terminal examples.',
    mcpDetail: 'When-to-use and sequencing hints.',
  }

Because a surface can only append, CLI help and MCP tool text cannot diverge —
the guard against CLI syntax in MCP descriptions becomes structural rather than
a review tripwire, since flag vocabulary only lives in cliDetail. All twenty
commands that previously carried two descriptions now share one body.

Also:

- Drop `summary` from the description fallback chain. It is the short list-view
  line, so falling back to it replaced the full description with a fragment on
  both surfaces: artifacts, boot, and shutdown each lost their real description.
- Stop writing the MCP variant back over `metadata.description`. That field
  feeds CLI help, `explain`, and docs; `explain` was printing MCP-only text.
  MCP now reads a separate `mcpDescription`.
- Drop `mcp.parameters`. It restated inputSchema property descriptions inside
  the tool description — 1232 characters duplicated verbatim across six tools,
  and three of sixteen declared hints silently rendered nothing because the
  property had no description. Those properties are documented in the schema
  instead, which serves MCP, --help, and docs at once.
- Drop `cli.flags`. Its one use appended "Relevant flags: --surface,
  --launch-console." to help text that already named both flags inline.

Tests assert the structural property (both surfaces share a canonical prefix)
and the summary-fallback regression, alongside the existing CLI-syntax guard.
CLI help wording assertions follow the new copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dp3J8UUgYxtw5vzJzvjkSf
@thymikee

thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Review of exact head 5a318b9:

  • P2 — command guidance is not yet an honest derived adapter. command-guidance.ts synthesizes CLI labels with camel-to-kebab conversion instead of resolving the canonical flag definitions. A valid key such as snapshotRaw would therefore be advertised as nonexistent --snapshot-raw, rather than its declared --raw. The MCP side silently drops requested parameters when their schema property is absent or lacks a description; this already happens on this head: open requests surface, but surface: enumField(SESSION_SURFACES) has no description, so the public MCP guidance omits it without a failing gate. Please resolve CLI labels from the flag-definition registry and fail/gate absent or undescribed selected MCP properties. Pin both paths with a non-mechanical alias (snapshotRaw) and the actual open.surface/typo case.

  • Owner-action CI: Coverage is deterministically red at cli-help-topics.test.ts:139 because the type help phrase changed from the pinned “Append text to the focused field” wording to “Append text to the currently focused input.” The missing LCOV report is only downstream of that test failure, so rerunning this unchanged head will not help. Preserve the contract wording or intentionally update its assertion.

The registry namespace correction itself is corroborated: the prior release publication authenticated successfully with GitHub OIDC, and the registry explicitly authorized io.github.callstack/* while rejecting the old io.github.callstackincubator/agent-device name.

Guidance no longer restates input fields in prose, so a tool's inputSchema is
the only place its inputs are documented — for the model, for --help, and for
the docs site. An undescribed property is a silent gap rather than a cosmetic
one, which is exactly the failure mode the removed `mcp.parameters` selection
had: it dropped hints for properties that carried no description and reported
nothing.

Describe the two trigger-app-event inputs that mechanism used to name, and add
a ratcheting gate over every MCP tool input. A property key that is not already
in the budget fails immediately; the total may never grow, and lowering it is
required once properties gain descriptions, so the 132 remaining stay visible
instead of settling in as permanent debt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dp3J8UUgYxtw5vzJzvjkSf

thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

The review targeted 5a318b9; head is now 59bcbd1, which removed both flagged mechanisms rather than repairing them.

P2, CLI label synthesis. Confirmed as described — snapshotRaw declares names: ['--raw'] (flag-definitions-workflow.ts:196), so toKebabCase would have advertised a nonexistent --snapshot-raw. Rather than resolve labels from the flag registry, cli.flags is gone. It had exactly one call site, on open, and it appended Relevant flags: --surface, --launch-console. to help text that already named both flags inline — so the feature was producing redundant output at its only use. Flags are documented in the flag section; guidance no longer restates them.

P2, silently dropped MCP parameters. Also confirmed, and broader than open.surface: trigger-app-event declared ['event', 'payload'] and rendered neither, so 3 of 16 declared hints produced nothing. mcp.parameters is gone too. It was copying property descriptions out of inputSchema into the tool description — 1232 characters duplicated verbatim across six tools, when MCP clients already receive that schema. open.surface, apps.appsFilter, trigger-app-event.event, and trigger-app-event.payload are now described in the schema itself, which serves MCP, --help, and the docs site at once.

The gate you asked for. With prose no longer restating inputs, inputSchema is the only place they are documented, so an undescribed property is now a silent gap rather than a cosmetic one. command-tools.test.ts ratchets over every MCP tool input: an unbudgeted property key fails immediately, the total may never grow, and the budget must be lowered once properties gain descriptions. Both arms verified by deliberately breaking them. It also quantifies the standing debt — 132 undescribed inputs across 88 distinct keys, the largest being action on 13 tools. That predates this PR and is left as follow-up rather than bulk-guessed here, but it can no longer grow or hide.

Beyond the review, the shared-description mechanism was unused: guidance.description, cli.description, cli.detail, and mcp.detail had zero call sites, and all 20 guidance blocks held a second hand-written string next to a near-identical CLI one. The type now offers one canonical description plus an append-only tail per surface, with no per-surface override, so drift is structurally impossible instead of review-enforced. Measured across all commands: divergent CLI/MCP text 20 → 0.

That also caught a regression on the reviewed head: summary sat above the canonical description in the fallback chain, so artifacts, boot, and shutdown each had their full description replaced by the short list-view line on both surfaces. Restored, with a test.

CI wording. Fixed in 5225628; Coverage is green. Worth noting the failure was not only the pinned type phrase — tv-remote, snapshot, and metro help assertions were also stale, they were just masked because the type assertion failed first.

iOS smoke failure — unrelated. RunnerTests.testBareTypeUsesTappedInputWhenSoftwareKeyboardIsHidden at RunnerTests+CommandExecution.swift:395: XCTAssertEqual failed: ("h") is not equal to ("hardware-keyboard"). The runner log shows all 17 characters dispatched (AGENT_DEVICE_RUNNER_TEXT_ENTRY_PHASE ... chars=17) with one landing — a dropped-keystroke flake in simulator hardware-keyboard entry. It is a Swift XCUITest; this PR touches only TypeScript description strings and adds no runtime behaviour. The Assert simulator automation preserved host focus step below it is if: always() and did not itself fail.


Generated by Claude Code

@thymikee

thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Re-review of exact head 59bcbd1: the prior camel→kebab/silent selected-parameter problems are removed, open.surface is now described, the pinned help assertion is updated, and CI is fully green. Two projection/gate issues remain:

  • P2 — the canonical description is not propagated to every claimed consumer. projectCommandGuidance selects the shared body, but defineCommandFacet writes it only to CLI help and metadata.mcpDescription; metadata.description and definition.description retain their old terse text. command-explain.ts prefers metadata.description, so explain click still reports “Click or tap a semantic UI target…” while CLI/MCP use the canonical “Activate a UI target…”. Please project the canonical shared body into metadata and the executable definition, retain only the MCP-specific tail in mcpDescription, and pin explain/metadata/definition parity in the surface gate.

  • P2 — the undocumented-input ratchet can preserve its count while gaps migrate. It allowlists bare property keys plus aggregate budget 132. If foo.text gains a description while a new undocumented bar.text is added, text is already allowed and the total remains 132, so the gate stays green. Stale keys can likewise authorize later gaps. Pin exact tool.property identities, or equivalent per-tool accounting, so every new/moved gap fails and removing a gap requires removing its exact baseline entry.

Storing only the MCP variant left the shared body unpropagated: `metadata.description`
and the executable definition kept their pre-guidance text, so `explain click` reported
"Click or tap a semantic UI target..." while CLI help and the MCP tool both used the
canonical "Activate a UI target...". 53 commands were affected — the CLI schema base,
`explain`, and docs all read `metadata.description`.

`projectCommandGuidance` now returns the canonical body plus the MCP-only tail, and
`defineCommandFacet` writes the body to both metadata and the definition. Only the tail
is stored apart, as `mcpDetail`, so the body has exactly one home instead of a second
full copy that could drift; `composeMcpDescription` joins them for the tool surface.

The surface gate pins the invariant: definition, metadata, and `explain` must report the
identical body, and neither CLI help nor the MCP description may do anything but extend
it. Both arms verified by breaking them.

Also replace the undocumented-input ratchet's bare-key allowlist plus aggregate budget
with exact `tool.property` identities. The old shape stayed green while a gap migrated:
describing `foo.text` and adding an undescribed `bar.text` left both the allowed-name set
and the total of 132 unchanged, and stale names kept authorizing later gaps. Verified
with that exact scenario — `app` was already an allowed name via push/reinstall/settings
and the total held at 132, yet a newly undescribed `open.app` now fails. Recording a fix
requires deleting its baseline entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dp3J8UUgYxtw5vzJzvjkSf

thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Both correct, both mine. Fixed in 3a50aed.

P2, canonical body not propagated. Confirmed and worse than stated: I measured 53 commands where metadata.description and the executable definition still held pre-guidance text while CLI help and MCP used the canonical body — click, press, fill, longpress among them. My previous commit message claimed description "stays canonical"; it stayed stale. Having criticized the original head for leaking MCP text into explain, I left explain reporting something worse: text that matches no other surface.

projectCommandGuidance now returns { description, cliSchema, mcpDetail } and defineCommandFacet writes the body to metadata and the definition. On your "retain only the MCP-specific tail" point — I took the tail-only reading rather than storing a second full string, since with no mcpDetail the two fields would otherwise be byte-identical for most commands, which is a drift risk of exactly the kind this PR exists to remove. The field is mcpDetail, and composeMcpDescription joins body and tail at the tool surface.

Parity is pinned in the surface gate: definition, metadata, and explain must report an identical body, and neither CLI help nor the MCP description may do anything but extend it. Verified by breaking each arm — dropping the definition write fails with click: executable definition description drifted from metadata.

P2, ratchet preserves count while gaps migrate. Also confirmed. Replaced the bare-key allowlist and aggregate budget with exact tool.property identities. I reproduced your scenario rather than assuming it: app was already an allowed name via push.app / reinstall.app / settings.app, and I simultaneously described logs.message so the total held at exactly 132 — the old gate would have stayed green, the new one fails with These MCP tool inputs need a schema description: open.app. Removing a gap now requires deleting its baseline entry, which the second assertion enforces.

Both problems shared a root cause worth naming: I was pinning derived outputs rather than the relation between surfaces. The count-and-names ratchet and the earlier shared-prefix check both admitted states where every individual assertion held and the invariant did not. The replacements assert identity between surfaces instead.

CI: check:affected --run is green apart from scripts/fuzz/harness.test.ts, which fails a different test each full-suite run and passes standalone on repeat — subprocess timing under load, unrelated to this diff. The iOS smoke flake is being fixed elsewhere, so I'm not tracking it here.


Generated by Claude Code

claude added 2 commits August 5, 2026 15:08
`guidance.description` restated what `metadata.description` already is. Setting it
shadowed the metadata literal rather than replacing it, so every command that used it
shipped two bodies: the canonical one and a terse original that no surface could
observe — 768 bytes of unreachable strings across 20 commands.

Move each canonical body to the metadata literal where it belongs and delete the field.
Guidance is now tails only, `cliDetail` and `mcpDetail`, which also removes the question
of where a body is written: there is one place, and no chain to consult. Three guidance
blocks held nothing else and are gone entirely.

registry.js drops 1117 bytes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dp3J8UUgYxtw5vzJzvjkSf
The CLI carried four prose fields with a fallback chain between them, and one of
them — `helpDescription` — was authored on 45 commands and generated on the rest.
That ambiguity is why surfaces drifted: whichever field a reader looked at, some
other field might be the one actually rendered.

Prose now lives in a single `CommandText`, and `CommandSchema` keeps only grammar:

  summary      what is this command, in a list of ninety?   (mandatory)
  description  what does it do, and when do I reach for it? (mandatory)
  cliDetail    flags, argument shapes, terminal examples
  mcpDetail    sequencing and cross-tool hints

`--help`, the command list, the MCP tool description and `explain` are projections
computed where they render, so nothing derived is stored and no field can be both
input and output. The four-field model was validated against the whole surface
before the migration: all 67 commands reproduce their MCP text exactly and derive
their help body from `description`, so none needed a fifth field.

Making `summary` mandatory fixes a regression this branch introduced. 23 commands
had none, so the command list fell back to the full detail paragraph; lengthening
those descriptions earlier turned `click`'s list entry from 61 characters into 267,
`fill`'s into 214, `devices`' into 159. Every command now states its own line, and
a gate holds them under 72 characters, non-empty, period-free, and distinct from
the description.

Two duplications go with it: the per-command help printed its synopsis twice, once
as a header and again under `Usage:`, and `press` said "use longpress" in both its
body and its tail.
@thymikee

thymikee commented Aug 5, 2026

Copy link
Copy Markdown
Member Author

Re-reviewed exact head c78808c. The two prior projection/gate findings are resolved: one command text model now feeds metadata, executable definitions, explain, CLI help, and MCP with only surface-specific tails; the undocumented MCP-input ratchet pins exact tool.property identities. The newer mandatory-summary consolidation is coherent across family facets and schema-only CLI commands, and namespace changes remain aligned across package, registry manifest, and setup docs. No new code finding; mergeable and conflict-free. Completed static/integration/packaged-CLI/macOS/Linux checks are green; remaining native/package/coverage jobs are still running.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 5, 2026
Self-review follow-ups on the text model, all quality-only:

`command-text.ts` moves from `cli-schema/` to `commands/`. It is a command concept
that MCP reads as much as the CLI does; living under `cli-schema` made the MCP
surface import a CLI module to render its own tool descriptions.

`defineCommandFacet` no longer casts. It took a facet and returned it with the
schema completed, but claimed to return the input type, which needed
`as unknown as` — a double cast is the type system reporting that the signature
was wrong. Splitting `CommandFacetInput` from `CommandFacet` states the completion
in the return type, so both that cast and the registry's `as CommandSchema` go.

`push`'s summary duplicated its description apart from a trailing period, which the
gate missed by comparing exact strings; it now compares normalised text, and the
summary says something the description does not. `install-from-source`'s summary
loses a clause it did not need.
@thymikee
thymikee merged commit 4269ca6 into main Aug 5, 2026
33 checks passed
@thymikee
thymikee deleted the agent/update-mcp-registry-namespace branch August 5, 2026 16:51
thymikee added a commit to szdziedzic/agent-device that referenced this pull request Aug 6, 2026
* origin/main:
  chore: Update GitHub Sponsors usernames in FUNDING.yml
  fix: resolve Dependabot security alerts (callstack#1623)
  fix: update MCP registry namespace (callstack#1618)
  fix(ios): corroborate recorded tap outcomes (callstack#1605)
  fix(ios): never signal a recycled runner pid from a stale lease (callstack#1621)
  build: eliminate tsdown bundle warnings (callstack#1607)
  refactor(contracts): one viewport-root predicate for the whole repo (callstack#1613)
  refactor(contracts): name façade exports explicitly and retire the pin table (callstack#1614)
  refactor(ios): share one private-XCTest event bridge between gesture and text synthesis (callstack#1608)
  refactor(daemon): give the Maestro fallback and ambiguous-match details real types (callstack#1612)
  docs: clarify iOS drag synthesis profiles (callstack#1616)

# Conflicts:
#	scripts/layering/facade-symbols.ts
#	src/commands/capture/screenshot.ts
#	src/commands/recording/index.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants