Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
Expand Down
20 changes: 13 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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`):

Expand Down
6 changes: 3 additions & 3 deletions docs/PROBLEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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.**
4 changes: 2 additions & 2 deletions packages/mcp/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stackcanvas",
"version": "0.2.2",
"version": "0.2.3",
"type": "module",
"main": "dist/cli.js",
"bin": {
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/mcp/src/version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
18 changes: 14 additions & 4 deletions plugin/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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.",
"version": "0.2.2",
"author": { "name": "kp", "url": "https://github.com/pshenok" },
"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.3",
"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"
]
}
2 changes: 1 addition & 1 deletion plugin/.mcp.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"mcpServers": {
"stackcanvas": { "command": "npx", "args": ["-y", "stackcanvas@0.2.2"] }
"stackcanvas": { "command": "npx", "args": ["-y", "stackcanvas@0.2.3"] }
}
}
Loading