praxis-agent 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +8 -0
  2. package/dist/application/background-agent-manager.d.ts +5 -0
  3. package/dist/application/background-agent-manager.js +21 -0
  4. package/dist/application/background-bash-manager.d.ts +14 -0
  5. package/dist/application/background-bash-manager.js +42 -4
  6. package/dist/application/background-task-runtime.d.ts +34 -0
  7. package/dist/application/background-task-runtime.js +59 -0
  8. package/dist/application/session-service.d.ts +21 -1
  9. package/dist/application/session-service.js +188 -17
  10. package/dist/application/session-worktree.d.ts +1 -0
  11. package/dist/application/session-worktree.js +13 -2
  12. package/dist/application/subagent-service.d.ts +3 -0
  13. package/dist/application/subagent-service.js +6 -0
  14. package/dist/application/workflow-manager.d.ts +70 -2
  15. package/dist/application/workflow-manager.js +21 -4
  16. package/dist/cli/agents-dashboard.d.ts +9 -0
  17. package/dist/cli/agents-dashboard.js +16 -5
  18. package/dist/cli/interactive.d.ts +41 -3
  19. package/dist/cli/interactive.js +1281 -183
  20. package/dist/cli/tui/claude-style.d.ts +31 -3
  21. package/dist/cli/tui/claude-style.js +171 -54
  22. package/dist/cli/tui/config-dashboard.d.ts +63 -0
  23. package/dist/cli/tui/config-dashboard.js +150 -0
  24. package/dist/cli/tui/config-settings.d.ts +32 -0
  25. package/dist/cli/tui/config-settings.js +276 -0
  26. package/dist/cli/tui/custom-themes.d.ts +21 -0
  27. package/dist/cli/tui/custom-themes.js +284 -0
  28. package/dist/cli/tui/file-picker.d.ts +3 -1
  29. package/dist/cli/tui/file-picker.js +8 -2
  30. package/dist/cli/tui/keybindings.js +0 -1
  31. package/dist/cli/tui/mcp-panel-controller.d.ts +32 -0
  32. package/dist/cli/tui/mcp-panel-controller.js +80 -0
  33. package/dist/cli/tui/mcp-panel-projector.d.ts +79 -0
  34. package/dist/cli/tui/mcp-panel-projector.js +181 -0
  35. package/dist/cli/tui/mcp-panel.d.ts +8 -0
  36. package/dist/cli/tui/mcp-panel.js +99 -0
  37. package/dist/cli/tui/recently-denied.d.ts +15 -0
  38. package/dist/cli/tui/recently-denied.js +19 -0
  39. package/dist/cli/tui/runtime-interactions.d.ts +17 -0
  40. package/dist/cli/tui/runtime-interactions.js +100 -0
  41. package/dist/cli/tui/runtime-settings.d.ts +40 -0
  42. package/dist/cli/tui/runtime-settings.js +84 -0
  43. package/dist/cli/tui/slash-commands.js +10 -0
  44. package/dist/cli/tui/task-panel.d.ts +53 -0
  45. package/dist/cli/tui/task-panel.js +204 -0
  46. package/dist/cli/tui/terminal-notifications.d.ts +10 -0
  47. package/dist/cli/tui/terminal-notifications.js +48 -0
  48. package/dist/cli/tui/terminal-setup.d.ts +24 -0
  49. package/dist/cli/tui/terminal-setup.js +618 -0
  50. package/dist/cli/tui/theme-settings.d.ts +8 -0
  51. package/dist/cli/tui/theme-settings.js +132 -0
  52. package/dist/cli/tui/theme.d.ts +53 -0
  53. package/dist/cli/tui/theme.js +265 -0
  54. package/dist/cli.d.ts +9 -0
  55. package/dist/cli.js +97 -17
  56. package/dist/compatibility/claude/schema.js +2 -0
  57. package/dist/core/runtime.d.ts +12 -0
  58. package/dist/core/runtime.js +24 -0
  59. package/dist/maintenance/self-update.js +1 -1
  60. package/dist/mcp/claude-mcp-management.d.ts +6 -1
  61. package/dist/mcp/claude-mcp-management.js +191 -44
  62. package/dist/mcp/claude-mcp-oauth.js +2 -0
  63. package/dist/mcp/claude-mcp-tools.d.ts +31 -3
  64. package/dist/mcp/claude-mcp-tools.js +116 -34
  65. package/dist/permissions/claude-permission-resolver.d.ts +3 -0
  66. package/dist/permissions/claude-permission-resolver.js +42 -20
  67. package/dist/platform/claude-version.js +2 -1
  68. package/dist/plugins/claude-plugin-runtime.js +11 -3
  69. package/dist/tools/claude-interactive-tools.d.ts +7 -0
  70. package/dist/tools/claude-interactive-tools.js +21 -8
  71. package/dist/tools/claude-task-tools.d.ts +4 -0
  72. package/dist/tools/claude-task-tools.js +7 -0
  73. package/package.json +2 -1
  74. package/dist/application/background-agent-manager.d.ts.map +0 -1
  75. package/dist/application/background-agent-manager.js.map +0 -1
  76. package/dist/application/background-bash-manager.d.ts.map +0 -1
  77. package/dist/application/background-bash-manager.js.map +0 -1
  78. package/dist/application/background-task-id.d.ts.map +0 -1
  79. package/dist/application/background-task-id.js.map +0 -1
  80. package/dist/application/claude-project-purge.d.ts.map +0 -1
  81. package/dist/application/claude-project-purge.js.map +0 -1
  82. package/dist/application/managed-worktree.d.ts.map +0 -1
  83. package/dist/application/managed-worktree.js.map +0 -1
  84. package/dist/application/model-compactor.d.ts.map +0 -1
  85. package/dist/application/model-compactor.js.map +0 -1
  86. package/dist/application/scheduled-prompt-manager.d.ts.map +0 -1
  87. package/dist/application/scheduled-prompt-manager.js.map +0 -1
  88. package/dist/application/session-service.d.ts.map +0 -1
  89. package/dist/application/session-service.js.map +0 -1
  90. package/dist/application/session-worktree.d.ts.map +0 -1
  91. package/dist/application/session-worktree.js.map +0 -1
  92. package/dist/application/subagent-service.d.ts.map +0 -1
  93. package/dist/application/subagent-service.js.map +0 -1
  94. package/dist/application/tool-use-summary.d.ts.map +0 -1
  95. package/dist/application/tool-use-summary.js.map +0 -1
  96. package/dist/application/top-level-agent-manager.d.ts.map +0 -1
  97. package/dist/application/top-level-agent-manager.js.map +0 -1
  98. package/dist/application/workflow-manager.d.ts.map +0 -1
  99. package/dist/application/workflow-manager.js.map +0 -1
  100. package/dist/application/workflow-meta.d.ts.map +0 -1
  101. package/dist/application/workflow-meta.js.map +0 -1
  102. package/dist/application/workflow-runtime.d.ts.map +0 -1
  103. package/dist/application/workflow-runtime.js.map +0 -1
  104. package/dist/application/workflow-worktree.d.ts.map +0 -1
  105. package/dist/application/workflow-worktree.js.map +0 -1
  106. package/dist/cli/agents-dashboard.d.ts.map +0 -1
  107. package/dist/cli/agents-dashboard.js.map +0 -1
  108. package/dist/cli/controls.d.ts.map +0 -1
  109. package/dist/cli/controls.js.map +0 -1
  110. package/dist/cli/debug.d.ts.map +0 -1
  111. package/dist/cli/debug.js.map +0 -1
  112. package/dist/cli/interactive.d.ts.map +0 -1
  113. package/dist/cli/interactive.js.map +0 -1
  114. package/dist/cli/protocol.d.ts.map +0 -1
  115. package/dist/cli/protocol.js.map +0 -1
  116. package/dist/cli/tui/claude-style.d.ts.map +0 -1
  117. package/dist/cli/tui/claude-style.js.map +0 -1
  118. package/dist/cli/tui/clipboard.d.ts.map +0 -1
  119. package/dist/cli/tui/clipboard.js.map +0 -1
  120. package/dist/cli/tui/composer-editor.d.ts.map +0 -1
  121. package/dist/cli/tui/composer-editor.js.map +0 -1
  122. package/dist/cli/tui/composer-images.d.ts.map +0 -1
  123. package/dist/cli/tui/composer-images.js.map +0 -1
  124. package/dist/cli/tui/conversation-export.d.ts.map +0 -1
  125. package/dist/cli/tui/conversation-export.js.map +0 -1
  126. package/dist/cli/tui/external-editor.d.ts.map +0 -1
  127. package/dist/cli/tui/external-editor.js.map +0 -1
  128. package/dist/cli/tui/file-picker.d.ts.map +0 -1
  129. package/dist/cli/tui/file-picker.js.map +0 -1
  130. package/dist/cli/tui/git-diff.d.ts.map +0 -1
  131. package/dist/cli/tui/git-diff.js.map +0 -1
  132. package/dist/cli/tui/hook-settings.d.ts.map +0 -1
  133. package/dist/cli/tui/hook-settings.js.map +0 -1
  134. package/dist/cli/tui/keybindings.d.ts.map +0 -1
  135. package/dist/cli/tui/keybindings.js.map +0 -1
  136. package/dist/cli/tui/memory-files.d.ts.map +0 -1
  137. package/dist/cli/tui/memory-files.js.map +0 -1
  138. package/dist/cli/tui/permission-settings.d.ts.map +0 -1
  139. package/dist/cli/tui/permission-settings.js.map +0 -1
  140. package/dist/cli/tui/slash-commands.d.ts.map +0 -1
  141. package/dist/cli/tui/slash-commands.js.map +0 -1
  142. package/dist/cli/tui/terminal-suspend.d.ts.map +0 -1
  143. package/dist/cli/tui/terminal-suspend.js.map +0 -1
  144. package/dist/cli/tui/workspace-directories.d.ts.map +0 -1
  145. package/dist/cli/tui/workspace-directories.js.map +0 -1
  146. package/dist/cli.d.ts.map +0 -1
  147. package/dist/cli.js.map +0 -1
  148. package/dist/compatibility/claude/compaction.d.ts.map +0 -1
  149. package/dist/compatibility/claude/compaction.js.map +0 -1
  150. package/dist/compatibility/claude/context.d.ts.map +0 -1
  151. package/dist/compatibility/claude/context.js.map +0 -1
  152. package/dist/compatibility/claude/dynamic-context.d.ts.map +0 -1
  153. package/dist/compatibility/claude/dynamic-context.js.map +0 -1
  154. package/dist/compatibility/claude/file-history.d.ts.map +0 -1
  155. package/dist/compatibility/claude/file-history.js.map +0 -1
  156. package/dist/compatibility/claude/file-resources.d.ts.map +0 -1
  157. package/dist/compatibility/claude/file-resources.js.map +0 -1
  158. package/dist/compatibility/claude/fork.d.ts.map +0 -1
  159. package/dist/compatibility/claude/fork.js.map +0 -1
  160. package/dist/compatibility/claude/history.d.ts.map +0 -1
  161. package/dist/compatibility/claude/history.js.map +0 -1
  162. package/dist/compatibility/claude/ownership.d.ts.map +0 -1
  163. package/dist/compatibility/claude/ownership.js.map +0 -1
  164. package/dist/compatibility/claude/paths.d.ts.map +0 -1
  165. package/dist/compatibility/claude/paths.js.map +0 -1
  166. package/dist/compatibility/claude/pr-links.d.ts.map +0 -1
  167. package/dist/compatibility/claude/pr-links.js.map +0 -1
  168. package/dist/compatibility/claude/projection.d.ts.map +0 -1
  169. package/dist/compatibility/claude/projection.js.map +0 -1
  170. package/dist/compatibility/claude/schema.d.ts.map +0 -1
  171. package/dist/compatibility/claude/schema.js.map +0 -1
  172. package/dist/compatibility/claude/shared-resources.d.ts.map +0 -1
  173. package/dist/compatibility/claude/shared-resources.js.map +0 -1
  174. package/dist/compatibility/claude/sidechain.d.ts.map +0 -1
  175. package/dist/compatibility/claude/sidechain.js.map +0 -1
  176. package/dist/compatibility/claude/tool-links.d.ts.map +0 -1
  177. package/dist/compatibility/claude/tool-links.js.map +0 -1
  178. package/dist/compatibility/claude/translation.d.ts.map +0 -1
  179. package/dist/compatibility/claude/translation.js.map +0 -1
  180. package/dist/compatibility/claude/workflow-replay.d.ts.map +0 -1
  181. package/dist/compatibility/claude/workflow-replay.js.map +0 -1
  182. package/dist/compatibility/claude/workflow.d.ts.map +0 -1
  183. package/dist/compatibility/claude/workflow.js.map +0 -1
  184. package/dist/core/compaction.d.ts.map +0 -1
  185. package/dist/core/compaction.js.map +0 -1
  186. package/dist/core/context-budget.d.ts.map +0 -1
  187. package/dist/core/context-budget.js.map +0 -1
  188. package/dist/core/context.d.ts.map +0 -1
  189. package/dist/core/context.js.map +0 -1
  190. package/dist/core/runtime.d.ts.map +0 -1
  191. package/dist/core/runtime.js.map +0 -1
  192. package/dist/core/usage.d.ts.map +0 -1
  193. package/dist/core/usage.js.map +0 -1
  194. package/dist/extensions/claude-extension-tools.d.ts.map +0 -1
  195. package/dist/extensions/claude-extension-tools.js.map +0 -1
  196. package/dist/extensions/claude-extensions.d.ts.map +0 -1
  197. package/dist/extensions/claude-extensions.js.map +0 -1
  198. package/dist/hooks/claude-hook-tools.d.ts.map +0 -1
  199. package/dist/hooks/claude-hook-tools.js.map +0 -1
  200. package/dist/hooks/claude-hooks.d.ts.map +0 -1
  201. package/dist/hooks/claude-hooks.js.map +0 -1
  202. package/dist/maintenance/doctor.d.ts.map +0 -1
  203. package/dist/maintenance/doctor.js.map +0 -1
  204. package/dist/maintenance/self-update.d.ts.map +0 -1
  205. package/dist/maintenance/self-update.js.map +0 -1
  206. package/dist/mcp/claude-mcp-management.d.ts.map +0 -1
  207. package/dist/mcp/claude-mcp-management.js.map +0 -1
  208. package/dist/mcp/claude-mcp-oauth.d.ts.map +0 -1
  209. package/dist/mcp/claude-mcp-oauth.js.map +0 -1
  210. package/dist/mcp/claude-mcp-tools.d.ts.map +0 -1
  211. package/dist/mcp/claude-mcp-tools.js.map +0 -1
  212. package/dist/mcp/praxis-mcp-server.d.ts.map +0 -1
  213. package/dist/mcp/praxis-mcp-server.js.map +0 -1
  214. package/dist/permissions/claude-auto-classifier.d.ts.map +0 -1
  215. package/dist/permissions/claude-auto-classifier.js.map +0 -1
  216. package/dist/permissions/claude-permission-resolver.d.ts.map +0 -1
  217. package/dist/permissions/claude-permission-resolver.js.map +0 -1
  218. package/dist/persistence/claude-job-store.d.ts.map +0 -1
  219. package/dist/persistence/claude-job-store.js.map +0 -1
  220. package/dist/persistence/claude-scheduled-task-store.d.ts.map +0 -1
  221. package/dist/persistence/claude-scheduled-task-store.js.map +0 -1
  222. package/dist/persistence/claude-sidechain-store.d.ts.map +0 -1
  223. package/dist/persistence/claude-sidechain-store.js.map +0 -1
  224. package/dist/persistence/claude-task-store.d.ts.map +0 -1
  225. package/dist/persistence/claude-task-store.js.map +0 -1
  226. package/dist/persistence/claude-transcript-store.d.ts.map +0 -1
  227. package/dist/persistence/claude-transcript-store.js.map +0 -1
  228. package/dist/persistence/claude-workflow-store.d.ts.map +0 -1
  229. package/dist/persistence/claude-workflow-store.js.map +0 -1
  230. package/dist/persistence/in-memory-sidechain-store.d.ts.map +0 -1
  231. package/dist/persistence/in-memory-sidechain-store.js.map +0 -1
  232. package/dist/persistence/in-memory-transcript-store.d.ts.map +0 -1
  233. package/dist/persistence/in-memory-transcript-store.js.map +0 -1
  234. package/dist/platform/atomic-write.d.ts.map +0 -1
  235. package/dist/platform/atomic-write.js.map +0 -1
  236. package/dist/platform/bounded-process-runner.d.ts.map +0 -1
  237. package/dist/platform/bounded-process-runner.js.map +0 -1
  238. package/dist/platform/claude-version.d.ts.map +0 -1
  239. package/dist/platform/claude-version.js.map +0 -1
  240. package/dist/platform/command-shell.d.ts.map +0 -1
  241. package/dist/platform/command-shell.js.map +0 -1
  242. package/dist/platform/exclusive-file-lease.d.ts.map +0 -1
  243. package/dist/platform/exclusive-file-lease.js.map +0 -1
  244. package/dist/platform/sensitive-data.d.ts.map +0 -1
  245. package/dist/platform/sensitive-data.js.map +0 -1
  246. package/dist/platform/tmux-worktree.d.ts.map +0 -1
  247. package/dist/platform/tmux-worktree.js.map +0 -1
  248. package/dist/plugins/claude-plugin-eval-discovery.d.ts.map +0 -1
  249. package/dist/plugins/claude-plugin-eval-discovery.js.map +0 -1
  250. package/dist/plugins/claude-plugin-eval-graders.d.ts.map +0 -1
  251. package/dist/plugins/claude-plugin-eval-graders.js.map +0 -1
  252. package/dist/plugins/claude-plugin-eval-runner.d.ts.map +0 -1
  253. package/dist/plugins/claude-plugin-eval-runner.js.map +0 -1
  254. package/dist/plugins/claude-plugin-eval-schema.d.ts.map +0 -1
  255. package/dist/plugins/claude-plugin-eval-schema.js.map +0 -1
  256. package/dist/plugins/claude-plugin-eval.d.ts.map +0 -1
  257. package/dist/plugins/claude-plugin-eval.js.map +0 -1
  258. package/dist/plugins/claude-plugin-maintenance.d.ts.map +0 -1
  259. package/dist/plugins/claude-plugin-maintenance.js.map +0 -1
  260. package/dist/plugins/claude-plugin-marketplace.d.ts.map +0 -1
  261. package/dist/plugins/claude-plugin-marketplace.js.map +0 -1
  262. package/dist/plugins/claude-plugin-mcpb.d.ts.map +0 -1
  263. package/dist/plugins/claude-plugin-mcpb.js.map +0 -1
  264. package/dist/plugins/claude-plugin-runtime.d.ts.map +0 -1
  265. package/dist/plugins/claude-plugin-runtime.js.map +0 -1
  266. package/dist/providers/anthropic-compatible.d.ts.map +0 -1
  267. package/dist/providers/anthropic-compatible.js.map +0 -1
  268. package/dist/providers/environment.d.ts.map +0 -1
  269. package/dist/providers/environment.js.map +0 -1
  270. package/dist/providers/fallback-provider.d.ts.map +0 -1
  271. package/dist/providers/fallback-provider.js.map +0 -1
  272. package/dist/providers/openai-compatible.d.ts.map +0 -1
  273. package/dist/providers/openai-compatible.js.map +0 -1
  274. package/dist/tools/claude-interactive-tools.d.ts.map +0 -1
  275. package/dist/tools/claude-interactive-tools.js.map +0 -1
  276. package/dist/tools/claude-lsp-formatters.d.ts.map +0 -1
  277. package/dist/tools/claude-lsp-formatters.js.map +0 -1
  278. package/dist/tools/claude-lsp-tool.d.ts.map +0 -1
  279. package/dist/tools/claude-lsp-tool.js.map +0 -1
  280. package/dist/tools/claude-scheduled-tools.d.ts.map +0 -1
  281. package/dist/tools/claude-scheduled-tools.js.map +0 -1
  282. package/dist/tools/claude-task-tools.d.ts.map +0 -1
  283. package/dist/tools/claude-task-tools.js.map +0 -1
  284. package/dist/tools/claude-user-message.d.ts.map +0 -1
  285. package/dist/tools/claude-user-message.js.map +0 -1
  286. package/dist/tools/claude-workflow-tools.d.ts.map +0 -1
  287. package/dist/tools/claude-workflow-tools.js.map +0 -1
  288. package/dist/tools/claude-worktree-tools.d.ts.map +0 -1
  289. package/dist/tools/claude-worktree-tools.js.map +0 -1
  290. package/dist/tools/filtered-tool-registry.d.ts.map +0 -1
  291. package/dist/tools/filtered-tool-registry.js.map +0 -1
  292. package/dist/tools/glob.d.ts.map +0 -1
  293. package/dist/tools/glob.js.map +0 -1
  294. package/dist/tools/local-tools.d.ts.map +0 -1
  295. package/dist/tools/local-tools.js.map +0 -1
  296. package/dist/tools/notebook.d.ts.map +0 -1
  297. package/dist/tools/notebook.js.map +0 -1
  298. package/dist/tools/pdf.d.ts.map +0 -1
  299. package/dist/tools/pdf.js.map +0 -1
  300. package/dist/tools/web.d.ts.map +0 -1
  301. package/dist/tools/web.js.map +0 -1
