@debuffjp/mcp 0.0.0-stage → 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.
package/README.md CHANGED
@@ -1,3 +1,47 @@
1
- # Temporary Holding Version
1
+ # @debuffjp/mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An [MCP](https://modelcontextprotocol.io) server that lets AI agents inspect, build, test and **play** a debuff game. It uses the same evidence a human would want: real state, real frames, real test results.
4
+
5
+ ## Setup
6
+
7
+ New projects from `npm create debuff` include a `.mcp.json`, so MCP clients that read it (such as Claude Code) pick the server up automatically. For other clients:
8
+
9
+ ```bash
10
+ claude mcp add debuff -- npx -y @debuffjp/mcp # Claude Code
11
+ ```
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "debuff": { "command": "npx", "args": ["-y", "@debuffjp/mcp"] }
17
+ }
18
+ }
19
+ ```
20
+
21
+ Run it from the game's folder, or pass `--root <dir>` (or set `DEBUFF_ROOT`).
22
+
23
+ ## Tools
24
+
25
+ | Tool | What it does |
26
+ | --- | --- |
27
+ | `inspect_project` | Dependencies, plugins and their contributions, assets, last build size |
28
+ | `list_assets` | Every asset with its type, size and importer, plus warnings and possibly unused ids |
29
+ | `build` | Production build plus size report; `leaks` must be empty |
30
+ | `run_scenarios` | Run `scenarios/*.json` headless; per-step results, errors, captures |
31
+ | `game_start` | Attach to the live game, or open a private headless copy, paused (see below) |
32
+ | `game_step` | Advance fixed simulation steps (pausing first); returns state |
33
+ | `game_pause` | Pause or resume a live game |
34
+ | `game_frames` | Recent frame breakdown: update/render ms, catch-up steps, quads, draw calls, long tasks |
35
+ | `game_notes` | Moments the human flagged in the overlay, with the state at that tick |
36
+ | `game_input` | `press` / `release` keys; `tap`, `pointer_down`, `pointer_move`, `pointer_up` |
37
+ | `game_state` | Scene, tick, renderer, frame stats, your `inspect()` data |
38
+ | `game_params` / `game_set_param` | Read and tune typed params (validated) |
39
+ | `game_capture` | The current frame as a PNG image |
40
+ | `game_errors` | Runtime errors, explained: whose problem (your code, a rejected call, a debuff bug), the source line, a hint |
41
+ | `game_stop` | Close the game |
42
+
43
+ ## Live and headless
44
+
45
+ If you have the game open under `debuff dev`, `game_start` attaches to **that tab** (mode `live`). The overlay shows an **agent** badge, and every pause, step and param change the agent makes appears in its activity log, where you can undo it. Type a note in the overlay ("jump feels floaty here") to flag a moment; the agent reads it, along with the game state at that tick, through `game_notes`. `game_stop` resumes your game and disconnects.
46
+
47
+ Otherwise, or with `mode: "headless"`, the agent gets a private copy in a headless browser. Time only advances through `game_step`, so what it sees is reproducible, and a scenario can replay it exactly.
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ // Committed shim so npm can link the bin before dist/ is built.
3
+ import "../dist/cli.js";
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,20 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { createServer } from "./server.js";
5
+ const args = process.argv.slice(2);
6
+ const rootFlag = args.indexOf("--root");
7
+ const root = resolve(rootFlag >= 0 ? (args[rootFlag + 1] ?? ".") : (process.env.DEBUFF_ROOT ?? process.cwd()));
8
+ const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
9
+ const server = createServer({ root, version });
10
+ await server.connect(new StdioServerTransport());
11
+ // stdout carries the protocol, so diagnostics go to stderr.
12
+ console.error(`debuff MCP server v${version} ready (root: ${root})`);
13
+ const shutdown = async () => {
14
+ await server.close();
15
+ process.exit(0);
16
+ };
17
+ process.on("SIGINT", shutdown);
18
+ process.on("SIGTERM", shutdown);
19
+ process.stdin.on("close", shutdown);
20
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;AACxC,MAAM,IAAI,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAC/G,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAwB,CAAC;AAEzH,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;AAC/C,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AAEjD,4DAA4D;AAC5D,OAAO,CAAC,KAAK,CAAC,sBAAsB,OAAO,iBAAiB,IAAI,GAAG,CAAC,CAAC;AAErE,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;IAC1B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC;AACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;AAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAChC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createServer, type ServerOptions } from "./server.js";
2
+ export { type GameSession, HeadlessSession, LiveSession, type StartMode, startSession } from "./session.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,KAAK,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createServer } from "./server.js";
2
+ export { HeadlessSession, LiveSession, startSession } from "./session.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAsB,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAoB,eAAe,EAAE,WAAW,EAAkB,YAAY,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export interface ServerOptions {
3
+ /** Default project root for tools that don't receive one. */
4
+ root: string;
5
+ version: string;
6
+ }
7
+ /**
8
+ * debuff's MCP server: project tools (inspect, assets, build, scenarios) and live-game tools that drive
9
+ * a running game through the same dev hooks scenarios use. Every result is JSON, plus images for captures.
10
+ */
11
+ export declare function createServer(options: ServerOptions): McpServer;
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AASpE,MAAM,WAAW,aAAa;IAC5B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAqR9D"}
package/dist/server.js ADDED
@@ -0,0 +1,198 @@
1
+ import { resolve } from "node:path";
2
+ import { buildReport, compileAssets, findUnused, inspectProject, loadConfig, runScenarios, viteConfig } from "@debuffjp/cli";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { z } from "zod";
5
+ import { startSession } from "./session.js";
6
+ const json = (value) => ({ content: [{ type: "text", text: JSON.stringify(value, null, 2) }] });
7
+ const fail = (message) => ({ isError: true, content: [{ type: "text", text: message }] });
8
+ /**
9
+ * debuff's MCP server: project tools (inspect, assets, build, scenarios) and live-game tools that drive
10
+ * a running game through the same dev hooks scenarios use. Every result is JSON, plus images for captures.
11
+ */
12
+ export function createServer(options) {
13
+ const server = new McpServer({ name: "debuff", version: options.version }, {
14
+ instructions: "Tools for building 2D web games with debuff. Start with inspect_project. To see the game, call game_start, then game_state / game_capture. " +
15
+ "If the human has the game open under `debuff dev`, game_start attaches to it (mode live): they see your actions in their overlay, and their " +
16
+ "flagged moments are in game_notes, so read those first. Otherwise you get a private paused copy (mode headless) where time only advances through game_step. " +
17
+ "Use game_pause before inspecting a live game, and game_frames to explain stutter. Prefer tuning params (game_params, game_set_param) over code changes, " +
18
+ "and verify changes with run_scenarios. Creative direction belongs to the human: propose alternatives rather than deciding art or feel.",
19
+ });
20
+ const rootOf = (root) => resolve(options.root, root ?? ".");
21
+ let session;
22
+ const live = () => {
23
+ if (!session)
24
+ throw new Error("No game running. Call game_start first.");
25
+ return session;
26
+ };
27
+ const safely = (fn) => async (args) => {
28
+ try {
29
+ return await fn(args);
30
+ }
31
+ catch (err) {
32
+ return fail(err instanceof Error ? err.message : String(err));
33
+ }
34
+ };
35
+ const root = { root: z.string().optional().describe("Project root, relative to the server's root. Default: the server's root.") };
36
+ // ---- project tools -------------------------------------------------------------------------
37
+ server.registerTool("inspect_project", {
38
+ title: "Inspect project",
39
+ description: "Summary of the project: dependencies, plugins and their contributions, assets, last build size.",
40
+ inputSchema: root,
41
+ annotations: { readOnlyHint: true },
42
+ }, safely(async ({ root: r }) => json(await inspectProject(rootOf(r)))));
43
+ server.registerTool("list_assets", {
44
+ title: "List assets",
45
+ description: "Run the asset pipeline: every asset id, type, size and importer, plus generated files, warnings and possibly unused ids.",
46
+ inputSchema: root,
47
+ annotations: { readOnlyHint: true },
48
+ }, safely(async ({ root: r }) => {
49
+ const dir = rootOf(r);
50
+ const { plugins, atlas } = await loadConfig(dir);
51
+ const { manifest, importedBy, outputs, warnings } = await compileAssets(dir, { plugins, atlas });
52
+ return json({ assets: manifest.assets, importedBy, generated: [...outputs.keys()], warnings, unused: findUnused(dir, manifest) });
53
+ }));
54
+ server.registerTool("build", {
55
+ title: "Production build",
56
+ description: "Build for production and return the size report (raw, gzip, brotli per file). `leaks` lists dev-only code found in the output; it must be empty.",
57
+ inputSchema: root,
58
+ }, safely(async ({ root: r }) => {
59
+ const dir = rootOf(r);
60
+ const { build } = await import("vite");
61
+ await build(await viteConfig(dir, { logLevel: "silent" }));
62
+ return json(buildReport(resolve(dir, "dist")));
63
+ }));
64
+ server.registerTool("run_scenarios", {
65
+ title: "Run scenarios",
66
+ description: "Run scenarios/*.json (or the given files) in a headless browser. Returns per-step results, runtime errors, captures and final state.",
67
+ inputSchema: {
68
+ ...root,
69
+ files: z.array(z.string()).optional().describe("Scenario files relative to the root. Default: all."),
70
+ update: z.boolean().optional().describe("Accept new captures as baselines. Only when the human has approved the visual change."),
71
+ },
72
+ }, safely(async ({ root: r, files, update }) => {
73
+ const dir = rootOf(r);
74
+ const { plugins } = await loadConfig(dir);
75
+ const reports = await runScenarios({ root: dir, files: files ?? [], plugins, update: update ?? false, strict: false });
76
+ return json({ passed: reports.filter((x) => x.ok).length, total: reports.length, reports });
77
+ }));
78
+ // ---- live game tools -----------------------------------------------------------------------
79
+ server.registerTool("game_start", {
80
+ title: "Start game",
81
+ description: "Connect to the game. auto (default): attach to the human's live game if `debuff dev` has it open in a browser, else start a private " +
82
+ "headless copy, paused. Returns the mode, state and how many notes the human has flagged. Replaces any running session.",
83
+ inputSchema: {
84
+ ...root,
85
+ page: z.string().optional().describe('Path to open in headless mode. Default "/".'),
86
+ mode: z.enum(["auto", "live", "headless"]).optional().describe("Default auto."),
87
+ },
88
+ }, safely(async ({ root: r, page, mode }) => {
89
+ await session?.stop();
90
+ session = undefined;
91
+ session = await startSession(rootOf(r), { ...(page ? { page } : {}), ...(mode ? { mode } : {}) });
92
+ return json({ mode: session.mode, url: session.url, state: await session.call("state") });
93
+ }));
94
+ server.registerTool("game_state", {
95
+ title: "Game state",
96
+ description: "Current scene, tick, time, renderer, frame stats, the scene's inspect() data and plugin inspect data.",
97
+ inputSchema: {},
98
+ annotations: { readOnlyHint: true },
99
+ }, safely(async () => json(await live().call("state"))));
100
+ server.registerTool("game_step", {
101
+ title: "Advance time",
102
+ description: "Advance the simulation by fixed steps (60 per second by default) and return the new state.",
103
+ inputSchema: { steps: z.number().int().min(1).max(36_000).describe("Fixed simulation steps to run.") },
104
+ }, safely(async ({ steps }) => json(await live().call("step", steps))));
105
+ server.registerTool("game_input", {
106
+ title: "Send input",
107
+ description: "Keyboard or pointer input. press/release take a KeyboardEvent.code (e.g. Space, ArrowLeft, KeyZ). tap/pointer_down/pointer_move take logical x, y. " +
108
+ "Input takes effect on the next game_step.",
109
+ inputSchema: {
110
+ action: z.enum(["press", "release", "tap", "pointer_down", "pointer_move", "pointer_up"]),
111
+ code: z.string().optional().describe("For press/release."),
112
+ x: z.number().optional(),
113
+ y: z.number().optional(),
114
+ },
115
+ }, safely(async ({ action, code, x, y }) => {
116
+ const s = live();
117
+ if (action === "press" || action === "release") {
118
+ if (!code)
119
+ throw new Error(`${action} needs a key code`);
120
+ await s.call(action, code);
121
+ }
122
+ else if (action === "pointer_up") {
123
+ await s.call("pointerUp");
124
+ }
125
+ else {
126
+ if (x === undefined || y === undefined)
127
+ throw new Error(`${action} needs x and y`);
128
+ await s.call({ tap: "tap", pointer_down: "pointerDown", pointer_move: "pointerMove" }[action], x, y);
129
+ }
130
+ return json({ ok: true });
131
+ }));
132
+ server.registerTool("game_params", {
133
+ title: "List params",
134
+ description: "Every tunable param group: schema (type, default, min, max, units, description) and current values.",
135
+ inputSchema: {},
136
+ annotations: { readOnlyHint: true },
137
+ }, safely(async () => json(await live().call("params"))));
138
+ server.registerTool("game_set_param", {
139
+ title: "Set param",
140
+ description: "Set a tunable param live. Values are validated against the param's type and range. Changes last until game_start or a reload.",
141
+ inputSchema: { group: z.string(), key: z.string(), value: z.union([z.number(), z.boolean()]) },
142
+ }, safely(async ({ group, key, value }) => {
143
+ await live().call("setParam", group, key, value);
144
+ return json({ ok: true, group, key, value });
145
+ }));
146
+ server.registerTool("game_capture", {
147
+ title: "Capture frame",
148
+ description: "Screenshot of the current frame as a PNG image.",
149
+ inputSchema: {},
150
+ annotations: { readOnlyHint: true },
151
+ }, safely(async () => {
152
+ const dataUrl = await live().call("capture");
153
+ return { content: [{ type: "image", data: dataUrl.slice(dataUrl.indexOf(",") + 1), mimeType: "image/png" }] };
154
+ }));
155
+ server.registerTool("game_pause", {
156
+ title: "Pause or resume",
157
+ description: "Pause or resume a live game (headless games are always paused). game_step also pauses first.",
158
+ inputSchema: { paused: z.boolean() },
159
+ }, safely(async ({ paused }) => json(await live().call(paused ? "pause" : "resume"))));
160
+ server.registerTool("game_frames", {
161
+ title: "Frame breakdown",
162
+ description: "Recent frames, oldest first: intervalMs (what the player feels), updateMs, renderMs, steps (>1 means catching up), quads, drawCalls and " +
163
+ "longTaskMs (browser-reported main-thread stalls). Use it to explain stutter; in headless mode times reflect stepping, not real play.",
164
+ inputSchema: { count: z.number().int().min(1).max(240).optional().describe("Default 60.") },
165
+ annotations: { readOnlyHint: true },
166
+ }, safely(async ({ count }) => json(await live().call("frames", count ?? 60))));
167
+ server.registerTool("game_notes", {
168
+ title: "Human notes",
169
+ description: "Moments the human flagged in the overlay (live mode): their words, the tick, the scene's inspect() state then, and the frames leading up to it.",
170
+ inputSchema: {},
171
+ annotations: { readOnlyHint: true },
172
+ }, safely(async () => json(await live().call("notes"))));
173
+ server.registerTool("game_errors", {
174
+ title: "Runtime errors",
175
+ description: "Runtime errors since the game started, explained: kind (your-code, debuff-usage, debuff-bug, outside), where (source file:line, " +
176
+ "source-mapped), a hint, the loop phase and tick, and a repeat count. A loop error stops the game; fix it, then game_pause false to resume. " +
177
+ "For debuff-bug, tell the human and share reportUrl rather than working around it.",
178
+ inputSchema: {},
179
+ annotations: { readOnlyHint: true },
180
+ }, safely(async () => json(await live().call("errors"))));
181
+ server.registerTool("game_stop", {
182
+ title: "Stop game",
183
+ description: "Headless: close the browser and dev server. Live: resume the human's game and disconnect.",
184
+ inputSchema: {},
185
+ }, safely(async () => {
186
+ await session?.stop();
187
+ session = undefined;
188
+ return json({ ok: true });
189
+ }));
190
+ const close = server.close.bind(server);
191
+ server.close = async () => {
192
+ await session?.stop();
193
+ session = undefined;
194
+ await close();
195
+ };
196
+ return server;
197
+ }
198
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /** A running game an agent can drive through the dev hooks. */
2
+ export interface GameSession {
3
+ /**
4
+ * "live": the game the human has open under `debuff dev`, running in real time; the agent's actions
5
+ * show in their overlay. "headless": a private paused copy where time only advances via step.
6
+ */
7
+ readonly mode: "live" | "headless";
8
+ readonly url: string;
9
+ call<R = unknown>(method: string, ...args: unknown[]): Promise<R>;
10
+ stop(): Promise<void>;
11
+ }
12
+ export type StartMode = "auto" | "live" | "headless";
13
+ /** Live if `debuff dev` is running with a page open (or `mode` says so), else headless. */
14
+ export declare function startSession(root: string, options?: {
15
+ mode?: StartMode;
16
+ page?: string;
17
+ width?: number;
18
+ height?: number;
19
+ }): Promise<GameSession>;
20
+ /** The developer's own browser tab, reached through the dev server's agent link. */
21
+ export declare class LiveSession implements GameSession {
22
+ readonly url: string;
23
+ private readonly attachment;
24
+ readonly mode = "live";
25
+ private constructor();
26
+ static find(root: string): Promise<LiveSession | undefined>;
27
+ call<R = unknown>(method: string, ...args: unknown[]): Promise<R>;
28
+ stop(): Promise<void>;
29
+ }
30
+ /**
31
+ * A private game: the project's dev server plus a headless browser page in controlled mode
32
+ * (`?debuff-scenario`), so time only advances when an agent steps it.
33
+ */
34
+ export declare class HeadlessSession implements GameSession {
35
+ readonly url: string;
36
+ private readonly server;
37
+ private readonly browser;
38
+ private readonly page;
39
+ readonly mode = "headless";
40
+ private constructor();
41
+ static start(root: string, options?: {
42
+ page?: string;
43
+ width?: number;
44
+ height?: number;
45
+ }): Promise<HeadlessSession>;
46
+ /** Call a `window.__DEBUFF_DEV__` method. */
47
+ call<R = unknown>(method: string, ...args: unknown[]): Promise<R>;
48
+ stop(): Promise<void>;
49
+ }
50
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAeA,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,CAAC;AAErD,2FAA2F;AAC3F,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,SAAS,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GACjF,OAAO,CAAC,WAAW,CAAC,CAUtB;AAED,oFAAoF;AACpF,qBAAa,WAAY,YAAW,WAAW;IAG3C,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAH7B,QAAQ,CAAC,IAAI,UAAU;IACvB,OAAO,eAGH;IAEJ,OAAa,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAoBhE;IAEK,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAStE;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAI1B;CACF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,WAAW;IAG/C,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,IAAI;IALvB,QAAQ,CAAC,IAAI,cAAc;IAC3B,OAAO,eAKH;IAEJ,OAAa,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAyB3H;IAED,6CAA6C;IAC7C,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAEhE;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1B;CACF"}
@@ -0,0 +1,119 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { AGENT_PATHS, callHook, createDevServer, DEV_SERVER_FILE, hooksReady, launchBrowser, } from "@debuffjp/cli";
4
+ /** Live if `debuff dev` is running with a page open (or `mode` says so), else headless. */
5
+ export async function startSession(root, options = {}) {
6
+ const mode = options.mode ?? "auto";
7
+ if (mode !== "headless") {
8
+ const live = await LiveSession.find(root);
9
+ if (live)
10
+ return live;
11
+ if (mode === "live") {
12
+ throw new Error("No live game found. Run `debuff dev` and open the game in a browser, or use mode headless.");
13
+ }
14
+ }
15
+ return HeadlessSession.start(root, options);
16
+ }
17
+ /** The developer's own browser tab, reached through the dev server's agent link. */
18
+ export class LiveSession {
19
+ url;
20
+ attachment;
21
+ mode = "live";
22
+ constructor(url, attachment) {
23
+ this.url = url;
24
+ this.attachment = attachment;
25
+ }
26
+ static async find(root) {
27
+ let info;
28
+ try {
29
+ info = JSON.parse(readFileSync(join(root, DEV_SERVER_FILE), "utf8"));
30
+ }
31
+ catch {
32
+ return undefined;
33
+ }
34
+ try {
35
+ const res = await fetch(new URL(AGENT_PATHS.status, info.url), { signal: AbortSignal.timeout(1500) });
36
+ const status = (await res.json());
37
+ if (status.pages === 0)
38
+ return undefined;
39
+ // Hold an attach connection while this session lives, so the overlay shows the agent until it stops
40
+ // (or the agent process exits), not just right after an action.
41
+ const attachment = new AbortController();
42
+ const held = await fetch(new URL(AGENT_PATHS.attach, info.url), { signal: attachment.signal });
43
+ void held.body?.pipeTo(new WritableStream()).catch(() => undefined);
44
+ return new LiveSession(info.url, attachment);
45
+ }
46
+ catch {
47
+ return undefined; // stale file: the dev server is gone
48
+ }
49
+ }
50
+ async call(method, ...args) {
51
+ const res = await fetch(new URL(AGENT_PATHS.rpc, this.url), {
52
+ method: "POST",
53
+ headers: { "Content-Type": "application/json" },
54
+ body: JSON.stringify({ method, args }),
55
+ });
56
+ const body = (await res.json());
57
+ if (!body.ok)
58
+ throw new Error(body.error);
59
+ return body.result;
60
+ }
61
+ async stop() {
62
+ // The human's tab: leave it running, just let go of any pause the agent made.
63
+ await this.call("resume").catch(() => undefined);
64
+ this.attachment.abort();
65
+ }
66
+ }
67
+ /**
68
+ * A private game: the project's dev server plus a headless browser page in controlled mode
69
+ * (`?debuff-scenario`), so time only advances when an agent steps it.
70
+ */
71
+ export class HeadlessSession {
72
+ url;
73
+ server;
74
+ browser;
75
+ page;
76
+ mode = "headless";
77
+ constructor(url, server, browser, page) {
78
+ this.url = url;
79
+ this.server = server;
80
+ this.browser = browser;
81
+ this.page = page;
82
+ }
83
+ static async start(root, options = {}) {
84
+ const server = await createDevServer(root, { port: 0, logLevel: "error" });
85
+ await server.listen();
86
+ const base = server.resolvedUrls?.local[0];
87
+ if (!base) {
88
+ await server.close();
89
+ throw new Error("Dev server did not report a URL");
90
+ }
91
+ let browser;
92
+ try {
93
+ browser = await launchBrowser();
94
+ const page = await browser.newPage({
95
+ viewport: { width: options.width ?? 960, height: options.height ?? 540 },
96
+ deviceScaleFactor: 1,
97
+ });
98
+ const url = new URL(options.page ?? "/", base);
99
+ url.searchParams.set("debuff-scenario", "1");
100
+ await page.goto(url.toString());
101
+ await page.waitForFunction(hooksReady, undefined, { timeout: 15_000 });
102
+ return new HeadlessSession(url.toString(), server, browser, page);
103
+ }
104
+ catch (err) {
105
+ await browser?.close();
106
+ await server.close();
107
+ throw err;
108
+ }
109
+ }
110
+ /** Call a `window.__DEBUFF_DEV__` method. */
111
+ call(method, ...args) {
112
+ return this.page.evaluate(callHook, [method, args]);
113
+ }
114
+ async stop() {
115
+ await this.browser.close();
116
+ await this.server.close();
117
+ }
118
+ }
119
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@debuffjp/mcp",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "MCP server for debuff: lets AI agents inspect, build, test and play a debuff game",
5
+ "keywords": [
6
+ "game",
7
+ "mcp",
8
+ "agents",
9
+ "model-context-protocol",
10
+ "debuff"
11
+ ],
12
+ "license": "MIT",
13
+ "homepage": "https://debuff.jp",
14
+ "bugs": "https://github.com/debuffjp/debuff/issues",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/debuffjp/debuff.git",
18
+ "directory": "tooling/mcp"
19
+ },
20
+ "type": "module",
21
+ "bin": {
22
+ "debuff-mcp": "bin/debuff-mcp.js"
23
+ },
24
+ "main": "./dist/index.js",
25
+ "types": "./dist/index.d.ts",
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/index.d.ts",
29
+ "default": "./dist/index.js"
30
+ }
31
+ },
32
+ "files": [
33
+ "bin",
34
+ "dist",
35
+ "README.md"
36
+ ],
37
+ "engines": {
38
+ "node": ">=22"
39
+ },
40
+ "scripts": {
41
+ "build": "tsc -b"
42
+ },
43
+ "dependencies": {
44
+ "@debuffjp/cli": "0.1.0",
45
+ "@modelcontextprotocol/sdk": "^1.32.1",
46
+ "zod": "^4.6.7",
47
+ "playwright-core": "^1.64.1",
48
+ "vite": "^8.3.4"
49
+ },
50
+ "devDependencies": {
51
+ "@types/node": "^24.0.0"
52
+ },
53
+ "publishConfig": {
54
+ "access": "public",
55
+ "provenance": false
56
+ }
57
+ }