@ait-co/devtools 0.1.46 → 0.1.49

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/ait-source.ts","../../src/mcp/server.ts"],"mappings":";;;;;;;AAsBA;;;;;AAGA;;;;;;;;;;;;;KAHY,kBAAA;AAqBZ;AAAA,UAlBiB,UAAA;;EAEf,MAAA;EAiBiB;EAfjB,IAAA;EAwBsB;EAtBtB,SAAA;EAsByB;EApBzB,MAAA;EAuBe;EArBf,MAAA;;EAEA,KAAA;EAuBU;EArBV,QAAA,EAAU,kBAAA;AAAA;;UAIK,iBAAA;EACf,KAAA,EAAO,UAAA;AAAA;;;;;;;KASG,YAAA,GAAe,MAAA;;UAGV,yBAAA;EAW2C;EAT1D,WAAA;EAYuB;EAVvB,UAAA;AAAA;;UAIe,YAAA;EACf,uBAAA,EAAyB,iBAAA;EACzB,kBAAA,EAAoB,YAAA;EACpB,+BAAA,EAAiC,yBAAA;AAAA;AAAA,KAGvB,aAAA,SAAsB,YAAA;;;;;UAMjB,SAAA;EACf,GAAA,WAAc,aAAA,EAAe,MAAA,EAAQ,CAAA,GAAI,OAAA,CAAQ,YAAA,CAAa,CAAA;AAAA;;;UCkN/C,mBAAA;EDnNS;ECqNxB,SAAA,GAAY,SAAA;AAAA;;iBA8IE,eAAA,CAAgB,IAAA,GAAM,mBAAA,GAA2B,MAAA;;iBAuF3C,YAAA,CAAA,GAAgB,OAAA"}
1
+ {"version":3,"file":"server.d.ts","names":[],"sources":["../../src/mcp/ait-source.ts","../../src/mcp/server.ts"],"mappings":";;;;;;;AAsBA;;;;;AAGA;;;;;;;;;;;;;KAHY,kBAAA;AAqBZ;AAAA,UAlBiB,UAAA;;EAEf,MAAA;EAiBiB;EAfjB,IAAA;EAwBsB;EAtBtB,SAAA;EAsByB;EApBzB,MAAA;EAuBe;EArBf,MAAA;;EAEA,KAAA;EAuBU;EArBV,QAAA,EAAU,kBAAA;AAAA;;UAIK,iBAAA;EACf,KAAA,EAAO,UAAA;AAAA;;;;;;;KASG,YAAA,GAAe,MAAA;;UAGV,yBAAA;EAW2C;EAT1D,WAAA;EAYuB;EAVvB,UAAA;AAAA;;UAIe,YAAA;EACf,uBAAA,EAAyB,iBAAA;EACzB,kBAAA,EAAoB,YAAA;EACpB,+BAAA,EAAiC,yBAAA;AAAA;AAAA,KAGvB,aAAA,SAAsB,YAAA;;;;;UAMjB,SAAA;EACf,GAAA,WAAc,aAAA,EAAe,MAAA,EAAQ,CAAA,GAAI,OAAA,CAAQ,YAAA,CAAa,CAAA;AAAA;;;UC6P/C,mBAAA;ED9PS;ECgQxB,SAAA,GAAY,SAAA;AAAA;;iBA8IE,eAAA,CAAgB,IAAA,GAAM,mBAAA,GAA2B,MAAA;;iBAqH3C,YAAA,CAAA,GAAgB,OAAA"}
@@ -38,6 +38,51 @@ var HttpAitSource = class {
38
38
  }
39
39
  };
40
40
  //#endregion
