@herbertgao/pi-extensions 2026.8.1 → 2026.8.2

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 (162) hide show
  1. package/README.md +5 -2
  2. package/node_modules/@czottmann/pi-automode/README.md +16 -2
  3. package/node_modules/@czottmann/pi-automode/docs/GLOSSARY.md +35 -0
  4. package/node_modules/@czottmann/pi-automode/docs/automode-classifier-flow.md +18 -9
  5. package/node_modules/@czottmann/pi-automode/docs/defaults.md +6 -2
  6. package/node_modules/@czottmann/pi-automode/examples/automode.local.json +6 -0
  7. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/classifier.ts +11 -7
  8. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/config.ts +100 -0
  9. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/constants.ts +32 -0
  10. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/extension.ts +78 -3
  11. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/hard-deny.ts +16 -4
  12. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/paths.ts +23 -1
  13. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/permissions.ts +22 -1
  14. package/node_modules/@czottmann/pi-automode/extensions/auto-mode/types.ts +18 -2
  15. package/node_modules/@czottmann/pi-automode/package.json +4 -4
  16. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-audit.md +5 -0
  17. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-debt.md +5 -0
  18. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-gain.md +5 -0
  19. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-help.md +5 -0
  20. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail-review.md +5 -0
  21. package/node_modules/@dietrichgebert/ponytail/.opencode/command/ponytail.md +5 -0
  22. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail-frontmatter.cjs +23 -0
  23. package/node_modules/@dietrichgebert/ponytail/.opencode/plugins/ponytail.mjs +99 -0
  24. package/node_modules/@dietrichgebert/ponytail/.qoder/rules/ponytail.md +30 -0
  25. package/node_modules/@dietrichgebert/ponytail/.qoder-plugin/plugin.json +16 -0
  26. package/node_modules/@dietrichgebert/ponytail/AGENTS.md +32 -0
  27. package/node_modules/@dietrichgebert/ponytail/LICENSE +21 -0
  28. package/node_modules/@dietrichgebert/ponytail/README.es.md +298 -0
  29. package/node_modules/@dietrichgebert/ponytail/README.ko.md +315 -0
  30. package/node_modules/@dietrichgebert/ponytail/README.md +351 -0
  31. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-3model.svg +21 -0
  32. package/node_modules/@dietrichgebert/ponytail/assets/benchmark-agentic.svg +62 -0
  33. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.png +0 -0
  34. package/node_modules/@dietrichgebert/ponytail/assets/logo-dark.svg +115 -0
  35. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt-dark.svg +27 -0
  36. package/node_modules/@dietrichgebert/ponytail/assets/logo-greenpt.svg +27 -0
  37. package/node_modules/@dietrichgebert/ponytail/assets/logo.png +0 -0
  38. package/node_modules/@dietrichgebert/ponytail/assets/social-preview.png +0 -0
  39. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-es.png +0 -0
  40. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner-ko.png +0 -0
  41. package/node_modules/@dietrichgebert/ponytail/assets/waitlist-banner.png +0 -0
  42. package/node_modules/@dietrichgebert/ponytail/hooks/claude-codex-hooks.json +44 -0
  43. package/node_modules/@dietrichgebert/ponytail/hooks/copilot-hooks.json +21 -0
  44. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-activate.js +96 -0
  45. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-config.js +169 -0
  46. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-instructions.js +98 -0
  47. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-mode-tracker.js +130 -0
  48. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-runtime.js +85 -0
  49. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.ps1 +24 -0
  50. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-statusline.sh +18 -0
  51. package/node_modules/@dietrichgebert/ponytail/hooks/ponytail-subagent.js +77 -0
  52. package/node_modules/@dietrichgebert/ponytail/hooks/qoder-hooks.json +26 -0
  53. package/node_modules/@dietrichgebert/ponytail/package.json +47 -0
  54. package/node_modules/@dietrichgebert/ponytail/pi-extension/index.js +211 -0
  55. package/node_modules/@dietrichgebert/ponytail/pi-extension/package.json +8 -0
  56. package/node_modules/@dietrichgebert/ponytail/scripts/uninstall.js +60 -0
  57. package/node_modules/@dietrichgebert/ponytail/skills/ponytail/SKILL.md +120 -0
  58. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-audit/SKILL.md +41 -0
  59. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-debt/SKILL.md +44 -0
  60. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-gain/SKILL.md +50 -0
  61. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-help/SKILL.md +71 -0
  62. package/node_modules/@dietrichgebert/ponytail/skills/ponytail-review/SKILL.md +57 -0
  63. package/node_modules/@herbertgao/pi-cc-extensions/README.en.md +1 -3
  64. package/node_modules/@herbertgao/pi-cc-extensions/README.md +1 -3
  65. package/node_modules/@herbertgao/pi-cc-extensions/extensions/config/panel.ts +10 -6
  66. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/agent-summary.ts +6 -2
  67. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/compact-thinking.ts +24 -83
  68. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/context.ts +1 -1
  69. package/node_modules/@herbertgao/pi-cc-extensions/extensions/feature/markdown-enhance.ts +119 -0
  70. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/compact-mode.ts +73 -9
  71. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/index.ts +2 -2
  72. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-hover.ts +130 -0
  73. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-interaction.ts +65 -542
  74. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-layout.ts +160 -0
  75. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-packets.ts +143 -0
  76. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/mouse-scroll.ts +184 -0
  77. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-diff/diff-renderer.ts +2 -2
  78. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/tool-grouping.ts +9 -2
  79. package/node_modules/@herbertgao/pi-cc-extensions/extensions/renderer/transcript-refresh.ts +91 -0
  80. package/node_modules/@herbertgao/pi-cc-extensions/extensions/utils/agent-summary.ts +1 -1
  81. package/node_modules/@herbertgao/pi-cc-extensions/package.json +3 -3
  82. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-dark.json +1 -0
  83. package/node_modules/@herbertgao/pi-cc-extensions/themes/cc-light.json +2 -2
  84. package/node_modules/@herbertgao/pi-stash/README.md +9 -9
  85. package/node_modules/@herbertgao/pi-stash/package.json +1 -1
  86. package/node_modules/@herbertgao/pi-stash/src/index.ts +2 -1
  87. package/node_modules/@herbertgao/pi-subagents/CHANGELOG.md +14 -0
  88. package/node_modules/@herbertgao/pi-subagents/README.md +6 -2
  89. package/node_modules/@herbertgao/pi-subagents/package.json +3 -3
  90. package/node_modules/@herbertgao/pi-subagents/src/custom-agents.ts +85 -11
  91. package/node_modules/@herbertgao/pi-subagents/src/index.ts +28 -4
  92. package/node_modules/@herbertgao/pi-subagents/src/settings.ts +12 -0
  93. package/node_modules/@herbertgao/pi-subagents/src/types.ts +2 -0
  94. package/node_modules/@narumitw/pi-btw/LICENSE +21 -0
  95. package/node_modules/@narumitw/pi-btw/README.md +210 -0
  96. package/node_modules/@narumitw/pi-btw/package.json +53 -0
  97. package/node_modules/@narumitw/pi-btw/src/bring-to-main.ts +632 -0
  98. package/node_modules/@narumitw/pi-btw/src/btw.ts +996 -0
  99. package/node_modules/@narumitw/pi-btw/src/fullscreen-ui.ts +339 -0
  100. package/node_modules/@narumitw/pi-btw/src/index.ts +1 -0
  101. package/node_modules/@narumitw/pi-btw/src/menu.ts +296 -0
  102. package/node_modules/@narumitw/pi-btw/src/settings.ts +237 -0
  103. package/node_modules/@narumitw/pi-btw/src/side-thread.ts +238 -0
  104. package/node_modules/@narumitw/pi-btw/src/text.ts +10 -0
  105. package/node_modules/@narumitw/pi-btw/src/transcript-pager.ts +620 -0
  106. package/node_modules/@pi-plugins/fast-mode/LICENSE +21 -0
  107. package/node_modules/@pi-plugins/fast-mode/README.md +92 -0
  108. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts +6 -0
  109. package/node_modules/@pi-plugins/fast-mode/dist/index.d.mts.map +1 -0
  110. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs +2026 -0
  111. package/node_modules/@pi-plugins/fast-mode/dist/index.mjs.map +1 -0
  112. package/node_modules/@pi-plugins/fast-mode/package.json +73 -0
  113. package/node_modules/pi-mcp-adapter/CHANGELOG.md +59 -0
  114. package/node_modules/pi-mcp-adapter/README.md +62 -13
  115. package/node_modules/pi-mcp-adapter/agent-plugin-loader.ts +377 -0
  116. package/node_modules/pi-mcp-adapter/commands.ts +9 -7
  117. package/node_modules/pi-mcp-adapter/config.ts +110 -49
  118. package/node_modules/pi-mcp-adapter/direct-tools.ts +34 -19
  119. package/node_modules/pi-mcp-adapter/elicitation-handler.ts +37 -36
  120. package/node_modules/pi-mcp-adapter/errors.ts +21 -17
  121. package/node_modules/pi-mcp-adapter/host-html-template.ts +13 -7
  122. package/node_modules/pi-mcp-adapter/index.ts +37 -14
  123. package/node_modules/pi-mcp-adapter/init.ts +9 -7
  124. package/node_modules/pi-mcp-adapter/json-schema-validator.ts +2 -2
  125. package/node_modules/pi-mcp-adapter/lifecycle.ts +8 -8
  126. package/node_modules/pi-mcp-adapter/logger.ts +1 -1
  127. package/node_modules/pi-mcp-adapter/mcp-auth-flow.ts +36 -22
  128. package/node_modules/pi-mcp-adapter/mcp-auth.ts +120 -2
  129. package/node_modules/pi-mcp-adapter/mcp-code.ts +13 -11
  130. package/node_modules/pi-mcp-adapter/mcp-oauth-provider.ts +19 -13
  131. package/node_modules/pi-mcp-adapter/mcp-output-guard.ts +15 -11
  132. package/node_modules/pi-mcp-adapter/mcp-panel.ts +34 -15
  133. package/node_modules/pi-mcp-adapter/mcp-probe.ts +131 -34
  134. package/node_modules/pi-mcp-adapter/mcp-script-worker.mjs +2 -2
  135. package/node_modules/pi-mcp-adapter/mcp-setup-panel.ts +7 -4
  136. package/node_modules/pi-mcp-adapter/mcp-trace.ts +59 -64
  137. package/node_modules/pi-mcp-adapter/metadata-cache.ts +34 -26
  138. package/node_modules/pi-mcp-adapter/npx-resolver.ts +6 -4
  139. package/node_modules/pi-mcp-adapter/oauth-handler.ts +1 -1
  140. package/node_modules/pi-mcp-adapter/onboarding-state.ts +19 -11
  141. package/node_modules/pi-mcp-adapter/package.json +13 -8
  142. package/node_modules/pi-mcp-adapter/panel-keys.ts +20 -4
  143. package/node_modules/pi-mcp-adapter/prompts.ts +2 -2
  144. package/node_modules/pi-mcp-adapter/proxy-modes.ts +48 -11
  145. package/node_modules/pi-mcp-adapter/sampling-handler.ts +22 -15
  146. package/node_modules/pi-mcp-adapter/search-ranking.ts +2 -1
  147. package/node_modules/pi-mcp-adapter/server-manager.ts +165 -113
  148. package/node_modules/pi-mcp-adapter/session-recovery.ts +11 -12
  149. package/node_modules/pi-mcp-adapter/skills/mcp-scripting/SKILL.md +3 -3
  150. package/node_modules/pi-mcp-adapter/state.ts +3 -1
  151. package/node_modules/pi-mcp-adapter/tool-approval.ts +67 -4
  152. package/node_modules/pi-mcp-adapter/tool-metadata.ts +6 -5
  153. package/node_modules/pi-mcp-adapter/tool-result-renderer.ts +14 -2
  154. package/node_modules/pi-mcp-adapter/ts-shape.ts +9 -2
  155. package/node_modules/pi-mcp-adapter/types.ts +109 -22
  156. package/node_modules/pi-mcp-adapter/ui-app-bridge-helpers.ts +40 -0
  157. package/node_modules/pi-mcp-adapter/ui-resource-handler.ts +28 -19
  158. package/node_modules/pi-mcp-adapter/ui-server.ts +90 -58
  159. package/node_modules/pi-mcp-adapter/ui-session.ts +16 -14
  160. package/node_modules/pi-mcp-adapter/unix-socket-transport.ts +4 -4
  161. package/node_modules/pi-mcp-adapter/utils.ts +23 -9
  162. package/package.json +22 -7
