capybara-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +59 -0
  2. package/dist/api/capybara-api.d.ts +21 -0
  3. package/dist/api/capybara-api.d.ts.map +1 -0
  4. package/dist/api/capybara-api.js +102 -0
  5. package/dist/api/capybara-api.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +26 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/lib/file-log.d.ts +12 -0
  11. package/dist/lib/file-log.d.ts.map +1 -0
  12. package/dist/lib/file-log.js +48 -0
  13. package/dist/lib/file-log.js.map +1 -0
  14. package/dist/lib/progress.d.ts +16 -0
  15. package/dist/lib/progress.d.ts.map +1 -0
  16. package/dist/lib/progress.js +40 -0
  17. package/dist/lib/progress.js.map +1 -0
  18. package/dist/lib/read-workspace-context.d.ts +6 -0
  19. package/dist/lib/read-workspace-context.d.ts.map +1 -0
  20. package/dist/lib/read-workspace-context.js +58 -0
  21. package/dist/lib/read-workspace-context.js.map +1 -0
  22. package/dist/lib/resolve-image.d.ts +24 -0
  23. package/dist/lib/resolve-image.d.ts.map +1 -0
  24. package/dist/lib/resolve-image.js +30 -0
  25. package/dist/lib/resolve-image.js.map +1 -0
  26. package/dist/lib/status-markdown.d.ts +8 -0
  27. package/dist/lib/status-markdown.d.ts.map +1 -0
  28. package/dist/lib/status-markdown.js +73 -0
  29. package/dist/lib/status-markdown.js.map +1 -0
  30. package/dist/lib/write-files.d.ts +3 -0
  31. package/dist/lib/write-files.d.ts.map +1 -0
  32. package/dist/lib/write-files.js +22 -0
  33. package/dist/lib/write-files.js.map +1 -0
  34. package/dist/setup.d.ts +3 -0
  35. package/dist/setup.d.ts.map +1 -0
  36. package/dist/setup.js +71 -0
  37. package/dist/setup.js.map +1 -0
  38. package/dist/tools/register-tools.d.ts +4 -0
  39. package/dist/tools/register-tools.d.ts.map +1 -0
  40. package/dist/tools/register-tools.js +90 -0
  41. package/dist/tools/register-tools.js.map +1 -0
  42. package/package.json +39 -0
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # capybara-mcp
2
+
3
+ MCP server for [Capybara](https://capybara.dev) game asset generation. Works with Claude Code, Cursor, Codex, OpenCode, Pi, and any MCP-capable agent.
4
+
5
+ ## Tools
6
+
7
+ | Tool | Description |
8
+ |------|-------------|
9
+ | `generate_asset` | Generate maps, characters, props, HUD, and audio |
10
+ | `check_job_status` | Recover results if a session was interrupted |
11
+
12
+ ## Quick start
13
+
14
+ ### 1. Configure (one time)
15
+
16
+ ```bash
17
+ npx capybara-mcp-setup --api-key <your-api-key> --server-url https://api.capybara.dev/v1
18
+ ```
19
+
20
+ This writes MCP config for Claude Desktop. For Claude Code:
21
+
22
+ ```bash
23
+ claude mcp add capybara-mcp \
24
+ -e CAPYBARA_API_KEY=<your-api-key> \
25
+ -e CAPYBARA_API_URL=https://api.capybara.dev/v1 \
26
+ -- npx -y capybara-mcp
27
+ ```
28
+
29
+ ### 2. Environment variables
30
+
31
+ | Variable | Required | Description |
32
+ |----------|----------|-------------|
33
+ | `CAPYBARA_API_KEY` | Yes | Your Capybara API key |
34
+ | `CAPYBARA_API_URL` | No | MCP API base URL (default: `https://api.capybara.dev/v1`) |
35
+
36
+ ### 3. Run manually
37
+
38
+ ```bash
39
+ CAPYBARA_API_KEY=<key> CAPYBARA_API_URL=https://api.capybara.dev/v1 npx capybara-mcp
40
+ ```
41
+
42
+ ## How it works
43
+
44
+ 1. The agent calls `generate_asset` with asset specs (maps, characters, etc.).
45
+ 2. The client reads `src/data/assets.md` and existing JSON from your game repo.
46
+ 3. Assets are generated via the Capybara API and written back to disk.
47
+ 4. Live progress appears in `.capybara/status.md` during the run.
48
+
49
+ If interrupted, use `check_job_status` with the returned `job_id`.
50
+
51
+ ## Requirements
52
+
53
+ - Node.js 18+
54
+ - A Capybara API key with sufficient credits
55
+ - A game project with `src/data/assets.md` (for non-first-build generation)
56
+
57
+ ## License
58
+
59
+ MIT
@@ -0,0 +1,21 @@
1
+ import { type JobStatusResponse, type McpGenerateAssetInput, type ProgressReporter } from "capybara-mcp-shared";
2
+ export type ClientContext = {
3
+ apiKey: string;
4
+ apiUrl: string;
5
+ report?: ProgressReporter;
6
+ onJobId?: (jobId: string) => void | Promise<void>;
7
+ signal?: AbortSignal;
8
+ };
9
+ export type GenerationResult = {
10
+ summary: string;
11
+ jobId?: string;
12
+ };
13
+ export declare function generateAssets(input: McpGenerateAssetInput, ctx: ClientContext): Promise<GenerationResult>;
14
+ export declare function formatJobStatus(status: JobStatusResponse): string;
15
+ export declare function checkJobStatus(jobId: string, ctx: ClientContext & {
16
+ output_dir?: string;
17
+ }): Promise<{
18
+ summary: string;
19
+ status: JobStatusResponse;
20
+ }>;
21
+ //# sourceMappingURL=capybara-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capybara-api.d.ts","sourceRoot":"","sources":["../../src/api/capybara-api.ts"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,qBAAqB,CAAC;AAI7B,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAWF,wBAAsB,cAAc,CAClC,KAAK,EAAE,qBAAqB,EAC5B,GAAG,EAAE,aAAa,GACjB,OAAO,CAAC,gBAAgB,CAAC,CA+C3B;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,CAcjE;AAuBD,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,aAAa,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAsBzD"}
@@ -0,0 +1,102 @@
1
+ import path from "node:path";
2
+ import { getJobStatus, mapAssetToolInputToLegacy, postApiRequest, } from "capybara-mcp-shared";
3
+ import { writeFiles } from "../lib/write-files.js";
4
+ import { readWorkspaceContext } from "../lib/read-workspace-context.js";
5
+ function withJobId(summary, jobId) {
6
+ if (!jobId) {
7
+ return summary;
8
+ }
9
+ return [summary, "", `job_id: ${jobId}`, "If interrupted, call check_job_status with this job_id."].join("\n");
10
+ }
11
+ export async function generateAssets(input, ctx) {
12
+ const { output_dir, workspace_root, plan_used_to_build, art_reference_url, ...schemaInput } = input;
13
+ const payload = mapAssetToolInputToLegacy(schemaInput);
14
+ const workspaceRoot = path.resolve(workspace_root || ".");
15
+ const workspace_files = await readWorkspaceContext(workspaceRoot);
16
+ await ctx.report?.({
17
+ message: `Queued ${payload.assets.length} asset(s) for generation...`,
18
+ progress: 0,
19
+ total: payload.assets.length,
20
+ });
21
+ const { jobId, data } = await postApiRequest({
22
+ url: `${ctx.apiUrl}/generate-assets`,
23
+ body: {
24
+ ...payload,
25
+ workspace_files,
26
+ workspace_root: workspaceRoot,
27
+ plan_used_to_build,
28
+ art_reference_url,
29
+ },
30
+ apiKey: ctx.apiKey,
31
+ apiUrl: ctx.apiUrl,
32
+ report: ctx.report,
33
+ onJobId: ctx.onJobId,
34
+ signal: ctx.signal,
35
+ });
36
+ const result = data;
37
+ const rootDir = path.resolve(output_dir || workspaceRoot);
38
+ const written = await writeFiles(result.assets ?? result.files ?? [], rootDir);
39
+ const summaryParts = [
40
+ result.message || `Generated ${payload.assets.length} asset(s).`,
41
+ result.status ? `Status: ${result.status}` : null,
42
+ result.assets_md ? "Updated src/data/assets.md." : null,
43
+ result.warnings?.length ? `Warnings:\n${result.warnings.map((w) => ` - ${w}`).join("\n")}` : null,
44
+ result.failedAssets?.length
45
+ ? `Failures:\n${result.failedAssets.map((f) => ` - ${f.id}: ${f.error}`).join("\n")}`
46
+ : null,
47
+ written.length > 0
48
+ ? [`Wrote ${written.length} file(s) to ${rootDir}:`, ...written.map((p) => ` - ${p}`)].join("\n")
49
+ : null,
50
+ ].filter(Boolean);
51
+ return { summary: withJobId(summaryParts.join("\n"), jobId), jobId };
52
+ }
53
+ export function formatJobStatus(status) {
54
+ const lines = [
55
+ `job_id: ${status.job_id}`,
56
+ `status: ${status.status}`,
57
+ status.progress_message || status.message
58
+ ? `message: ${status.progress_message || status.message}`
59
+ : null,
60
+ status.progress != null
61
+ ? `progress: ${status.progress}${status.total != null ? `/${status.total}` : ""}`
62
+ : null,
63
+ status.error ? `error: ${status.error}` : null,
64
+ ].filter(Boolean);
65
+ return lines.join("\n");
66
+ }
67
+ async function applyCompletedJob(status, outputDir) {
68
+ if (status.status !== "complete" || status.result == null) {
69
+ return null;
70
+ }
71
+ const result = status.result;
72
+ const rootDir = path.resolve(outputDir || ".");
73
+ const written = await writeFiles(result.assets ?? result.files ?? [], rootDir);
74
+ return [
75
+ result.message || "Recovered generated assets.",
76
+ written.length > 0
77
+ ? [`Wrote ${written.length} file(s) to ${rootDir}:`, ...written.map((p) => ` - ${p}`)].join("\n")
78
+ : null,
79
+ ]
80
+ .filter(Boolean)
81
+ .join("\n");
82
+ }
83
+ export async function checkJobStatus(jobId, ctx) {
84
+ const status = await getJobStatus({
85
+ apiUrl: ctx.apiUrl,
86
+ apiKey: ctx.apiKey,
87
+ jobId,
88
+ signal: ctx.signal,
89
+ });
90
+ let summary = formatJobStatus(status);
91
+ if (status.status === "complete") {
92
+ const recovered = await applyCompletedJob(status, ctx.output_dir);
93
+ if (recovered) {
94
+ summary = [recovered, "", `job_id: ${status.job_id}`].join("\n");
95
+ }
96
+ }
97
+ if (status.status === "failed") {
98
+ summary = [summary, status.error ? `error: ${status.error}` : null].filter(Boolean).join("\n");
99
+ }
100
+ return { summary, status };
101
+ }
102
+ //# sourceMappingURL=capybara-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capybara-api.js","sourceRoot":"","sources":["../../src/api/capybara-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { registerTools } from "./tools/register-tools.js";
5
+ const API_KEY = process.env.CAPYBARA_API_KEY;
6
+ const API_URL = process.env.CAPYBARA_API_URL || "https://api.capybara.dev/v1";
7
+ if (!API_KEY) {
8
+ console.error("CAPYBARA_API_KEY is not set. Run the setup command first:");
9
+ console.error(" npx -y capybara-mcp-setup --api-key <your-key>");
10
+ process.exit(1);
11
+ }
12
+ const server = new McpServer({
13
+ name: "capybara_tools",
14
+ version: "0.1.0",
15
+ }, {
16
+ capabilities: {
17
+ logging: {},
18
+ },
19
+ });
20
+ registerTools(server, {
21
+ apiKey: API_KEY,
22
+ apiUrl: API_URL,
23
+ });
24
+ const transport = new StdioServerTransport();
25
+ await server.connect(transport);
26
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;AAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,6BAA6B,CAAC;AAE9E,IAAI,CAAC,OAAO,EAAE,CAAC;IACb,OAAO,CAAC,KAAK,CAAC,2DAA2D,CAAC,CAAC;IAC3E,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;IACE,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,OAAO;CACjB,EACD;IACE,YAAY,EAAE;QACZ,OAAO,EAAE,EAAE;KACZ;CACF,CACF,CAAC;AAEF,aAAa,CAAC,MAAM,EAAE;IACpB,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,OAAO;CAChB,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ export type FileLogger = {
2
+ latestPath: string;
3
+ archivePath: string;
4
+ append: (line: string) => Promise<void>;
5
+ close: (footer?: string) => Promise<void>;
6
+ };
7
+ export declare function createFileLogger(label: string): Promise<FileLogger>;
8
+ export declare function jobLogPath(jobId: string): string;
9
+ export declare function appendJobLog(jobId: string, line: string): Promise<string>;
10
+ export declare function initJobLog(jobId: string, label: string): Promise<string>;
11
+ export declare function formatLogHint(filePath: string): string;
12
+ //# sourceMappingURL=file-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-log.d.ts","sourceRoot":"","sources":["../../src/lib/file-log.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C,CAAC;AAUF,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAyBzE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK/E;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAK9E;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,48 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ function logDir() {
4
+ return process.env.CAPYBARA_LOG_DIR || path.join(process.cwd(), ".capybara", "logs");
5
+ }
6
+ function formatLine(line) {
7
+ return `[${new Date().toISOString()}] ${line}\n`;
8
+ }
9
+ export async function createFileLogger(label) {
10
+ const dir = logDir();
11
+ await fs.mkdir(dir, { recursive: true });
12
+ const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
13
+ const latestPath = path.resolve(dir, "latest.log");
14
+ const archivePath = path.resolve(dir, `${label}-${timestamp}.log`);
15
+ const header = `=== ${label} started ${new Date().toISOString()} ===\n`;
16
+ await fs.writeFile(latestPath, header);
17
+ await fs.writeFile(archivePath, header);
18
+ const append = async (line) => {
19
+ const formatted = formatLine(line);
20
+ await Promise.all([fs.appendFile(latestPath, formatted), fs.appendFile(archivePath, formatted)]);
21
+ };
22
+ const close = async (footer) => {
23
+ if (footer) {
24
+ await append(footer);
25
+ }
26
+ await append(`=== ${label} finished ===`);
27
+ };
28
+ return { latestPath, archivePath, append, close };
29
+ }
30
+ export function jobLogPath(jobId) {
31
+ return path.resolve(logDir(), `job-${jobId}.log`);
32
+ }
33
+ export async function appendJobLog(jobId, line) {
34
+ const filePath = jobLogPath(jobId);
35
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
36
+ await fs.appendFile(filePath, formatLine(line));
37
+ return filePath;
38
+ }
39
+ export async function initJobLog(jobId, label) {
40
+ const filePath = jobLogPath(jobId);
41
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
42
+ await fs.writeFile(filePath, `=== ${label} job ${jobId} started ${new Date().toISOString()} ===\n`);
43
+ return filePath;
44
+ }
45
+ export function formatLogHint(filePath) {
46
+ return `Live log: ${filePath}\nWatch with: tail -f ${filePath}`;
47
+ }
48
+ //# sourceMappingURL=file-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-log.js","sourceRoot":"","sources":["../../src/lib/file-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js";
3
+ import type { ServerNotification, ServerRequest } from "@modelcontextprotocol/sdk/types.js";
4
+ import type { ProgressUpdate } from "capybara-mcp-shared";
5
+ import type { StatusLogger } from "./status-markdown.js";
6
+ type ToolExtra = RequestHandlerExtra<ServerRequest, ServerNotification>;
7
+ export declare function createProgressReporter({ extra, server, label, statusLog, }: {
8
+ extra: ToolExtra;
9
+ server: McpServer;
10
+ label: string;
11
+ statusLog?: StatusLogger;
12
+ }): {
13
+ report: (update: ProgressUpdate) => Promise<void>;
14
+ };
15
+ export {};
16
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACd,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,KAAK,SAAS,GAAG,mBAAmB,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;AAaxE,wBAAgB,sBAAsB,CAAC,EACrC,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,GACV,EAAE;IACD,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;qBAI+B,cAAc;EAiC7C"}
@@ -0,0 +1,40 @@
1
+ function formatProgressLine({ message, progress, total }) {
2
+ const parts = [];
3
+ if (progress != null) {
4
+ parts.push(total != null ? `${progress}/${total}` : String(progress));
5
+ }
6
+ if (message) {
7
+ parts.push(message);
8
+ }
9
+ return parts.join(" — ") || "working...";
10
+ }
11
+ export function createProgressReporter({ extra, server, label, statusLog, }) {
12
+ const progressToken = extra._meta?.progressToken;
13
+ let lastProgress = 0;
14
+ const report = async (update) => {
15
+ if (extra.signal.aborted) {
16
+ throw new DOMException("Cancelled", "AbortError");
17
+ }
18
+ if (update.progress != null) {
19
+ lastProgress = update.progress;
20
+ }
21
+ await statusLog?.append(formatProgressLine(update));
22
+ if (progressToken !== undefined) {
23
+ await extra.sendNotification({
24
+ method: "notifications/progress",
25
+ params: {
26
+ progressToken,
27
+ progress: update.progress ?? lastProgress,
28
+ ...(update.total != null ? { total: update.total } : {}),
29
+ ...(update.message ? { message: update.message } : {}),
30
+ },
31
+ });
32
+ return;
33
+ }
34
+ if (extra.sessionId && update.message) {
35
+ await server.sendLoggingMessage({ level: "info", data: `[${label}] ${update.message}` }, extra.sessionId);
36
+ }
37
+ };
38
+ return { report };
39
+ }
40
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/lib/progress.ts"],"names":[],"mappings":"AAWA,SAAS,kBAAkB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAkB;IACtE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,OAAO,EAAE,CAAC;QACZ,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,EACrC,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,GAMV;IACC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC;IACjD,IAAI,YAAY,GAAG,CAAC,CAAC;IAErB,MAAM,MAAM,GAAG,KAAK,EAAE,MAAsB,EAAE,EAAE;QAC9C,IAAI,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACzB,MAAM,IAAI,YAAY,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QACpD,CAAC;QAED,IAAI,MAAM,CAAC,QAAQ,IAAI,IAAI,EAAE,CAAC;YAC5B,YAAY,GAAG,MAAM,CAAC,QAAQ,CAAC;QACjC,CAAC;QAED,MAAM,SAAS,EAAE,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;QAEpD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,MAAM,KAAK,CAAC,gBAAgB,CAAC;gBAC3B,MAAM,EAAE,wBAAwB;gBAChC,MAAM,EAAE;oBACN,aAAa;oBACb,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,YAAY;oBACzC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBACxD,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBACvD;aACF,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,IAAI,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACtC,MAAM,MAAM,CAAC,kBAAkB,CAC7B,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK,KAAK,MAAM,CAAC,OAAO,EAAE,EAAE,EACvD,KAAK,CAAC,SAAS,CAChB,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Reads the local workspace context the asset pipeline expects:
3
+ * assets.md plus generated JSON under src/data.
4
+ */
5
+ export declare function readWorkspaceContext(workspaceRoot?: string): Promise<Record<string, string>>;
6
+ //# sourceMappingURL=read-workspace-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-workspace-context.d.ts","sourceRoot":"","sources":["../../src/lib/read-workspace-context.ts"],"names":[],"mappings":"AAuCA;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,SAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAyBzG"}
@@ -0,0 +1,58 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ const ASSETS_REGISTRY_PATH = "src/data/assets.md";
4
+ const DATA_DIR = "src/data";
5
+ const normalizeRepoPath = (rootDir, absolutePath) => path.relative(rootDir, absolutePath).split(path.sep).join("/");
6
+ async function readFileIfExists(filePath) {
7
+ try {
8
+ return await fs.readFile(filePath, "utf8");
9
+ }
10
+ catch {
11
+ return null;
12
+ }
13
+ }
14
+ async function collectDataJsonFiles(dataDir, rootDir, files) {
15
+ let entries = [];
16
+ try {
17
+ entries = await fs.readdir(dataDir);
18
+ }
19
+ catch {
20
+ return;
21
+ }
22
+ await Promise.all(entries
23
+ .filter((name) => name.endsWith(".json"))
24
+ .map(async (name) => {
25
+ const absolutePath = path.join(dataDir, name);
26
+ const content = await readFileIfExists(absolutePath);
27
+ if (content == null) {
28
+ return;
29
+ }
30
+ files[normalizeRepoPath(rootDir, absolutePath)] = content;
31
+ }));
32
+ }
33
+ /**
34
+ * Reads the local workspace context the asset pipeline expects:
35
+ * assets.md plus generated JSON under src/data.
36
+ */
37
+ export async function readWorkspaceContext(workspaceRoot = process.cwd()) {
38
+ const rootDir = path.resolve(workspaceRoot);
39
+ const files = {};
40
+ const assetsMdPath = path.join(rootDir, ASSETS_REGISTRY_PATH);
41
+ const assetsMd = await readFileIfExists(assetsMdPath);
42
+ if (assetsMd != null) {
43
+ files[ASSETS_REGISTRY_PATH] = assetsMd;
44
+ }
45
+ await collectDataJsonFiles(path.join(rootDir, DATA_DIR), rootDir, files);
46
+ const indexTsPath = path.join(rootDir, DATA_DIR, "index.ts");
47
+ const indexTs = await readFileIfExists(indexTsPath);
48
+ if (indexTs != null) {
49
+ files[normalizeRepoPath(rootDir, indexTsPath)] = indexTs;
50
+ }
51
+ const propsTsPath = path.join(rootDir, DATA_DIR, "props.ts");
52
+ const propsTs = await readFileIfExists(propsTsPath);
53
+ if (propsTs != null) {
54
+ files[normalizeRepoPath(rootDir, propsTsPath)] = propsTs;
55
+ }
56
+ return files;
57
+ }
58
+ //# sourceMappingURL=read-workspace-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-workspace-context.js","sourceRoot":"","sources":["../../src/lib/read-workspace-context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export declare function isUrl(value: string): boolean;
2
+ export declare function resolveImageInput(value: string): Promise<{
3
+ type: "url";
4
+ url: string;
5
+ mediaType?: undefined;
6
+ data?: undefined;
7
+ } | {
8
+ type: "base64";
9
+ mediaType: string;
10
+ data: string;
11
+ url?: undefined;
12
+ }>;
13
+ export declare function resolveImageInputs(values?: string[]): Promise<({
14
+ type: "url";
15
+ url: string;
16
+ mediaType?: undefined;
17
+ data?: undefined;
18
+ } | {
19
+ type: "base64";
20
+ mediaType: string;
21
+ data: string;
22
+ url?: undefined;
23
+ })[]>;
24
+ //# sourceMappingURL=resolve-image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-image.d.ts","sourceRoot":"","sources":["../../src/lib/resolve-image.ts"],"names":[],"mappings":"AAWA,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5C;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM;;;;;;;;;;GAepD;AAED,wBAAsB,kBAAkB,CAAC,MAAM,GAAE,MAAM,EAAO;;;;;;;;;;MAE7D"}
@@ -0,0 +1,30 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ const EXT_TO_MIME = {
4
+ ".png": "image/png",
5
+ ".jpg": "image/jpeg",
6
+ ".jpeg": "image/jpeg",
7
+ ".webp": "image/webp",
8
+ ".gif": "image/gif",
9
+ };
10
+ export function isUrl(value) {
11
+ return /^https?:\/\//i.test(value);
12
+ }
13
+ export async function resolveImageInput(value) {
14
+ if (isUrl(value)) {
15
+ return { type: "url", url: value };
16
+ }
17
+ const absolute = path.resolve(value);
18
+ const buffer = await fs.readFile(absolute);
19
+ const ext = path.extname(absolute).toLowerCase();
20
+ const mediaType = EXT_TO_MIME[ext] || "application/octet-stream";
21
+ return {
22
+ type: "base64",
23
+ mediaType,
24
+ data: buffer.toString("base64"),
25
+ };
26
+ }
27
+ export async function resolveImageInputs(values = []) {
28
+ return Promise.all(values.map(resolveImageInput));
29
+ }
30
+ //# sourceMappingURL=resolve-image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-image.js","sourceRoot":"","sources":["../../src/lib/resolve-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,WAAW,GAA2B;IAC1C,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,KAAa;IACnD,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACjB,OAAO,EAAE,IAAI,EAAE,KAAc,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;IAC9C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;IAEjE,OAAO;QACL,IAAI,EAAE,QAAiB;QACvB,SAAS;QACT,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;KAChC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAmB,EAAE;IAC5D,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;AACpD,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type StatusOutcome = "running" | "complete" | "cancelled" | "failed";
2
+ export type StatusLogger = {
3
+ filePath: string;
4
+ append: (line: string) => Promise<void>;
5
+ finish: (outcome: Exclude<StatusOutcome, "running">, message?: string) => Promise<void>;
6
+ };
7
+ export declare function createStatusLogger(label: string): Promise<StatusLogger>;
8
+ //# sourceMappingURL=status-markdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-markdown.d.ts","sourceRoot":"","sources":["../../src/lib/status-markdown.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE5E,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzF,CAAC;AAyEF,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwB7E"}
@@ -0,0 +1,73 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ function statusFilePath() {
4
+ const custom = process.env.CAPYBARA_STATUS_FILE;
5
+ if (custom) {
6
+ return path.resolve(custom);
7
+ }
8
+ return path.resolve(process.cwd(), ".capybara", "status.md");
9
+ }
10
+ function formatTimestamp() {
11
+ return new Date().toISOString();
12
+ }
13
+ function renderMarkdown({ label, outcome, startedAt, lines, footer, }) {
14
+ const statusLabel = outcome === "running"
15
+ ? "running"
16
+ : outcome === "complete"
17
+ ? "complete"
18
+ : outcome === "cancelled"
19
+ ? "cancelled"
20
+ : "failed";
21
+ const body = [
22
+ `# Capybara — ${label}`,
23
+ "",
24
+ `**Status:** ${statusLabel} `,
25
+ `**Started:** ${startedAt} `,
26
+ `**Updated:** ${formatTimestamp()} `,
27
+ "",
28
+ "> Temporary status file. Removed automatically when this tool finishes.",
29
+ "",
30
+ "## Progress",
31
+ "",
32
+ ...lines.map((line) => `- ${line}`),
33
+ ];
34
+ if (footer) {
35
+ body.push("", "## Result", "", footer);
36
+ }
37
+ body.push("");
38
+ return body.join("\n");
39
+ }
40
+ async function writeStatus(filePath, content) {
41
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
42
+ await fs.writeFile(filePath, content, "utf-8");
43
+ }
44
+ async function removeStatus(filePath) {
45
+ try {
46
+ await fs.unlink(filePath);
47
+ }
48
+ catch (err) {
49
+ if (err.code !== "ENOENT") {
50
+ throw err;
51
+ }
52
+ }
53
+ }
54
+ export async function createStatusLogger(label) {
55
+ const filePath = statusFilePath();
56
+ const startedAt = formatTimestamp();
57
+ const lines = [];
58
+ const refresh = async (outcome, footer) => {
59
+ await writeStatus(filePath, renderMarkdown({ label, outcome, startedAt, lines, footer }));
60
+ };
61
+ await refresh("running");
62
+ return {
63
+ filePath,
64
+ append: async (line) => {
65
+ lines.push(`\`${formatTimestamp()}\` ${line}`);
66
+ await refresh("running");
67
+ },
68
+ finish: async () => {
69
+ await removeStatus(filePath);
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=status-markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-markdown.js","sourceRoot":"","sources":["../../src/lib/status-markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { FileWriteEntry } from "capybara-mcp-shared";
2
+ export declare function writeFiles(files: FileWriteEntry[], rootDir: string): Promise<string[]>;
3
+ //# sourceMappingURL=write-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-files.d.ts","sourceRoot":"","sources":["../../src/lib/write-files.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1D,wBAAsB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsB5F"}
@@ -0,0 +1,22 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function writeFiles(files, rootDir) {
4
+ const resolvedRoot = path.resolve(rootDir);
5
+ const written = [];
6
+ for (const file of files) {
7
+ const targetPath = path.resolve(resolvedRoot, file.path);
8
+ if (targetPath !== resolvedRoot && !targetPath.startsWith(resolvedRoot + path.sep)) {
9
+ throw new Error(`Refusing to write outside of ${resolvedRoot}: ${file.path}`);
10
+ }
11
+ await fs.mkdir(path.dirname(targetPath), { recursive: true });
12
+ if (file.content_base64) {
13
+ await fs.writeFile(targetPath, Buffer.from(file.content_base64, "base64"));
14
+ }
15
+ else {
16
+ await fs.writeFile(targetPath, file.content ?? "", "utf-8");
17
+ }
18
+ written.push(targetPath);
19
+ }
20
+ return written;
21
+ }
22
+ //# sourceMappingURL=write-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-files.js","sourceRoot":"","sources":["../../src/lib/write-files.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAuB,EAAE,OAAe;IACvE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACzD,IAAI,UAAU,KAAK,YAAY,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACnF,MAAM,IAAI,KAAK,CAAC,gCAAgC,YAAY,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9D,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC7E,CAAC;aAAM,CAAC;YACN,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":""}
package/dist/setup.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ function parseArgs(argv) {
6
+ const args = {};
7
+ for (let i = 0; i < argv.length; i++) {
8
+ if (argv[i].startsWith("--")) {
9
+ const key = argv[i].slice(2);
10
+ const next = argv[i + 1];
11
+ const hasValue = next !== undefined && !next.startsWith("--");
12
+ args[key] = hasValue ? argv[++i] : true;
13
+ }
14
+ }
15
+ return args;
16
+ }
17
+ const args = parseArgs(process.argv.slice(2));
18
+ const apiKey = args["api-key"];
19
+ const serverUrl = args["server-url"] || "https://api.capybara.dev/v1";
20
+ if (!apiKey || typeof apiKey !== "string") {
21
+ console.error("Missing required flag: --api-key <your-key>");
22
+ console.error("\nUsage:");
23
+ console.error(" npx -y capybara-mcp-setup --api-key <your-key>");
24
+ process.exit(1);
25
+ }
26
+ function getClaudeDesktopConfigPath() {
27
+ const platform = process.platform;
28
+ if (platform === "darwin") {
29
+ return path.join(os.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
30
+ }
31
+ if (platform === "win32") {
32
+ const appData = process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming");
33
+ return path.join(appData, "Claude", "claude_desktop_config.json");
34
+ }
35
+ return path.join(os.homedir(), ".config", "Claude", "claude_desktop_config.json");
36
+ }
37
+ function loadConfig(configPath) {
38
+ if (!fs.existsSync(configPath)) {
39
+ return { mcpServers: {} };
40
+ }
41
+ try {
42
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
43
+ if (!parsed.mcpServers)
44
+ parsed.mcpServers = {};
45
+ return parsed;
46
+ }
47
+ catch (err) {
48
+ const message = err instanceof Error ? err.message : String(err);
49
+ console.error(`Could not parse existing config at ${configPath}: ${message}`);
50
+ process.exit(1);
51
+ }
52
+ }
53
+ const configPath = getClaudeDesktopConfigPath();
54
+ const parsed = loadConfig(configPath);
55
+ parsed.mcpServers ??= {};
56
+ parsed.mcpServers["capybara-mcp"] = {
57
+ command: "npx",
58
+ args: ["-y", "capybara-mcp"],
59
+ env: {
60
+ CAPYBARA_API_KEY: apiKey,
61
+ CAPYBARA_API_URL: serverUrl,
62
+ },
63
+ };
64
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
65
+ fs.writeFileSync(configPath, JSON.stringify(parsed, null, 2), "utf-8");
66
+ console.log(`capybara-mcp configured in:\n ${configPath}`);
67
+ console.log("\nRestart Claude Desktop to load the new tools.");
68
+ console.log("\nUsing Claude Code instead? Run this:");
69
+ console.log(` claude mcp add capybara-mcp -e CAPYBARA_API_KEY=${apiKey} -e CAPYBARA_API_URL=${serverUrl} -- npx -y capybara-mcp`);
70
+ console.log("\nIf a generate tool is interrupted, use check_job_status with the returned job_id.");
71
+ //# sourceMappingURL=setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { type ClientContext } from "../api/capybara-api.js";
3
+ export declare function registerTools(server: McpServer, ctx: Omit<ClientContext, "report" | "signal" | "onJobId">): void;
4
+ //# sourceMappingURL=register-tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-tools.d.ts","sourceRoot":"","sources":["../../src/tools/register-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWzE,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,wBAAwB,CAAC;AAsDhC,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC,QA4DzG"}
@@ -0,0 +1,90 @@
1
+ import { z } from "zod";
2
+ import { ASSET_TOOL_DESCRIPTION, mcpGenerateAssetInputSchema, } from "capybara-mcp-shared";
3
+ import { checkJobStatus, generateAssets, } from "../api/capybara-api.js";
4
+ import { createProgressReporter } from "../lib/progress.js";
5
+ import { createStatusLogger } from "../lib/status-markdown.js";
6
+ async function runLongTool({ label, extra, server, work, }) {
7
+ const statusLog = await createStatusLogger(label);
8
+ const { report } = createProgressReporter({ extra, server, label, statusLog });
9
+ const onJobId = async (jobId) => {
10
+ await statusLog.append(`job_id: ${jobId} (use check_job_status if interrupted)`);
11
+ };
12
+ try {
13
+ await statusLog.append("Tool call accepted");
14
+ const result = await work({ report, signal: extra.signal, onJobId });
15
+ await statusLog.finish("complete", "Completed successfully");
16
+ return {
17
+ content: [{ type: "text", text: result.summary }],
18
+ };
19
+ }
20
+ catch (err) {
21
+ if (err instanceof DOMException && err.name === "AbortError") {
22
+ await statusLog.finish("cancelled", "Cancelled — use check_job_status with your job_id");
23
+ return {
24
+ content: [
25
+ {
26
+ type: "text",
27
+ text: `${label} cancelled. If a job_id was issued, call check_job_status to recover results.`,
28
+ },
29
+ ],
30
+ isError: true,
31
+ };
32
+ }
33
+ const message = err instanceof Error ? err.message : String(err);
34
+ await statusLog.finish("failed", message);
35
+ return {
36
+ content: [{ type: "text", text: `Error in ${label}: ${message}` }],
37
+ isError: true,
38
+ };
39
+ }
40
+ }
41
+ export function registerTools(server, ctx) {
42
+ server.registerTool("generate_asset", {
43
+ title: "Generate game assets",
44
+ description: ASSET_TOOL_DESCRIPTION +
45
+ " Returns a job_id for check_job_status if interrupted. " +
46
+ "Live progress is written to `.capybara/status.md` (removed when the tool finishes).",
47
+ inputSchema: mcpGenerateAssetInputSchema,
48
+ annotations: { openWorldHint: true },
49
+ }, async (input, extra) => runLongTool({
50
+ label: "generate_asset",
51
+ extra,
52
+ server,
53
+ work: ({ report, signal, onJobId }) => generateAssets(input, { ...ctx, report, signal, onJobId }).then((r) => ({
54
+ summary: r.summary,
55
+ })),
56
+ }));
57
+ server.registerTool("check_job_status", {
58
+ title: "Check job status",
59
+ description: "Poll a generation job by job_id. Use when generate_asset was interrupted, or to check " +
60
+ "progress after closing the agent. When status is complete, writes any recovered files to disk.",
61
+ inputSchema: {
62
+ job_id: z.string().min(1).describe("The job_id from generate_asset."),
63
+ output_dir: z
64
+ .string()
65
+ .optional()
66
+ .describe("Optional output directory when recovering asset files."),
67
+ },
68
+ }, async ({ job_id, output_dir }, extra) => {
69
+ try {
70
+ const { summary, status } = await checkJobStatus(job_id, {
71
+ ...ctx,
72
+ output_dir,
73
+ signal: extra.signal,
74
+ });
75
+ const isError = status.status === "failed";
76
+ return {
77
+ content: [{ type: "text", text: summary }],
78
+ ...(isError ? { isError: true } : {}),
79
+ };
80
+ }
81
+ catch (err) {
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ return {
84
+ content: [{ type: "text", text: `Error checking job: ${message}` }],
85
+ isError: true,
86
+ };
87
+ }
88
+ });
89
+ }
90
+ //# sourceMappingURL=register-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-tools.js","sourceRoot":"","sources":["../../src/tools/register-tools.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "capybara-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Capybara MCP tools — generate game assets from Claude Code, Cursor, Codex, and other MCP agents",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "capybara-mcp": "./dist/index.js",
9
+ "capybara-mcp-setup": "./dist/setup.js"
10
+ },
11
+ "files": ["dist", "README.md"],
12
+ "scripts": {
13
+ "build": "tsc -p tsconfig.json",
14
+ "dev": "tsx src/index.ts",
15
+ "typecheck": "tsc -p tsconfig.json --noEmit",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "dependencies": {
19
+ "capybara-mcp-shared": "0.1.0",
20
+ "@modelcontextprotocol/sdk": "^1.11.0",
21
+ "zod": "^3.23.8"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^22.15.3",
25
+ "tsx": "^4.19.4",
26
+ "typescript": "^5.8.3"
27
+ },
28
+ "engines": {
29
+ "node": ">=18"
30
+ },
31
+ "keywords": [
32
+ "mcp",
33
+ "model-context-protocol",
34
+ "capybara",
35
+ "game-assets",
36
+ "claude",
37
+ "cursor"
38
+ ]
39
+ }