Skip to content

feat(mcp): add create_stack + get_stack tools (CEO wedge unblock)#30

Merged
mastermanas805 merged 2 commits into
masterfrom
feat/create-stack-mcp-tool
May 29, 2026
Merged

feat(mcp): add create_stack + get_stack tools (CEO wedge unblock)#30
mastermanas805 merged 2 commits into
masterfrom
feat/create-stack-mcp-tool

Conversation

@mastermanas805
Copy link
Copy Markdown
Member

What

Closes the wedge gap surfaced by the QA round-2 hello-world.sh run: an MCP-only agent had no path to ship a bundle (postgres + redis + deploy) in a single tool call. /stacks/new on the API is OptionalAuth — anonymous callers get a 24h-TTL stack with a live URL on *.deployment.instanode.dev, no card required — but the MCP exposed no wrapper for it. This PR:

  • Adds create_stackPOST /stacks/new (multipart). Accepts name, manifest YAML, and service_tarballs (map of service-name → base64 gzip tarball). Anonymous-friendly. Returns stack_id, per-service URLs + status, env, expires_in, and the same upgrade_jwt / claim-URL block every create_* surfaces.
  • Adds get_stackGET /stacks/{slug}. Polls a stack until every service is healthy. Anonymous-friendly (uses the public /stacks/{slug} route, not the dashboard-only /api/v1/stacks/{slug}).

