BugCraftIntegration docs for agents

Integrate BugCraft

BugCraft collects element-anchored UAT feedback (screenshots, console / error / network diagnostics) from an in-app widget and serves it to Claude Code as an MCP work queue. This page is written for the agent doing the integration and the triage — copy-paste the snippets.

Agents: fetch this page as plain markdown at /docs/agents.md.

Tenancy

Organization → Project (one host app) → Environment (uat, staging, …). Each environment has a public ingest key the widget ships with; the org has secret API tokens for read/triage (REST, MCP, and CLI). A report belongs to exactly one project + environment; queries never cross tenants.

1Add the widget to a Vite host

Install the widget

@bugcraft/widget is a private, self-hosted package — it is not on the public npm registry. Install it straight from the BugCraft service; the URL is the install source, so no registry auth or repo access is needed:

# Self-hosted tarball — not on the public npm registry.
npm install https://bugcraft.betacraft.com/widget/bugcraft-widget-latest.tgz

# Or pin an exact version (same path):
# npm install https://bugcraft.betacraft.com/widget/bugcraft-widget-0.1.0.tgz

The latest URL always resolves to the currently deployed widget; a pinned version URL is also available at the same path.

Wire it into your entry

Statically import the capture bootstrap at the very top of your entry and gate it on the env key. The UI is imported dynamically so it only loads where the key is set. The widget is UAT-only by construction: with no key, the block tree-shakes to zero bytes.

// src/main.tsx — import the capture bootstrap FIRST (before app code),
// so console/fetch/XHR are wrapped before any library captures references.
import { initCaptureBootstrap } from "@bugcraft/widget/bootstrap";

if (import.meta.env.VITE_BUGCRAFT_KEY) {
  initCaptureBootstrap({
    key: import.meta.env.VITE_BUGCRAFT_KEY,      // per-environment ingest key
    endpoint: import.meta.env.VITE_BUGCRAFT_URL, // bugcraft service origin
    user,                        // optional — see "The user object" below
    appVersion: __APP_VERSION__, // optional — see "Build metadata" below
    commitSha: __COMMIT_SHA__,   // optional
  });
  // Dynamically import the UI (Shadow DOM, picker, snapdom) — lazy, ~50kB gz.
  import("@bugcraft/widget").then((m) => m.mount());
}

// ...then your normal bootstrap: createRoot(el).render(<App />)

The launcher

The floating button opens the element picker first — click an element or drag a region to anchor the report — then the feedback form opens. The selected element, both screenshots (element + viewport), and the console / network / error diagnostics attach automatically. The widget sets page_url to the current location.href; ingest requires it to be an http(s) URL (non-http schemes are rejected with a 422), since the dashboard renders it as a link.

The user object (optional)

Identifies the tester so reports are attributed without them typing a name. Pass it from your host app's session — shape { name?, email?, role? }, every field optional:

// from your host app's session
const user = { name: session.name, email: session.email, role: session.role };

When omitted, the widget asks the tester for a name and remembers it locally.

Build metadata (optional)

appVersion and commitSha let the triaging agent pin the exact build (symbolicate a stack trace, check out the right SHA). Pass plain strings, or wire the __APP_VERSION__ / __COMMIT_SHA__ globals used above through Vite's define:

// vite.config.ts
import { execSync } from "node:child_process";
import { defineConfig } from "vite";

export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify(process.env.npm_package_version ?? "dev"),
    __COMMIT_SHA__: JSON.stringify(
      execSync("git rev-parse --short HEAD").toString().trim(),
    ),
  },
});

…and declare them for TypeScript:

// src/vite-env.d.ts
declare const __APP_VERSION__: string;
declare const __COMMIT_SHA__: string;

Provide the env vars per environment (get the ingest key from Settings → your project → environment):

# .env.uat — one ingest key per environment (Railway per-env vars)
VITE_BUGCRAFT_KEY=bc_ing_your_uat_environment_key
VITE_BUGCRAFT_URL=https://your-bugcraft.up.railway.app

# Production build: DO NOT set VITE_BUGCRAFT_KEY.
# The whole block above then tree-shakes out — provably zero bytes shipped.

Origins & serving

