Skip to content

feat(cli): add the instatic CLI with browser sign-in and batch calls - #17

Merged
flyingwebie merged 6 commits into
mainfrom
feat/instatic-cli
Nov 3, 2026
Merged

flyingwebie merged 6 commits into
mainfrom
feat/instatic-cli

Conversation

@flyingwebie

Copy link
Copy Markdown
Owner

Summary

Agents working through MCP reported that the Instatic connection kept dropping, and shell-based agents had no reliable way to drive a site. This adds an unofficial instatic CLI (packages/cli) that is a thin client of the existing MCP endpoint, plus server fixes that make MCP sessions more reliable and steer agents toward the God Mode panels for CSS and JS.

CLI

  • instatic login --url <site> signs in through the browser (OAuth 2.1 with PKCE, loopback callback, dynamic client registration). --with-token, --no-browser, and --port cover token use and SSH. Saved OAuth sessions refresh themselves without reusing rotated refresh tokens.
  • call, batch, tools, guide, wait-editor, upload, publish, status, profiles, and mcp (a stdio proxy for desktop clients).
  • Stable exit codes. Exit 5 means the Site editor is not open, so an agent knows to run wait-editor and retry.
  • packages/cli/README.md and --help state that the CLI is unofficial and not made or endorsed by the Instatic project.

Server and MCP

  • Browser tools wait up to 15 seconds for the editor workspace to reconnect before failing. The error carries _meta["instatic/error"] = "workspace_not_connected".
  • Editor tabs keep a stable claim across reconnects, so a reconnecting tab does not take the workspace from the active one.
  • OAuth redirects carry iss (RFC 9207), and the metadata advertises it.
  • media_upload follows the installation's upload policy (Settings → Uploads), not a hardcoded image list.
  • Server instructions and the agent's code tools now direct page-only JS to site_write_page_script and shared JS to a narrowly scoped site_write_code_asset. Scripts therefore load only on the pages that use them.

Impact

  • Users and agents get a shell command that prints JSON and returns meaningful exit codes, plus batch for many calls over one connection.
  • Existing MCP clients see the new error metadata and the longer editor wait. Nothing is removed.
  • Publishing the npm package is not part of this PR; it remains a manual step.
  • Not yet covered: site import/export, backups, plugins, npm dependencies, user management, and Visual Component tools.

Verification

  • bun run build
  • bun test (7,669 pass, 0 fail)
  • bun run lint
  • Docker/deployment check, if relevant (not run; no deployment changes)

Also ran the bundled CLI under Node against a running server: login, token login, status, call, batch, and exit code 5 with no editor open.

Checklist

  • Tests cover behavior changes.
  • Docs were updated when behavior, config, deployment, or public surfaces changed. (docs/features/cli.md, docs/features/mcp-connectors.md, docs/features/agent.md, docs/features/god-mode.md, docs/README.md)
  • No compatibility shim was added for old pre-release behavior.
  • No secrets, local databases, uploads, or generated artifacts are included.

flyingwebie and others added 4 commits November 2, 2026 17:58
Agents reported the Instatic MCP tools "keep disconnecting". The cause was
the browser bridge, not the MCP endpoint:

- A browser tool failed instantly when the editor tab's stream was in a
  routine reconnect gap (idle recycle, network blip, restart). Calls now
  wait up to 15s for the workspace (waitForEditorWorkspace).
- Two open editor tabs stole the workspace from each other on every idle
  recycle, cancelling in-flight calls. Tabs now send a stable tabId and keep
  their claim across reconnects; superseded tabs stay connected and take
  over when the active tab closes.
- Interrupted calls surfaced the in-app chat message "AI chat stream ended
  before tool result arrived". The transport now raises a typed
  BrowserToolInterruptedError recording whether the editor received the
  call: undelivered calls retry once transparently, delivered ones tell the
  agent the edit may or may not have applied and to re-read first.

Agents now follow the God Mode build practice: single-page JS goes in the
new site_write_page_script tool (the page script the JS panel edits, loaded
only on that page), and a new script written with site_write_code_asset
must declare its runtime.scope instead of silently loading on every page.
MCP server instructions, the site system prompt, and docs are updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A new npm package, `instatic` (packages/cli), run with `npx instatic` or
`bunx instatic`. It is a thin client of the existing /_instatic/mcp
endpoint using a personal access token, so capability checks, the editor
bridge, and drafts-until-publish behave exactly as for any MCP client.

Commands: login/logout/profiles, status, guide (prints the server's build
rules, which shell agents otherwise never see), wait-editor, tools, call,
publish, upload, and `mcp` — a local stdio MCP proxy for desktop clients
that replaces mcp-remote and reconnects after site restarts.

Reliability: failures are classified (auth / unreachable / server) into
stable exit codes; reads retry on network and gateway errors; tool calls
are re-sent only when the request provably never reached the server, so a
write is never applied twice; tool calls get a 150s timeout to cover the
server's editor wait plus its 90s per-call limit.

The bundle (scripts/build-cli.ts, `bun run cli:build`) inlines every
dependency into one file for Node 20+. The MCP SDK import gate now scans
packages/ and allows packages/cli/. Publishing to npm stays a manual step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…upload policy

- OAuth redirects (approve and deny) carry `iss` and the metadata
  advertises authorization_response_iss_parameter_supported (RFC 9207),
  so a client signed in to several sites can reject a mixed-up code.
- The "no workspace connected" tool error carries
  `_meta: { "instatic/error": "workspace_not_connected" }`, so clients can
  tell "open the editor, then retry" from other errors without matching
  message text. The retry path's fallback returns the same result.
- media_upload accepts the installation's upload policy (Settings →
  Uploads) instead of a hardcoded image list, matching the Media workspace.
- handleMcpHttp gains a workspaceWaitMs test seam.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- `instatic login --url <site>` signs in through the browser (OAuth 2.1 +
  PKCE, loopback callback, dynamic client registration, constant-time
  state check, RFC 9207 iss check, resource taken from the site's own
  metadata). `--with-token` / `--token` keep personal access tokens;
  `--no-browser` and `--port` cover SSH.
- OAuth profiles refresh themselves. Rotated refresh tokens must never be
  reused, so the profile is read from disk per request and refreshes run
  only under a cross-process config lock after re-reading it; every config
  write is locked and atomic. One renew-and-resend after a 401.
- A profile's credentials are never sent to a different --url.
- `instatic batch` runs many tool calls over one connection (JSON array or
  NDJSON), one JSON line per result, stop-on-failure or --keep-going.
- Exit code 5 when a call needs the editor and none is open; per-command
  --help; `call --timeout`; `tools --json --schemas`.
- README and --help state the CLI is unofficial; README lists all tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Caution

Review failed

The pull request is closed.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

flyingwebie and others added 2 commits November 3, 2026 13:11
npm rejects the unscoped name as too similar to the existing ecstatic
package. The command stays `instatic`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Republish so the npm package page picks up the README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@flyingwebie
flyingwebie merged commit 2241788 into main Nov 3, 2026
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