Skip to content

@rgstr/mcp: rgstr as MCP tools (plan/apply, confirm before apply); API errors print code + hint - #31

Merged
HeyGarrison merged 13 commits into
mainfrom
devin/1792533348-rgstr-mcp
Oct 21, 2026
Merged

HeyGarrison merged 13 commits into
mainfrom
devin/1792533348-rgstr-mcp

Conversation

@HeyGarrison

@HeyGarrison HeyGarrison commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

rgstr becomes usable by an agent without a shell, and every failure says its next step.

Repository → pnpm workspace. @rgstr/cli moves to packages/cli (unchanged bin, files, tests; its build copies the root skills/ in so the npm tarball still ships the skill), @rgstr/mcp 0.0.1 is packages/mcp. Changesets' root package can't be versioned in a workspace, hence two package dirs. CI/release workflows run the same root scripts (pnpm build/test/typecheck/lint are now pnpm -r). First publish of @rgstr/mcp needs the npm trusted publisher (or a token) added for the new package name.

CLI flows become importable (packages/cli/src): claim-flow.ts (resolveClaimTargets → planClaims → applyClaims, plus checkOne, previewJson, outcomeJson), route-flow.ts (planRoutes → renderRoutePlan → applyRoutes), check-flow.ts (checkProject), api-types.ts. index.ts keeps commander + prompting only. The human lines go through a say: Say callback — the terminal in the CLI, a collected plan in MCP — so the plan the MCP returns is byte-for-byte what rgstr claim prints. No behaviour change; cli.test.ts is unchanged except for the new error tests.

Errors print their next step.

class RgstrApiError { status; code?: string; hint?: string; requestId?: string; get details() }
toErrorEnvelope(err) → { error, status, code?, hint?, requestId? }   // --json
renderError(env)     → "Error: <error>\n<hint>"                       // hint is the last line

claim --check reads the platform's new 422 proof_not_found (outcome in details) as "no proof found yet" and prints its hint.

@rgstr/mcp (@modelcontextprotocol/sdk pinned 1.32.1, stdio). Tools: rgstr_whois, rgstr_search, rgstr_check, rgstr_claim_plan/rgstr_claim_apply, rgstr_claim_check, rgstr_setup_plan/rgstr_setup_apply, rgstr_route_plan/rgstr_route_apply, rgstr_login. Plan tools return the CLI's plan text + JSON with a plan_id; apply takes only that plan_id and applies exactly that plan, once — there is no apply without a plan. A plan whose files (README / .npmrc / pip.conf, byte fingerprint), organization or host changed since it was shown answers isError code plan_stale, hint "Run rgstr__plan again and show the user the new plan", and writes nothing. Every apply description and the server instructions carry the rule from SKILL.md / llms.txt: show the user the plan, get confirmation, then apply. Auth is the CLI's stored device login (RGSTR_HOME), RGSTR_TOKEN/RGSTR_ORG/RGSTR_URL honoured; rgstr_login runs device-code and, like rgstr login, makes the user's one organization the active one via the shared packages/cli/src/login.ts — if /api/v1/me fails after approval, both forget the previously stored organization and answer no_organization ("Signed in, but no organization was selected; run rgstr org use <slug>"); no credentials → { error: "Not logged in.", code: "no_credentials", hint: "Run npx @rgstr/cli@latest login" }. Errors are isError results: the JSON envelope first, then the renderError text, so the hint is the last line as in the CLI.