An environment's allowed originsmust exactly match the host page's origin — scheme + host + port (e.g. https://uat.acme.com or http://localhost:5173; an origin only — no path, no trailing slash). A browser submission from any other origin is rejected with 403. Add every origin the UAT build is served from (dev localhost ports included). Server-to-server calls (curl with no Origin header) are exempt.

2Register the MCP server

Add BugCraft as an HTTP MCP server. <BASE_URL> is the service origin (e.g. your Railway URL, or http://localhost:3000 in dev); $BUGCRAFT_TOKEN is an org API token from Settings.

claude mcp add --transport http bugcraft <BASE_URL>/mcp \
  --header "Authorization: Bearer $BUGCRAFT_TOKEN"

Tools (5)

list_reportstext ≤ ~6k tokens/page

Compact summaries, oldest-first. Filters: project, environment, status, kind, since, limit, cursor. Never returns attachments.

get_reportimage ≈1.5–2k, tail ≈2k, metadata <500

One report: metadata, element anchor (incl. React component path), description, console-error tail, + the screenshot as a native image block.

get_console_logtext ≤ ~6k tokens/page

Full console / error / network log, 50 entries per page (pass the cursor back).

update_report_statusconfirmation <200

Transition status (matrix-validated); optional note becomes an agent comment; optional commit_sha is stored.

get_report_stats<500 tokens

One-call triage overview: counts by status / kind / environment.

The triage loop

  1. list_reports(status="new") — oldest first.
  2. get_report(id) — one at a time; read the screenshot, element anchor, and console-error tail.
  3. Reproduce and fix in the repo (use the commit SHA to symbolicate).
  4. update_report_status(id, "fixed", commit_sha) when the fix lands.

Install the /bugcraft skill

Rather than wire the loop by hand, drop the ready-made /bugcraft Claude Code skill into your project's .claude/skills/ with one line:

mkdir -p .claude/skills/bugcraft && curl -fsSL \
  <BASE_URL>/skill/bugcraft/SKILL.md \
  -o .claude/skills/bugcraft/SKILL.md

Then run /bugcraft: it lists open reports oldest-first, reads each report's element anchor, screenshot, and console-error tail, fixes it in your repo, and closes it with the commit SHA — over the registered bugcraft MCP server, or the CLI when BUGCRAFT_URL / BUGCRAFT_TOKEN are set.

3REST v1 (source of truth)

Every route takes Authorization: Bearer <org token>. Reports are ordered oldest-first with an opaque keyset cursor (next_cursor). A cross-org id returns 404, never 403.

# List new reports, oldest first (the agent work-queue order)
curl -s "<BASE_URL>/api/v1/reports?project=nexdigm-v2&status=new" \
  -H "Authorization: Bearer $BUGCRAFT_TOKEN"

# One report (metadata + element anchor + console-error tail)
curl -s "<BASE_URL>/api/v1/reports/<id>" \
  -H "Authorization: Bearer $BUGCRAFT_TOKEN"

# Close it with the fix's commit SHA
curl -s -X PATCH "<BASE_URL>/api/v1/reports/<id>" \
  -H "Authorization: Bearer $BUGCRAFT_TOKEN" \
  -H "content-type: application/json" \
  -d '{"status":"fixed","commit_sha":"<sha>","note":"Fixed the null deref"}'

Uniform error shape:

{ "error": { "code": "not_found", "message": "Report not found" } }
// codes: unauthorized(401) forbidden(403) not_found(404)
//        validation(422) bad_request(400) payload_too_large(413) rate_limited(429)
  • GET /api/v1/reports — list (filters: project, environment, status, kind, since, limit, cursor)
  • GET /api/v1/reports/:id — full detail
  • GET /api/v1/reports/:id/console — paginated log
  • GET /api/v1/reports/:id/screenshot?which=element|viewport — raw PNG
  • PATCH /api/v1/reports/:id — status / note / commit_sha
  • POST /api/v1/reports/:id/comments — add a comment
  • GET /api/v1/stats?project=<slug> — counts

4CLI (token-efficient alternative)

The @bugcraft/cli package (bin bugcraft) wraps REST v1. Same org token. Screenshots arrive as native image blocks over MCP (~1.6k tokens); the CLI writes them to a file for you to Read instead.

export BUGCRAFT_URL=<BASE_URL>
export BUGCRAFT_TOKEN=bc_tok_...   # org API token (same one MCP uses)
bugcraft reports [--project <slug>] [--env <slug>] [--status <s>] \
                 [--kind <k>] [--since <ISO>] [--limit 20] [--cursor <c>] [--json]
bugcraft report <id>                       # full metadata + element + console tail
bugcraft console <id>                      # paginated console / error / network log
bugcraft screenshot <id> [--which element|viewport] [-o out.png]  # writes a PNG file
bugcraft status <id> <status> [--note <text>] [--sha <commit>]
bugcraft comment <id> <body> [--question]
bugcraft stats [--project <slug>]