bla-agents 1.1.0 → 1.5.3

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 (276) hide show
  1. package/.agents/skills/blind-llm-agents/SKILL.md +20 -14
  2. package/DISCLAIMER.md +15 -7
  3. package/README.md +153 -198
  4. package/dist/bundler/index.d.ts +19 -2
  5. package/dist/bundler/index.d.ts.map +1 -1
  6. package/dist/bundler/index.js +47 -4
  7. package/dist/bundler/index.js.map +1 -1
  8. package/dist/cli.d.ts +5 -0
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cli.js +123 -18
  11. package/dist/cli.js.map +1 -1
  12. package/dist/core/config.d.ts +54 -0
  13. package/dist/core/config.d.ts.map +1 -0
  14. package/dist/core/config.js +160 -0
  15. package/dist/core/config.js.map +1 -0
  16. package/dist/core/coverage-contract.d.ts +97 -0
  17. package/dist/core/coverage-contract.d.ts.map +1 -0
  18. package/dist/core/coverage-contract.js +164 -0
  19. package/dist/core/coverage-contract.js.map +1 -0
  20. package/dist/core/debug-posture.d.ts +40 -0
  21. package/dist/core/debug-posture.d.ts.map +1 -0
  22. package/dist/core/debug-posture.js +197 -0
  23. package/dist/core/debug-posture.js.map +1 -0
  24. package/dist/core/detector.d.ts.map +1 -1
  25. package/dist/core/detector.js +58 -6
  26. package/dist/core/detector.js.map +1 -1
  27. package/dist/core/identifiers.d.ts +32 -0
  28. package/dist/core/identifiers.d.ts.map +1 -0
  29. package/dist/core/identifiers.js +263 -0
  30. package/dist/core/identifiers.js.map +1 -0
  31. package/dist/core/llm-directives.d.ts +12 -0
  32. package/dist/core/llm-directives.d.ts.map +1 -0
  33. package/dist/core/llm-directives.js +23 -0
  34. package/dist/core/llm-directives.js.map +1 -0
  35. package/dist/core/prng.d.ts +10 -1
  36. package/dist/core/prng.d.ts.map +1 -1
  37. package/dist/core/prng.js +27 -2
  38. package/dist/core/prng.js.map +1 -1
  39. package/dist/core/release-plan.d.ts +68 -0
  40. package/dist/core/release-plan.d.ts.map +1 -0
  41. package/dist/core/release-plan.js +235 -0
  42. package/dist/core/release-plan.js.map +1 -0
  43. package/dist/core/sarif.d.ts +53 -0
  44. package/dist/core/sarif.d.ts.map +1 -0
  45. package/dist/core/sarif.js +91 -0
  46. package/dist/core/sarif.js.map +1 -0
  47. package/dist/core/sqlite-runtime.d.ts +13 -0
  48. package/dist/core/sqlite-runtime.d.ts.map +1 -1
  49. package/dist/core/sqlite-runtime.js +38 -8
  50. package/dist/core/sqlite-runtime.js.map +1 -1
  51. package/dist/core/types.d.ts +7 -2
  52. package/dist/core/types.d.ts.map +1 -1
  53. package/dist/engine/auditor.d.ts.map +1 -1
  54. package/dist/engine/auditor.js +251 -8
  55. package/dist/engine/auditor.js.map +1 -1
  56. package/dist/engine/benchmark.d.ts +1 -1
  57. package/dist/engine/benchmark.d.ts.map +1 -1
  58. package/dist/engine/benchmark.js +121 -41
  59. package/dist/engine/benchmark.js.map +1 -1
  60. package/dist/engine/defender.d.ts +1 -0
  61. package/dist/engine/defender.d.ts.map +1 -1
  62. package/dist/engine/defender.js +7 -1
  63. package/dist/engine/defender.js.map +1 -1
  64. package/dist/engine/harden.d.ts +61 -0
  65. package/dist/engine/harden.d.ts.map +1 -0
  66. package/dist/engine/harden.js +292 -0
  67. package/dist/engine/harden.js.map +1 -0
  68. package/dist/engine/protector.d.ts.map +1 -1
  69. package/dist/engine/protector.js +162 -21
  70. package/dist/engine/protector.js.map +1 -1
  71. package/dist/identity.d.ts +112 -2
  72. package/dist/identity.d.ts.map +1 -1
  73. package/dist/identity.js +112 -2
  74. package/dist/identity.js.map +1 -1
  75. package/dist/index.d.ts +26 -9
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +26 -9
  78. package/dist/index.js.map +1 -1
  79. package/dist/mcp/server.d.ts +59 -152
  80. package/dist/mcp/server.d.ts.map +1 -1
  81. package/dist/mcp/server.js +466 -50
  82. package/dist/mcp/server.js.map +1 -1
  83. package/dist/setup/clients.d.ts +49 -1
  84. package/dist/setup/clients.d.ts.map +1 -1
  85. package/dist/setup/clients.js +1193 -20
  86. package/dist/setup/clients.js.map +1 -1
  87. package/dist/setup/doctor.d.ts +58 -14
  88. package/dist/setup/doctor.d.ts.map +1 -1
  89. package/dist/setup/doctor.js +316 -22
  90. package/dist/setup/doctor.js.map +1 -1
  91. package/dist/setup/setup.d.ts +77 -7
  92. package/dist/setup/setup.d.ts.map +1 -1
  93. package/dist/setup/setup.js +591 -62
  94. package/dist/setup/setup.js.map +1 -1
  95. package/dist/shields/chaff/chaff-generator.d.ts.map +1 -1
  96. package/dist/shields/chaff/chaff-generator.js +12 -3
  97. package/dist/shields/chaff/chaff-generator.js.map +1 -1
  98. package/dist/shields/deception/architecture-mirage.js +1 -1
  99. package/dist/shields/deception/architecture-mirage.js.map +1 -1
  100. package/dist/shields/deception/canary-honeypot.d.ts +33 -0
  101. package/dist/shields/deception/canary-honeypot.d.ts.map +1 -0
  102. package/dist/shields/deception/canary-honeypot.js +95 -0
  103. package/dist/shields/deception/canary-honeypot.js.map +1 -0
  104. package/dist/shields/deception/cognitive-armor-trigger.d.ts +43 -0
  105. package/dist/shields/deception/cognitive-armor-trigger.d.ts.map +1 -0
  106. package/dist/shields/deception/cognitive-armor-trigger.js +204 -0
  107. package/dist/shields/deception/cognitive-armor-trigger.js.map +1 -0
  108. package/dist/shields/deception/crash-trace-decoy.js +1 -1
  109. package/dist/shields/deception/crash-trace-decoy.js.map +1 -1
  110. package/dist/shields/deception/decompiler-illusion.js +1 -1
  111. package/dist/shields/deception/decompiler-illusion.js.map +1 -1
  112. package/dist/shields/deception/diff-noise-injector.js +1 -1
  113. package/dist/shields/deception/diff-noise-injector.js.map +1 -1
  114. package/dist/shields/deception/evidence-contract-poison.js +1 -1
  115. package/dist/shields/deception/evidence-contract-poison.js.map +1 -1
  116. package/dist/shields/deception/format-payloads.d.ts +21 -0
  117. package/dist/shields/deception/format-payloads.d.ts.map +1 -0
  118. package/dist/shields/deception/format-payloads.js +24 -0
  119. package/dist/shields/deception/format-payloads.js.map +1 -0
  120. package/dist/shields/deception/index.d.ts +10 -2
  121. package/dist/shields/deception/index.d.ts.map +1 -1
  122. package/dist/shields/deception/index.js +20 -3
  123. package/dist/shields/deception/index.js.map +1 -1
  124. package/dist/shields/deception/prompt-completion-poison.d.ts +3 -2
  125. package/dist/shields/deception/prompt-completion-poison.d.ts.map +1 -1
  126. package/dist/shields/deception/prompt-completion-poison.js +6 -3
  127. package/dist/shields/deception/prompt-completion-poison.js.map +1 -1
  128. package/dist/shields/deception/shadow-business-logic.js +4 -4
  129. package/dist/shields/deception/shadow-business-logic.js.map +1 -1
  130. package/dist/shields/deception/system-diagnostic-decoy.d.ts +36 -0
  131. package/dist/shields/deception/system-diagnostic-decoy.d.ts.map +1 -0
  132. package/dist/shields/deception/system-diagnostic-decoy.js +101 -0
  133. package/dist/shields/deception/system-diagnostic-decoy.js.map +1 -0
  134. package/dist/shields/exhaustion/asar-header-amplifier.d.ts +12 -3
  135. package/dist/shields/exhaustion/asar-header-amplifier.d.ts.map +1 -1
  136. package/dist/shields/exhaustion/asar-header-amplifier.js +30 -8
  137. package/dist/shields/exhaustion/asar-header-amplifier.js.map +1 -1
  138. package/dist/shields/exhaustion/bpe-token-amplifier.js +2 -2
  139. package/dist/shields/exhaustion/bpe-token-amplifier.js.map +1 -1
  140. package/dist/shields/exhaustion/callgraph-labyrinth.js +2 -2
  141. package/dist/shields/exhaustion/callgraph-labyrinth.js.map +1 -1
  142. package/dist/shields/exhaustion/cdp-shape-budget-breaker.js +1 -1
  143. package/dist/shields/exhaustion/cdp-shape-budget-breaker.js.map +1 -1
  144. package/dist/shields/exhaustion/frida-capture-saturation.d.ts +10 -5
  145. package/dist/shields/exhaustion/frida-capture-saturation.d.ts.map +1 -1
  146. package/dist/shields/exhaustion/frida-capture-saturation.js +21 -8
  147. package/dist/shields/exhaustion/frida-capture-saturation.js.map +1 -1
  148. package/dist/shields/exhaustion/index.d.ts +2 -2
  149. package/dist/shields/exhaustion/index.d.ts.map +1 -1
  150. package/dist/shields/exhaustion/index.js +2 -2
  151. package/dist/shields/exhaustion/index.js.map +1 -1
  152. package/dist/shields/exhaustion/obligation-flooder.d.ts +17 -5
  153. package/dist/shields/exhaustion/obligation-flooder.d.ts.map +1 -1
  154. package/dist/shields/exhaustion/obligation-flooder.js +20 -8
  155. package/dist/shields/exhaustion/obligation-flooder.js.map +1 -1
  156. package/dist/shields/exhaustion/rea-budget-matrix.d.ts +8 -2
  157. package/dist/shields/exhaustion/rea-budget-matrix.d.ts.map +1 -1
  158. package/dist/shields/exhaustion/rea-budget-matrix.js +139 -16
  159. package/dist/shields/exhaustion/rea-budget-matrix.js.map +1 -1
  160. package/dist/shields/exhaustion/regex-worker-tarpit.d.ts.map +1 -1
  161. package/dist/shields/exhaustion/regex-worker-tarpit.js +2 -1
  162. package/dist/shields/exhaustion/regex-worker-tarpit.js.map +1 -1
  163. package/dist/shields/exhaustion/wasm-interface-exhaustion.d.ts +7 -4
  164. package/dist/shields/exhaustion/wasm-interface-exhaustion.d.ts.map +1 -1
  165. package/dist/shields/exhaustion/wasm-interface-exhaustion.js +16 -7
  166. package/dist/shields/exhaustion/wasm-interface-exhaustion.js.map +1 -1
  167. package/dist/shields/formats/android-dex-xref.d.ts +19 -0
  168. package/dist/shields/formats/android-dex-xref.d.ts.map +1 -0
  169. package/dist/shields/formats/android-dex-xref.js +427 -0
  170. package/dist/shields/formats/android-dex-xref.js.map +1 -0
  171. package/dist/shields/formats/android-manifest.d.ts +44 -0
  172. package/dist/shields/formats/android-manifest.d.ts.map +1 -0
  173. package/dist/shields/formats/android-manifest.js +240 -0
  174. package/dist/shields/formats/android-manifest.js.map +1 -0
  175. package/dist/shields/formats/android-r8.d.ts +44 -0
  176. package/dist/shields/formats/android-r8.d.ts.map +1 -0
  177. package/dist/shields/formats/android-r8.js +246 -0
  178. package/dist/shields/formats/android-r8.js.map +1 -0
  179. package/dist/shields/formats/apple-plist.d.ts +15 -0
  180. package/dist/shields/formats/apple-plist.d.ts.map +1 -1
  181. package/dist/shields/formats/apple-plist.js +59 -1
  182. package/dist/shields/formats/apple-plist.js.map +1 -1
  183. package/dist/shields/formats/archive.d.ts +27 -6
  184. package/dist/shields/formats/archive.d.ts.map +1 -1
  185. package/dist/shields/formats/archive.js +247 -45
  186. package/dist/shields/formats/archive.js.map +1 -1
  187. package/dist/shields/formats/asar.d.ts +50 -10
  188. package/dist/shields/formats/asar.d.ts.map +1 -1
  189. package/dist/shields/formats/asar.js +265 -39
  190. package/dist/shields/formats/asar.js.map +1 -1
  191. package/dist/shields/formats/bun-standalone.d.ts +78 -0
  192. package/dist/shields/formats/bun-standalone.d.ts.map +1 -0
  193. package/dist/shields/formats/bun-standalone.js +272 -0
  194. package/dist/shields/formats/bun-standalone.js.map +1 -0
  195. package/dist/shields/formats/dotnet-managed.d.ts +9 -0
  196. package/dist/shields/formats/dotnet-managed.d.ts.map +1 -1
  197. package/dist/shields/formats/dotnet-managed.js +26 -1
  198. package/dist/shields/formats/dotnet-managed.js.map +1 -1
  199. package/dist/shields/formats/electron-fuses.d.ts +63 -8
  200. package/dist/shields/formats/electron-fuses.d.ts.map +1 -1
  201. package/dist/shields/formats/electron-fuses.js +297 -65
  202. package/dist/shields/formats/electron-fuses.js.map +1 -1
  203. package/dist/shields/formats/erlang-beam.d.ts +34 -0
  204. package/dist/shields/formats/erlang-beam.d.ts.map +1 -0
  205. package/dist/shields/formats/erlang-beam.js +319 -0
  206. package/dist/shields/formats/erlang-beam.js.map +1 -0
  207. package/dist/shields/formats/evm-bytecode.d.ts +28 -0
  208. package/dist/shields/formats/evm-bytecode.d.ts.map +1 -0
  209. package/dist/shields/formats/evm-bytecode.js +124 -0
  210. package/dist/shields/formats/evm-bytecode.js.map +1 -0
  211. package/dist/shields/formats/go-flutter-exhaustion.d.ts +55 -0
  212. package/dist/shields/formats/go-flutter-exhaustion.d.ts.map +1 -0
  213. package/dist/shields/formats/go-flutter-exhaustion.js +346 -0
  214. package/dist/shields/formats/go-flutter-exhaustion.js.map +1 -0
  215. package/dist/shields/formats/gpu-ptx.d.ts +40 -0
  216. package/dist/shields/formats/gpu-ptx.d.ts.map +1 -0
  217. package/dist/shields/formats/gpu-ptx.js +62 -0
  218. package/dist/shields/formats/gpu-ptx.js.map +1 -0
  219. package/dist/shields/formats/headers.d.ts.map +1 -1
  220. package/dist/shields/formats/headers.js +54 -14
  221. package/dist/shields/formats/headers.js.map +1 -1
  222. package/dist/shields/formats/jvm-bytecode.d.ts +20 -0
  223. package/dist/shields/formats/jvm-bytecode.d.ts.map +1 -0
  224. package/dist/shields/formats/jvm-bytecode.js +134 -0
  225. package/dist/shields/formats/jvm-bytecode.js.map +1 -0
  226. package/dist/shields/formats/mobile-game.d.ts +4 -0
  227. package/dist/shields/formats/mobile-game.d.ts.map +1 -1
  228. package/dist/shields/formats/mobile-game.js +16 -0
  229. package/dist/shields/formats/mobile-game.js.map +1 -1
  230. package/dist/shields/formats/model.d.ts +22 -8
  231. package/dist/shields/formats/model.d.ts.map +1 -1
  232. package/dist/shields/formats/model.js +231 -59
  233. package/dist/shields/formats/model.js.map +1 -1
  234. package/dist/shields/formats/native.d.ts +65 -11
  235. package/dist/shields/formats/native.d.ts.map +1 -1
  236. package/dist/shields/formats/native.js +413 -108
  237. package/dist/shields/formats/native.js.map +1 -1
  238. package/dist/shields/formats/pcap-capture.d.ts +27 -0
  239. package/dist/shields/formats/pcap-capture.d.ts.map +1 -0
  240. package/dist/shields/formats/pcap-capture.js +201 -0
  241. package/dist/shields/formats/pcap-capture.js.map +1 -0
  242. package/dist/shields/formats/python.d.ts +22 -1
  243. package/dist/shields/formats/python.d.ts.map +1 -1
  244. package/dist/shields/formats/python.js +568 -27
  245. package/dist/shields/formats/python.js.map +1 -1
  246. package/dist/shields/formats/sourcemap.d.ts.map +1 -1
  247. package/dist/shields/formats/sourcemap.js +2 -0
  248. package/dist/shields/formats/sourcemap.js.map +1 -1
  249. package/dist/shields/formats/sqlite.d.ts +87 -15
  250. package/dist/shields/formats/sqlite.d.ts.map +1 -1
  251. package/dist/shields/formats/sqlite.js +377 -42
  252. package/dist/shields/formats/sqlite.js.map +1 -1
  253. package/dist/shields/formats/wasm.d.ts +6 -0
  254. package/dist/shields/formats/wasm.d.ts.map +1 -1
  255. package/dist/shields/formats/wasm.js +118 -3
  256. package/dist/shields/formats/wasm.js.map +1 -1
  257. package/dist/shields/javascript/ast-shield.d.ts +25 -5
  258. package/dist/shields/javascript/ast-shield.d.ts.map +1 -1
  259. package/dist/shields/javascript/ast-shield.js +428 -58
  260. package/dist/shields/javascript/ast-shield.js.map +1 -1
  261. package/dist/shields/runtime/memory-diff-shield.d.ts +56 -0
  262. package/dist/shields/runtime/memory-diff-shield.d.ts.map +1 -0
  263. package/dist/shields/runtime/memory-diff-shield.js +170 -0
  264. package/dist/shields/runtime/memory-diff-shield.js.map +1 -0
  265. package/dist/shields/runtime/runtime-shield.d.ts +12 -16
  266. package/dist/shields/runtime/runtime-shield.d.ts.map +1 -1
  267. package/dist/shields/runtime/runtime-shield.js +30 -24
  268. package/dist/shields/runtime/runtime-shield.js.map +1 -1
  269. package/dist/shields/tarpit/opaque-predicates.d.ts.map +1 -1
  270. package/dist/shields/tarpit/opaque-predicates.js +3 -3
  271. package/dist/shields/tarpit/opaque-predicates.js.map +1 -1
  272. package/dist/shields/tarpit/tarpit-generator.d.ts +9 -4
  273. package/dist/shields/tarpit/tarpit-generator.d.ts.map +1 -1
  274. package/dist/shields/tarpit/tarpit-generator.js +30 -23
  275. package/dist/shields/tarpit/tarpit-generator.js.map +1 -1
  276. package/package.json +13 -6