Tests (packages/mcp/src/__tests__/server.test.ts, in-memory transport, mocked fetch): tool list + rule text, whois/search, claim_plan posts/writes nothing, claim_apply = exactly one POST /api/v1/registry/claims and the badge with the real token, plan_id single-use and required, plan_stale on a README/config edit or organization change since the plan, no-credentials message, route/setup plan→apply; login.test.ts covers the organization selection and the failed-lookup path. Docs: packages/mcp/README.md (Claude Desktop / Cursor / Devin config), skills/rgstr/SKILL.md "Without a shell: MCP" section + errors rule (mirrored in benchmarks-platform's lib/registry/skill.ts on the companion PR), changesets for both packages.

Companion: computesdk/benchmarks-platform "rgstr API: every 4xx names its next step".

Stdio against prod

node packages/mcp/scripts/stdio-smoke.mjs computesdk (initialize → tools/list → tools/call):

# initialize → {"serverInfo":{"name":"rgstr","version":"0.0.1"},"instructions":"rgstr (https://rgstr.org) is the package name registry over npm and PyPI: who claims a name, how much to trust it, and where installs of it are served from. …"}
# tools/list → rgstr_whois, rgstr_search, rgstr_check, rgstr_claim_plan, rgstr_claim_apply, rgstr_claim_check, rgstr_setup_plan, rgstr_setup_apply, rgstr_route_plan, rgstr_route_apply, rgstr_login
# tools/call rgstr_whois computesdk →
{
  "name": "computesdk",
  "url": "https://rgstr.org/registry/computesdk",
  "trust": "claimed",
  "state": "held",
  "holder": {
    "name": "ComputeSDK",
    "slug": "computesdk",
    "url": "https://rgstr.org/~computesdk",
    "via": "computesdk",
    "kind": "package",
    "status": "verified"
  },
  "routes": [],
  "relationship": null,
  "hiddenCopies": null
}

Link to Devin session: https://app.devin.ai/sessions/fe389c0a51364fe5bf8a7724b7049bc9
Open in Devin Desktop: https://app.devin.ai/desktop/session/fe389c0a51364fe5bf8a7724b7049bc9?variant=devin
Requested by: @HeyGarrison

Link to Devin session: https://app.devin.ai/sessions/fe389c0a51364fe5bf8a7724b7049bc9
Open in Devin Desktop: https://app.devin.ai/desktop/session/fe389c0a51364fe5bf8a7724b7049bc9?variant=devin
Requested by: @HeyGarrison

…I errors print code + hint

- pnpm workspace: packages/cli (@rgstr/cli) and packages/mcp (@rgstr/mcp 0.0.1)
- claim, route and check flows moved out of index.ts into claim-flow.ts, route-flow.ts, check-flow.ts so the MCP server runs exactly the CLI's code
- RgstrApiError carries code, hint, requestId, details; the CLI prints the hint last, --json includes code/hint/requestId; claim --check reads 422 proof_not_found as 'no proof yet'
- MCP server: rgstr_whois/search/check, claim/setup/route plan+apply, rgstr_claim_check, rgstr_login; errors are isError results with the same envelope
- SKILL.md: MCP section + the errors rule; changesets for both packages
@devin-ai-integration

Copy link
Copy Markdown
Contributor

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin resolved all 11 findings on d8e33c8

Fixed by Devin (11)

  • Device login leaves organization unset
  • Concurrent logins lose a registry's credentials
  • Completed login still prompts for approval
  • Concurrent login loses tool errors
  • Login guidance points to another host
  • Claim plan promises an impossible pull request
  • Badge pull requests become unreachable
  • Changed setup configuration still gets deleted
  • Stored plans can target another organization
  • Failed login hangs the tool call
  • MCP hints lose last-line placement

View all findings in Devin Review

Devin Review

- rgstr_login no longer hangs when the device-code request fails before a prompt; after login the one organization becomes active (rememberOrganization, shared with rgstr login)
- stored plans remember the organization and host they were made for; apply refuses a different one
- rgstr_setup_apply plans again and refuses when the files changed since the plan
- isError text puts the envelope first so the hint is the last line, as in the CLI
devin-ai-integration[bot]

This comment was marked as resolved.

…m (fingerprint includes the current file bytes)
devin-ai-integration[bot]

This comment was marked as resolved.

…; a failed /api/v1/me after login forgets the stored organization

- rgstr_claim_apply / rgstr_setup_apply / rgstr_route_apply accept plan_id only; the name/raw-argument paths are gone
- claim plans carry a byte fingerprint of the READMEs they would edit (setup already did); a changed file, organization or host answers isError code plan_stale, hint "Run rgstr_<x>_plan again and show the user the new plan"
- rememberOrganization (rgstr login and rgstr_login): when /api/v1/me fails after approval, the remembered organization is cleared and the error says "Signed in, but no organization was selected; run `rgstr org use <slug>`" (no_organization)
- SKILL.md / README: no apply without a plan; plan_stale
… so rgstr_claim_apply can open the badge pull request
devin-ai-integration[bot]

This comment was marked as resolved.

- search: rgstr.owner is the organization's display name (a string); the CLI prints it again (test against the real API shape)
- rgstr_login tracks the outcome: call again for waiting / approved (and for which organization) / rejected / expired / failed with the next step; no_credentials hints mention the last login outcome; the flow is injectable for tests
- claim plans fingerprint the target manifests too (a renamed package is a new plan); route plans are pinned to the organization they were made for (plan_stale on mismatch)
devin-ai-integration Bot pushed a commit that referenced this pull request Oct 21, 2026
…nization switch, search shows the organization
HeyGarrison added a commit that referenced this pull request Oct 21, 2026
* @rgstr/mcp: rgstr as MCP tools (plan/apply, confirm before apply); API errors print code + hint

- pnpm workspace: packages/cli (@rgstr/cli) and packages/mcp (@rgstr/mcp 0.0.1)
- claim, route and check flows moved out of index.ts into claim-flow.ts, route-flow.ts, check-flow.ts so the MCP server runs exactly the CLI's code
- RgstrApiError carries code, hint, requestId, details; the CLI prints the hint last, --json includes code/hint/requestId; claim --check reads 422 proof_not_found as 'no proof yet'
- MCP server: rgstr_whois/search/check, claim/setup/route plan+apply, rgstr_claim_check, rgstr_login; errors are isError results with the same envelope
- SKILL.md: MCP section + the errors rule; changesets for both packages

* @rgstr/mcp: review fixes

- rgstr_login no longer hangs when the device-code request fails before a prompt; after login the one organization becomes active (rememberOrganization, shared with rgstr login)
- stored plans remember the organization and host they were made for; apply refuses a different one
- rgstr_setup_apply plans again and refuses when the files changed since the plan
- isError text puts the envelope first so the hint is the last line, as in the CLI

* @rgstr/mcp: a setup plan is applied only to the files it was made from (fingerprint includes the current file bytes)

* @rgstr/mcp: apply takes only a plan_id; stale plans answer plan_stale; a failed /api/v1/me after login forgets the stored organization

- rgstr_claim_apply / rgstr_setup_apply / rgstr_route_apply accept plan_id only; the name/raw-argument paths are gone
- claim plans carry a byte fingerprint of the READMEs they would edit (setup already did); a changed file, organization or host answers isError code plan_stale, hint "Run rgstr_<x>_plan again and show the user the new plan"
- rememberOrganization (rgstr login and rgstr_login): when /api/v1/me fails after approval, the remembered organization is cleared and the error says "Signed in, but no organization was selected; run `rgstr org use <slug>`" (no_organization)
- SKILL.md / README: no apply without a plan; plan_stale

* @rgstr/mcp: pr is part of the claim plan (rgstr_claim_plan pr: true), so rgstr_claim_apply can open the badge pull request

* @rgstr/mcp: a claim plan promises the pull request only when the checkout has an origin remote

* docs: one story across README, SKILL.md, MCP README and --help — claimed, badge-only proof, person reviews, claim --check records proof

* changeset: docs pass

* @rgstr/cli + @rgstr/mcp: review fixes from the docs lane

- search: rgstr.owner is the organization's display name (a string); the CLI prints it again (test against the real API shape)
- rgstr_login tracks the outcome: call again for waiting / approved (and for which organization) / rejected / expired / failed with the next step; no_credentials hints mention the last login outcome; the flow is injectable for tests
- claim plans fingerprint the target manifests too (a renamed package is a new plan); route plans are pinned to the organization they were made for (plan_stale on mismatch)

* @rgstr/mcp: a login note is only shown for the registry the login was for

* @rgstr/mcp: the login note reads a snapshot of the outcome, and can never replace the tool's own error

* @rgstr/mcp: read the login outcome once, after resolving the registry

* docs: merge #31 head b811560; rgstr_login outcome, plan_stale on organization switch, search shows the organization
@HeyGarrison
HeyGarrison merged commit 1dff730 into main Oct 21, 2026
2 checks 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