@@ -1,25 +1,102 @@
1
1
  const PROBE_TIMEOUT_MS = 5_000;
2
+ const MODERN_PROTOCOL_VERSION = "2026-07-28";
3
+ const LEGACY_PROTOCOL_VERSION = "2025-06-18";
4
+ const JSON_ACCEPT = "application/json, text/event-stream";
5
+ const SSE_ACCEPT = "text/event-stream";
6
+ const MODERN_FALLBACK_STATUSES = new Set([400, 401, 404, 405, 406, 415]);
7
+ const POST_ENDPOINT_MISMATCH_STATUSES = new Set([404, 405, 406, 415]);
2
8
 
3
9
  export interface McpProbeResult {
4
10
  isMcp: boolean;
5
11
  classification: string;
6
12
  }
7
13
 
14
+ const DISCOVER_REQUEST = {
15
+ jsonrpc: "2.0",
16
+ id: 1,
17
+ method: "server/discover",
18
+ params: {},
19
+ };
20
+
8
21
  const INITIALIZE_REQUEST = {
9
22
  jsonrpc: "2.0",
10
23
  id: 1,
11
24
  method: "initialize",
12
25
  params: {
13
- protocolVersion: "2025-06-18",
26
+ protocolVersion: LEGACY_PROTOCOL_VERSION,
14
27
  capabilities: {},
15
28
  clientInfo: { name: "pi-mcp-probe", version: "2.1.2" },
16
29
  },
17
30
  };