@@ -1,86 +1,1252 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { dirname, join } from "node:path";
4
- export function resolveSupportedAgentClients(home = homedir(), platform = process.platform) {
3
+ import { join } from "node:path";
4
+ import { BLA_IDENTITY } from "../identity.js";
5
+ export const CLIENT_COMPATIBILITY_MATRIX = Object.freeze([
6
+ {
7
+ clientId: "claude_code",
8
+ displayName: "Claude Code",
9
+ format: "json-mcpServers",
10
+ rootKey: "mcpServers",
11
+ configPathTemplate: "~/.claude.json",
12
+ schemaVersion: "claude-code-mcp-v1",
13
+ officialSpecUrl: "https://docs.anthropic.com/en/docs/claude-code/mcp",
14
+ verificationMode: "automated-stdio-smoke",
15
+ verifiedClientVersion: "1.0.x (automated stdio protocol + schema fixture)",
16
+ },
17
+ {
18
+ clientId: "claude_desktop",
19
+ displayName: "Claude Desktop",
20
+ format: "json-mcpServers",
21
+ rootKey: "mcpServers",
22
+ configPathTemplate: "~/Library/Application Support/Claude/claude_desktop_config.json",
23
+ schemaVersion: "claude-desktop-mcp-v1",
24
+ officialSpecUrl: "https://modelcontextprotocol.io/quickstart/user",
25
+ verificationMode: "schema-and-fixture-verified",
26
+ verifiedClientVersion: "0.9.x (GUI client — schema fixture verified, runtime unverified in headless CI)",
27
+ },
28
+ {
29
+ clientId: "cursor",
30
+ displayName: "Cursor",
31
+ format: "json-mcpServers",
32
+ rootKey: "mcpServers",
33
+ configPathTemplate: "~/.cursor/mcp.json",
34
+ schemaVersion: "cursor-mcp-v1",
35
+ officialSpecUrl: "https://docs.cursor.com/context/model-context-protocol",
36
+ verificationMode: "schema-and-fixture-verified",
37
+ verifiedClientVersion: "0.48.x (GUI client — schema fixture verified, runtime unverified in headless CI)",
38
+ },
39
+ {
40
+ clientId: "codex",
41
+ displayName: "Codex CLI",
42
+ format: "toml-codex",
43
+ rootKey: "mcp_servers",
44
+ configPathTemplate: "~/.codex/config.toml",
45
+ schemaVersion: "codex-config-toml-v1",
46
+ officialSpecUrl: "https://developers.openai.com/codex/mcp/",
47
+ verificationMode: "automated-stdio-smoke",
48
+ verifiedClientVersion: "0.1.x (TOML [mcp_servers.bla] spec + automated stdio smoke)",
49
+ },
50
+ {
51
+ clientId: "gemini_cli",
52
+ displayName: "Gemini CLI",
53
+ format: "json-mcpServers",
54
+ rootKey: "mcpServers",
55
+ configPathTemplate: "~/.gemini/settings.json",
56
+ schemaVersion: "gemini-cli-settings-v1",
57
+ officialSpecUrl: "https://github.com/google-gemini/gemini-cli",
58
+ verificationMode: "automated-stdio-smoke",
59
+ verifiedClientVersion: "0.1.x (settings.json mcpServers + automated stdio smoke)",
60
+ },
61
+ {
62
+ clientId: "antigravity",
63
+ displayName: "Google Antigravity",
64
+ format: "json-mcpServers",
65
+ rootKey: "mcpServers",
66
+ configPathTemplate: "~/.gemini/antigravity/mcp_config.json",
67
+ schemaVersion: "antigravity-mcp-v1",
68
+ officialSpecUrl: "https://idx.dev",
69
+ verificationMode: "schema-and-fixture-verified",
70
+ verifiedClientVersion: "2.0.x (mcpServers schema fixture verified)",
71
+ },
72
+ {
73
+ clientId: "windsurf",
74
+ displayName: "Windsurf",
75
+ format: "json-mcpServers",
76
+ rootKey: "mcpServers",
77
+ configPathTemplate: "~/.codeium/windsurf/mcp_config.json",
78
+ schemaVersion: "windsurf-mcp-v1",
79
+ officialSpecUrl: "https://docs.windsurf.com/windsurf/mcp",
80
+ verificationMode: "schema-and-fixture-verified",
81
+ verifiedClientVersion: "1.6.x (GUI client — schema fixture verified, runtime unverified in headless CI)",
82
+ },
83
+ {
84
+ clientId: "vscode",
85
+ displayName: "VS Code",
86
+ format: "json-vscode",
87
+ rootKey: "servers",
88
+ configPathTemplate: "<UserConfig>/Code/User/mcp.json",
89
+ schemaVersion: "vscode-mcp-servers-v1",
90
+ officialSpecUrl: "https://code.visualstudio.com/docs/agent-customization/mcp-servers",
91
+ verificationMode: "schema-and-fixture-verified",
92
+ verifiedClientVersion: "1.99+ (user-profile mcp.json 'servers' schema fixture verified; GUI runtime unverified in CI)",
93
+ },
94
+ {
95
+ clientId: "opencode",
96
+ displayName: "OpenCode",
97
+ format: "json-opencode",
98
+ rootKey: "mcp",
99
+ configPathTemplate: "~/.config/opencode/opencode.json",
100
+ schemaVersion: "opencode-mcp-local-v1",
101
+ officialSpecUrl: "https://opencode.ai/docs/mcp-servers/",
102
+ verificationMode: "automated-stdio-smoke",
103
+ verifiedClientVersion: "0.1.x (opencode.json mcp.bla local command array + automated stdio smoke)",
104
+ },
105
+ {
106
+ clientId: "copilot_cli",
107
+ displayName: "GitHub Copilot CLI",
108
+ format: "json-mcpServers",
109
+ rootKey: "mcpServers",
110
+ configPathTemplate: "~/.copilot/mcp-config.json",
111
+ schemaVersion: "copilot-cli-mcp-v1",
112
+ officialSpecUrl: "https://docs.github.com/en/copilot",
113
+ verificationMode: "schema-and-fixture-verified",
114
+ verifiedClientVersion: "1.0.x (mcpServers schema fixture verified)",
115
+ },
116
+ ]);
117
+ const CLIENT_ID_ALIASES = Object.freeze({
118
+ claude_code: "claude_code",
119
+ "claude-code": "claude_code",
120
+ claude_desktop: "claude_desktop",
121
+ "claude-desktop": "claude_desktop",
122
+ cursor: "cursor",
123
+ codex: "codex",
124
+ gemini_cli: "gemini_cli",
125
+ "gemini-cli": "gemini_cli",
126
+ windsurf: "windsurf",
127
+ antigravity: "antigravity",
128
+ vscode: "vscode",
129
+ "vs-code": "vscode",
130
+ opencode: "opencode",
131
+ copilot_cli: "copilot_cli",
132
+ "copilot-cli": "copilot_cli",
133
+ });
134
+ export function normalizeClientId(id) {
135
+ return CLIENT_ID_ALIASES[id.trim().toLowerCase()] ?? null;
136
+ }
137
+ function isPlainObject(value) {
138
+ return typeof value === "object" && value !== null && !Array.isArray(value);
139
+ }
140
+ /**
141
+ * Strip inline `#` comments outside quoted strings on a single TOML line.
142
+ */
143
+ function stripTomlInlineComment(line) {
144
+ let inDouble = false;
145
+ let inSingle = false;
146
+ let escaped = false;
147
+ for (let i = 0; i < line.length; i++) {
148
+ const ch = line[i];
149
+ if (escaped) {
150
+ escaped = false;
151
+ continue;
152
+ }
153
+ if (inDouble) {
154
+ if (ch === "\\") {
155
+ escaped = true;
156
+ }
157
+ else if (ch === '"') {
158
+ inDouble = false;
159
+ }
160
+ continue;
161
+ }
162
+ if (inSingle) {
163
+ if (ch === "'") {
164
+ inSingle = false;
165
+ }
166
+ continue;
167
+ }
168
+ if (ch === '"') {
169
+ inDouble = true;
170
+ }
171
+ else if (ch === "'") {
172
+ inSingle = true;
173
+ }
174
+ else if (ch === "#") {
175
+ return { code: line.slice(0, i).trim(), unclosedQuote: false };
176
+ }
177
+ }
178
+ return { code: line.trim(), unclosedQuote: inDouble || inSingle };
179
+ }
180
+ function parseTomlStringLiteral(raw) {
181
+ const trimmed = raw.trim();
182
+ if (trimmed.length < 2)
183
+ return { ok: false };
184
+ if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
185
+ try {
186
+ const parsed = JSON.parse(trimmed);
187
+ if (typeof parsed === "string") {
188
+ return { ok: true, value: parsed };
189
+ }
190
+ }
191
+ catch {
192
+ return { ok: false };
193
+ }
194
+ return { ok: false };
195
+ }
196
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
197
+ const inner = trimmed.slice(1, -1);
198
+ if (inner.includes("'"))
199
+ return { ok: false };
200
+ return { ok: true, value: inner };
201
+ }
202
+ return { ok: false };
203
+ }
204
+ function parseTomlStringArray(raw) {
205
+ const trimmed = raw.trim();
206
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
207
+ return { ok: false };
208
+ }
209
+ const inner = trimmed.slice(1, -1).trim();
210
+ if (inner.length === 0) {
211
+ return { ok: true, values: [] };
212
+ }
213
+ const items = [];
214
+ let current = "";
215
+ let inDouble = false;
216
+ let inSingle = false;
217
+ let escaped = false;
218
+ let depth = 0;
219
+ for (let i = 0; i < inner.length; i++) {
220
+ const ch = inner[i];
221
+ if (escaped) {
222
+ current += ch;
223
+ escaped = false;
224
+ continue;
225
+ }
226
+ if (inDouble) {
227
+ current += ch;
228
+ if (ch === "\\")
229
+ escaped = true;
230
+ else if (ch === '"')
231
+ inDouble = false;
232
+ continue;
233
+ }
234
+ if (inSingle) {
235
+ current += ch;
236
+ if (ch === "'")
237
+ inSingle = false;
238
+ continue;
239
+ }
240
+ if (ch === '"') {
241
+ inDouble = true;
242
+ current += ch;
243
+ }
244
+ else if (ch === "'") {
245
+ inSingle = true;
246
+ current += ch;
247
+ }
248
+ else if (ch === "[") {
249
+ depth++;
250
+ current += ch;
251
+ }
252
+ else if (ch === "]") {
253
+ depth--;
254
+ if (depth < 0)
255
+ return { ok: false };
256
+ current += ch;
257
+ }
258
+ else if (ch === "," && depth === 0) {
259
+ const parsedItem = parseTomlStringLiteral(current);
260
+ if (!parsedItem.ok)
261
+ return { ok: false };
262
+ items.push(parsedItem.value);
263
+ current = "";
264
+ }
265
+ else {
266
+ current += ch;
267
+ }
268
+ }
269
+ if (inDouble || inSingle || depth !== 0) {
270
+ return { ok: false };
271
+ }
272
+ if (current.trim().length > 0) {
273
+ const parsedItem = parseTomlStringLiteral(current);
274
+ if (!parsedItem.ok)
275
+ return { ok: false };
276
+ items.push(parsedItem.value);
277
+ }
278
+ return { ok: true, values: items };
279
+ }
280
+ function inspectCodexTomlDocument(rawToml) {
281
+ const lines = rawToml.split(/\r?\n/);
282
+ const sections = [];
283
+ const topLevelAssignments = new Map();
284
+ let currentSection = null;
285
+ let pendingKey = null;
286
+ let pendingArrayBuffer = "";
287
+ let bracketBalance = 0;
288
+ const countArrayBracketsOutsideQuotes = (code) => {
289
+ let delta = 0;
290
+ let inDouble = false;
291
+ let inSingle = false;
292
+ let escaped = false;
293
+ for (let i = 0; i < code.length; i++) {
294
+ const ch = code[i];
295
+ if (escaped) {
296
+ escaped = false;
297
+ continue;
298
+ }
299
+ if (inDouble) {
300
+ if (ch === "\\")
301
+ escaped = true;
302
+ else if (ch === '"')
303
+ inDouble = false;
304
+ continue;
305
+ }
306
+ if (inSingle) {
307
+ if (ch === "'")
308
+ inSingle = false;
309
+ continue;
310
+ }
311
+ if (ch === '"')
312
+ inDouble = true;
313
+ else if (ch === "'")
314
+ inSingle = true;
315
+ else if (ch === "[")
316
+ delta++;
317
+ else if (ch === "]")
318
+ delta--;
319
+ }
320
+ return { delta, valid: !inDouble && !inSingle };
321
+ };
322
+ for (let idx = 0; idx < lines.length; idx++) {
323
+ const rawLine = lines[idx];
324
+ const stripped = stripTomlInlineComment(rawLine);
325
+ if (stripped.unclosedQuote) {
326
+ return { ok: false, error: `Malformed TOML at line ${idx + 1}: unclosed string quote` };
327
+ }
328
+ const code = stripped.code;
329
+ if (code.length === 0)
330
+ continue;
331
+ if (pendingKey !== null) {
332
+ const bracketCheck = countArrayBracketsOutsideQuotes(code);
333
+ if (!bracketCheck.valid) {
334
+ return { ok: false, error: `Malformed TOML array at line ${idx + 1}` };
335
+ }
336
+ pendingArrayBuffer += " " + code;
337
+ bracketBalance += bracketCheck.delta;
338
+ if (bracketBalance < 0) {
339
+ return { ok: false, error: `Malformed TOML at line ${idx + 1}: unexpected closing bracket` };
340
+ }
341
+ if (bracketBalance === 0) {
342
+ const targetMap = currentSection ? currentSection.assignments : topLevelAssignments;
343
+ targetMap.set(pendingKey, pendingArrayBuffer.trim());
344
+ pendingKey = null;
345
+ pendingArrayBuffer = "";
346
+ }
347
+ continue;
348
+ }
349
+ if (code.startsWith("[")) {
350
+ if (!code.endsWith("]")) {
351
+ return { ok: false, error: `Malformed TOML table header at line ${idx + 1}: '${code}'` };
352
+ }
353
+ const isArrayOfTables = code.startsWith("[[") && code.endsWith("]]");
354
+ const tableName = (isArrayOfTables ? code.slice(2, -2) : code.slice(1, -1)).trim();
355
+ if (tableName.length === 0 || tableName.includes("[") || tableName.includes("]")) {
356
+ return { ok: false, error: `Invalid TOML table header at line ${idx + 1}: '${code}'` };
357
+ }
358
+ if (currentSection) {
359
+ currentSection.endLineIndex = idx;
360
+ }
361
+ currentSection = {
362
+ name: tableName,
363
+ headerLineIndex: idx,
364
+ endLineIndex: lines.length,
365
+ assignments: new Map(),
366
+ };
367
+ sections.push(currentSection);
368
+ continue;
369
+ }
370
+ const eqIdx = code.indexOf("=");
371
+ if (eqIdx <= 0) {
372
+ return { ok: false, error: `Malformed TOML statement at line ${idx + 1}: '${code}'` };
373
+ }
374
+ const key = code.slice(0, eqIdx).trim();
375
+ const val = code.slice(eqIdx + 1).trim();
376
+ if (!/^[A-Za-z0-9_.-]+$/.test(key) && !(key.startsWith('"') && key.endsWith('"'))) {
377
+ return { ok: false, error: `Invalid TOML key '${key}' at line ${idx + 1}` };
378
+ }
379
+ if (val.length === 0) {
380
+ return { ok: false, error: `Empty TOML value for key '${key}' at line ${idx + 1}` };
381
+ }
382
+ if (val.startsWith("[")) {
383
+ const bracketCheck = countArrayBracketsOutsideQuotes(val);
384
+ if (!bracketCheck.valid || bracketCheck.delta < 0) {
385
+ return { ok: false, error: `Malformed TOML array for key '${key}' at line ${idx + 1}` };
386
+ }
387
+ if (bracketCheck.delta > 0) {
388
+ pendingKey = key;
389
+ pendingArrayBuffer = val;
390
+ bracketBalance = bracketCheck.delta;
391
+ continue;
392
+ }
393
+ }
394
+ else if (val.startsWith("{")) {
395
+ if (!val.endsWith("}")) {
396
+ return { ok: false, error: `Unclosed TOML inline table for key '${key}' at line ${idx + 1}` };
397
+ }
398
+ }
399
+ else if (val.startsWith('"') || val.startsWith("'")) {
400
+ const parsedStr = parseTomlStringLiteral(val);
401
+ if (!parsedStr.ok) {
402
+ return { ok: false, error: `Invalid TOML string literal for key '${key}' at line ${idx + 1}` };
403
+ }
404
+ }
405
+ else if (val !== "true" &&
406
+ val !== "false" &&
407
+ !/^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?$/.test(val) &&
408
+ !/^\d{4}-\d{2}-\d{2}/.test(val)) {
409
+ return { ok: false, error: `Invalid TOML literal '${val}' for key '${key}' at line ${idx + 1}` };
410
+ }
411
+ const targetMap = currentSection ? currentSection.assignments : topLevelAssignments;
412
+ targetMap.set(key, val);
413
+ }
414
+ if (pendingKey !== null || bracketBalance !== 0) {
415
+ return { ok: false, error: `Malformed TOML: unclosed multiline array for key '${pendingKey}'` };
416
+ }
417
+ // Schema check: top-level `mcp_servers` cannot be a scalar or array
418
+ const topLevelMcpServers = topLevelAssignments.get("mcp_servers");
419
+ if (topLevelMcpServers !== undefined && !topLevelMcpServers.startsWith("{")) {
420
+ return {
421
+ ok: false,
422
+ error: "Invalid Codex config.toml schema: 'mcp_servers' must be a table, not a scalar or array",
423
+ };
424
+ }
425
+ return {
426
+ ok: true,
427
+ lines,
428
+ sections,
429
+ topLevelAssignments,
430
+ };
431
+ }
432
+ export function readCodexTomlMcp(rawToml, serverName = BLA_IDENTITY.mcpServerName) {
433
+ const inspected = inspectCodexTomlDocument(rawToml);
434
+ if (!inspected.ok) {
435
+ return {
436
+ status: "malformed",
437
+ entry: null,
438
+ error: inspected.error,
439
+ otherServersCount: 0,
440
+ };
441
+ }
442
+ const targetTable = `mcp_servers.${serverName}`;
443
+ let otherServersCount = 0;
444
+ let targetSection;
445
+ for (const sec of inspected.sections) {
446
+ if (sec.name === targetTable) {
447
+ targetSection = sec;
448
+ }
449
+ else if (sec.name.startsWith("mcp_servers.")) {
450
+ const remainder = sec.name.slice("mcp_servers.".length);
451
+ if (remainder.length > 0 && !remainder.includes(".")) {
452
+ otherServersCount++;
453
+ }
454
+ }
455
+ else if (sec.name === "mcp_servers") {
456
+ const inlineBla = sec.assignments.get(serverName);
457
+ if (inlineBla !== undefined && !inlineBla.startsWith("{")) {
458
+ return {
459
+ status: "malformed",
460
+ entry: null,
461
+ error: `Invalid Codex config.toml schema: '[mcp_servers].${serverName}' must be a table`,
462
+ otherServersCount,
463
+ };
464
+ }
465
+ }
466
+ }
467
+ if (!targetSection) {
468
+ return {
469
+ status: "not_configured",
470
+ entry: null,
471
+ otherServersCount,
472
+ };
473
+ }
474
+ const rawCommand = targetSection.assignments.get("command");
475
+ if (rawCommand === undefined) {
476
+ return {
477
+ status: "invalid_entry",
478
+ entry: null,
479
+ error: `Codex [${targetTable}] is missing required 'command' string`,
480
+ otherServersCount,
481
+ };
482
+ }
483
+ const parsedCommand = parseTomlStringLiteral(rawCommand);
484
+ if (!parsedCommand.ok || parsedCommand.value.trim().length === 0) {
485
+ return {
486
+ status: "invalid_entry",
487
+ entry: null,
488
+ error: `Codex [${targetTable}].command must be a non-empty string`,
489
+ otherServersCount,
490
+ };
491
+ }
492
+ let parsedArgs = [];
493
+ const rawArgs = targetSection.assignments.get("args");
494
+ if (rawArgs !== undefined) {
495
+ const arrRes = parseTomlStringArray(rawArgs);
496
+ if (!arrRes.ok) {
497
+ return {
498
+ status: "invalid_entry",
499
+ entry: null,
500
+ error: `Codex [${targetTable}].args must be an array of strings`,
501
+ otherServersCount,
502
+ };
503
+ }
504
+ parsedArgs = arrRes.values;
505
+ }
506
+ const rawEnabled = targetSection.assignments.get("enabled");
507
+ if (rawEnabled === "false") {
508
+ return {
509
+ status: "invalid_entry",
510
+ entry: null,
511
+ error: `Codex [${targetTable}] is explicitly disabled (enabled = false)`,
512
+ otherServersCount,
513
+ };
514
+ }
515
+ return {
516
+ status: "valid",
517
+ entry: {
518
+ command: parsedCommand.value,
519
+ args: parsedArgs,
520
+ },
521
+ rawEntry: Object.fromEntries(targetSection.assignments.entries()),
522
+ otherServersCount,
523
+ };
524
+ }
525
+ export function writeCodexTomlMcp(rawToml, serverName, reg) {
526
+ const existing = rawToml ?? "";
527
+ if (existing.trim().length > 0) {
528
+ const inspected = inspectCodexTomlDocument(existing);
529
+ if (!inspected.ok) {
530
+ throw new Error(inspected.error);
531
+ }
532
+ const targetTable = `mcp_servers.${serverName}`;
533
+ for (const sec of inspected.sections) {
534
+ if (sec.name === "mcp_servers") {
535
+ const inlineVal = sec.assignments.get(serverName);
536
+ if (inlineVal !== undefined && !inlineVal.startsWith("{")) {
537
+ throw new Error(`Invalid Codex config.toml schema: '[mcp_servers].${serverName}' is not a table`);
538
+ }
539
+ }
540
+ }
541
+ const formattedSectionLines = [
542
+ `[${targetTable}]`,
543
+ `command = ${JSON.stringify(reg.command)}`,
544
+ `args = [${reg.args.map((a) => JSON.stringify(a)).join(", ")}]`,
545
+ ];
546
+ const existingSection = inspected.sections.find((s) => s.name === targetTable);
547
+ if (existingSection) {
548
+ // Preserve trailing comments before the next section header
549
+ let replaceEnd = existingSection.endLineIndex;
550
+ while (replaceEnd > existingSection.headerLineIndex + 1 &&
551
+ (inspected.lines[replaceEnd - 1].trim() === "" ||
552
+ inspected.lines[replaceEnd - 1].trim().startsWith("#"))) {
553
+ if (inspected.lines[replaceEnd - 1].trim().startsWith("#")) {
554
+ break;
555
+ }
556
+ replaceEnd--;
557
+ }
558
+ const before = inspected.lines.slice(0, existingSection.headerLineIndex);
559
+ const after = inspected.lines.slice(replaceEnd);
560
+ const combined = [...before, ...formattedSectionLines, ...(after.length > 0 ? ["", ...after] : [])];
561
+ return combined.join("\n").replace(/\n{3,}/g, "\n\n").trimEnd() + "\n";
562
+ }
563
+ const trimmedExisting = existing.trimEnd();
564
+ return `${trimmedExisting}\n\n${formattedSectionLines.join("\n")}\n`;
565
+ }
566
+ return [
567
+ `[mcp_servers.${serverName}]`,
568
+ `command = ${JSON.stringify(reg.command)}`,
569
+ `args = [${reg.args.map((a) => JSON.stringify(a)).join(", ")}]`,
570
+ "",
571
+ ].join("\n");
572
+ }
573
+ export function removeCodexTomlMcp(rawToml, serverName = BLA_IDENTITY.mcpServerName) {
574
+ const inspected = inspectCodexTomlDocument(rawToml);
575
+ if (!inspected.ok) {
576
+ throw new Error(inspected.error);
577
+ }
578
+ const targetTable = `mcp_servers.${serverName}`;
579
+ const existingSection = inspected.sections.find((s) => s.name === targetTable);
580
+ if (!existingSection) {
581
+ return { content: rawToml, removed: false };
582
+ }
583
+ const before = inspected.lines.slice(0, existingSection.headerLineIndex);
584
+ const after = inspected.lines.slice(existingSection.endLineIndex);
585
+ const joined = [...before, ...after].join("\n").replace(/\n{3,}/g, "\n\n").trimEnd();
586
+ return {
587
+ content: joined.length > 0 ? `${joined}\n` : "",
588
+ removed: true,
589
+ };
590
+ }
591
+ function createJsonMcpServersAdapter() {
592
+ return {
593
+ format: "json-mcpServers",
594
+ readServer(rawContent, serverName = BLA_IDENTITY.mcpServerName) {
595
+ let parsed;
596
+ try {
597
+ parsed = JSON.parse(rawContent);
598
+ }
599
+ catch (err) {
600
+ return {
601
+ status: "malformed",
602
+ entry: null,
603
+ error: `Malformed JSON configuration: ${err instanceof Error ? err.message : String(err)}`,
604
+ otherServersCount: 0,
605
+ };
606
+ }
607
+ if (!isPlainObject(parsed)) {
608
+ return {
609
+ status: "malformed",
610
+ entry: null,
611
+ error: "Invalid configuration schema: root value must be a JSON object",
612
+ otherServersCount: 0,
613
+ };
614
+ }
615
+ if (parsed.mcpServers === undefined) {
616
+ return {
617
+ status: "not_configured",
618
+ entry: null,
619
+ otherServersCount: 0,
620
+ };
621
+ }
622
+ if (!isPlainObject(parsed.mcpServers)) {
623
+ return {
624
+ status: "malformed",
625
+ entry: null,
626
+ error: "Invalid configuration schema: 'mcpServers' must be a JSON object",
627
+ otherServersCount: 0,
628
+ };
629
+ }
630
+ const servers = parsed.mcpServers;
631
+ const otherServersCount = Object.keys(servers).filter((k) => k !== serverName).length;
632
+ if (!(serverName in servers)) {
633
+ return {
634
+ status: "not_configured",
635
+ entry: null,
636
+ otherServersCount,
637
+ };
638
+ }
639
+ const rawEntry = servers[serverName];
640
+ if (!isPlainObject(rawEntry)) {
641
+ return {
642
+ status: "invalid_entry",
643
+ entry: null,
644
+ rawEntry,
645
+ error: `Invalid MCP server entry '${serverName}': must be a JSON object`,
646
+ otherServersCount,
647
+ };
648
+ }
649
+ if (typeof rawEntry.command !== "string" || rawEntry.command.trim().length === 0) {
650
+ return {
651
+ status: "invalid_entry",
652
+ entry: null,
653
+ rawEntry,
654
+ error: `Invalid MCP server entry '${serverName}': 'command' must be a non-empty string`,
655
+ otherServersCount,
656
+ };
657
+ }
658
+ let args = [];
659
+ if (rawEntry.args !== undefined) {
660
+ if (!Array.isArray(rawEntry.args) || !rawEntry.args.every((a) => typeof a === "string")) {
661
+ return {
662
+ status: "invalid_entry",
663
+ entry: null,
664
+ rawEntry,
665
+ error: `Invalid MCP server entry '${serverName}': 'args' must be an array of strings`,
666
+ otherServersCount,
667
+ };
668
+ }
669
+ args = [...rawEntry.args];
670
+ }
671
+ if (rawEntry.disabled === true || rawEntry.enabled === false) {
672
+ return {
673
+ status: "invalid_entry",
674
+ entry: null,
675
+ rawEntry,
676
+ error: `MCP server entry '${serverName}' is disabled`,
677
+ otherServersCount,
678
+ };
679
+ }
680
+ return {
681
+ status: "valid",
682
+ entry: {
683
+ command: rawEntry.command,
684
+ args,
685
+ },
686
+ rawEntry,
687
+ otherServersCount,
688
+ };
689
+ },
690
+ upsertServer(rawContent, serverName, reg) {
691
+ let root = {};
692
+ if (rawContent !== null && rawContent.trim().length > 0) {
693
+ let parsed;
694
+ try {
695
+ parsed = JSON.parse(rawContent);
696
+ }
697
+ catch (err) {
698
+ throw new Error(`Malformed JSON configuration: ${err instanceof Error ? err.message : String(err)}`);
699
+ }
700
+ if (!isPlainObject(parsed)) {
701
+ throw new Error("Invalid configuration schema: root value must be a JSON object");
702
+ }
703
+ if (parsed.mcpServers !== undefined && !isPlainObject(parsed.mcpServers)) {
704
+ throw new Error("Invalid configuration schema: 'mcpServers' must be a JSON object");
705
+ }
706
+ root = { ...parsed };
707
+ }
708
+ const existingServers = isPlainObject(root.mcpServers) ? { ...root.mcpServers } : {};
709
+ existingServers[serverName] = {
710
+ command: reg.command,
711
+ args: [...reg.args],
712
+ };
713
+ root.mcpServers = existingServers;
714
+ return JSON.stringify(root, null, 2) + "\n";
715
+ },
716
+ removeServer(rawContent, serverName) {
717
+ let parsed;
718
+ try {
719
+ parsed = JSON.parse(rawContent);
720
+ }
721
+ catch (err) {
722
+ throw new Error(`Malformed JSON configuration: ${err instanceof Error ? err.message : String(err)}`);
723
+ }
724
+ if (!isPlainObject(parsed)) {
725
+ throw new Error("Invalid configuration schema: root value must be a JSON object");
726
+ }
727
+ if (parsed.mcpServers !== undefined && !isPlainObject(parsed.mcpServers)) {
728
+ throw new Error("Invalid configuration schema: 'mcpServers' must be a JSON object");
729
+ }
730
+ if (!isPlainObject(parsed.mcpServers) || !(serverName in parsed.mcpServers)) {
731
+ return { content: rawContent, removed: false };
732
+ }
733
+ const nextServers = { ...parsed.mcpServers };
734
+ delete nextServers[serverName];
735
+ const nextRoot = { ...parsed, mcpServers: nextServers };
736
+ return {
737
+ content: JSON.stringify(nextRoot, null, 2) + "\n",
738
+ removed: true,
739
+ };
740
+ },
741
+ };
742
+ }
743
+ function createJsonVscodeAdapter() {
744
+ return {
745
+ format: "json-vscode",
746
+ readServer(rawContent, serverName = BLA_IDENTITY.mcpServerName) {
747
+ let parsed;
748
+ try {
749
+ parsed = JSON.parse(rawContent);
750
+ }
751
+ catch (err) {
752
+ return {
753
+ status: "malformed",
754
+ entry: null,
755
+ error: `Malformed VS Code mcp.json: ${err instanceof Error ? err.message : String(err)}`,
756
+ otherServersCount: 0,
757
+ };
758
+ }
759
+ if (!isPlainObject(parsed)) {
760
+ return {
761
+ status: "malformed",
762
+ entry: null,
763
+ error: "Invalid VS Code mcp.json schema: root value must be a JSON object",
764
+ otherServersCount: 0,
765
+ };
766
+ }
767
+ if (parsed.servers === undefined) {
768
+ if ("mcpServers" in parsed) {
769
+ return {
770
+ status: "malformed",
771
+ entry: null,
772
+ error: "Invalid VS Code user-profile mcp.json schema: expected root key 'servers', found 'mcpServers'",
773
+ otherServersCount: 0,
774
+ };
775
+ }
776
+ return {
777
+ status: "not_configured",
778
+ entry: null,
779
+ otherServersCount: 0,
780
+ };
781
+ }
782
+ if (!isPlainObject(parsed.servers)) {
783
+ return {
784
+ status: "malformed",
785
+ entry: null,
786
+ error: "Invalid VS Code mcp.json schema: 'servers' must be a JSON object",
787
+ otherServersCount: 0,
788
+ };
789
+ }
790
+ const servers = parsed.servers;
791
+ const otherServersCount = Object.keys(servers).filter((k) => k !== serverName).length;
792
+ if (!(serverName in servers)) {
793
+ return {
794
+ status: "not_configured",
795
+ entry: null,
796
+ otherServersCount,
797
+ };
798
+ }
799
+ const rawEntry = servers[serverName];
800
+ if (!isPlainObject(rawEntry)) {
801
+ return {
802
+ status: "invalid_entry",
803
+ entry: null,
804
+ rawEntry,
805
+ error: `Invalid VS Code MCP server entry '${serverName}': must be a JSON object`,
806
+ otherServersCount,
807
+ };
808
+ }
809
+ if (rawEntry.type !== undefined && rawEntry.type !== "stdio") {
810
+ return {
811
+ status: "invalid_entry",
812
+ entry: null,
813
+ rawEntry,
814
+ error: `Invalid VS Code MCP server entry '${serverName}': 'type' must be 'stdio'`,
815
+ otherServersCount,
816
+ };
817
+ }
818
+ if (typeof rawEntry.command !== "string" || rawEntry.command.trim().length === 0) {
819
+ return {
820
+ status: "invalid_entry",
821
+ entry: null,
822
+ rawEntry,
823
+ error: `Invalid VS Code MCP server entry '${serverName}': 'command' must be a non-empty string`,
824
+ otherServersCount,
825
+ };
826
+ }
827
+ let args = [];
828
+ if (rawEntry.args !== undefined) {
829
+ if (!Array.isArray(rawEntry.args) || !rawEntry.args.every((a) => typeof a === "string")) {
830
+ return {
831
+ status: "invalid_entry",
832
+ entry: null,
833
+ rawEntry,
834
+ error: `Invalid VS Code MCP server entry '${serverName}': 'args' must be an array of strings`,
835
+ otherServersCount,
836
+ };
837
+ }
838
+ args = [...rawEntry.args];
839
+ }
840
+ return {
841
+ status: "valid",
842
+ entry: {
843
+ type: "stdio",
844
+ command: rawEntry.command,
845
+ args,
846
+ },
847
+ rawEntry,
848
+ otherServersCount,
849
+ };
850
+ },
851
+ upsertServer(rawContent, serverName, reg) {
852
+ let root = {};
853
+ if (rawContent !== null && rawContent.trim().length > 0) {
854
+ let parsed;
855
+ try {
856
+ parsed = JSON.parse(rawContent);
857
+ }
858
+ catch (err) {
859
+ throw new Error(`Malformed VS Code mcp.json: ${err instanceof Error ? err.message : String(err)}`);
860
+ }
861
+ if (!isPlainObject(parsed)) {
862
+ throw new Error("Invalid VS Code mcp.json schema: root value must be a JSON object");
863
+ }
864
+ if (parsed.servers !== undefined && !isPlainObject(parsed.servers)) {
865
+ throw new Error("Invalid VS Code mcp.json schema: 'servers' must be a JSON object");
866
+ }
867
+ root = { ...parsed };
868
+ }
869
+ const existingServers = isPlainObject(root.servers) ? { ...root.servers } : {};
870
+ existingServers[serverName] = {
871
+ type: "stdio",
872
+ command: reg.command,
873
+ args: [...reg.args],
874
+ };
875
+ root.servers = existingServers;
876
+ return JSON.stringify(root, null, 2) + "\n";
877
+ },
878
+ removeServer(rawContent, serverName) {
879
+ let parsed;
880
+ try {
881
+ parsed = JSON.parse(rawContent);
882
+ }
883
+ catch (err) {
884
+ throw new Error(`Malformed VS Code mcp.json: ${err instanceof Error ? err.message : String(err)}`);
885
+ }
886
+ if (!isPlainObject(parsed)) {
887
+ throw new Error("Invalid VS Code mcp.json schema: root value must be a JSON object");
888
+ }
889
+ if (parsed.servers !== undefined && !isPlainObject(parsed.servers)) {
890
+ throw new Error("Invalid VS Code mcp.json schema: 'servers' must be a JSON object");
891
+ }
892
+ if (!isPlainObject(parsed.servers) || !(serverName in parsed.servers)) {
893
+ return { content: rawContent, removed: false };
894
+ }
895
+ const nextServers = { ...parsed.servers };
896
+ delete nextServers[serverName];
897
+ const nextRoot = { ...parsed, servers: nextServers };
898
+ return {
899
+ content: JSON.stringify(nextRoot, null, 2) + "\n",
900
+ removed: true,
901
+ };
902
+ },
903
+ };
904
+ }
905
+ function createJsonOpencodeAdapter() {
906
+ return {
907
+ format: "json-opencode",
908
+ readServer(rawContent, serverName = BLA_IDENTITY.mcpServerName) {
909
+ let parsed;
910
+ try {
911
+ parsed = JSON.parse(rawContent);
912
+ }
913
+ catch (err) {
914
+ return {
915
+ status: "malformed",
916
+ entry: null,
917
+ error: `Malformed OpenCode configuration: ${err instanceof Error ? err.message : String(err)}`,
918
+ otherServersCount: 0,
919
+ };
920
+ }
921
+ if (!isPlainObject(parsed)) {
922
+ return {
923
+ status: "malformed",
924
+ entry: null,
925
+ error: "Invalid OpenCode configuration schema: root value must be a JSON object",
926
+ otherServersCount: 0,
927
+ };
928
+ }
929
+ if (parsed.mcp === undefined) {
930
+ if ("mcpServers" in parsed) {
931
+ return {
932
+ status: "malformed",
933
+ entry: null,
934
+ error: "Invalid OpenCode configuration schema: expected root key 'mcp', found 'mcpServers'",
935
+ otherServersCount: 0,
936
+ };
937
+ }
938
+ return {
939
+ status: "not_configured",
940
+ entry: null,
941
+ otherServersCount: 0,
942
+ };
943
+ }
944
+ if (!isPlainObject(parsed.mcp)) {
945
+ return {
946
+ status: "malformed",
947
+ entry: null,
948
+ error: "Invalid OpenCode configuration schema: 'mcp' must be a JSON object",
949
+ otherServersCount: 0,
950
+ };
951
+ }
952
+ const mcp = parsed.mcp;
953
+ const otherServersCount = Object.keys(mcp).filter((k) => k !== serverName).length;
954
+ if (!(serverName in mcp)) {
955
+ return {
956
+ status: "not_configured",
957
+ entry: null,
958
+ otherServersCount,
959
+ };
960
+ }
961
+ const rawEntry = mcp[serverName];
962
+ if (!isPlainObject(rawEntry)) {
963
+ return {
964
+ status: "invalid_entry",
965
+ entry: null,
966
+ rawEntry,
967
+ error: `Invalid OpenCode MCP entry '${serverName}': must be a JSON object`,
968
+ otherServersCount,
969
+ };
970
+ }
971
+ if (rawEntry.type !== "local") {
972
+ return {
973
+ status: "invalid_entry",
974
+ entry: null,
975
+ rawEntry,
976
+ error: `Invalid OpenCode MCP entry '${serverName}': 'type' must be 'local'`,
977
+ otherServersCount,
978
+ };
979
+ }
980
+ if (!Array.isArray(rawEntry.command) ||
981
+ rawEntry.command.length === 0 ||
982
+ !rawEntry.command.every((c) => typeof c === "string" && c.length > 0) ||
983
+ rawEntry.command[0].trim().length === 0) {
984
+ return {
985
+ status: "invalid_entry",
986
+ entry: null,
987
+ rawEntry,
988
+ error: `Invalid OpenCode MCP entry '${serverName}': 'command' must be a non-empty string array`,
989
+ otherServersCount,
990
+ };
991
+ }
992
+ if (rawEntry.enabled !== undefined && typeof rawEntry.enabled !== "boolean") {
993
+ return {
994
+ status: "invalid_entry",
995
+ entry: null,
996
+ rawEntry,
997
+ error: `Invalid OpenCode MCP entry '${serverName}': 'enabled' must be a boolean`,
998
+ otherServersCount,
999
+ };
1000
+ }
1001
+ if (rawEntry.enabled === false) {
1002
+ return {
1003
+ status: "invalid_entry",
1004
+ entry: null,
1005
+ rawEntry,
1006
+ error: `OpenCode MCP entry '${serverName}' is disabled (enabled: false)`,
1007
+ otherServersCount,
1008
+ };
1009
+ }
1010
+ const cmdArray = rawEntry.command;
1011
+ return {
1012
+ status: "valid",
1013
+ entry: {
1014
+ type: "local",
1015
+ command: cmdArray[0],
1016
+ args: cmdArray.slice(1),
1017
+ enabled: rawEntry.enabled ?? true,
1018
+ },
1019
+ rawEntry,
1020
+ otherServersCount,
1021
+ };
1022
+ },
1023
+ upsertServer(rawContent, serverName, reg) {
1024
+ let root = {};
1025
+ if (rawContent !== null && rawContent.trim().length > 0) {
1026
+ let parsed;
1027
+ try {
1028
+ parsed = JSON.parse(rawContent);
1029
+ }
1030
+ catch (err) {
1031
+ throw new Error(`Malformed OpenCode configuration: ${err instanceof Error ? err.message : String(err)}`);
1032
+ }
1033
+ if (!isPlainObject(parsed)) {
1034
+ throw new Error("Invalid OpenCode configuration schema: root value must be a JSON object");
1035
+ }
1036
+ if (parsed.mcp !== undefined && !isPlainObject(parsed.mcp)) {
1037
+ throw new Error("Invalid OpenCode configuration schema: 'mcp' must be a JSON object");
1038
+ }
1039
+ root = { ...parsed };
1040
+ }
1041
+ const existingMcp = isPlainObject(root.mcp) ? { ...root.mcp } : {};
1042
+ existingMcp[serverName] = {
1043
+ type: "local",
1044
+ command: [reg.command, ...reg.args],
1045
+ enabled: reg.enabled ?? true,
1046
+ };
1047
+ root.mcp = existingMcp;
1048
+ return JSON.stringify(root, null, 2) + "\n";
1049
+ },
1050
+ removeServer(rawContent, serverName) {
1051
+ let parsed;
1052
+ try {
1053
+ parsed = JSON.parse(rawContent);
1054
+ }
1055
+ catch (err) {
1056
+ throw new Error(`Malformed OpenCode configuration: ${err instanceof Error ? err.message : String(err)}`);
1057
+ }
1058
+ if (!isPlainObject(parsed)) {
1059
+ throw new Error("Invalid OpenCode configuration schema: root value must be a JSON object");
1060
+ }
1061
+ if (parsed.mcp !== undefined && !isPlainObject(parsed.mcp)) {
1062
+ throw new Error("Invalid OpenCode configuration schema: 'mcp' must be a JSON object");
1063
+ }
1064
+ if (!isPlainObject(parsed.mcp) || !(serverName in parsed.mcp)) {
1065
+ return { content: rawContent, removed: false };
1066
+ }
1067
+ const nextMcp = { ...parsed.mcp };
1068
+ delete nextMcp[serverName];
1069
+ const nextRoot = { ...parsed, mcp: nextMcp };
1070
+ return {
1071
+ content: JSON.stringify(nextRoot, null, 2) + "\n",
1072
+ removed: true,
1073
+ };
1074
+ },
1075
+ };
1076
+ }
1077
+ function createTomlCodexAdapter() {
1078
+ return {
1079
+ format: "toml-codex",
1080
+ readServer(rawContent, serverName = BLA_IDENTITY.mcpServerName) {
1081
+ return readCodexTomlMcp(rawContent, serverName);
1082
+ },
1083
+ upsertServer(rawContent, serverName, reg) {
1084
+ return writeCodexTomlMcp(rawContent, serverName, reg);
1085
+ },
1086
+ removeServer(rawContent, serverName) {
1087
+ return removeCodexTomlMcp(rawContent, serverName);
1088
+ },
1089
+ };
1090
+ }
1091
+ const ADAPTERS = Object.freeze({
1092
+ "json-mcpServers": createJsonMcpServersAdapter(),
1093
+ "json-vscode": createJsonVscodeAdapter(),
1094
+ "json-opencode": createJsonOpencodeAdapter(),
1095
+ "toml-codex": createTomlCodexAdapter(),
1096
+ });
1097
+ const CLIENT_FORMAT_MAP = Object.freeze({
1098
+ claude_code: "json-mcpServers",
1099
+ claude_desktop: "json-mcpServers",
1100
+ cursor: "json-mcpServers",
1101
+ codex: "toml-codex",
1102
+ gemini_cli: "json-mcpServers",
1103
+ antigravity: "json-mcpServers",
1104
+ windsurf: "json-mcpServers",
1105
+ vscode: "json-vscode",
1106
+ opencode: "json-opencode",
1107
+ copilot_cli: "json-mcpServers",
1108
+ });
1109
+ export function getClientConfigAdapter(clientOrFormat) {
1110
+ if (typeof clientOrFormat === "object" && clientOrFormat !== null && "format" in clientOrFormat) {
1111
+ return ADAPTERS[clientOrFormat.format];
1112
+ }
1113
+ if (typeof clientOrFormat === "string") {
1114
+ if (clientOrFormat in ADAPTERS) {
1115
+ return ADAPTERS[clientOrFormat];
1116
+ }
1117
+ const normalized = normalizeClientId(clientOrFormat);
1118
+ if (normalized) {
1119
+ return ADAPTERS[CLIENT_FORMAT_MAP[normalized]];
1120
+ }
1121
+ }
1122
+ return ADAPTERS["json-mcpServers"];
1123
+ }
1124
+ export function readClientMcpConfig(configPath, format, serverName = BLA_IDENTITY.mcpServerName) {
1125
+ if (!existsSync(configPath)) {
1126
+ return {
1127
+ status: "missing",
1128
+ entry: null,
1129
+ otherServersCount: 0,
1130
+ };
1131
+ }
1132
+ let rawContent;
1133
+ try {
1134
+ rawContent = readFileSync(configPath, "utf8");
1135
+ }
1136
+ catch (err) {
1137
+ return {
1138
+ status: "malformed",
1139
+ entry: null,
1140
+ error: `Failed to read configuration file '${configPath}': ${err instanceof Error ? err.message : String(err)}`,
1141
+ otherServersCount: 0,
1142
+ };
1143
+ }
1144
+ return getClientConfigAdapter(format).readServer(rawContent, serverName);
1145
+ }
1146
+ export function resolveSupportedAgentClients(home = homedir(), platform = process.platform, env) {
1147
+ const winAppData = env?.APPDATA && env.APPDATA.trim().length > 0
1148
+ ? env.APPDATA
1149
+ : join(home, "AppData", "Roaming");
1150
+ const xdgConfigHome = env?.XDG_CONFIG_HOME && env.XDG_CONFIG_HOME.trim().length > 0
1151
+ ? env.XDG_CONFIG_HOME
1152
+ : join(home, ".config");
5
1153
  const claudeDesktopDir = platform === "darwin"
6
1154
  ? join(home, "Library", "Application Support", "Claude")
7
1155
  : platform === "win32"
8
- ? join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "Claude")
9
- : join(home, ".config", "Claude");
1156
+ ? join(winAppData, "Claude")
1157
+ : join(xdgConfigHome, "Claude");
10
1158
  const vscodeDir = platform === "darwin"