41
+ //#region src/mcp/envelope.ts
42
+ /**
43
+ * Returns `true` when `AIT_MCP_COMPAT=chrome-devtools` is set, which bypasses
44
+ * envelope wrapping and returns raw payloads (0.1.x back-compat).
45
+ */
46
+ function isCompatMode() {
47
+ return process.env.AIT_MCP_COMPAT === "chrome-devtools";
48
+ }
49
+ /**
50
+ * Maps `McpEnvironment` to `EnvelopeEnv`. After #307 these are the same
51
+ * union (`mock | relay-dev | relay-live`), so this is identity — kept as a
52
+ * named export for surface stability if envelope env diverges in the future.
53
+ */
54
+ function toEnvelopeEnv(env) {
55
+ return env;
56
+ }
57
+ /**
58
+ * Wraps `data` in a `ToolEnvelope<T>` **unless** compat mode is active, in
59
+ * which case `data` is returned as-is.
60
+ *
61
+ * Use this at every tool call-site in `debug-server.ts` and `server.ts`.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * return jsonResult(wrapEnvelope(listPages(connection, tunnel), {
66
+ * tool: 'list_pages',
67
+ * env: resolveEnvironment(),
68
+ * attached: connection.listTargets().length > 0,
69
+ * }));
70
+ * ```
71
+ */
72
+ function wrapEnvelope(data, ctx) {
73
+ if (isCompatMode()) return data;
74
+ return {
75
+ ok: true,
76
+ data,
77
+ meta: {
78
+ tool: ctx.tool,
79
+ env: toEnvelopeEnv(ctx.env),
80
+ attached: ctx.attached,
81
+ contentType: ctx.contentType ?? "json"
82
+ }
83
+ };
84
+ }
85
+ //#endregion
41
86
  //#region src/mcp/errors.ts
42
87
  /**
43
88
  * 한국어 한 줄 "원인 + 다음 행동" 포맷으로 에러 결과를 빌드한다.
@@ -53,6 +98,17 @@ function mcpError(message) {
53
98
  isError: true
54
99
  };
55
100
  }
101
+ /**
102
+ * Tier A/B 환경 불일치 거부 메시지.
103
+ *
104
+ * @param toolName - 거부된 tool 이름.
105
+ * @param requiredEnv - 해당 tool이 요구하는 환경 ('mock' | 'relay').
106
+ * @param currentEnv - 현재 세션 환경.
107
+ * @param reason - 환경이 결정된 근거 (EnvironmentReason 문자열).
108
+ */
109
+ function tierRejectionError(toolName, requiredEnv, currentEnv, reason) {
110
+ return mcpError(`${`${toolName}은 ${requiredEnv === "relay" ? "relay (실기기 연결)" : "mock (로컬 브라우저)"} 환경에서만 사용할 수 있습니다. 현재 환경: ${currentEnv === "relay" ? "relay" : "mock"} (${reason}). ${requiredEnv === "relay" ? "relay로 전환하려면 MCP_ENV=relay 설정 후 서버를 재시작하고 build_attach_url → QR 스캔으로 실기기를 attach하세요." : "mock으로 전환하려면 MCP_ENV=mock 설정 후 서버를 재시작하세요."}`}\n\n${`tool ${toolName} is available only in ${requiredEnv}. Current environment is ${currentEnv} (${reason}).`}`);
111
+ }
56
112
  //#endregion
57
113
  //#region src/mcp/sdk-signatures.ts