18
31
 
19
- function isJsonRpcEnvelope(value: unknown): boolean {
20
- return typeof value === "object" && value !== null &&
21
- (value as { jsonrpc?: unknown }).jsonrpc === "2.0" &&
22
- ("result" in value || "error" in value);
32
+ type ProbeStrategy =
33
+ | { kind: "modern"; request: RequestInit; allowJson: true }
34
+ | { kind: "legacy-post"; request: RequestInit; allowJson: true }
35
+ | { kind: "legacy-sse"; request: RequestInit; allowJson: false };
36
+
37
+ const MODERN_STRATEGY: ProbeStrategy = {
38
+ kind: "modern",
39
+ request: {
40
+ method: "POST",
41
+ headers: {
42
+ Accept: JSON_ACCEPT,
43
+ "Content-Type": "application/json",
44
+ "MCP-Protocol-Version": MODERN_PROTOCOL_VERSION,
45
+ "Mcp-Method": "server/discover",
46
+ },
47
+ body: JSON.stringify(DISCOVER_REQUEST),
48
+ },
49
+ allowJson: true,
50
+ };
51
+
52
+ const LEGACY_POST_STRATEGY: ProbeStrategy = {
53
+ kind: "legacy-post",
54
+ request: {
55
+ method: "POST",
56
+ headers: {
57
+ Accept: JSON_ACCEPT,
58
+ "Content-Type": "application/json",
59
+ },
60
+ body: JSON.stringify(INITIALIZE_REQUEST),
61
+ },
62
+ allowJson: true,
63
+ };
64
+
65
+ const LEGACY_SSE_STRATEGY: ProbeStrategy = {
66
+ kind: "legacy-sse",
67
+ request: { headers: { Accept: SSE_ACCEPT } },
68
+ allowJson: false,
69
+ };
70
+
71
+ type JsonRpcEnvelopeInfo =
72
+ | { kind: "result"; protocolVersion: unknown }
73
+ | { kind: "error" };
74
+
75
+ type ProbeOutcome =
76
+ | { kind: "mcp"; result: McpProbeResult }
77
+ | { kind: "unsupported-modern" }
78
+ | { kind: "unrecognized" };
79
+
80
+ interface ProbeResult {
81
+ response: Response;
82
+ outcome: ProbeOutcome;
83
+ }
84
+
85
+ function jsonRpcEnvelopeInfo(value: unknown): JsonRpcEnvelopeInfo | null {
86
+ if (typeof value !== "object" || value === null || (value as { jsonrpc?: unknown }).jsonrpc !== "2.0") {
87
+ return null;
88
+ }
89
+ if ("result" in value) {
90
+ const result = (value as { result?: unknown }).result;
91
+ return {
92
+ kind: "result",
93
+ protocolVersion: typeof result === "object" && result !== null
94
+ ? (result as { protocolVersion?: unknown }).protocolVersion
95
+ : undefined,
96
+ };
97
+ }
98
+ if ("error" in value) return { kind: "error" };
99
+ return null;
23
100
  }
24
101
 
25
102
  function isBearerChallenge(response: Response): boolean {
@@ -33,29 +110,56 @@ function responseKind(response: Response): string {
33
110
  return "an untyped response";
34
111
  }
35
112
 
36
- async function hasJsonRpcEnvelope(response: Response): Promise<boolean> {
113
+ async function getJsonRpcEnvelopeInfo(response: Response): Promise<JsonRpcEnvelopeInfo | null> {
37
114
  try {
38
- return isJsonRpcEnvelope(JSON.parse(await response.text()));
115
+ return jsonRpcEnvelopeInfo(JSON.parse(await response.text()));
39
116
  } catch {
40
- return false;
117
+ return null;
41
118
  }
42
119
  }
43
120
 
44
- async function classifyResponse(response: Response, allowJson: boolean): Promise<McpProbeResult | null> {
121
+ async function classifyResponse(response: Response, strategy: ProbeStrategy): Promise<ProbeOutcome> {
45
122
  const isSse = response.headers.get("content-type")?.toLowerCase().startsWith("text/event-stream");
46
123
  if (response.ok && isSse) {
47
- return { isMcp: true, classification: "endpoint responded with an MCP event stream" };
124
+ return { kind: "mcp", result: { isMcp: true, classification: "endpoint responded with an MCP event stream" } };
48
125
  }
49
126
 
50
- const isJsonRpc = (allowJson || response.status === 401) && await hasJsonRpcEnvelope(response);
51
- if (response.ok && allowJson && isJsonRpc) {
52
- return { isMcp: true, classification: "endpoint responded with a JSON-RPC 2.0 envelope" };
127
+ const envelope = (strategy.allowJson || response.status === 401) ? await getJsonRpcEnvelopeInfo(response) : null;
128
+ if (response.ok && strategy.allowJson && envelope) {
129
+ if (strategy.kind === "modern" && (envelope.kind === "error" || envelope.protocolVersion !== MODERN_PROTOCOL_VERSION)) {
130
+ return { kind: "unsupported-modern" };
131
+ }
132
+ return {
133
+ kind: "mcp",
134
+ result: {
135
+ isMcp: true,
136
+ classification: strategy.kind === "modern"
137
+ ? `endpoint supports stateless MCP ${MODERN_PROTOCOL_VERSION} server/discover`
138
+ : "endpoint responded with a JSON-RPC 2.0 envelope",
139
+ },
140
+ };
53
141
  }
54
- if (response.status === 401 && isBearerChallenge(response) && isJsonRpc) {
55
- return { isMcp: true, classification: "endpoint requires Bearer authentication and responded with a JSON-RPC 2.0 error" };
142
+ if (response.status === 401 && isBearerChallenge(response) && envelope) {
143
+ return {
144
+ kind: "mcp",
145
+ result: {
146
+ isMcp: true,
147
+ classification: strategy.kind === "modern"
148
+ ? `endpoint requires Bearer authentication during MCP ${MODERN_PROTOCOL_VERSION} server/discover probing`
149
+ : "endpoint requires Bearer authentication and responded with a JSON-RPC 2.0 error",
150
+ },
151
+ };
56
152
  }
57
153
 
58
- return null;
154
+ return { kind: "unrecognized" };
155
+ }
156
+
157
+ async function probe(url: string | URL, strategy: ProbeStrategy): Promise<ProbeResult> {
158
+ const response = await fetch(url, {
159
+ ...strategy.request,
160
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
161
+ });
162
+ return { response, outcome: await classifyResponse(response, strategy) };
59
163
  }
60
164
 
61
165
  function notMcp(response: Response): McpProbeResult {
@@ -67,24 +171,17 @@ function notMcp(response: Response): McpProbeResult {
67
171
 
68
172
  /** Makes one unauthenticated metadata-only request to identify an HTTP endpoint's protocol shape. */
69
173
  export async function probeMcpEndpoint(url: string | URL): Promise<McpProbeResult> {
70
- const postResponse = await fetch(url, {
71
- method: "POST",
72
- headers: {
73
- Accept: "application/json, text/event-stream",
74
- "Content-Type": "application/json",
75
- },
76
- body: JSON.stringify(INITIALIZE_REQUEST),
77
- signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
78
- });
79
- const postClassification = await classifyResponse(postResponse, true);
80
- if (postClassification) return postClassification;
174
+ const { response: modernResponse, outcome: modernOutcome } = await probe(url, MODERN_STRATEGY);
175
+ if (modernOutcome.kind === "mcp") return modernOutcome.result;
81
176
 
82
- if (![404, 405, 406, 415].includes(postResponse.status)) return notMcp(postResponse);
177
+ if (modernOutcome.kind !== "unsupported-modern" && !MODERN_FALLBACK_STATUSES.has(modernResponse.status)) {
178
+ return notMcp(modernResponse);
179
+ }
83
180
 
84
- const getResponse = await fetch(url, {
85
- headers: { Accept: "text/event-stream" },
86
- signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
87
- });
88
- const getClassification = await classifyResponse(getResponse, false);
89
- return getClassification ?? notMcp(getResponse);
181
+ const { response: postResponse, outcome: postOutcome } = await probe(url, LEGACY_POST_STRATEGY);
182
+ if (postOutcome.kind === "mcp") return postOutcome.result;
183
+ if (!POST_ENDPOINT_MISMATCH_STATUSES.has(postResponse.status)) return notMcp(postResponse);
184
+
185
+ const { response: getResponse, outcome: getOutcome } = await probe(url, LEGACY_SSE_STRATEGY);
186
+ return getOutcome.kind === "mcp" ? getOutcome.result : notMcp(getResponse);
90
187
  }
@@ -113,9 +113,9 @@ try {
113
113
  console: capturedConsole,
114
114
  }), {
115
115
  codeGeneration: { strings: false, wasm: false },
116
- name: "mcp_script",
116
+ name: "mcpScript",
117
117
  });
118
- const script = new vm.Script(`(async () => {\n${workerData.code}\n})()`, { filename: "mcp_script.js" });
118
+ const script = new vm.Script(`(async () => {\n${workerData.code}\n})()`, { filename: "mcpScript.js" });
119
119
  const returnValue = await Promise.resolve(script.runInContext(context));
120
120
  parentPort.postMessage(returnValue === undefined
121
121
  ? { type: "done" }
@@ -169,9 +169,9 @@ export class McpSetupPanel {
169
169
  return [...new Set(paths)];
170
170
  }
171
171
 
172
- private getSelectedAction(): Action | null {
172
+ private getSelectedAction(): Action | undefined {
173
173
  const actions = this.getActions();
174
- return actions[this.actionCursor] ?? null;
174
+ return actions[this.actionCursor];
175
175
  }
176
176
 
177
177
  handleInput(data: string): void {
@@ -414,6 +414,7 @@ export class McpSetupPanel {
414
414
  }
415
415
  for (let index = start; index < end; index++) {
416
416
  const action = actions[index];
417
+ if (!action) continue;
417
418
  if (action.id === "add-known-server" && (index === start || actions[index - 1]?.id !== "add-known-server")) {
418
419
  lines.push(this.padLine(fg(this.t.title, "Add a known server"), innerW));
419
420
  }
@@ -442,6 +443,7 @@ export class McpSetupPanel {
442
443
  lines.push(this.padLine("", innerW));
443
444
  for (let index = 0; index < this.discovery.imports.length; index++) {
444
445
  const entry = this.discovery.imports[index];
446
+ if (!entry) continue;
445
447
  const selected = this.selectedImports.has(entry.kind) ? "[x]" : "[ ]";
446
448
  const cursor = index === this.importCursor ? fg(this.t.selected, "›") : " ";
447
449
  lines.push(this.padLine(`${cursor} ${selected} ${entry.kind} ${entry.path}`, innerW));
@@ -462,7 +464,8 @@ export class McpSetupPanel {
462
464
  const paths = this.getDetectedPaths();
463
465
  for (let index = 0; index < paths.length; index++) {
464
466
  const cursor = index === this.pathCursor ? fg(this.t.selected, "›") : " ";
465
- lines.push(this.padLine(`${cursor} ${paths[index]}`, innerW));
467
+ const path = paths[index];
468
+ if (path !== undefined) lines.push(this.padLine(`${cursor} ${path}`, innerW));
466
469
  }
467
470
  return lines;
468
471
  }
@@ -537,7 +540,7 @@ export class McpSetupPanel {
537
540
  ' "mcpServers": {',
538
541
  ' "chrome-devtools": {',
539
542
  ' "command": "npx",',
540
- ' "args": ["-y", "chrome-devtools-mcp@latest"]',
543
+ ' "args": ["-y", "chrome-devtools-mcp@1.6.0"]',
541
544
  " }",
542
545
  " }",
543
546
  "}",
@@ -1,8 +1,8 @@
1
1
  import { appendFile, mkdir, writeFile } from "node:fs/promises";
2
2
  import { Buffer } from "node:buffer";
3
3
  import { dirname, isAbsolute, resolve } from "node:path";
4
- import type { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
5
- import type { JSONRPCMessage, MessageExtraInfo } from "@modelcontextprotocol/sdk/types.js";
4
+ import type { Transport } from "@modelcontextprotocol/client";
5
+ import type { JSONRPCMessage, MessageExtraInfo } from "@modelcontextprotocol/client";
6
6
 
7
7
  export const MCP_TRACE_SCHEMA_VERSION = 1;
8
8
  export const DEFAULT_MCP_TRACE_MAX_BYTES = 256 * 1024;
@@ -95,6 +95,7 @@ export function createMcpTraceEvent(
95
95
  options?: { relatedRequestId?: unknown; durationMs?: number },
96
96
  ): McpTraceEvent {
97
97
  const kind = messageKind(message);
98
+ const bytes = messageBytes(message);
98
99
  const event: McpTraceEvent = {
99
100
  version: MCP_TRACE_SCHEMA_VERSION,
100
101
  timestamp: new Date().toISOString(),
@@ -103,7 +104,7 @@ export function createMcpTraceEvent(
103
104
  transport,
104
105
  kind,
105
106
  status,
106
- bytes: messageBytes(message),
107
+ ...(bytes !== undefined ? { bytes } : {}),
107
108
  };
108
109
  if ("method" in message) event.method = redactTraceText(message.method, 120);
109
110
  if ("id" in message) event.id = traceId(message.id) ?? null;
@@ -210,8 +211,8 @@ export function createMcpTraceWriter(
210
211
  : resolve(sessionCwd ?? process.cwd(), ".pi", "mcp-traces", `mcp-${timestamp}-${randomSuffix}.jsonl`);
211
212
  return new McpTraceWriter({
212
213
  filePath,
213
- maxBytes: settings.maxBytes,
214
- maxEvents: settings.maxEvents,
214
+ ...(settings.maxBytes !== undefined ? { maxBytes: settings.maxBytes } : {}),
215
+ ...(settings.maxEvents !== undefined ? { maxEvents: settings.maxEvents } : {}),
215
216
  });
216
217
  }
217
218
 
@@ -227,17 +228,20 @@ export function isMcpTraceEnabled(
227
228
  }
228
229
 
229
230
  /**
230
- * Compose the SDK's transport callbacks instead of replacing them. Protocol
231
- * assigns `onmessage` during connect, so the setter must keep the observer in
232
- * front of whichever callback the SDK installs.
231
+ * Compose the SDK's transport callbacks in place instead of replacing the
232
+ * transport object. SDK v2 detects its base stdio transport before connect so
233
+ * it can run `server/discover` on a disposable sibling process; returning a
234
+ * wrapper hides that identity and makes tracing change negotiation behavior.
233
235
  */
234
- export function wrapTransportWithMcpTrace<T extends Transport>(
235
- transport: T,
236
+ export function wrapTransportWithMcpTrace(
237
+ transport: Transport,
236
238
  server: string,
237
239
  transportKind: McpTraceTransport,
238
240
  observer: McpTraceObserver,
239
- ): T {
240
- let messageHandler: Transport["onmessage"];
241
+ ): Transport {
242
+ let messageHandler = transport.onmessage;
243
+ let tracedMessageHandler: Transport["onmessage"];
244
+ const originalSend = transport.send;
241
245
  const record = (event: McpTraceEvent): void => {
242
246
  try {
243
247
  observer.record(event);
@@ -245,60 +249,51 @@ export function wrapTransportWithMcpTrace<T extends Transport>(
245
249
  // An observer failure must never alter SDK transport behavior.
246
250
  }
247
251
  };
248
- const traced: Transport = {
249
- start: () => transport.start(),
250
- send: async (message, options) => {
251
- const started = performance.now();
252
- const messages = Array.isArray(message) ? message : [message];
253
- try {
254
- await transport.send(message, options);
255
- for (const item of messages) {
256
- record(createMcpTraceEvent("outbound", server, transportKind, item, "sent", {
257
- durationMs: performance.now() - started,
258
- }));
259
- }
260
- } catch (error) {
261
- for (const item of messages) {
262
- record(createMcpTraceEvent("outbound", server, transportKind, item, "error", {
263
- durationMs: performance.now() - started,
264
- }));
265
- }
266
- throw error;
252
+
253
+ try {
254
+ Object.defineProperty(transport, "onmessage", {
255
+ configurable: true,
256
+ enumerable: true,
257
+ get() {
258
+ return tracedMessageHandler;
259
+ },
260
+ set(handler: Transport["onmessage"]) {
261
+ messageHandler = handler;
262
+ tracedMessageHandler = handler
263
+ ? ((message: JSONRPCMessage, extra?: MessageExtraInfo) => {
264
+ record(createMcpTraceEvent("inbound", server, transportKind, message, "received"));
265
+ handler(message, extra);
266
+ })
267
+ : undefined;
268
+ },
269
+ });
270
+ transport.onmessage = messageHandler;
271
+ } catch {
272
+ // Some future transport may make onmessage non-configurable. Keep tracing
273
+ // best-effort rather than changing connection behavior.
274
+ }
275
+
276
+ transport.send = async (message: JSONRPCMessage, options?: Parameters<Transport["send"]>[1]) => {
277
+ const started = performance.now();
278
+ const messages = Array.isArray(message) ? message : [message];
279
+ try {
280
+ await originalSend.call(transport, message, options);
281
+ for (const item of messages) {
282
+ record(createMcpTraceEvent("outbound", server, transportKind, item, "sent", {
283
+ durationMs: performance.now() - started,
284
+ }));
285
+ }
286
+ } catch (error) {
287
+ for (const item of messages) {
288
+ record(createMcpTraceEvent("outbound", server, transportKind, item, "error", {
289
+ durationMs: performance.now() - started,
290
+ }));
267
291
  }
268
- },
269
- close: () => transport.close(),
270
- get onclose() {
271
- return transport.onclose;
272
- },
273
- set onclose(handler) {
274
- transport.onclose = handler;
275
- },
276
- get onerror() {
277
- return transport.onerror;
278
- },
279
- set onerror(handler) {
280
- transport.onerror = handler;
281
- },
282
- get onmessage() {
283
- return messageHandler;
284
- },
285
- set onmessage(handler) {
286
- messageHandler = handler;
287
- transport.onmessage = handler
288
- ? ((message: JSONRPCMessage, extra?: MessageExtraInfo) => {
289
- record(createMcpTraceEvent("inbound", server, transportKind, message, "received"));
290
- handler(message, extra);
291
- }) as Transport["onmessage"]
292
- : undefined;
293
- },
294
- get sessionId() {
295
- return transport.sessionId;
296
- },
297
- setProtocolVersion: transport.setProtocolVersion
298
- ? version => transport.setProtocolVersion!(version)
299
- : undefined,
292
+ throw error;
293
+ }
300
294
  };
301
- return traced as T;
295
+
296
+ return transport;
302
297
  }
303
298
 
304
299
  export function traceTransportKind(definition: { command?: string; url?: string; socket?: string }, transport: Transport): McpTraceTransport {
@@ -3,7 +3,7 @@ import { existsSync, readFileSync, writeFileSync, renameSync, mkdirSync } from "
3
3
  import { dirname } from "node:path";
4
4
  import { getAgentPath } from "./agent-dir.ts";
5
5
  import { createHash } from "node:crypto";
6
- import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge";
6
+ import { getToolUiResourceUri } from "./ui-app-bridge-helpers.ts";
7
7
  import type {
8
8
  CachedPrompt,
9
9
  CachedResource,
@@ -91,6 +91,7 @@ export function computeServerHash(definition: ServerEntry): string {
91
91
  url: resolveServerUrl(definition),
92
92
  headers: interpolateEnvRecord(definition.headers),
93
93
  auth: definition.auth,
94
+ protocolVersion: definition.protocolVersion,
94
95
  bearerToken: resolveBearerToken(definition),
95
96
  bearerTokenEnv: definition.bearerTokenEnv,
96
97
  exposeResources: definition.exposeResources,
@@ -201,10 +202,10 @@ export function reconstructToolMetadata(
201
202
  name,
202
203
  originalName: tool.name,
203
204
  description: tool.description ?? "",
204
- inputSchema: tool.inputSchema,
205
- uiResourceUri: tool.uiResourceUri,
206
- uiVisibility: tool.uiVisibility,
207
- uiStreamMode: tool.uiStreamMode,
205
+ ...(tool.inputSchema !== undefined ? { inputSchema: tool.inputSchema } : {}),
206
+ ...(tool.uiResourceUri !== undefined ? { uiResourceUri: tool.uiResourceUri } : {}),
207
+ ...(tool.uiVisibility !== undefined ? { uiVisibility: tool.uiVisibility } : {}),
208
+ ...(tool.uiStreamMode !== undefined ? { uiStreamMode: tool.uiStreamMode } : {}),
208
209
  });
209
210
  }
210
211
 
@@ -237,14 +238,19 @@ export function reconstructToolMetadata(
237
238
  export function serializeTools(tools: McpTool[]): CachedTool[] {
238
239
  return tools
239
240
  .filter(t => t?.name)
240
- .map(t => ({
241
- name: t.name,
242
- description: t.description,
243
- inputSchema: t.inputSchema,
244
- uiResourceUri: tryGetToolUiResourceUri(t),
245
- uiVisibility: extractUiToolVisibility(t._meta),
246
- uiStreamMode: extractToolUiStreamMode(t._meta),
247
- }));
241
+ .map(t => {
242
+ const uiResourceUri = tryGetToolUiResourceUri(t);
243
+ const uiVisibility = extractUiToolVisibility(t._meta);
244
+ const uiStreamMode = extractToolUiStreamMode(t._meta);
245
+ return {
246
+ name: t.name,
247
+ ...(t.description !== undefined ? { description: t.description } : {}),
248
+ ...(t.inputSchema !== undefined ? { inputSchema: t.inputSchema } : {}),
249
+ ...(uiResourceUri !== undefined ? { uiResourceUri } : {}),
250
+ ...(uiVisibility !== undefined ? { uiVisibility } : {}),
251
+ ...(uiStreamMode !== undefined ? { uiStreamMode } : {}),
252
+ };
253
+ });
248
254
  }
249
255
 
250
256
  export function serializeResources(resources: McpResource[]): CachedResource[] {
@@ -253,7 +259,7 @@ export function serializeResources(resources: McpResource[]): CachedResource[] {
253
259
  .map(r => ({
254
260
  uri: r.uri,
255
261
  name: r.name,
256
- description: r.description,
262
+ ...(r.description !== undefined ? { description: r.description } : {}),
257
263
  }));
258
264
  }
259
265
 
@@ -262,15 +268,17 @@ export function serializePrompts(prompts: McpPrompt[]): CachedPrompt[] {
262
268
  .filter(prompt => prompt?.name)
263
269
  .map(prompt => ({
264
270
  name: prompt.name,
265
- title: prompt.title,
266
- description: prompt.description,
267
- arguments: Array.isArray(prompt.arguments)
268
- ? prompt.arguments.filter(argument => argument?.name).map(argument => ({
269
- name: argument.name,
270
- description: argument.description,
271
- required: argument.required,
272
- }))
273
- : undefined,
271
+ ...(prompt.title !== undefined ? { title: prompt.title } : {}),
272
+ ...(prompt.description !== undefined ? { description: prompt.description } : {}),
273
+ ...(Array.isArray(prompt.arguments)
274
+ ? {
275
+ arguments: prompt.arguments.filter(argument => argument?.name).map(argument => ({
276
+ name: argument.name,
277
+ ...(argument.description !== undefined ? { description: argument.description } : {}),
278
+ ...(argument.required !== undefined ? { required: argument.required } : {}),
279
+ })),
280
+ }
281
+ : {}),
274
282
  }));
275
283
  }
276
284
 
@@ -285,15 +293,15 @@ export function reconstructPromptMetadata(
285
293
  const args: McpPromptArgument[] = Array.isArray(prompt.arguments)
286
294
  ? prompt.arguments.filter(argument => argument?.name).map(argument => ({
287
295
  name: argument.name,
288
- description: argument.description,
289
- required: argument.required,
296
+ ...(argument.description !== undefined ? { description: argument.description } : {}),
297
+ ...(argument.required !== undefined ? { required: argument.required } : {}),
290
298
  }))
291
299
  : [];
292
300
  return {
293
301
  serverName,
294
302
  originalName: prompt.name,
295
303
  commandName: formatPromptCommandName(prompt.name, serverName, effectivePrefix),
296
- title: prompt.title,
304
+ ...(prompt.title !== undefined ? { title: prompt.title } : {}),
297
305
  description: prompt.description ?? "",
298
306
  arguments: args,
299
307
  };
@@ -95,6 +95,7 @@ function parseNpxArgs(args: string[]): ParsedInvocation | null {
95
95
 
96
96
  for (let i = 0; i < before.length; i++) {
97
97
  const arg = before[i];
98
+ if (arg === undefined) return null;
98
99
  if (foundFirstPositional) {
99
100
  positionals.push(arg);
100
101
  continue;
@@ -149,6 +150,7 @@ function parseNpmExecArgs(args: string[]): ParsedInvocation | null {
149
150
  let packageSpec: string | undefined;
150
151
  for (let i = 0; i < before.length; i++) {
151
152
  const arg = before[i];
153
+ if (arg === undefined) return null;
152
154
  if (arg === "-y" || arg === "--yes") continue;
153
155
  if (arg === "--package") {
154
156
  const value = before[i + 1];
@@ -239,7 +241,7 @@ function resolveFromNpmCache(packageSpec: string, binName?: string): NpxCacheEnt
239
241
  return {
240
242
  resolvedBin,
241
243
  resolvedAt: Date.now(),
242
- packageVersion: pkg?.version,
244
+ ...(pkg?.version !== undefined ? { packageVersion: pkg.version } : {}),
243
245
  isJs,
244
246
  };
245
247
  }
@@ -329,9 +331,9 @@ function parsePackageSpec(spec: string): ParsedPackageSpec | null {
329
331
  const normalizedVersion = requestedVersion?.replace(/^=/, "").replace(/^v/i, "");
330
332
  return {
331
333
  packageName,
332
- exactVersion: normalizedVersion && EXACT_PACKAGE_VERSION_RE.test(normalizedVersion)
333
- ? normalizedVersion
334
- : undefined,
334
+ ...(normalizedVersion && EXACT_PACKAGE_VERSION_RE.test(normalizedVersion)
335
+ ? { exactVersion: normalizedVersion }
336
+ : {}),
335
337
  };
336
338
  }
337
339
 
@@ -1,5 +1,5 @@
1
1
  // oauth-handler.ts - OAuth token compatibility helpers for MCP servers
2
- import type { OAuthTokens } from "@modelcontextprotocol/sdk/shared/auth.js";
2
+ import type { OAuthTokens } from "@modelcontextprotocol/client";
3
3
  import { getAuthEntry } from "./mcp-auth.ts";
4
4
 
5
5
  /**
@@ -30,7 +30,9 @@ export function loadOnboardingState(): McpOnboardingState {
30
30
  version: 1,
31
31
  sharedConfigHintShown: raw.sharedConfigHintShown === true,
32
32
  setupCompleted: raw.setupCompleted === true,
33
- lastDiscoveryFingerprint: typeof raw.lastDiscoveryFingerprint === "string" ? raw.lastDiscoveryFingerprint : undefined,
33
+ ...(typeof raw.lastDiscoveryFingerprint === "string"
34
+ ? { lastDiscoveryFingerprint: raw.lastDiscoveryFingerprint }
35
+ : {}),
34
36
  };
35
37
  } catch {
36
38
  return { ...DEFAULT_STATE };
@@ -52,17 +54,23 @@ export function updateOnboardingState(updater: (state: McpOnboardingState) => Mc
52
54
  }
53
55
 
54
56
  export function markSharedConfigHintShown(fingerprint?: string): McpOnboardingState {
55
- return updateOnboardingState((state) => ({
56
- ...state,
57
- sharedConfigHintShown: true,
58
- lastDiscoveryFingerprint: fingerprint ?? state.lastDiscoveryFingerprint,
59
- }));
57
+ return updateOnboardingState((state) => {
58
+ const lastDiscoveryFingerprint = fingerprint ?? state.lastDiscoveryFingerprint;
59
+ return {
60
+ ...state,
61
+ sharedConfigHintShown: true,
62
+ ...(lastDiscoveryFingerprint !== undefined ? { lastDiscoveryFingerprint } : {}),
63
+ };
64
+ });
60
65
  }
61
66
 
62
67
  export function markSetupCompleted(fingerprint?: string): McpOnboardingState {
63
- return updateOnboardingState((state) => ({
64
- ...state,
65
- setupCompleted: true,
66
- lastDiscoveryFingerprint: fingerprint ?? state.lastDiscoveryFingerprint,
67
- }));
68
+ return updateOnboardingState((state) => {
69
+ const lastDiscoveryFingerprint = fingerprint ?? state.lastDiscoveryFingerprint;
70
+ return {
71
+ ...state,
72
+ setupCompleted: true,
73
+ ...(lastDiscoveryFingerprint !== undefined ? { lastDiscoveryFingerprint } : {}),
74
+ };
75
+ });
68
76
  }