11
1159
  ? join(home, "Library", "Application Support", "Code", "User")
12
1160
  : platform === "win32"
13
- ? join(process.env.APPDATA ?? join(home, "AppData", "Roaming"), "Code", "User")
14
- : join(home, ".config", "Code", "User");
1161
+ ? join(winAppData, "Code", "User")
1162
+ : join(xdgConfigHome, "Code", "User");
1163
+ const opencodeDir = join(xdgConfigHome, "opencode");
15
1164
  const rawDefs = [
16
1165
  {
17
1166
  id: "claude_code",
18
1167
  displayName: "Claude Code",
19
1168
  configPath: join(home, ".claude.json"),
20
- markerPath: join(home, ".claude"),
1169
+ format: "json-mcpServers",
1170
+ // Never probe `dirname(configPath)` because for `~/.claude.json` that is `home` itself!
1171
+ probePaths: [join(home, ".claude.json"), join(home, ".claude")],
21
1172
  skillDirectory: join(home, ".claude", "skills", "blind-llm-agents"),
22
1173
  },
23
1174
  {
24
1175
  id: "claude_desktop",
25
1176
  displayName: "Claude Desktop",
26
1177
  configPath: join(claudeDesktopDir, "claude_desktop_config.json"),
27
- markerPath: claudeDesktopDir,
1178
+ format: "json-mcpServers",
1179
+ probePaths: [join(claudeDesktopDir, "claude_desktop_config.json"), claudeDesktopDir],
28
1180
  skillDirectory: join(home, ".agents", "skills", "blind-llm-agents"),
29
1181
  },
30
1182
  {
31
1183
  id: "cursor",
32
1184
  displayName: "Cursor",
33
1185
  configPath: join(home, ".cursor", "mcp.json"),
34
- markerPath: join(home, ".cursor"),
1186
+ format: "json-mcpServers",
1187
+ probePaths: [join(home, ".cursor", "mcp.json"), join(home, ".cursor")],
35
1188
  skillDirectory: join(home, ".cursor", "skills", "blind-llm-agents"),
36
1189
  },
37
1190
  {
38
1191
  id: "codex",
39
1192
  displayName: "Codex CLI",
40
- configPath: join(home, ".codex", "mcp.json"),
41
- markerPath: join(home, ".codex"),
1193
+ configPath: join(home, ".codex", "config.toml"),
1194
+ format: "toml-codex",
1195
+ probePaths: [join(home, ".codex", "config.toml"), join(home, ".codex")],
42
1196
  skillDirectory: join(home, ".codex", "skills", "blind-llm-agents"),
43
1197
  },
44
1198
  {
45
1199
  id: "gemini_cli",
46
1200
  displayName: "Gemini CLI",
47
1201
  configPath: join(home, ".gemini", "settings.json"),
48
- markerPath: join(home, ".gemini"),
1202
+ format: "json-mcpServers",
1203
+ probePaths: [join(home, ".gemini", "settings.json"), join(home, ".gemini")],
49
1204
  skillDirectory: join(home, ".gemini", "skills", "blind-llm-agents"),
50
1205
  },
51
1206
  {
52
1207
  id: "antigravity",
53
1208
  displayName: "Google Antigravity",
54
1209
  configPath: join(home, ".gemini", "antigravity", "mcp_config.json"),
55
- markerPath: join(home, ".gemini", "antigravity"),
1210
+ format: "json-mcpServers",
1211
+ probePaths: [
1212
+ join(home, ".gemini", "antigravity", "mcp_config.json"),
1213
+ join(home, ".gemini", "antigravity"),
1214
+ ],
56
1215
  skillDirectory: join(home, ".gemini", "antigravity", "skills", "blind-llm-agents"),
57
1216
  },
58
1217
  {
59
1218
  id: "windsurf",
60
1219
  displayName: "Windsurf",
61
1220
  configPath: join(home, ".codeium", "windsurf", "mcp_config.json"),
62
- markerPath: join(home, ".codeium", "windsurf"),
1221
+ format: "json-mcpServers",
1222
+ probePaths: [
1223
+ join(home, ".codeium", "windsurf", "mcp_config.json"),
1224
+ join(home, ".codeium", "windsurf"),
1225
+ ],
63
1226
  skillDirectory: join(home, ".agents", "skills", "blind-llm-agents"),
64
1227
  },
65
1228
  {
66
1229
  id: "vscode",
67
1230
  displayName: "VS Code",
68
1231
  configPath: join(vscodeDir, "mcp.json"),
69
- markerPath: vscodeDir,
1232
+ format: "json-vscode",
1233
+ probePaths: [join(vscodeDir, "mcp.json"), vscodeDir],
70
1234
  skillDirectory: join(home, ".agents", "skills", "blind-llm-agents"),
71
1235
  },
72
1236
  {
73
1237
  id: "opencode",
74
1238
  displayName: "OpenCode",
75
- configPath: join(home, ".config", "opencode", "opencode.json"),
76
- markerPath: join(home, ".config", "opencode"),
1239
+ configPath: join(opencodeDir, "opencode.json"),
1240
+ format: "json-opencode",
1241
+ probePaths: [join(opencodeDir, "opencode.json"), opencodeDir],
77
1242
  skillDirectory: join(home, ".agents", "skills", "blind-llm-agents"),
78
1243
  },
79
1244
  {
80
1245
  id: "copilot_cli",
81
1246
  displayName: "GitHub Copilot CLI",
82
1247
  configPath: join(home, ".copilot", "mcp-config.json"),
83
- markerPath: join(home, ".copilot"),
1248
+ format: "json-mcpServers",
1249
+ probePaths: [join(home, ".copilot", "mcp-config.json"), join(home, ".copilot")],
84
1250
  skillDirectory: join(home, ".agents", "skills", "blind-llm-agents"),
85
1251
  },
86
1252
  ];
@@ -89,7 +1255,14 @@ export function resolveSupportedAgentClients(home = homedir(), platform = proces
89
1255
  displayName: def.displayName,
90
1256
  configPath: def.configPath,
91
1257
  skillDirectory: def.skillDirectory,
92
- detected: existsSync(def.markerPath) || existsSync(def.configPath) || existsSync(dirname(def.configPath)),
1258
+ format: def.format,
1259
+ probePaths: def.probePaths,
1260
+ detected: def.probePaths.some((p) => existsSync(p)),
93
1261
  }));
94
1262
  }
1263
+ export function detectInstalledClients(home = homedir(), platform = process.platform, env) {
1264
+ return resolveSupportedAgentClients(home, platform, env)
1265
+ .filter((c) => c.detected)
1266
+ .map((c) => c.id);
1267
+ }
95
1268
  //# sourceMappingURL=clients.js.map