From af0e69c91b587afb9d753497d48265fccd61e277 Mon Sep 17 00:00:00 2001 From: kp Date: Mon, 13 Jul 2026 15:11:00 -0700 Subject: [PATCH 1/2] =?UTF-8?q?copy:=20agent-agnostic=20positioning=20?= =?UTF-8?q?=E2=80=94=20MCP-first,=20Claude=20Code=20as=20verified=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude-plugin/marketplace.json | 9 ++++++--- README.md | 20 +++++++++++++------- docs/PROBLEM.md | 6 +++--- packages/mcp/package.json | 2 +- plugin/.claude-plugin/plugin.json | 16 +++++++++++++--- 5 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index dc14d80..67cc897 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,12 +1,15 @@ { "name": "stackcanvas", - "description": "Live infrastructure canvas for Claude Code.", - "owner": { "name": "kp", "url": "https://github.com/pshenok" }, + "description": "Live infrastructure canvas for AI coding agents.", + "owner": { + "name": "kp", + "url": "https://github.com/pshenok" + }, "plugins": [ { "name": "stackcanvas", "source": "./plugin", - "description": "Live infrastructure canvas for Claude Code: watch the agent build your Terraform, drag new resources, the agent writes the HCL.", + "description": "Live infrastructure canvas: watch your agent build Terraform, drag new resources, the agent writes the HCL. This plugin wires it into Claude Code.", "category": "infrastructure" } ] diff --git a/README.md b/README.md index a39d859..5269b71 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # stackcanvas -Live infrastructure canvas for [Claude Code](https://claude.com/claude-code). +Live infrastructure canvas for AI coding agents — any agent that speaks [MCP](https://modelcontextprotocol.io) ([Claude Code](https://claude.com/claude-code) is the CI-verified path). The agent writes and plans your Terraform — stackcanvas shows it as a living diagram. Drag new resources onto the canvas; the agent turns them into idiomatic HCL. No SaaS, no credentials leave your machine: everything runs on @@ -23,7 +23,13 @@ localhost, reading your local state and plan. canvas highlights what will change. Only the agent executes Terraform — the canvas has no apply button by design. -## Install (Claude Code) +## Install + +stackcanvas is a standard MCP stdio server (`npx -y stackcanvas`) — point any +MCP-capable coding agent at it. The paths below, in order of how battle-tested +they are: + +### Claude Code (CI-verified) claude plugin marketplace add pshenok/stackcanvas claude plugin install stackcanvas@stackcanvas @@ -42,12 +48,12 @@ Or without the plugin system: Then, inside a repo with Terraform, just ask: *open the stackcanvas canvas for this repo*. -## Other MCP clients +### Other MCP clients -stackcanvas is a standard [MCP](https://modelcontextprotocol.io) server, so -any MCP-compatible client can run it. The snippets below are **reported to -work as standard MCP servers; not yet CI-verified** — only the Claude Code -path above is exercised in CI. +The snippets below are **reported to work; not yet CI-verified** — only the +Claude Code path above is exercised in CI. Codex CLI and other MCP-capable +agents should work with the equivalent stdio config (`npx -y stackcanvas`) — +untested, reports welcome in issues. **Cursor** (`.cursor/mcp.json`): diff --git a/docs/PROBLEM.md b/docs/PROBLEM.md index f0e27fe..e1d9f9c 100644 --- a/docs/PROBLEM.md +++ b/docs/PROBLEM.md @@ -60,7 +60,7 @@ Supervision fail-safety: if the state is corrupt or terraform fails, the canvas 1. The palette (provider packs — AWS first, plus an "any terraform type" field for everything else) → drag → a **dashed draft node** appears on the canvas. A draft is not reality — the visual grammar is strict: what exists is solid, what's intended is dashed. 2. You pull edges from the draft to existing nodes ("connect it to *this* VPC" — as a gesture, not an address from memory). The inspector has a free-text "wishes" field ("db.t4g.micro, no multi-AZ"). Right-click on an existing node — "request change" or "mark for removal" (rendered struck-through, dashed). An edge drawn between two existing nodes = a "connect them" request. 3. The **Send to agent** button collects everything into a single intent JSON with strict resource addresses: `{add: [{type, wishes, connect_to: ["aws_vpc.main"]}], modify: [{address, wishes}], remove: [{address}]}`. No "which of the three webs" ambiguity — addresses come from the graph, not from the human's memory. -4. The agent receives the intent through a blocking MCP tool, `await_canvas_intent`: it waits in a loop (45-second iterations — tuned to MCP client request timeouts, a number discovered by live testing), and the moment you hit Send to agent, the intent arrives as the tool's return value. A bundled skill teaches Claude Code the whole cycle: open the canvas → run a plan → await intents → write HCL → plan again → await again. The user runs one command and then lives in the browser with a mouse. +4. The agent receives the intent through a blocking MCP tool, `await_canvas_intent`: it waits in a loop (45-second iterations — tuned to MCP client request timeouts, a number discovered by live testing), and the moment you hit Send to agent, the intent arrives as the tool's return value. A bundled skill teaches the agent the whole cycle (shipped for Claude Code; the tool descriptions alone carry the loop for other MCP clients): open the canvas → run a plan → await intents → write HCL → plan again → await again. The user runs one command and then lives in the browser with a mouse. 5. **Only the agent writes HCL.** The tool contains zero code-generation templates — a deliberate architectural bet. Template codegen (the Brainboard path) is an eternal race to cover thousands of resource types, producing code that's foreign to your repo. The agent writes in *your repository's* style, knows its modules and conventions, and covers all of Terraform at once. Our "codegen" gets cheaper and smarter with every model release — for free. 6. The loop closes by itself: the agent writes code → runs a plan → the watcher notices → drafts dissolve into real nodes with plan statuses → the human reviews the diff → tells the agent to apply. If no agent is waiting (or you use a different client), a "Copy as prompt" button turns the intent into a ready-to-paste text prompt. @@ -75,11 +75,11 @@ The concrete mechanisms that make trusting us unnecessary: - **The server binds to `127.0.0.1` only.** Not "behind a login" — physically unreachable from outside. There is no cloud of ours to run your infrastructure through — state, HCL, and credentials have nowhere to go. (One honest exception: an **opt-in** anonymous telemetry counter — install/reopen/intent counts only, never resource data — declinable at first run, documented in [TELEMETRY.md](../TELEMETRY.md).) - **The tool holds no credentials at all** (v1): it reads local state/plan files. Even the future account scan uses your local `~/.aws` profile with read-only sessions. - **Secrets are masked before they reach the UI:** everything the state marks `sensitive` (passwords, keys) becomes `•••` inside the parser — verified by live testing against `random_password`. -- **The agent is your own.** We don't proxy tokens and don't embed our own LLM: it's the Claude Code (or any MCP client) you already pay for, with your key. +- **The agent is your own.** We don't proxy tokens and don't embed our own LLM: it's whatever MCP-capable coding agent you already pay for — Claude Code, Cursor, Windsurf — with your key. - **The UI has no `terraform apply` control — by design.** The primary button is "Send to agent": it hands your intent over, nothing more. The canvas proposes; only the agent executes, and the skill explicitly forbids `terraform apply` without an explicit human request. Today this is supervision *by convention*; in the Studio stage it becomes supervision *by construction* — a credential broker will physically withhold mutate-scope credentials until a human approves. ### Packaging: why it spreads on its own -Everything ships as one npm package (`npx stackcanvas` = an MCP server with the UI embedded) plus a Claude Code plugin (config + skill). Installation is one command; Cursor and Windsurf are reported to work as standard MCP clients; Claude Code is the CI-verified path. Extension is data, not code: a new cloud = a PR with a provider pack (palette + containment rules + icons) — a deliberate moat mechanic: the gesture→HCL corpus and provider packs accrete in the OSS repo the way oh-my-zsh accretes plugins. +Everything ships as one npm package (`npx stackcanvas` = a standard MCP stdio server with the UI embedded) plus a Claude Code plugin (config + skill) for one-command setup there. Installation is one command; Cursor and Windsurf are reported to work as standard MCP clients; Claude Code is the CI-verified path. Extension is data, not code: a new cloud = a PR with a provider pack (palette + containment rules + icons) — a deliberate moat mechanic: the gesture→HCL corpus and provider packs accrete in the OSS repo the way oh-my-zsh accretes plugins. **The anti-scope is part of the solution:** not a PaaS (we don't hide Terraform), not monitoring (we won't rebuild Datadog — only signals available with zero setup), never SaaS, no codegen of our own. Every "not" keeps the product in the one empty niche: **eyes and a steering wheel for the human whose agent already has hands.** diff --git a/packages/mcp/package.json b/packages/mcp/package.json index fd66e4c..f9c78fa 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -29,7 +29,7 @@ "@types/node": "^22.0.0", "tsup": "^8.0.0" }, - "description": "Live infrastructure canvas for Claude Code: see what your AI agent is about to do to your Terraform \u2014 and approve it \u2014 before it happens. Local-first, credentials never leave your machine.", + "description": "Live infrastructure canvas for AI coding agents (MCP): see what your agent is about to do to your Terraform \u2014 and approve it \u2014 before it happens. Local-first, credentials never leave your machine.", "license": "MIT", "repository": { "type": "git", diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index ed5a944..ddc0042 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,10 +1,20 @@ { "name": "stackcanvas", - "description": "Live infrastructure canvas for Claude Code: watch the agent build your Terraform, drag new resources, the agent writes the HCL.", + "description": "Live infrastructure canvas: watch your agent build Terraform, drag new resources, the agent writes the HCL. This plugin wires it into Claude Code.", "version": "0.2.2", - "author": { "name": "kp", "url": "https://github.com/pshenok" }, + "author": { + "name": "kp", + "url": "https://github.com/pshenok" + }, "homepage": "https://github.com/pshenok/stackcanvas", "repository": "https://github.com/pshenok/stackcanvas", "license": "MIT", - "keywords": ["terraform", "opentofu", "infrastructure", "canvas", "diagram", "mcp"] + "keywords": [ + "terraform", + "opentofu", + "infrastructure", + "canvas", + "diagram", + "mcp" + ] } From c8f67f3749824ce877a55b66c0e5fbd8e114b3a4 Mon Sep 17 00:00:00 2001 From: kp Date: Mon, 13 Jul 2026 15:11:00 -0700 Subject: [PATCH 2/2] chore: v0.2.3 --- packages/mcp/package.json | 2 +- packages/mcp/src/version.ts | 2 +- plugin/.claude-plugin/plugin.json | 2 +- plugin/.mcp.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/mcp/package.json b/packages/mcp/package.json index f9c78fa..f1357d3 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "stackcanvas", - "version": "0.2.2", + "version": "0.2.3", "type": "module", "main": "dist/cli.js", "bin": { diff --git a/packages/mcp/src/version.ts b/packages/mcp/src/version.ts index 2bddc5f..4570851 100644 --- a/packages/mcp/src/version.ts +++ b/packages/mcp/src/version.ts @@ -10,4 +10,4 @@ // script) is release-engineering scope (docs/SPEC.md Telemetry chapter, §5 / // increment 8), not part of the emitter wiring here — keep the two in sync // by hand until that lands. -export const VERSION = '0.2.2' +export const VERSION = '0.2.3' diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index ddc0042..c5f4d5e 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "stackcanvas", "description": "Live infrastructure canvas: watch your agent build Terraform, drag new resources, the agent writes the HCL. This plugin wires it into Claude Code.", - "version": "0.2.2", + "version": "0.2.3", "author": { "name": "kp", "url": "https://github.com/pshenok" diff --git a/plugin/.mcp.json b/plugin/.mcp.json index 37399c0..f06ef1c 100644 --- a/plugin/.mcp.json +++ b/plugin/.mcp.json @@ -1,5 +1,5 @@ { "mcpServers": { - "stackcanvas": { "command": "npx", "args": ["-y", "stackcanvas@0.2.2"] } + "stackcanvas": { "command": "npx", "args": ["-y", "stackcanvas@0.2.3"] } } }