@buoy-gg/mcp 7.0.19 → 7.0.21

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.
@@ -344,7 +344,7 @@ async function waitForBroker(url, attempts = 20) {
344
344
  var delay = (ms) => new Promise((r) => setTimeout(r, ms));
345
345
 
346
346
  // src/version.ts
347
- var VERSION = "7.0.19" ? "7.0.19" : "0.0.0-dev";
347
+ var VERSION = "7.0.21" ? "7.0.21" : "0.0.0-dev";
348
348
  var PACKAGE_NAME = "@buoy-gg/mcp";
349
349
 
350
350
  // src/update/checkForUpdate.ts
@@ -1254,7 +1254,7 @@ function registerEventsTools(server, sync) {
1254
1254
  server.registerTool(
1255
1255
  "get_events",
1256
1256
  {
1257
- description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1257
+ description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. Check this timeline BEFORE driving the UI to reproduce a bug \u2014 the failure is usually already recorded here. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1258
1258
  inputSchema: {
1259
1259
  limit: z3.number().optional().describe("Most-recent N events to include. Default 25."),
1260
1260
  sources: z3.array(z3.string()).optional().describe('Only these sources. Coarse names accepted: "network", "redux", "react-query", "storage", "route", "render", "zustand", "jotai". Omit for all.'),
@@ -1649,7 +1649,7 @@ function registerStorageTools(server, sync) {
1649
1649
  server.registerTool(
1650
1650
  "get_storage",
1651
1651
  {
1652
- description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out. Use for 'what's in storage / read this key'.",
1652
+ description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Data wrong or missing after an app restart \u2192 read the persisted values here FIRST \u2014 the bug is usually a stale, missing, or malformed stored value, and reading it beats hunting for it in code. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out.",
1653
1653
  inputSchema: {
1654
1654
  pattern: z8.string().optional().describe("Only keys containing this substring (case-insensitive)."),
1655
1655
  includeValues: z8.boolean().optional().describe("Include each key's value (truncated). Default false (keys only)."),
@@ -1850,7 +1850,7 @@ function registerConsoleTools(server, sync) {
1850
1850
  server.registerTool(
1851
1851
  "get_console",
1852
1852
  {
1853
- description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. Use for 'what's logging / any console errors'. For full args/stack of an entry, fall back to get_snapshot('console').",
1853
+ description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. App crashing or erroring \u2192 read this FIRST: it captures crashes and errors including FATAL uncaught JS errors (tagged [FATAL], hooked from RN's global error handler), so a dead app's last words are here, not in the code. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. For full args/stack of an entry, fall back to get_snapshot('console').",
1854
1854
  inputSchema: {
1855
1855
  level: z10.enum(["verbose", "info", "warning", "error"]).optional().describe("Minimum level to include (severity threshold). e.g. 'warning' = warnings + errors. Default all."),
1856
1856
  limit: z10.number().optional().describe("Most-recent N entries. Default 50."),
@@ -2192,7 +2192,7 @@ function registerNetworkTools(server, sync) {
2192
2192
  server.registerTool(
2193
2193
  "get_network_requests",
2194
2194
  {
2195
- description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
2195
+ description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. API data wrong or calls failing \u2192 read the actual request+response bodies here instead of guessing from code; capture starts at app launch, so boot-time requests (session bootstrap, first queries) are included. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
2196
2196
  inputSchema: {
2197
2197
  limit: z12.number().optional().describe("Most-recent N requests. Default 25."),
2198
2198
  pattern: z12.string().optional().describe("Only requests whose URL contains this substring (case-insensitive)."),
@@ -3659,6 +3659,8 @@ function runInit(opts = {}) {
3659
3659
  upsertMcpConfig(path3.join(cwd, ".vscode", "mcp.json"), "servers", entry, cwd, done);
3660
3660
  }
3661
3661
  installSkills(skillSrcDir, cwd, done);
3662
+ appendDebuggingGuide(path3.join(cwd, "CLAUDE.md"), cwd, done);
3663
+ appendDebuggingGuide(path3.join(cwd, "AGENTS.md"), cwd, done);
3662
3664
  log("Buoy MCP installed:");
3663
3665
  for (const line of done) log(" \u2022 " + line);
3664
3666
  log("");
@@ -3805,6 +3807,49 @@ function upsertMcpConfig(file, key, entry, cwd, done) {
3805
3807
  fs3.writeFileSync(file, JSON.stringify(json3, null, 2) + "\n");
3806
3808
  done.push(`${existed ? "updated" : "added"} \`buoy\` in ${rel(file, cwd)}`);
3807
3809
  }
3810
+ var GUIDE_HEADING = "# Debugging with Buoy";
3811
+ var DEBUGGING_GUIDE = `${GUIDE_HEADING}
3812
+
3813
+ This app's runtime is inspectable while it runs: the Buoy MCP tools read live
3814
+ state straight from the device. Route by symptom before reading source:
3815
+
3816
+ - \`get_events\` \u2014 the universal activity timeline (network, state changes,
3817
+ storage writes, routes, renders). Check it BEFORE driving the UI to
3818
+ reproduce a bug \u2014 the failure is usually already recorded.
3819
+ - \`get_network_requests\` \u2014 API data wrong or calls failing \u2192 read the actual
3820
+ request + response bodies instead of guessing from code. Boot-time requests
3821
+ are captured too.
3822
+ - \`get_console\` \u2014 crashes and errors, including FATAL uncaught JS errors
3823
+ (tagged \`[FATAL]\`).
3824
+ - \`get_storage\` \u2014 data wrong or missing after an app restart \u2192 read the
3825
+ persisted values FIRST.
3826
+ - \`get_snapshot\` \u2014 full current state of any one tool when the compact views
3827
+ aren't enough.
3828
+
3829
+ Runtime facts beat guesses.
3830
+ `;
3831
+ function appendDebuggingGuide(file, cwd, done) {
3832
+ let existing = "";
3833
+ if (fs3.existsSync(file)) {
3834
+ try {
3835
+ existing = fs3.readFileSync(file, "utf8");
3836
+ } catch {
3837
+ done.push(`\u26A0 could not read ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3838
+ return;
3839
+ }
3840
+ if (existing.includes(GUIDE_HEADING)) {
3841
+ done.push(`${rel(file, cwd)} already has the "${GUIDE_HEADING}" block`);
3842
+ return;
3843
+ }
3844
+ }
3845
+ const separator = existing === "" ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
3846
+ try {
3847
+ fs3.writeFileSync(file, existing + separator + DEBUGGING_GUIDE);
3848
+ done.push(`${existing ? "appended" : "created"} "${GUIDE_HEADING}" block in ${rel(file, cwd)}`);
3849
+ } catch {
3850
+ done.push(`\u26A0 could not write ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3851
+ }
3852
+ }
3808
3853
  function installSkills(skillSrcDir, cwd, done) {
3809
3854
  if (!fs3.existsSync(skillSrcDir)) {
3810
3855
  done.push("\u26A0 no bundled skills found \u2014 skipped");
package/dist/cli.js CHANGED
@@ -384,7 +384,7 @@ var import_node_os = __toESM(require("os"));
384
384
  var import_node_path = __toESM(require("path"));
385
385
 
386
386
  // src/version.ts
387
- var VERSION = "7.0.19" ? "7.0.19" : "0.0.0-dev";
387
+ var VERSION = "7.0.21" ? "7.0.21" : "0.0.0-dev";
388
388
  var PACKAGE_NAME = "@buoy-gg/mcp";
389
389
 
390
390
  // src/update/checkForUpdate.ts
@@ -1290,7 +1290,7 @@ function registerEventsTools(server, sync) {
1290
1290
  server.registerTool(
1291
1291
  "get_events",
1292
1292
  {
1293
- description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1293
+ description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. Check this timeline BEFORE driving the UI to reproduce a bug \u2014 the failure is usually already recorded here. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1294
1294
  inputSchema: {
1295
1295
  limit: import_zod3.z.number().optional().describe("Most-recent N events to include. Default 25."),
1296
1296
  sources: import_zod3.z.array(import_zod3.z.string()).optional().describe('Only these sources. Coarse names accepted: "network", "redux", "react-query", "storage", "route", "render", "zustand", "jotai". Omit for all.'),
@@ -1455,7 +1455,7 @@ function registerNetworkTools(server, sync) {
1455
1455
  server.registerTool(
1456
1456
  "get_network_requests",
1457
1457
  {
1458
- description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
1458
+ description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. API data wrong or calls failing \u2192 read the actual request+response bodies here instead of guessing from code; capture starts at app launch, so boot-time requests (session bootstrap, first queries) are included. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
1459
1459
  inputSchema: {
1460
1460
  limit: import_zod4.z.number().optional().describe("Most-recent N requests. Default 25."),
1461
1461
  pattern: import_zod4.z.string().optional().describe("Only requests whose URL contains this substring (case-insensitive)."),
@@ -2172,7 +2172,7 @@ function registerStorageTools(server, sync) {
2172
2172
  server.registerTool(
2173
2173
  "get_storage",
2174
2174
  {
2175
- description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out. Use for 'what's in storage / read this key'.",
2175
+ description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Data wrong or missing after an app restart \u2192 read the persisted values here FIRST \u2014 the bug is usually a stale, missing, or malformed stored value, and reading it beats hunting for it in code. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out.",
2176
2176
  inputSchema: {
2177
2177
  pattern: import_zod9.z.string().optional().describe("Only keys containing this substring (case-insensitive)."),
2178
2178
  includeValues: import_zod9.z.boolean().optional().describe("Include each key's value (truncated). Default false (keys only)."),
@@ -2373,7 +2373,7 @@ function registerConsoleTools(server, sync) {
2373
2373
  server.registerTool(
2374
2374
  "get_console",
2375
2375
  {
2376
- description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. Use for 'what's logging / any console errors'. For full args/stack of an entry, fall back to get_snapshot('console').",
2376
+ description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. App crashing or erroring \u2192 read this FIRST: it captures crashes and errors including FATAL uncaught JS errors (tagged [FATAL], hooked from RN's global error handler), so a dead app's last words are here, not in the code. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. For full args/stack of an entry, fall back to get_snapshot('console').",
2377
2377
  inputSchema: {
2378
2378
  level: import_zod11.z.enum(["verbose", "info", "warning", "error"]).optional().describe("Minimum level to include (severity threshold). e.g. 'warning' = warnings + errors. Default all."),
2379
2379
  limit: import_zod11.z.number().optional().describe("Most-recent N entries. Default 50."),
@@ -3691,6 +3691,8 @@ function runInit(opts = {}) {
3691
3691
  upsertMcpConfig(import_node_path3.default.join(cwd, ".vscode", "mcp.json"), "servers", entry, cwd, done);
3692
3692
  }
3693
3693
  installSkills(skillSrcDir, cwd, done);
3694
+ appendDebuggingGuide(import_node_path3.default.join(cwd, "CLAUDE.md"), cwd, done);
3695
+ appendDebuggingGuide(import_node_path3.default.join(cwd, "AGENTS.md"), cwd, done);
3694
3696
  log("Buoy MCP installed:");
3695
3697
  for (const line of done) log(" \u2022 " + line);
3696
3698
  log("");
@@ -3837,6 +3839,49 @@ function upsertMcpConfig(file, key, entry, cwd, done) {
3837
3839
  import_node_fs3.default.writeFileSync(file, JSON.stringify(json3, null, 2) + "\n");
3838
3840
  done.push(`${existed ? "updated" : "added"} \`buoy\` in ${rel(file, cwd)}`);
3839
3841
  }
3842
+ var GUIDE_HEADING = "# Debugging with Buoy";
3843
+ var DEBUGGING_GUIDE = `${GUIDE_HEADING}
3844
+
3845
+ This app's runtime is inspectable while it runs: the Buoy MCP tools read live
3846
+ state straight from the device. Route by symptom before reading source:
3847
+
3848
+ - \`get_events\` \u2014 the universal activity timeline (network, state changes,
3849
+ storage writes, routes, renders). Check it BEFORE driving the UI to
3850
+ reproduce a bug \u2014 the failure is usually already recorded.
3851
+ - \`get_network_requests\` \u2014 API data wrong or calls failing \u2192 read the actual
3852
+ request + response bodies instead of guessing from code. Boot-time requests
3853
+ are captured too.
3854
+ - \`get_console\` \u2014 crashes and errors, including FATAL uncaught JS errors
3855
+ (tagged \`[FATAL]\`).
3856
+ - \`get_storage\` \u2014 data wrong or missing after an app restart \u2192 read the
3857
+ persisted values FIRST.
3858
+ - \`get_snapshot\` \u2014 full current state of any one tool when the compact views
3859
+ aren't enough.
3860
+
3861
+ Runtime facts beat guesses.
3862
+ `;
3863
+ function appendDebuggingGuide(file, cwd, done) {
3864
+ let existing = "";
3865
+ if (import_node_fs3.default.existsSync(file)) {
3866
+ try {
3867
+ existing = import_node_fs3.default.readFileSync(file, "utf8");
3868
+ } catch {
3869
+ done.push(`\u26A0 could not read ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3870
+ return;
3871
+ }
3872
+ if (existing.includes(GUIDE_HEADING)) {
3873
+ done.push(`${rel(file, cwd)} already has the "${GUIDE_HEADING}" block`);
3874
+ return;
3875
+ }
3876
+ }
3877
+ const separator = existing === "" ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
3878
+ try {
3879
+ import_node_fs3.default.writeFileSync(file, existing + separator + DEBUGGING_GUIDE);
3880
+ done.push(`${existing ? "appended" : "created"} "${GUIDE_HEADING}" block in ${rel(file, cwd)}`);
3881
+ } catch {
3882
+ done.push(`\u26A0 could not write ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3883
+ }
3884
+ }
3840
3885
  function installSkills(skillSrcDir, cwd, done) {
3841
3886
  if (!import_node_fs3.default.existsSync(skillSrcDir)) {
3842
3887
  done.push("\u26A0 no bundled skills found \u2014 skipped");
package/dist/cli.mjs CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  SyncClient,
4
4
  createMcpServer,
5
5
  runInit
6
- } from "./chunk-TZ2C4X6V.mjs";
6
+ } from "./chunk-JQ5GZ4LG.mjs";
7
7
 
8
8
  // src/cli.ts
9
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/dist/index.js CHANGED
@@ -419,7 +419,7 @@ var import_node_os = __toESM(require("os"));
419
419
  var import_node_path = __toESM(require("path"));
420
420
 
421
421
  // src/version.ts
422
- var VERSION = "7.0.19" ? "7.0.19" : "0.0.0-dev";
422
+ var VERSION = "7.0.21" ? "7.0.21" : "0.0.0-dev";
423
423
  var PACKAGE_NAME = "@buoy-gg/mcp";
424
424
 
425
425
  // src/update/checkForUpdate.ts
@@ -1325,7 +1325,7 @@ function registerEventsTools(server, sync) {
1325
1325
  server.registerTool(
1326
1326
  "get_events",
1327
1327
  {
1328
- description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1328
+ description: "The UNIVERSAL, token-bounded activity reader for what's happening in the app across ALL Buoy tools. Check this timeline BEFORE driving the UI to reproduce a bug \u2014 the failure is usually already recorded here. The events tool aggregates every source \u2014 network requests, redux/zustand/jotai actions, react-query query/mutation updates, storage writes, route changes, renders \u2014 so filter to one with `sources`: ['network'] lists HTTP requests, ['redux'] dispatched actions, ['react-query'] query updates, ['storage'] writes, ['route'] navigation. Returns a compact one-line summary per event (source \xB7 status \xB7 title \xB7 timing) by DEFAULT \u2014 not the heavy raw payloads \u2014 so it's cheap. Re-call with includeData:true, a bigger limit, or specific sources only when you need detail. PREFER this over get_snapshot (and over reading source code) for any 'what happened' / 'show me the requests/actions' question; get_snapshot is the raw heavy current-state escape hatch.",
1329
1329
  inputSchema: {
1330
1330
  limit: import_zod3.z.number().optional().describe("Most-recent N events to include. Default 25."),
1331
1331
  sources: import_zod3.z.array(import_zod3.z.string()).optional().describe('Only these sources. Coarse names accepted: "network", "redux", "react-query", "storage", "route", "render", "zustand", "jotai". Omit for all.'),
@@ -1490,7 +1490,7 @@ function registerNetworkTools(server, sync) {
1490
1490
  server.registerTool(
1491
1491
  "get_network_requests",
1492
1492
  {
1493
- description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
1493
+ description: "List captured HTTP requests WITH their request ids, plus which ones are pinned/saved. API data wrong or calls failing \u2192 read the actual request+response bodies here instead of guessing from code; capture starts at app launch, so boot-time requests (session bootstrap, first queries) are included. Use this (not get_events) when you need to ACT on a request \u2014 every network action is keyed by the id, and get_events does not emit ids. Compact by default: one line per request (id \xB7 status \xB7 method \xB7 path \xB7 timing \xB7 size). Set flagged:'pinned'|'saved'|'any' to read the developer's KEPT requests \u2014 those are snapshots that outlive Clear, the 500-request cap and app restarts, so treat a pinned request as 'a human flagged this as the problem'. Filter with status:'errors' to go straight to failures, or `pattern` to match a URL substring. includeBodies:true adds truncated request/response payloads (heavier).",
1494
1494
  inputSchema: {
1495
1495
  limit: import_zod4.z.number().optional().describe("Most-recent N requests. Default 25."),
1496
1496
  pattern: import_zod4.z.string().optional().describe("Only requests whose URL contains this substring (case-insensitive)."),
@@ -2207,7 +2207,7 @@ function registerStorageTools(server, sync) {
2207
2207
  server.registerTool(
2208
2208
  "get_storage",
2209
2209
  {
2210
- description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out. Use for 'what's in storage / read this key'.",
2210
+ description: "Read the app's storage keys across ALL backends: AsyncStorage, MMKV (per instance), and Expo SecureStore. Data wrong or missing after an app restart \u2192 read the persisted values here FIRST \u2014 the bug is usually a stale, missing, or malformed stored value, and reading it beats hunting for it in code. Compact by DEFAULT \u2014 keys only \u2014 so it's token-cheap; pass includeValues:true for the values (truncated), or a `pattern` substring to narrow. Buoy's own internal keys are already filtered out.",
2211
2211
  inputSchema: {
2212
2212
  pattern: import_zod9.z.string().optional().describe("Only keys containing this substring (case-insensitive)."),
2213
2213
  includeValues: import_zod9.z.boolean().optional().describe("Include each key's value (truncated). Default false (keys only)."),
@@ -2408,7 +2408,7 @@ function registerConsoleTools(server, sync) {
2408
2408
  server.registerTool(
2409
2409
  "get_console",
2410
2410
  {
2411
- description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. Use for 'what's logging / any console errors'. For full args/stack of an entry, fall back to get_snapshot('console').",
2411
+ description: "Read the app's captured console logs (console.log/warn/error/\u2026) as a compact tail \u2014 time \xB7 level \xB7 message. App crashing or erroring \u2192 read this FIRST: it captures crashes and errors including FATAL uncaught JS errors (tagged [FATAL], hooked from RN's global error handler), so a dead app's last words are here, not in the code. Token-cheap (uses each entry's pre-rendered message, not raw args). Filter by minimum level (e.g. level:'warning' for warnings+errors) or a message substring. For full args/stack of an entry, fall back to get_snapshot('console').",
2412
2412
  inputSchema: {
2413
2413
  level: import_zod11.z.enum(["verbose", "info", "warning", "error"]).optional().describe("Minimum level to include (severity threshold). e.g. 'warning' = warnings + errors. Default all."),
2414
2414
  limit: import_zod11.z.number().optional().describe("Most-recent N entries. Default 50."),
@@ -3726,6 +3726,8 @@ function runInit(opts = {}) {
3726
3726
  upsertMcpConfig(import_node_path3.default.join(cwd, ".vscode", "mcp.json"), "servers", entry, cwd, done);
3727
3727
  }
3728
3728
  installSkills(skillSrcDir, cwd, done);
3729
+ appendDebuggingGuide(import_node_path3.default.join(cwd, "CLAUDE.md"), cwd, done);
3730
+ appendDebuggingGuide(import_node_path3.default.join(cwd, "AGENTS.md"), cwd, done);
3729
3731
  log("Buoy MCP installed:");
3730
3732
  for (const line of done) log(" \u2022 " + line);
3731
3733
  log("");
@@ -3872,6 +3874,49 @@ function upsertMcpConfig(file, key, entry, cwd, done) {
3872
3874
  import_node_fs3.default.writeFileSync(file, JSON.stringify(json3, null, 2) + "\n");
3873
3875
  done.push(`${existed ? "updated" : "added"} \`buoy\` in ${rel(file, cwd)}`);
3874
3876
  }
3877
+ var GUIDE_HEADING = "# Debugging with Buoy";
3878
+ var DEBUGGING_GUIDE = `${GUIDE_HEADING}
3879
+
3880
+ This app's runtime is inspectable while it runs: the Buoy MCP tools read live
3881
+ state straight from the device. Route by symptom before reading source:
3882
+
3883
+ - \`get_events\` \u2014 the universal activity timeline (network, state changes,
3884
+ storage writes, routes, renders). Check it BEFORE driving the UI to
3885
+ reproduce a bug \u2014 the failure is usually already recorded.
3886
+ - \`get_network_requests\` \u2014 API data wrong or calls failing \u2192 read the actual
3887
+ request + response bodies instead of guessing from code. Boot-time requests
3888
+ are captured too.
3889
+ - \`get_console\` \u2014 crashes and errors, including FATAL uncaught JS errors
3890
+ (tagged \`[FATAL]\`).
3891
+ - \`get_storage\` \u2014 data wrong or missing after an app restart \u2192 read the
3892
+ persisted values FIRST.
3893
+ - \`get_snapshot\` \u2014 full current state of any one tool when the compact views
3894
+ aren't enough.
3895
+
3896
+ Runtime facts beat guesses.
3897
+ `;
3898
+ function appendDebuggingGuide(file, cwd, done) {
3899
+ let existing = "";
3900
+ if (import_node_fs3.default.existsSync(file)) {
3901
+ try {
3902
+ existing = import_node_fs3.default.readFileSync(file, "utf8");
3903
+ } catch {
3904
+ done.push(`\u26A0 could not read ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3905
+ return;
3906
+ }
3907
+ if (existing.includes(GUIDE_HEADING)) {
3908
+ done.push(`${rel(file, cwd)} already has the "${GUIDE_HEADING}" block`);
3909
+ return;
3910
+ }
3911
+ }
3912
+ const separator = existing === "" ? "" : existing.endsWith("\n") ? "\n" : "\n\n";
3913
+ try {
3914
+ import_node_fs3.default.writeFileSync(file, existing + separator + DEBUGGING_GUIDE);
3915
+ done.push(`${existing ? "appended" : "created"} "${GUIDE_HEADING}" block in ${rel(file, cwd)}`);
3916
+ } catch {
3917
+ done.push(`\u26A0 could not write ${rel(file, cwd)} \u2014 skipped the debugging guide`);
3918
+ }
3919
+ }
3875
3920
  function installSkills(skillSrcDir, cwd, done) {
3876
3921
  if (!import_node_fs3.default.existsSync(skillSrcDir)) {
3877
3922
  done.push("\u26A0 no bundled skills found \u2014 skipped");
package/dist/index.mjs CHANGED
@@ -27,7 +27,7 @@ import {
27
27
  registerStorageTools,
28
28
  registerZustandTools,
29
29
  runInit
30
- } from "./chunk-TZ2C4X6V.mjs";
30
+ } from "./chunk-JQ5GZ4LG.mjs";
31
31
  export {
32
32
  ALL_DEVICES,
33
33
  BROKER_PORT,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@buoy-gg/mcp",
3
3
  "mcpName": "io.github.buoy-gg/mcp",
4
- "version": "7.0.19",
4
+ "version": "7.0.21",
5
5
  "description": "MCP server for Buoy devtools — lets any LLM copilot drive Buoy tools (perf-monitor benchmarks, etc.) over the devtool sync protocol",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -31,7 +31,7 @@
31
31
  "@modelcontextprotocol/sdk": "^1.29.0",
32
32
  "socket.io-client": "^4.8.1",
33
33
  "zod": "^3.25.0",
34
- "@buoy-gg/sync-broker": "^7.0.19"
34
+ "@buoy-gg/sync-broker": "^7.0.21"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/node": "^22.0.0",