@@ -1,31 +1,92 @@
1
- import { readFile } from 'node:fs/promises';
1
+ import { constants } from 'node:fs';
2
+ import { mkdir, open, realpath } from 'node:fs/promises';
2
3
  import { homedir } from 'node:os';
3
- import { join, resolve } from 'node:path';
4
+ import { basename, dirname, join, resolve } from 'node:path';
5
+ import { setTimeout } from 'node:timers/promises';
4
6
  import { writeFileAtomically } from '../platform/atomic-write.js';
7
+ import { ExclusiveFileLease, } from '../platform/exclusive-file-lease.js';
5
8
  import { loadClaudeSharedResources, resolveClaudeProjectIdentity, } from '../compatibility/claude/shared-resources.js';
6
9
  function isRecord(value) {
7
10
  return typeof value === 'object' && value !== null && !Array.isArray(value);
8
11
  }
9
- async function readJson(path) {
10
- let source;
12
+ export function filterDisabledMcpResources(resources, disabledNames) {
13
+ if (disabledNames.length === 0)
14
+ return resources;
15
+ const disabled = new Set(disabledNames);
16
+ return resources.map((resource) => {
17
+ if (!isRecord(resource.value) || !isRecord(resource.value.mcpServers)) {
18
+ return resource;
19
+ }
20
+ return {
21
+ ...resource,
22
+ value: {
23
+ ...resource.value,
24
+ mcpServers: Object.fromEntries(Object.entries(resource.value.mcpServers).filter(([name]) => !disabled.has(name))),
25
+ },
26
+ };
27
+ });
28
+ }
29
+ async function readJsonSource(path, rejectSymlinks = true) {
30
+ let handle;
11
31
  try {
12
- source = await readFile(path, 'utf8');
32
+ handle = await open(path, constants.O_RDONLY | (rejectSymlinks ? constants.O_NOFOLLOW : 0));
33
+ const before = await handle.stat();
34
+ const source = await handle.readFile('utf8');
35
+ const after = await handle.stat();
36
+ if (before.dev !== after.dev ||
37
+ before.ino !== after.ino ||
38
+ before.size !== after.size ||
39
+ before.mtimeMs !== after.mtimeMs) {
40
+ throw new Error(`MCP config changed while reading: ${path}`);
41
+ }
42
+ let value;
43
+ try {
44
+ value = JSON.parse(source);
45
+ }
46
+ catch (error) {
47
+ throw new Error(`Invalid MCP config JSON: ${path}`, { cause: error });
48
+ }
49
+ if (!isRecord(value))
50
+ throw new Error(`MCP config must be an object: ${path}`);
51
+ return {
52
+ value,
53
+ fingerprint: {
54
+ source,
55
+ device: after.dev,
56
+ inode: after.ino,
57
+ size: after.size,
58
+ modifiedMs: after.mtimeMs,
59
+ },
60
+ };
13
61
  }
14
62
  catch (error) {
15
63
  if (error.code === 'ENOENT')
16
- return {};
64
+ return { value: {} };
65
+ if (error.code === 'ELOOP') {
66
+ throw new Error(`MCP config must not be a symbolic link: ${path}`, {
67
+ cause: error,
68
+ });
69
+ }
17
70
  throw error;
18
71
  }
19
- let value;
20
- try {
21
- value = JSON.parse(source);
72
+ finally {
73
+ await handle?.close();
22
74
  }
23
- catch (error) {
24
- throw new Error(`Invalid MCP config JSON: ${path}`, { cause: error });
75
+ }
76
+ async function sourceUnchanged(path, fingerprint) {
77
+ const current = await readJsonSource(path, true);
78
+ if (!fingerprint || !current.fingerprint) {
79
+ return fingerprint === current.fingerprint;
25
80
  }
26
- if (!isRecord(value))
27
- throw new Error(`MCP config must be an object: ${path}`);
28
- return value;
81
+ return (current.fingerprint.source === fingerprint.source &&
82
+ current.fingerprint.device === fingerprint.device &&
83
+ current.fingerprint.inode === fingerprint.inode &&
84
+ current.fingerprint.size === fingerprint.size &&
85
+ current.fingerprint.modifiedMs === fingerprint.modifiedMs);
86
+ }
87
+ async function canonicalTarget(path) {
88
+ await mkdir(dirname(path), { recursive: true });
89
+ return join(await realpath(dirname(path)), basename(path));
29
90
  }
30
91
  function serverMap(value, path) {
31
92
  const servers = value.mcpServers;
@@ -58,19 +119,66 @@ function setScopedValue(root, target, value) {
58
119
  projects[target.projectIdentity] = value;
59
120
  root.projects = projects;
60
121
  }
61
- async function writeJson(path, value) {
62
- const committed = await writeFileAtomically(path, `${JSON.stringify(value, null, 2)}\n`);
63
- if (!committed)
64
- throw new Error(`MCP config write was interrupted: ${path}`);
122
+ async function mutateJson(path, mutate, beforeCommit) {
123
+ path = await canonicalTarget(path);
124
+ const lease = new ExclusiveFileLease(`${path}.praxis.lock`);
125
+ let handle = null;
126
+ for (let attempt = 0; attempt < 400; attempt += 1) {
127
+ handle = await lease.tryAcquire();
128
+ if (handle)
129
+ break;
130
+ await setTimeout(5);
131
+ }
132
+ if (!handle)
133
+ throw new Error(`MCP config write lock timed out: ${path}`);
134
+ try {
135
+ for (let attempt = 0; attempt < 3; attempt += 1) {
136
+ const { value, fingerprint } = await readJsonSource(path, true);
137
+ const next = mutate(value);
138
+ if (!next)
139
+ return;
140
+ await beforeCommit?.(path, attempt);
141
+ const committed = await writeFileAtomically(path, `${JSON.stringify(next, null, 2)}\n`, { beforeCommit: () => sourceUnchanged(path, fingerprint) });
142
+ if (committed)
143
+ return;
144
+ }
145
+ throw new Error(`MCP config changed concurrently: ${path}`);
146
+ }
147
+ finally {
148
+ await handle.release();
149
+ }
150
+ }
151
+ function projectState(root, identity, path) {
152
+ if (root.projects !== undefined && !isRecord(root.projects)) {
153
+ throw new Error(`MCP config projects must be an object: ${path}`);
154
+ }
155
+ const projects = root.projects;
156
+ const project = projects?.[identity];
157
+ if (project !== undefined && !isRecord(project)) {
158
+ throw new Error(`MCP project state must be an object: ${path}`);
159
+ }
160
+ return project ? { ...project } : {};
161
+ }
162
+ function disabledServerNames(project, path) {
163
+ const disabled = project.disabledMcpServers;
164
+ if (disabled === undefined)
165
+ return [];
166
+ if (!Array.isArray(disabled) ||
167
+ disabled.some((name) => typeof name !== 'string')) {
168
+ throw new Error(`disabledMcpServers must be a string array: ${path}`);
169
+ }
170
+ return [...disabled];
65
171
  }
66
172
  export class ClaudeMcpManagement {
67
173
  configRoot;
68
174
  cwd;
175
+ beforeCommit;
69
176
  constructor(options) {
70
177
  this.configRoot = resolve(options.configRoot ??
71
178
  process.env.CLAUDE_CONFIG_DIR ??
72
179
  join(homedir(), '.claude'));
73
180
  this.cwd = resolve(options.cwd);
181
+ this.beforeCommit = options.beforeCommit;
74
182
  }
75
183
  async list(scope) {
76
184
  const resources = await loadClaudeSharedResources({
@@ -104,13 +212,16 @@ export class ClaudeMcpManagement {
104
212
  if (!isRecord(config))
105
213
  throw new Error('MCP server config must be an object');
106
214
  const target = await this.target(scope);
107
- const root = await readJson(target.path);
108
- const scoped = scopedValue(root, target);
109
- const servers = serverMap(scoped, target.path);
110
- servers[name] = config;
111
- setServerMap(scoped, servers);
112
- setScopedValue(root, target, scoped);
113
- await writeJson(target.path, root);
215
+ await mutateJson(target.path, (root) => {
216
+ const scoped = { ...scopedValue(root, target) };
217
+ const servers = { ...serverMap(scoped, target.path), [name]: config };
218
+ setServerMap(scoped, servers);
219
+ if (!target.projectIdentity)
220
+ return scoped;
221
+ const next = { ...root };
222
+ setScopedValue(next, target, scoped);
223
+ return next;
224
+ }, this.beforeCommit);
114
225
  return { name, scope, path: target.path, config };
115
226
  }
116
227
  async remove(name, scope) {
@@ -118,31 +229,67 @@ export class ClaudeMcpManagement {
118
229
  const scopes = scope ? [scope] : ['local', 'project', 'user'];
119
230
  for (const candidate of scopes) {
120
231
  const target = await this.target(candidate);
121
- const root = await readJson(target.path);
122
- const scoped = scopedValue(root, target);
123
- const servers = serverMap(scoped, target.path);
124
- const config = servers[name];
125
- if (!config)
126
- continue;
127
- delete servers[name];
128
- setServerMap(scoped, servers);
129
- setScopedValue(root, target, scoped);
130
- await writeJson(target.path, root);
131
- return { name, scope: candidate, path: target.path, config };
232
+ let removed;
233
+ await mutateJson(target.path, (root) => {
234
+ const scoped = { ...scopedValue(root, target) };
235
+ const servers = { ...serverMap(scoped, target.path) };
236
+ removed = servers[name];
237
+ if (!removed)
238
+ return undefined;
239
+ delete servers[name];
240
+ setServerMap(scoped, servers);
241
+ if (!target.projectIdentity)
242
+ return scoped;
243
+ const next = { ...root };
244
+ setScopedValue(next, target, scoped);
245
+ return next;
246
+ }, this.beforeCommit);
247
+ if (removed)
248
+ return { name, scope: candidate, path: target.path, config: removed };
132
249
  }
133
250
  throw new Error(`MCP server not found: ${name}`);
134
251
  }
135
252
  async resetProjectChoices() {
136
253
  const path = join(this.configRoot, '.claude.json');
137
- const root = await readJson(path);
138
254
  const identity = await resolveClaudeProjectIdentity({ cwd: this.cwd });
139
- const projects = isRecord(root.projects) ? root.projects : {};
140
- const project = isRecord(projects[identity]) ? projects[identity] : {};
141
- project.enabledMcpjsonServers = [];
142
- project.disabledMcpjsonServers = [];
143
- projects[identity] = project;
144
- root.projects = projects;
145
- await writeJson(path, root);
255
+ await mutateJson(path, (root) => {
256
+ const projects = isRecord(root.projects) ? { ...root.projects } : {};
257
+ const project = isRecord(projects[identity]) ? projects[identity] : {};
258
+ projects[identity] = {
259
+ ...project,
260
+ enabledMcpjsonServers: [],
261
+ disabledMcpjsonServers: [],
262
+ };
263
+ return { ...root, projects };
264
+ }, this.beforeCommit);
265
+ }
266
+ async disabled() {
267
+ const path = join(this.configRoot, '.claude.json');
268
+ const identity = await resolveClaudeProjectIdentity({ cwd: this.cwd });
269
+ const root = (await readJsonSource(path, true)).value;
270
+ return disabledServerNames(projectState(root, identity, path), path);
271
+ }
272
+ async setEnabled(name, scope, enabled) {
273
+ this.validateName(name);
274
+ await this.get(name, scope);
275
+ const path = join(this.configRoot, '.claude.json');
276
+ const identity = await resolveClaudeProjectIdentity({ cwd: this.cwd });
277
+ await mutateJson(path, (root) => {
278
+ const project = projectState(root, identity, path);
279
+ const disabled = disabledServerNames(project, path);
280
+ const next = enabled
281
+ ? disabled.filter((candidate) => candidate !== name)
282
+ : disabled.includes(name)
283
+ ? disabled
284
+ : [...disabled, name];
285
+ if (next.length === disabled.length &&
286
+ next.every((candidate, index) => candidate === disabled[index])) {
287
+ return undefined;
288
+ }
289
+ const projects = isRecord(root.projects) ? { ...root.projects } : {};
290
+ projects[identity] = { ...project, disabledMcpServers: next };
291
+ return { ...root, projects };
292
+ }, this.beforeCommit);
146
293
  }
147
294
  async target(scope) {
148
295
  if (scope === 'project')
@@ -74,6 +74,7 @@ function stableServerConfig(server) {
74
74
  }
75
75
  export function mcpOAuthRecordKey(server) {
76
76
  const digest = createHash('sha256')
77
+ // codeql[js/insufficient-password-hash] deterministic key derivation, not password storage
77
78
  .update(stableServerConfig(server))
78
79
  .digest('hex')
79
80
  .slice(0, 16);
@@ -85,6 +86,7 @@ export function mcpOAuthCredentialService(configRoot) {
85
86
  if (canonical === defaultRoot)
86
87
  return CREDENTIAL_SERVICE;
87
88
  const digest = createHash('sha256')
89
+ // codeql[js/insufficient-password-hash] keychain service name from config path, not secret hashing
88
90
  .update(canonical)
89
91
  .digest('hex')
90
92
  .slice(0, 8);
@@ -18,7 +18,25 @@ export interface ClaudeMcpPromptDefinition {
18
18
  }
19
19
  export interface ClaudeMcpServerStatus {
20
20
  name: string;
21
- status: 'connected' | 'failed';
21
+ status: 'connected' | 'failed' | 'needs-authentication';
22
+ statusDetail?: string;
23
+ authDetail?: string;
24
+ capabilities?: readonly ('tools' | 'resources' | 'prompts')[];
25
+ toolCount?: number;
26
+ }
27
+ export interface ClaudeMcpToolInspection {
28
+ name: string;
29
+ fullName: string;
30
+ description?: string;
31
+ }
32
+ export interface ClaudeMcpRuntime {
33
+ inspect(): Promise<readonly ClaudeMcpServerStatus[]>;
34
+ reconnect(name: string): Promise<void>;
35
+ authenticate(name: string): Promise<void>;
36
+ reload(): Promise<void>;
37
+ tools(name: string): Promise<readonly ClaudeMcpToolInspection[]>;
38
+ /** Release MCP transports and child processes owned by this runtime. */
39
+ close?(): Promise<void>;
22
40
  }
23
41
  export interface ClaudeMcpConfigurationStatus {
24
42
  name: string;
@@ -40,6 +58,8 @@ export interface ClaudeMcpToolRegistryOptions {
40
58
  signal?: AbortSignal;
41
59
  eventSink?: RuntimeEventSink;
42
60
  onPromptsChanged?: (prompts: readonly ClaudeMcpPromptDefinition[]) => void;
61
+ authenticateServer?: (name: string) => Promise<void>;
62
+ reloadResources?: () => Promise<readonly ClaudeJsonResource[]>;
43
63
  onElicitation?: (request: {
44
64
  serverName: string;
45
65
  message: ElicitRequest['params']['message'];
@@ -53,9 +73,9 @@ export interface ClaudeMcpToolRegistryOptions {
53
73
  }>;
54
74
  }
55
75
  export declare function validateClaudeMcpConfiguration(resources: readonly ClaudeJsonResource[]): ClaudeMcpConfigurationReport;
56
- export declare class ClaudeMcpToolRegistry implements ToolRegistry {
76
+ export declare class ClaudeMcpToolRegistry implements ToolRegistry, ClaudeMcpRuntime {
57
77
  private readonly options;
58
- private readonly tools;
78
+ private readonly connectedTools;
59
79
  private readonly reservedTools;
60
80
  private readonly resourceServers;
61
81
  private readonly promptServers;
@@ -63,6 +83,7 @@ export declare class ClaudeMcpToolRegistry implements ToolRegistry {
63
83
  private readonly clients;
64
84
  private readonly serverClients;
65
85
  private readonly reconnectableServers;
86
+ private readonly serverCapabilities;
66
87
  private readonly reconnectingServers;
67
88
  private readonly promptOperations;
68
89
  private promptResultDirectoryPromise;
@@ -73,6 +94,13 @@ export declare class ClaudeMcpToolRegistry implements ToolRegistry {
73
94
  static connect(options: ClaudeMcpToolRegistryOptions): Promise<ClaudeMcpToolRegistry>;
74
95
  definitions(): readonly ModelToolDefinition[];
75
96
  serverStatuses(): readonly ClaudeMcpServerStatus[];
97
+ inspect(): Promise<readonly ClaudeMcpServerStatus[]>;
98
+ reconnect(name: string): Promise<void>;
99
+ authenticate(name: string): Promise<void>;
100
+ reload(): Promise<void>;
101
+ tools(name: string): Promise<readonly ClaudeMcpToolInspection[]>;
102
+ private runtimeStatuses;
103
+ private configure;
76
104
  prompts(): readonly ClaudeMcpPromptDefinition[];
77
105
  permissionPrompt(name: string): (call: ModelToolCall, originalCall?: ModelToolCall) => Promise<PermissionApproval>;
78
106
  prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
@@ -641,7 +641,7 @@ async function resourceContent(server, result, context, sensitiveValues) {
641
641
  }
642
642
  export class ClaudeMcpToolRegistry {
643
643
  options;
644
- tools = new Map();
644
+ connectedTools = new Map();
645
645
  reservedTools = new Set();
646
646
  resourceServers = new Map();
647
647
  promptServers = new Map();
@@ -649,6 +649,7 @@ export class ClaudeMcpToolRegistry {
649
649
  clients = new Set();
650
650
  serverClients = new Map();
651
651
  reconnectableServers = new Map();
652
+ serverCapabilities = new Map();
652
653
  reconnectingServers = new Map();
653
654
  promptOperations = new Set();
654
655
  promptResultDirectoryPromise;
@@ -663,26 +664,7 @@ export class ClaudeMcpToolRegistry {
663
664
  const ambientSensitiveValues = sensitiveEnvironmentValues(process.env);
664
665
  const warn = (message) => options.onWarning?.(redactSensitiveText(message, ambientSensitiveValues));
665
666
  try {
666
- for (const server of configuredServers(options.resources, warn)) {
667
- registry.statuses.set(server.name, {
668
- name: server.name,
669
- status: 'failed',
670
- });
671
- let config;
672
- try {
673
- config = parseServerConfig(server.name, server.value, server.path);
674
- }
675
- catch (error) {
676
- warn(`MCP server ${server.name} unavailable: ${error instanceof Error ? error.message : String(error)}`);
677
- continue;
678
- }
679
- registry.reconnectableServers.set(server.name, {
680
- config,
681
- sensitiveValues: server.sensitiveValues ?? [],
682
- });
683
- await registry.connectServer(server.name, config, server.sensitiveValues);
684
- }
685
- registry.publishPrompts();
667
+ await registry.configure(options.resources, warn);
686
668
  }
687
669
  catch (error) {
688
670
  await registry.close();
@@ -693,14 +675,94 @@ export class ClaudeMcpToolRegistry {
693
675
  definitions() {
694
676
  return [
695
677
  ...this.options.base.definitions(),
696
- ...[...this.tools.entries()]
678
+ ...[...this.connectedTools.entries()]
697
679
  .filter(([name]) => !this.reservedTools.has(name))
698
680
  .map(([, tool]) => tool.definition),
699
681
  ...(this.resourceServers.size > 0 ? MCP_RESOURCE_TOOL_DEFINITIONS : []),
700
682
  ];
701
683
  }
702
684
  serverStatuses() {
703
- return [...this.statuses.values()];
685
+ return [...this.statuses.values()].map(({ name, status }) => ({
686
+ name,
687
+ status,
688
+ }));
689
+ }
690
+ async inspect() {
691
+ return this.runtimeStatuses();
692
+ }
693
+ async reconnect(name) {
694
+ await this.reconnectServer(name);
695
+ }
696
+ async authenticate(name) {
697
+ if (!this.statuses.has(name))
698
+ throw new Error(`Unknown MCP server ${name}`);
699
+ if (!this.options.authenticateServer) {
700
+ throw new Error('MCP authentication is not available in this runtime');
701
+ }
702
+ await this.options.authenticateServer(name);
703
+ await this.reconnectServer(name);
704
+ }
705
+ async reload() {
706
+ if (this.closed)
707
+ throw new Error('MCP registry is closed');
708
+ this.generation += 1;
709
+ const clients = [...this.serverClients.values()];
710
+ this.serverClients.clear();
711
+ await Promise.allSettled(clients.map((client) => client.close()));
712
+ for (const client of clients)
713
+ this.clients.delete(client);
714
+ this.connectedTools.clear();
715
+ this.reservedTools.clear();
716
+ this.resourceServers.clear();
717
+ this.promptServers.clear();
718
+ this.statuses.clear();
719
+ this.reconnectableServers.clear();
720
+ this.serverCapabilities.clear();
721
+ const ambientSensitiveValues = sensitiveEnvironmentValues(process.env);
722
+ const warn = (message) => this.options.onWarning?.(redactSensitiveText(message, ambientSensitiveValues));
723
+ await this.configure(this.options.reloadResources
724
+ ? await this.options.reloadResources()
725
+ : this.options.resources, warn);
726
+ }
727
+ async tools(name) {
728
+ if (!this.statuses.has(name))
729
+ throw new Error(`Unknown MCP server ${name}`);
730
+ return [...this.connectedTools.entries()]
731
+ .filter(([, tool]) => tool.serverName === name)
732
+ .map(([fullName, tool]) => ({
733
+ name: tool.toolName,
734
+ fullName,
735
+ ...(tool.definition.description
736
+ ? { description: tool.definition.description }
737
+ : {}),
738
+ }))
739
+ .sort((left, right) => left.name.localeCompare(right.name));
740
+ }
741
+ runtimeStatuses() {
742
+ return [...this.statuses.values()].map((status) => {
743
+ const capabilities = this.serverCapabilities.get(status.name) ?? [];
744
+ const toolCount = [...this.connectedTools.values()].filter((tool) => tool.serverName === status.name).length;
745
+ return { ...status, capabilities, toolCount };
746
+ });
747
+ }
748
+ async configure(resources, warn) {
749
+ for (const server of configuredServers(resources, warn)) {
750
+ this.statuses.set(server.name, { name: server.name, status: 'failed' });
751
+ let config;
752
+ try {
753
+ config = parseServerConfig(server.name, server.value, server.path);
754
+ }
755
+ catch (error) {
756
+ warn(`MCP server ${server.name} unavailable: ${error instanceof Error ? error.message : String(error)}`);
757
+ continue;
758
+ }
759
+ this.reconnectableServers.set(server.name, {
760
+ config,
761
+ sensitiveValues: server.sensitiveValues ?? [],
762
+ });
763
+ await this.connectServer(server.name, config, server.sensitiveValues);
764
+ }
765
+ this.publishPrompts();
704
766
  }
705
767
  prompts() {
706
768
  const prompts = [];
@@ -722,13 +784,13 @@ export class ClaudeMcpToolRegistry {
722
784
  return prompts;
723
785
  }
724
786
  permissionPrompt(name) {
725
- if (!this.tools.has(name)) {
726
- const available = [...this.tools.keys()].join(', ') || 'none';
787
+ if (!this.connectedTools.has(name)) {
788
+ const available = [...this.connectedTools.keys()].join(', ') || 'none';
727
789
  throw new Error(`MCP tool ${name} (from --permission-prompt-tool) not found. Available MCP tools: ${available}`);
728
790
  }
729
791
  this.reservedTools.add(name);
730
792
  return async (call, originalCall = call) => {
731
- const tool = this.tools.get(name);
793
+ const tool = this.connectedTools.get(name);
732
794
  if (!tool)
733
795
  return invalidPermissionResult();
734
796
  let result;
@@ -758,7 +820,7 @@ export class ClaudeMcpToolRegistry {
758
820
  };
759
821
  }
760
822
  async prepare(call, context) {
761
- return this.tools.has(call.name) ||
823
+ return this.connectedTools.has(call.name) ||
762
824
  MCP_RESOURCE_TOOL_DEFINITIONS.some((definition) => definition.name === call.name)
763
825
  ? call
764
826
  : this.options.base.prepare(call, context);
@@ -778,7 +840,7 @@ export class ClaudeMcpToolRegistry {
778
840
  if (call.name === 'ReadMcpResourceTool') {
779
841
  return this.readResource(call.input, context);
780
842
  }
781
- const tool = this.tools.get(call.name);
843
+ const tool = this.connectedTools.get(call.name);
782
844
  if (!tool)
783
845
  return this.options.base.execute(call, context);
784
846
  let result;
@@ -898,6 +960,7 @@ export class ClaudeMcpToolRegistry {
898
960
  this.clients.delete(client);
899
961
  if (this.closed || this.serverClients.get(serverName) !== client)
900
962
  return;
963
+ this.serverCapabilities.delete(serverName);
901
964
  this.statuses.set(serverName, { name: serverName, status: 'failed' });
902
965
  };
903
966
  client.setRequestHandler(ElicitRequestSchema, async (request) => {
@@ -952,7 +1015,7 @@ export class ClaudeMcpToolRegistry {
952
1015
  if (redactSensitiveText(name, sensitiveValues) !== name) {
953
1016
  throw new Error('MCP tool name contains sensitive data');
954
1017
  }
955
- if (this.tools.has(name) || connectedTools.has(name)) {
1018
+ if (this.connectedTools.has(name) || connectedTools.has(name)) {
956
1019
  throw new Error(`Duplicate MCP tool ${name}`);
957
1020
  }
958
1021
  connectedTools.set(name, {
@@ -969,8 +1032,13 @@ export class ClaudeMcpToolRegistry {
969
1032
  }
970
1033
  this.assertOpenGeneration(expectedGeneration);
971
1034
  this.serverClients.set(serverName, client);
1035
+ this.serverCapabilities.set(serverName, [
1036
+ ...(capabilities?.tools ? ['tools'] : []),
1037
+ ...(capabilities?.resources ? ['resources'] : []),
1038
+ ...(capabilities?.prompts ? ['prompts'] : []),
1039
+ ]);
972
1040
  for (const [name, tool] of connectedTools)
973
- this.tools.set(name, tool);
1041
+ this.connectedTools.set(name, tool);
974
1042
  if (capabilities?.resources) {
975
1043
  this.resourceServers.set(serverName, {
976
1044
  client,
@@ -990,7 +1058,11 @@ export class ClaudeMcpToolRegistry {
990
1058
  });
991
1059
  }
992
1060
  }
993
- this.statuses.set(serverName, { name: serverName, status: 'connected' });
1061
+ this.statuses.set(serverName, {
1062
+ name: serverName,
1063
+ status: 'connected',
1064
+ statusDetail: 'connected',
1065
+ });
994
1066
  }
995
1067
  catch (error) {
996
1068
  await client.close().catch(() => undefined);
@@ -999,7 +1071,15 @@ export class ClaudeMcpToolRegistry {
999
1071
  this.closed ||
1000
1072
  expectedGeneration !== this.generation)
1001
1073
  throw error;
1002
- this.options.onWarning?.(redactSensitiveText(`MCP server ${serverName} unavailable: ${error instanceof Error ? error.message : String(error)}`, sensitiveValues));
1074
+ const detail = redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveValues);
1075
+ const needsAuthentication = /(?:401|unauthori[sz]ed|not authenticated|oauth|authentication required)/iu.test(detail);
1076
+ this.statuses.set(serverName, {
1077
+ name: serverName,
1078
+ status: needsAuthentication ? 'needs-authentication' : 'failed',
1079
+ statusDetail: detail,
1080
+ ...(needsAuthentication ? { authDetail: `failed: ${detail}` } : {}),
1081
+ });
1082
+ this.options.onWarning?.(`MCP server ${serverName} unavailable: ${detail}`);
1003
1083
  }
1004
1084
  }
1005
1085
  async discoverTools(client, serverName, sensitiveValues) {
@@ -1202,11 +1282,13 @@ export class ClaudeMcpToolRegistry {
1202
1282
  if (previous)
1203
1283
  this.clients.delete(previous);
1204
1284
  this.assertOpenGeneration(expectedGeneration);
1205
- for (const [name, tool] of this.tools) {
1285
+ for (const [name, tool] of this.connectedTools) {
1206
1286
  if (tool.serverName === serverName)
1207
- this.tools.delete(name);
1287
+ this.connectedTools.delete(name);
1208
1288
  }
1209
1289
  this.resourceServers.delete(serverName);
1290
+ this.promptServers.delete(serverName);
1291
+ this.serverCapabilities.delete(serverName);
1210
1292
  await this.connectServer(serverName, reconnectable.config, reconnectable.sensitiveValues, expectedGeneration);
1211
1293
  if (this.statuses.get(serverName)?.status !== 'connected') {
1212
1294
  throw new Error(`MCP server ${serverName} could not reconnect`);
@@ -11,8 +11,10 @@ export interface ClaudePermissionResolverOptions {
11
11
  permissionMode?: ClaudePermissionMode;
12
12
  autoClassifier?: ClaudeAutoClassifier;
13
13
  autoModeConfig?: ClaudeAutoModeConfig;
14
+ isSessionActionApproved?: (call: ModelToolCall) => boolean;
14
15
  }
15
16
  export type ClaudePermissionMode = 'acceptEdits' | 'auto' | 'bypassPermissions' | 'manual' | 'dontAsk' | 'plan' | 'default';
17
+ export declare function claudePermissionActionKey(call: ModelToolCall): string;
16
18
  export declare function validateClaudePermissionSettings(settings: readonly ClaudeJsonResource[]): void;
17
19
  export declare class ClaudePermissionResolver implements PermissionResolver {
18
20
  private readonly rules;
@@ -22,6 +24,7 @@ export declare class ClaudePermissionResolver implements PermissionResolver {
22
24
  private readonly permissionMode;
23
25
  private readonly autoClassifier;
24
26
  private readonly autoModeConfig;
27
+ private readonly isSessionActionApproved;
25
28
  constructor(options: ClaudePermissionResolverOptions);
26
29
  resolve(call: ModelToolCall, context?: PermissionResolutionContext): Promise<PermissionDecision>;
27
30
  private askDecision;