Bundled fixes (same file touch — ship-while-we're-here)

  • FINDING-7create_deploy.env description claimed defaults to "production". Per CLAUDE.md convention fix(mcp): wave 3 — BugHunt 2026-05-20 T17 P2 fixes #11 / mig 026 the server default is "development". Description + client.ts typedoc updated.
  • FINDING-8 — all 7 provisioning tools (create_postgres / vector / cache / nosql / queue / storage / webhook) silently dropped the env param. Now they accept and forward env to /<resource>/new as a JSON body field. New nameAndEnvArg / envArg shared schemas keep tool signatures uniform. Empty/undefined env stays off the wire so the server-side default still applies.
  • FINDING-12 / BIZ FINDING-4create_cache description quoted halved Redis limits ("hobby 25 MB / pro 256 MB"). Real values per api/plans.yaml: hobby 50 MB / hobby_plus 50 MB / pro 512 MB / growth 1024 MB / team unlimited. Fixed.

Why

CEO ask: prove or disprove the bundle thesis. Pre-this-PR, an MCP-only agent walking the documented surface could only stand up resources individually; assembling them into a running app required create_deploy (gated by INSTANODE_TOKEN) — which broke the "no account, no Docker, no setup" promise the wedge marketing makes. create_stack collapses the multi-call dance into one anonymous-friendly call.

Tests

  • 287 / 287 pass (was 248 / 248 — +39 new tests across client-unit, tools-unit, integration).
  • 99.83% line / 95.71% branch coverage overall; only the existing if (!INSTANODE_MCP_NO_LISTEN) listen block remains uncovered.
  • diff-cover: 100% patch coverage on the 420 changed src/ lines.

New harness:

  • test/mock-api.ts gains /stacks/new (multipart) + /stacks/{slug} (GET) routes mirroring the live API contract (services-section YAML parse, declared-service file-part matching, build→healthy auto-flip on poll).
  • EXPECTED_TOOLS bumped 17 → 19.
  • Branch tests cover every urlPart ternary (exposed-with-url / exposed-pending / cluster-internal) and every optional-field render path.
  • Anonymous-friendliness pinned by a no-INSTANODE_TOKEN test that asserts Authorization is never sent.

Live verify (rule-13, partial — see hello-world-success.log)

Spawned the rebuilt MCP (node dist/index.js), pointed at port-forwarded prod API:

  • tools/list → 19 tools incl. create_stack, get_stack, with correct schemas.
  • tools/call create_stack (anonymous, no INSTANODE_TOKEN) → HTTP 202, stack_id stk-2a0cbb2e, env defaulted to development.
  • Kaniko build completed (~33s).
  • tools/call get_stack polled status correctly through building → failed.

NOT verified: the final *.deployment.instanode.dev URL. The deploy phase failed at a pre-existing api/k8s RBAC gap (system:serviceaccount:instant:instant-api cannot update namespaces — see api/internal/handlers/stack.go:366). This same gap blocks every anonymous /deploy/new and /stacks/new today and is out of scope of this MCP PR. Filing a follow-up against the api repo.

Note on port-forward: used ONLY to bypass the anonymous /24 daily cap that prior QA round depleted. Request shape is identical to the public api.instanode.dev endpoint (same handler, same OptionalAuth route).

Surface checklist (rule 22)

  • mcp/package.json — 0.11.1 → 0.12.0 (new tool surface)
  • mcp/README.mdcreate_stack / get_stack rows added
  • mcp/src/index.ts + client.ts — new tool + client + types
  • mcp/test/ — mock + unit + integration coverage
  • [N/A] api/plans.yaml — limits already correct; only the MCP description drifted
  • [follow-up PR] content/llms.txt — needs create_stack + get_stack rows
  • [N/A] CLAUDE.md — wedge tools live in the mcp repo surface, not CLAUDE.md

Follow-ups (separate PRs / repos)

  1. api repo (P0) — RBAC patch: grant instant-api ServiceAccount namespaces.update on instant-stack-* namespaces. Until this lands, the wedge produces a stack_id but no live URL.
  2. content repollms.txt row updates for create_stack / get_stack.
  3. CLI parityinstant stack new once the api gap above is resolved.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 (1M context) noreply@anthropic.com

mastermanas805 and others added 2 commits May 29, 2026 12:10
Closes the wedge gap surfaced by hello-world.sh QA round 2: an MCP-only
agent could provision postgres/cache/etc one at a time, but had no path
to ship a *bundle* in a single call. /stacks/new on the api is
OptionalAuth — anonymous callers get a 24h-TTL stack with a live URL on
*.deployment.instanode.dev, no card, no dashboard round-trip — but the
MCP exposed no wrapper for it. This PR ships:

* create_stack — POST /stacks/new (multipart): name + manifest YAML +
  one base64 gzip tarball per service. Anonymous-friendly (no
  INSTANODE_TOKEN required). Returns stack_id, per-service URL /
  status, env, expires_in, plus the same upgrade_jwt / claim-URL block
  every create_* surfaces. Per-tarball 50 MiB cap enforced client-side,
  mirroring create_deploy.
* get_stack — GET /stacks/{slug}: anonymous-friendly poll for stack +
  per-service status. Uses the public /stacks/{slug} route (StackResponse
  shape, no auth) — not the dashboard-only /api/v1/stacks/{slug}
  (flatter, requires auth).

Bundled fixes (same file touch, ship-while-we're-here, all from the
CLI-MCP QA backlog):

* FINDING-7 — create_deploy's `env` param description said
  `defaults to "production"`. Per CLAUDE.md convention #11 / mig 026 the
  server default is "development". Description + client.ts typedoc
  updated.
* FINDING-8 — all 7 provisioning tools (create_postgres / vector /
  cache / nosql / queue / storage / webhook) silently dropped the `env`
  param. Now they accept `env` and forward it to /<resource>/new as a
  JSON body field. New `nameAndEnvArg` / `envArg` shared schemas keep
  the tool signatures uniform. Empty-string env stays off the wire so
  the server default still applies.
* FINDING-12 / BIZ FINDING-4 — create_cache description quoted halved
  Redis limits ("hobby 25 MB / pro 256 MB"). Real values per
  api/plans.yaml: hobby 50 MB / hobby_plus 50 MB / pro 512 MB /
  growth 1024 MB / team unlimited. Fixed.

Tests
-----
+39 tests across client-unit, tools-unit, integration. Mock-api gains
/stacks/new multipart route + /stacks/{slug} GET. EXPECTED_TOOLS bumped
17→19. Anonymous-friendly path pinned via a no-INSTANODE_TOKEN test
that asserts the Authorization header never goes out. Per-handler
branch tests cover all three urlPart ternaries (exposed-with-url /
exposed-pending / cluster-internal) and every optional-field render
path for both create_stack and get_stack.

Local: 287/287 pass, 99.83% line / 95.71% branch coverage. diff-cover
100% patch coverage on the 420 changed src/ lines.

Live verify (rule-13, partial)
------------------------------
MCP server spawned (node dist/index.js), pointed at port-forwarded
api.instanode.dev (used ONLY to bypass the anonymous /24 daily cap which
prior QA depleted — request shape identical). tools/list returns 19
tools incl. create_stack + get_stack. tools/call create_stack succeeded
HTTP 202 with stack_id stk-2a0cbb2e at the anonymous tier, env defaulted
to "development". Kaniko image build completed (~33s).

NOT verified: the final *.deployment.instanode.dev URL. Build phase
succeeded; deploy phase failed at a pre-existing api/k8s RBAC gap
(`system:serviceaccount:instant:instant-api` cannot update namespaces it
just created — see api/internal/handlers/stack.go:366). This same RBAC
gap blocks every /deploy/new + /stacks/new anonymous flow today and is
out of scope of this MCP PR. Follow-up filed for the api repo.

Evidence: /tmp/qa-session/CLI/hello-world-success.log

Surface checklist (rule 22)
---------------------------
- mcp/package.json ............... 0.11.1 → 0.12.0 (new tool surface)
- mcp/README.md .................. create_stack/get_stack rows added
- mcp/src/index.ts + client.ts ... new tool + client method + types
- mcp/test/ ...................... mock, unit, integration coverage
- api/plans.yaml ................. no change (limits already correct;
                                   only the MCP description drifted)
- content/llms.txt ............... follow-up — add create_stack +
                                   get_stack rows in a separate PR
                                   on the content repo
- /Users/manassrivastava/Documents/InstaNode/CLAUDE.md
                                  no change — wedge tools live in the
                                  mcp repo's surface, not CLAUDE.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nomial-redos

CodeQL flagged /boundary=(.+)$/ on the parseMultipart helper (pre-existing
on master; only flagged on this PR because the file shifted). The regex
runs on a Content-Type header — controlled by tests today, but bounding
the capture group to [^;\s]{1,200} ensures it can't backtrack on an
adversarial input. Same semantics for every real multipart boundary token
(RFC 7578 caps them at 70 chars).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 merged commit b20d739 into master May 29, 2026
9 checks passed
@mastermanas805 mastermanas805 deleted the feat/create-stack-mcp-tool branch May 29, 2026 06:45
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