58
114
  function isObject(v) {
@@ -277,7 +333,7 @@ new Set([
277
333
  },
278
334
  {
279
335
  name: "measure_safe_area",
280
- description: "Runs a safe-area probe on the attached mini-app page via Runtime.evaluate and returns normalized safe-area insets, viewport geometry, device pixel ratio, and User-Agent. Read-only — does not modify page state. Tier C per RFC #277: the same Runtime.evaluate probe runs in both `mock` (devtools panel page with window.__ait state) and `relay` (real-device WebView with window.__sdk). The result includes a `source: \"mock\" | \"relay\"` field so consumers can identify provenance without inspecting payload values. Use in a relay session (phone attached) to get ground-truth values for upgrading a viewport preset from extrapolated/placeholder to measured. Requires a page to be attached — call list_pages first.",
336
+ description: "Runs a safe-area probe on the attached mini-app page via Runtime.evaluate and returns normalized safe-area insets, viewport geometry, device pixel ratio, and User-Agent. Read-only — does not modify page state. Tier C per RFC #277: the same Runtime.evaluate probe runs in both `mock` (devtools panel page with window.__ait state) and `relay` (real-device WebView with window.__sdk). The result includes a `source: \"mock\" | \"relay-dev\" | \"relay-live\"` field so consumers can identify provenance without inspecting payload values. Use in a relay session (phone attached) to get ground-truth values for upgrading a viewport preset from extrapolated/placeholder to measured. Requires a page to be attached — call list_pages first.",
281
337
  inputSchema: {
282
338
  type: "object",
283
339
  properties: {},
@@ -319,7 +375,7 @@ new Set([
319
375
  },
320
376
  {
321
377
  name: "call_sdk",
322
- description: "Calls a dogfood SDK method via the window.__sdkCall bridge (exported by @apps-in-toss/web-framework only in __DEBUG_BUILD__ bundles). NOT read-only — SDK calls have side effects (navigation, payments, permissions, etc.). On env 2/3 (real device relay) this hits the real SDK; on env 1 (local mock) it hits the mock SDK. Requires the relay to be attached — call list_pages first. Returns {ok: true, value} on success or {ok: false, error} on failure. If a Runtime.exceptionThrown event was observed within [callStart-50ms, callEnd+200ms], the result also includes `recentException` for crash triage. Returns a clear error if window.__sdkCall is not available (non-dogfood bundle) — redeploy via dogfood channel: `ait build && aitcc app deploy`.\n\nSECURITY: method name, args, and result value are not redacted — never include secrets.\n\nLIVE guard: when running against a live/production relay (relay-live env, MCP_ENV=relay-live), this tool requires `confirm: true` to acknowledge that the SDK call may affect real users. Without it the call is rejected with a structured error. mock and relay-dev sessions are unaffected.\n\nIMPORTANT — 인자 시그니처 (잘못된 인자로 호출하면 토스 앱 crash 위험):\n setDeviceOrientation: call_sdk(\"setDeviceOrientation\", [{ type: \"landscape\" }]) // NOT \"landscape\"\n setIosSwipeGestureEnabled: call_sdk(\"setIosSwipeGestureEnabled\", [{ isEnabled: false }])\n setSecureScreen: call_sdk(\"setSecureScreen\", [{ enabled: true }])\n setScreenAwakeMode: call_sdk(\"setScreenAwakeMode\", [{ enabled: true }])\n getOperationalEnvironment: call_sdk(\"getOperationalEnvironment\", [])\n getPlatformOS: call_sdk(\"getPlatformOS\", [])\n getDeviceId: call_sdk(\"getDeviceId\", [])\n getLocale: call_sdk(\"getLocale\", [])\n getNetworkStatus: call_sdk(\"getNetworkStatus\", [])\n getSchemeUri: call_sdk(\"getSchemeUri\", [])\n requestReview: call_sdk(\"requestReview\", [])\n closeView: call_sdk(\"closeView\", [])",
378
+ description: "Calls a dogfood SDK method via the window.__sdkCall bridge (exported by @apps-in-toss/web-framework only in __DEBUG_BUILD__ bundles). NOT read-only — SDK calls have side effects (navigation, payments, permissions, etc.). On env 3/4 (real device relay) this hits the real SDK; on env 1 (local mock) it hits the mock SDK. (env 2 PWA does not inject the SDK — call_sdk is not available there.) Requires the relay to be attached — call list_pages first. Returns {ok: true, value} on success or {ok: false, error} on failure. If a Runtime.exceptionThrown event was observed within [callStart-50ms, callEnd+200ms], the result also includes `recentException` for crash triage. Returns a clear error if window.__sdkCall is not available (non-dogfood bundle) — redeploy via dogfood channel: `ait build && aitcc app deploy`.\n\nSECURITY: method name, args, and result value are not redacted — never include secrets.\n\nLIVE guard: when running against a live/production relay (relay-live env, MCP_ENV=relay-live), this tool requires `confirm: true` to acknowledge that the SDK call may affect real users. Without it the call is rejected with a structured error. mock and relay-dev sessions are unaffected.\n\nIMPORTANT — 인자 시그니처 (잘못된 인자로 호출하면 토스 앱 crash 위험):\n setDeviceOrientation: call_sdk(\"setDeviceOrientation\", [{ type: \"landscape\" }]) // NOT \"landscape\"\n setIosSwipeGestureEnabled: call_sdk(\"setIosSwipeGestureEnabled\", [{ isEnabled: false }])\n setSecureScreen: call_sdk(\"setSecureScreen\", [{ enabled: true }])\n setScreenAwakeMode: call_sdk(\"setScreenAwakeMode\", [{ enabled: true }])\n getOperationalEnvironment: call_sdk(\"getOperationalEnvironment\", [])\n getPlatformOS: call_sdk(\"getPlatformOS\", [])\n getDeviceId: call_sdk(\"getDeviceId\", [])\n getLocale: call_sdk(\"getLocale\", [])\n getNetworkStatus: call_sdk(\"getNetworkStatus\", [])\n getSchemeUri: call_sdk(\"getSchemeUri\", [])\n requestReview: call_sdk(\"requestReview\", [])\n closeView: call_sdk(\"closeView\", [])",
323
379
  inputSchema: {
324
380
  type: "object",
325
381
  properties: {
@@ -373,7 +429,7 @@ new Set([
373
429
  },
374
430
  {
375
431
  name: "get_diagnostics",
376
- description: "Returns a single-call server status snapshot so the agent can diagnose \"why is this not working?\" without calling multiple tools. Fields: mcpVersion (MCP SDK version), devtoolsVersion (@ait-co/devtools package version), tunnel (up/wssUrl/pid/startedAt), pages (list_pages result + lastSeenAt stats), lastAttachAt, lastDetachAt, recentErrors (last N server-side errors, PII/secret redacted), environment (kind: mock|relay-dev|relay-live, env: mock|relay backward-compat, reason, liveGuardActive: true when relay-live LIVE guard is active), serverLockHolder (pid + startedAt from the lock file, or null), nextRecommendedAction ({tool, reason} or null — the single next tool to call). All fields are nullable — missing data is null, not an error. debug-mode only — dev-mode (--mode=dev) does not support relay diagnostics. Tier C (both mock and relay). Call this first when debugging session state.",
432
+ description: "Returns a single-call server status snapshot so the agent can diagnose \"why is this not working?\" without calling multiple tools. Fields: mcpVersion (MCP SDK version), devtoolsVersion (@ait-co/devtools package version), tunnel (up/wssUrl/pid/startedAt), pages (list_pages result + lastSeenAt stats), lastAttachAt, lastDetachAt, recentErrors (last N server-side errors, PII/secret redacted), environment (kind: mock|relay-dev|relay-live, env: mock|relay backward-compat, reason, liveGuardActive: true when relay-live LIVE guard is active), serverLockHolder (pid + startedAt from the lock file, or null), nextRecommendedAction ({tool, reason} or null — the single next tool to call; in local-target mode tunnel.up=false is normal so \"restart\" is never recommended). All fields are nullable — missing data is null, not an error. debug-mode only — dev-mode (--mode=dev) does not support relay diagnostics. Tier C (both mock and relay). Call this first when debugging session state.",
377
433
  inputSchema: {
378
434
  type: "object",
379
435
  properties: { recent_errors_limit: {
@@ -640,6 +696,29 @@ const DEV_TOOL_DEFINITIONS = [
640
696
  },
641
697
  availableIn: "both"
642
698
  },
699
+ {
700
+ name: "build_attach_url",
701
+ description: "Turns an `ait deploy --scheme-only` URL into a self-attaching deep link for a real device. NOT available in dev-mode — requires a live cloudflared relay (Tier B, relay-only). To use this tool: restart the MCP server with `--mode=debug` (or omit --mode) and set MCP_ENV=relay, then call build_attach_url to generate the QR for phone scanning. See: https://docs.aitc.dev/guides/debug-relay",
702
+ inputSchema: {
703
+ type: "object",
704
+ properties: {
705
+ scheme_url: {
706
+ type: "string",
707
+ description: "The intoss-private:// URL from `ait deploy --scheme-only`."
708
+ },
709
+ wait_for_attach: {
710
+ type: "boolean",
711
+ description: "If true, block until a page attaches."
712
+ },
713
+ open_in_browser: {
714
+ type: "boolean",
715
+ description: "If true (default), open the QR PNG in the OS browser."
716
+ }
717
+ },
718
+ required: ["scheme_url"]
719
+ },
720
+ availableIn: "relay"
721
+ },
643
722
  {
644
723
  name: "evaluate",
645
724
  description: "Evaluates an arbitrary JavaScript expression via CDP Runtime.evaluate. NOT available in dev-mode (no CDP connection). Switch to `--mode=local` or `--mode=debug` for CDP access.",
@@ -730,6 +809,12 @@ const CDP_ONLY_TOOL_NAMES = new Set([
730
809
  "list_exceptions"
731
810
  ]);
732
811
  /**
812
+ * Tier B tools — relay-only per RFC #277.
813
+ * Listed in dev-mode tool surface (issue #323) so agents get a hand-off hint
814
+ * toward `--mode=debug` instead of "Unknown tool".
815
+ */
816
+ const TIER_B_TOOL_NAMES = new Set(["build_attach_url"]);
817
+ /**
733
818
  * Builds the `list_pages` dev-mode shim response.
734
819
  * Returns the Vite dev URL as a single-entry page list with `devMode: true`.
735
820
  */
@@ -844,13 +929,14 @@ function createDevServer(deps = {}) {
844
929
  const aitSource = deps.aitSource ?? new HttpAitSource({ stateEndpoint });
845
930
  const server = new Server({
846
931
  name: "ait-devtools",
847
- version: "0.1.46"
932
+ version: "0.1.49"
848
933
  }, { capabilities: { tools: {} } });
849
934
  server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: DEV_TOOL_DEFINITIONS.map((tool) => ({ ...tool })) }));
850
935
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
851
936
  const name = request.params.name;
852
937
  if (!DEV_TOOL_NAMES.has(name)) return mcpError(`알 수 없는 tool: ${name}`);
853
938
  if (CDP_ONLY_TOOL_NAMES.has(name)) return mcpError(`${name}: ${CDP_UNAVAILABLE_IN_DEV_MODE}`);
939
+ if (TIER_B_TOOL_NAMES.has(name)) return tierRejectionError(name, "relay", "mock", "dev-mode — Vite HTTP endpoint, no CDP/relay connection. `--mode=debug` (または `devtools-mcp` without --mode) + MCP_ENV=relay로 재시작하세요.");
854
940
  try {
855
941
  const effective = name === "devtools_get_mock_state" ? "AIT.getMockState" : name;
856
942
  if (isAitToolName(effective)) switch (effective) {
@@ -860,13 +946,13 @@ function createDevServer(deps = {}) {
860
946
  default: return mcpError(`알 수 없는 tool: ${name}`);
861
947
  }
862
948
  switch (name) {
863
- case "list_pages": return jsonResult(buildDevListPagesResult(devtoolsUrl));
864
- case "get_diagnostics": return jsonResult(await buildDevDiagnostics(devtoolsUrl, stateEndpoint, (url) => fetch(url)));
865
- case "measure_safe_area": return jsonResult(await buildDevMeasureSafeArea(aitSource));
949
+ case "list_pages": return envelopeResult("list_pages", buildDevListPagesResult(devtoolsUrl));
950
+ case "get_diagnostics": return envelopeResult("get_diagnostics", await buildDevDiagnostics(devtoolsUrl, stateEndpoint, (url) => fetch(url)));
951
+ case "measure_safe_area": return envelopeResult("measure_safe_area", await buildDevMeasureSafeArea(aitSource));
866
952
  case "call_sdk": {
867
953
  const sdkName = request.params.arguments?.name;
868
954
  if (typeof sdkName !== "string" || sdkName === "") return mcpError("call_sdk: name 인자가 비어 있습니다. 호출할 메서드 이름을 전달하세요.");
869
- return jsonResult(await buildDevCallSdk(sdkName, aitSource));
955
+ return envelopeResult("call_sdk", await buildDevCallSdk(sdkName, aitSource));
870
956
  }
871
957
  default: return mcpError(`알 수 없는 tool: ${name}`);
872
958
  }
@@ -882,6 +968,26 @@ function jsonResult(value) {
882
968
  text: JSON.stringify(value, null, 2)
883
969
  }] };
884
970
  }
971
+ /**
972
+ * Wraps `value` in a `ToolEnvelope` (when compat mode is off) and returns it
973
+ * as a text content block. In dev-mode `env` is always `'mock'` and
974
+ * `attached` is always `true` (the Vite dev server is the single implicit
975
+ * "attached" page).
976
+ *
977
+ * When `AIT_MCP_COMPAT=chrome-devtools` the envelope is skipped and the raw
978
+ * value is returned — identical to `jsonResult` (0.1.x back-compat).
979
+ */
980
+ function envelopeResult(tool, value) {
981
+ const wrapped = wrapEnvelope(value, {
982
+ tool,
983
+ env: "mock",
984
+ attached: true
985
+ });
986
+ return { content: [{
987
+ type: "text",
988
+ text: JSON.stringify(wrapped, null, 2)
989
+ }] };
990
+ }
885
991
  /** Builds the dev-mode server and connects it over stdio. */
886
992
  async function runDevServer() {
887
993
  const server = createDevServer();