@lifeaitools/clauth 2.1.1 → 2.10.1

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 (98) hide show
  1. package/.clauth-skill/references/keys-guide.md +270 -270
  2. package/cli/api.js +238 -238
  3. package/cli/commands/install.js +413 -396
  4. package/cli/commands/login.test.js +49 -1
  5. package/cli/commands/ops-install.js +11 -10
  6. package/cli/commands/ops.test.js +270 -0
  7. package/cli/commands/scrub.test.js +70 -0
  8. package/cli/commands/serve.js +2618 -11238
  9. package/cli/commands/uninstall.js +164 -164
  10. package/cli/dashboard/dashboard.js +592 -0
  11. package/cli/dashboard/global.css +65 -0
  12. package/cli/dashboard/panel-element.js +79 -0
  13. package/cli/dashboard/panels/build-status-panel.css +12 -0
  14. package/cli/dashboard/panels/build-status-panel.js +86 -0
  15. package/cli/dashboard/panels/panel-registry.js +58 -0
  16. package/cli/dashboard/panels/services-panel.css +95 -0
  17. package/cli/dashboard/panels/services-panel.js +872 -0
  18. package/cli/dashboard/panels/surfaces-panel.css +59 -0
  19. package/cli/dashboard/panels/surfaces-panel.js +474 -0
  20. package/cli/dashboard/panels/toolbar-panel.css +42 -0
  21. package/cli/dashboard/panels/toolbar-panel.js +374 -0
  22. package/cli/dashboard/panels/tunnel-panel.css +61 -0
  23. package/cli/dashboard/panels/tunnel-panel.js +688 -0
  24. package/cli/dashboard/panels/webdav-panel.css +38 -0
  25. package/cli/dashboard/panels/webdav-panel.js +166 -0
  26. package/cli/fingerprint.test.js +95 -0
  27. package/cli/http/components/callagent-terminal-component.js +310 -0
  28. package/cli/http/components/dashboard-component.js +123 -0
  29. package/cli/http/components/inbox-component.js +41 -0
  30. package/cli/http/components/mcp-transport-component.js +275 -0
  31. package/cli/http/components/oauth-component.js +207 -0
  32. package/cli/http/components/service-crud-component.js +288 -0
  33. package/cli/http/components/supervisor-component.js +104 -0
  34. package/cli/http/components/system-component.js +184 -0
  35. package/cli/http/components/tunnel-component.js +508 -0
  36. package/cli/http/components/vault-auth-component.js +264 -0
  37. package/cli/http/components/watchdog-component.js +28 -0
  38. package/cli/http/request-utils.js +21 -0
  39. package/cli/http/servers/ops-server.js +335 -0
  40. package/cli/index.js +5 -91
  41. package/cli/lib/fs-git.test.js +467 -0
  42. package/cli/mcp/providers/call-agent-provider.js +30 -0
  43. package/cli/mcp/providers/clauth-core-provider.js +461 -0
  44. package/cli/mcp/providers/ops-provider.js +42 -0
  45. package/cli/mcp/providers/terminal-provider.js +81 -0
  46. package/cli/mcp/tool-registry.js +48 -0
  47. package/cli/mcp/tool-schemas.js +822 -0
  48. package/cli/mcp/tool-schemas.test.js +49 -0
  49. package/cli/ops/pm2-preflight.js +54 -0
  50. package/cli/recovery.js +6 -0
  51. package/cli/recovery.test.js +179 -0
  52. package/cli/services/agent-pool-instance.js +38 -0
  53. package/cli/services/claude-binary.js +29 -0
  54. package/cli/services/delegation-lane-instance.js +35 -0
  55. package/cli/services/delegation-lane-instance.test.js +37 -0
  56. package/cli/services/file-io.js +38 -0
  57. package/cli/services/file-io.test.js +10 -0
  58. package/cli/services/fs-mount.js +66 -0
  59. package/cli/services/fs-mount.test.js +14 -0
  60. package/cli/services/fs-upload-sessions.js +20 -0
  61. package/cli/services/fs-upload-sessions.test.js +14 -0
  62. package/cli/services/git-exec.js +37 -0
  63. package/cli/services/git-exec.test.js +41 -0
  64. package/cli/services/git-import-guard.js +28 -0
  65. package/cli/services/git-import-guard.test.js +18 -0
  66. package/cli/services/service-status.js +191 -0
  67. package/cli/services/service-status.test.js +39 -0
  68. package/cli/services/terminal-dispatch.js +135 -0
  69. package/cli/services/terminal-dispatch.test.js +9 -0
  70. package/cli/services/terminal-registry.js +39 -0
  71. package/cli/services/terminal-registry.test.js +31 -0
  72. package/cli/services/terminal-sessions.js +100 -0
  73. package/cli/services/terminal-sessions.test.js +26 -0
  74. package/cli/services/terminal-window.js +106 -0
  75. package/cli/services/vault-read.js +14 -0
  76. package/cli/services/vault-read.test.js +35 -0
  77. package/cli/supervisor-registry.js +116 -0
  78. package/cli/supervisor-registry.test.js +165 -0
  79. package/cli/supervisor-ui.test.js +230 -30
  80. package/cli/webdav-config.js +84 -0
  81. package/cli/webdav-config.test.js +79 -0
  82. package/cli/webdav-obscure.js +23 -0
  83. package/cli/webdav-obscure.test.js +41 -0
  84. package/cli/webdav-rclone-discovery.js +44 -0
  85. package/cli/webdav-service.js +30 -118
  86. package/cli/webdav-service.test.js +107 -0
  87. package/install.ps1 +102 -102
  88. package/install.sh +49 -49
  89. package/package.json +2 -2
  90. package/scripts/bootstrap.cjs +121 -121
  91. package/supabase/functions/auth-vault/index.ts +350 -350
  92. package/supabase/migrations/001_clauth_schema.sql +94 -94
  93. package/supabase/migrations/002_vault_helpers.sql +90 -90
  94. package/supabase/migrations/20260317_lockout.sql +26 -26
  95. package/cli/assets/codevelop/launcher-active.cmd.template +0 -20
  96. package/cli/assets/codevelop/launcher-static.cmd.template +0 -7
  97. package/cli/assets/codevelop/windows-terminal.profiles.json +0 -48
  98. package/cli/commands/codevelop.js +0 -1190
@@ -0,0 +1,822 @@
1
+ // cli/mcp/tool-schemas.js
2
+ // The MCP tool catalog: every clauth_*/fs_*/terminal_*/gws_*
3
+ // tool schema, plus the write-tool filter. Pure data — no vault, no HTTP, no
4
+ // session state. Extracted from serve.js as the first slice of splitting the
5
+ // MCP interface out of the monolithic HTTP+dashboard+MCP file.
6
+ export const MCP_TOOLS = [
7
+ {
8
+ name: "clauth_ping",
9
+ description: "Check if the vault is locked or unlocked, show failure count",
10
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
11
+ },
12
+ {
13
+ name: "clauth_unlock",
14
+ description: "Unlock the vault with the master password (password stays in MCP server memory only)",
15
+ inputSchema: { type: "object", properties: { password: { type: "string", description: "clauth master password" } }, required: ["password"], additionalProperties: false }
16
+ },
17
+ {
18
+ name: "clauth_lock",
19
+ description: "Lock the vault — clears password from memory",
20
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
21
+ },
22
+ {
23
+ name: "clauth_status",
24
+ description: "List all services with type, enabled state, key presence, and last retrieval time. Uses cached metadata and never retrieves raw secrets.",
25
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
26
+ },
27
+ {
28
+ name: "clauth_knowledge",
29
+ description: "Return sanitized service metadata for agent caching. Never includes raw secrets or vault key names.",
30
+ inputSchema: {
31
+ type: "object",
32
+ properties: {
33
+ project: { type: "string", description: "Optional project scope" },
34
+ refresh: { type: "boolean", default: false, description: "Force one fresh status read instead of cached metadata" }
35
+ },
36
+ additionalProperties: false
37
+ }
38
+ },
39
+ {
40
+ name: "clauth_list",
41
+ description: "List registered service names",
42
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
43
+ },
44
+ {
45
+ name: "clauth_search",
46
+ description: "Search registered service metadata. Redacted address hints are opt-in because they require secret reads.",
47
+ inputSchema: {
48
+ type: "object",
49
+ properties: {
50
+ query: { type: "string", description: "Search text, such as part of a service name, project, host, URL, or filesystem path" },
51
+ project: { type: "string", description: "Optional project scope" },
52
+ addresses: { type: "boolean", default: false, description: "Include redacted address hints from connstring/fileserver/oauth secrets. This may retrieve multiple secrets; leave false for normal discovery." }
53
+ },
54
+ required: ["query"],
55
+ additionalProperties: false
56
+ }
57
+ },
58
+ {
59
+ name: "clauth_get",
60
+ description: "Retrieve a secret and deliver to a temp file (default), clipboard, or stdout. Temp files auto-delete after 30 seconds.",
61
+ inputSchema: {
62
+ type: "object",
63
+ properties: {
64
+ service: { type: "string", description: "Service name (e.g. github, anthropic, vercel)" },
65
+ target: { type: "string", enum: ["file", "clipboard", "stdout"], default: "file", description: "Where to deliver the secret. 'file' writes a temp file, 'clipboard' copies to clipboard, 'stdout' returns the value in the response (DANGEROUS — enters transcript)" }
66
+ },
67
+ required: ["service"],
68
+ additionalProperties: false
69
+ }
70
+ },
71
+ {
72
+ name: "clauth_inject",
73
+ description: "Retrieve a small explicit set of services into a sourceable env file. Guarded to prevent accidental bulk vault sweeps.",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {
77
+ services: { type: "array", items: { type: "string" }, description: "Service names to inject (e.g. ['github', 'vercel', 'anthropic']). Do not pass every service." },
78
+ allow_many: { type: "boolean", default: false, description: "Required when retrieving more than 5 services in one call." }
79
+ },
80
+ required: ["services"],
81
+ additionalProperties: false
82
+ }
83
+ },
84
+ {
85
+ name: "clauth_enable",
86
+ description: "Enable a service in the vault",
87
+ inputSchema: { type: "object", properties: { service: { type: "string" } }, required: ["service"], additionalProperties: false }
88
+ },
89
+ {
90
+ name: "clauth_disable",
91
+ description: "Disable a service in the vault",
92
+ inputSchema: { type: "object", properties: { service: { type: "string" } }, required: ["service"], additionalProperties: false }
93
+ },
94
+ {
95
+ name: "clauth_test",
96
+ description: "Test the HMAC handshake without retrieving any keys",
97
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
98
+ },
99
+ {
100
+ name: "clauth_scrub",
101
+ description: "Scrub secrets from Claude Code transcript .jsonl files",
102
+ inputSchema: {
103
+ type: "object",
104
+ properties: {
105
+ target: { type: "string", enum: ["latest", "all"], default: "latest", description: "Scrub the most recent transcript or all transcripts" }
106
+ },
107
+ additionalProperties: false
108
+ }
109
+ },
110
+ {
111
+ name: "clauth_set_project",
112
+ description: "Set or clear the project scope on a service. Pass empty string to clear.",
113
+ inputSchema: {
114
+ type: "object",
115
+ properties: {
116
+ service: { type: "string", description: "Service name (e.g. gmail, github)" },
117
+ project: { type: "string", description: "Project name to assign (empty string to clear)" }
118
+ },
119
+ required: ["service", "project"],
120
+ additionalProperties: false
121
+ }
122
+ },
123
+ {
124
+ name: "clauth_self_check",
125
+ description: "Test whether the clauth MCP connector is reachable via the Cloudflare tunnel. Returns connectivity status and tunnel URL.",
126
+ inputSchema: { type: "object", properties: {}, additionalProperties: false }
127
+ },
128
+ {
129
+ name: "clauth_generate_token",
130
+ description: "Generate a cryptographically random bearer token and store it as a clauth service value",
131
+ inputSchema: {
132
+ type: "object",
133
+ properties: {
134
+ service: { type: "string", description: "Service name to store the token under (e.g. web-research-api)" },
135
+ prefix: { type: "string", description: "Token prefix (e.g. rdc_wr_). Default: empty string" }
136
+ },
137
+ required: ["service"],
138
+ additionalProperties: false
139
+ }
140
+ },
141
+ {
142
+ name: "clauth_enroll_machine",
143
+ description: "Create a one-time enrollment code for adding a new computer to this clauth install. Requires the vault to be unlocked.",
144
+ inputSchema: {
145
+ type: "object",
146
+ properties: {
147
+ label: { type: "string", description: "Suggested label for the new computer" },
148
+ ttl_minutes: { type: "number", description: "Enrollment lifetime in minutes, 5 to 1440. Default: 60" },
149
+ install_id: { type: "string", description: "Optional install id override. Defaults to current machine install id." }
150
+ },
151
+ additionalProperties: false
152
+ }
153
+ },
154
+ {
155
+ name: "call_agent",
156
+ description: "Call a headless Haiku agent for a single prompt or skill and get the answer back. Lean one-shot through a warm pool (model default claude-haiku-4-5, ~4-5s warm). sync mode (default) blocks and returns { ok, package, jobId, model, ms }; async returns { ok, jobId } for polling.",
157
+ inputSchema: {
158
+ type: "object",
159
+ properties: {
160
+ prompt: { type: "string", description: "The full prompt for the agent. Either this or skill_slug is required." },
161
+ skill_slug: { type: "string", description: "An rdc skill slug (e.g. 'rdc:status'); composed as '/<slug> <input>'." },
162
+ input: { type: "string", description: "Input/argument appended after skill_slug." },
163
+ model: { type: "string", description: "Model alias. Default claude-haiku-4-5." },
164
+ timeout_ms: { type: "number", description: "Hard timeout in ms. Default 120000." },
165
+ mode: { type: "string", enum: ["sync", "async"], description: "sync (block for answer, default) or async (return jobId)." },
166
+ agent_context: { type: "object", description: "Optional app/repo/runtime/task envelope. If CLAUTH_AGENT_DELEGATION is enabled and you are a call_agent worker delegating to a SUB-agent, set agent_context.delegation = { hop, max } (e.g. { hop: 1, max: 2 }) — the sub-call runs on a separate cold lane, never the warm pool, and is rejected once hop exceeds max. Omit delegation for normal (top-level) calls.", additionalProperties: true },
167
+ bootstrap: { type: "string", enum: ["none", "identity", "full"], description: "Startup tier (default 'identity'). 'none' = lean fast path (trivial one-shots, warmest). 'identity' = short who-am-I + how-to-get-access (clauth daemon, Supabase ref, work_items RPCs) on a neutral cwd — every default agent at least knows its identity/access. 'full' = real repo/DB work: runs in the repo cwd and points the agent at .rdc/guides/agent-bootstrap.md (creds, git rules, completion report)." },
168
+ repo_root: { type: "string", description: "Override the repo root for bootstrap='full' (default env CLAUTH_AGENT_REPO_ROOT or C:/Dev/regen-root)." },
169
+ append_system_prompt: { type: "string", description: "Optional extra system-prompt directive appended AFTER the bootstrap tier text." },
170
+ corpus: {
171
+ type: "object",
172
+ description: "Regen Agent corpus handoff (same shape @regen/claude-dispatch delivers). Assembled into a `## Corpus` section appended to the agent's prompt body. With bootstrap='full', .rdc/guides/agent-bootstrap.md content is auto-included inline as a Supporting Doc even if corpus is omitted.",
173
+ properties: {
174
+ product: { type: "string", description: "Product MD context." },
175
+ design: { type: "string", description: "Design MD context." },
176
+ docs: {
177
+ type: "array",
178
+ description: "Supporting documents.",
179
+ items: {
180
+ type: "object",
181
+ properties: {
182
+ path: { type: "string", description: "Document path/identifier." },
183
+ title: { type: "string", description: "Optional document title." },
184
+ content: { type: "string", description: "Document content." },
185
+ },
186
+ required: ["path", "content"],
187
+ additionalProperties: false,
188
+ },
189
+ },
190
+ },
191
+ additionalProperties: false,
192
+ },
193
+ },
194
+ additionalProperties: false,
195
+ },
196
+ },
197
+
198
+ // ── Terminal session manager ───────────────────────────────────────────
199
+ {
200
+ name: "terminal_start",
201
+ description: "Start a named persistent Claude session. Sessions maintain rolling context across sends. Returns session_id for subsequent sends.",
202
+ inputSchema: {
203
+ type: "object",
204
+ properties: {
205
+ name: { type: "string", description: "Human-readable session name (e.g. 'knowledgedude-prt')" },
206
+ knowledge_tier: { type: "string", enum: ["db_only", "corpus", "project"], description: "Knowledge tier for the session. 'corpus' injects context_md as preamble." },
207
+ context_md: { type: "string", description: "Optional markdown preamble injected as session context (used for 'corpus' tier)" },
208
+ cwd: { type: "string", description: "Optional working directory. Defaults to a neutral temp directory so browser chat avoids repo hooks." },
209
+ use_warm: { type: "boolean", description: "Opt into experimental pinned warm-worker mode. Defaults to false for reliable browser chat turns." },
210
+ },
211
+ required: ["name"],
212
+ additionalProperties: false,
213
+ },
214
+ },
215
+ {
216
+ name: "terminal_send",
217
+ description: "Send a message to a running terminal session. Spawns a Claude worker with rolling context + message. Returns immediately — session processes async.",
218
+ inputSchema: {
219
+ type: "object",
220
+ properties: {
221
+ session_id: { type: "string", description: "Session ID returned by terminal_start" },
222
+ message: { type: "string", description: "Message to send to the session" },
223
+ },
224
+ required: ["session_id", "message"],
225
+ additionalProperties: false,
226
+ },
227
+ },
228
+ {
229
+ name: "terminal_list",
230
+ description: "List all active terminal sessions with their status (ready/busy/stopped), knowledge tier, and start time.",
231
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
232
+ },
233
+ {
234
+ name: "terminal_show",
235
+ description: "Reveal/focus a clauth terminal session on this Windows machine. If no live Claude process window exists, opens a visible session inspector at the session cwd.",
236
+ inputSchema: {
237
+ type: "object",
238
+ properties: {
239
+ session_id: { type: "string", description: "Session ID returned by terminal_start" },
240
+ },
241
+ required: ["session_id"],
242
+ additionalProperties: false,
243
+ },
244
+ },
245
+ {
246
+ name: "terminal_recv",
247
+ description: "Read the last response from a terminal session. Returns response if ready, or status='busy' if still processing. Poll every 3-5 seconds until status='ready'.",
248
+ inputSchema: {
249
+ type: "object",
250
+ properties: {
251
+ session_id: { type: "string", description: "Session ID returned by terminal_start" },
252
+ },
253
+ required: ["session_id"],
254
+ additionalProperties: false,
255
+ },
256
+ },
257
+ {
258
+ name: "terminal_stop",
259
+ description: "Stop a terminal session and remove it from memory. Kills any active process.",
260
+ inputSchema: {
261
+ type: "object",
262
+ properties: {
263
+ session_id: { type: "string", description: "Session ID to stop" },
264
+ },
265
+ required: ["session_id"],
266
+ additionalProperties: false,
267
+ },
268
+ },
269
+
270
+ {
271
+ name: "channel_list",
272
+ description:
273
+ "List webhook events received via POST /channel (Coolify/GitHub deployments, pushes, etc.). " +
274
+ "Returns up to 100 recent events. Use 'since' to get only new events.",
275
+ inputSchema: {
276
+ type: "object",
277
+ properties: {
278
+ since: {
279
+ type: "string",
280
+ description: "ISO 8601 timestamp — return only events created after this time",
281
+ },
282
+ limit: { type: "number", description: "Max results (1–100, default 50)" },
283
+ },
284
+ additionalProperties: false,
285
+ },
286
+ },
287
+ {
288
+ name: "channel_clear",
289
+ description: "Clear all queued channel events.",
290
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
291
+ },
292
+
293
+ // ── Google Workspace (gws CLI) ──────────────────────────────────────────
294
+ {
295
+ name: "gws_run",
296
+ description: "Run any gws CLI command for Google Workspace (Drive, Gmail, Calendar, Docs, Sheets, Slides, Tasks, People, Chat, etc.).\nCLI pattern: gws <service> <resource> [sub_resource] <method> [--params JSON] [--body JSON]",
297
+ inputSchema: {
298
+ type: "object",
299
+ properties: {
300
+ service: { type: "string", description: "Google Workspace service: drive, sheets, gmail, calendar, docs, slides, tasks, people, chat, classroom, forms, keep, meet, script" },
301
+ resource: { type: "string", description: "Resource noun, e.g. 'files', 'users', 'events'" },
302
+ sub_resource: { type: "string", description: "Optional sub-resource, e.g. 'messages' in 'users messages'" },
303
+ method: { type: "string", description: "Method verb: list, get, insert, update, delete, send, create" },
304
+ params: { type: "object", description: "Query/path params passed as --params JSON" },
305
+ body: { type: "object", description: "Request body passed as --json JSON" }
306
+ },
307
+ required: ["service", "resource", "method"],
308
+ additionalProperties: false
309
+ }
310
+ },
311
+ {
312
+ name: "gws_gmail_list",
313
+ description: "List Gmail messages. Supports Gmail search syntax (e.g. 'from:alice subject:meeting is:unread').",
314
+ inputSchema: {
315
+ type: "object",
316
+ properties: {
317
+ query: { type: "string", description: "Gmail search query" },
318
+ max_results: { type: "number", description: "Max messages (1-500, default 10)" }
319
+ },
320
+ additionalProperties: false
321
+ }
322
+ },
323
+ {
324
+ name: "gws_gmail_read",
325
+ description: "Read a single Gmail message by ID. Returns full message with headers and body.",
326
+ inputSchema: {
327
+ type: "object",
328
+ properties: { message_id: { type: "string", description: "Gmail message ID" } },
329
+ required: ["message_id"],
330
+ additionalProperties: false
331
+ }
332
+ },
333
+ {
334
+ name: "gws_gmail_send",
335
+ description: "Send an email via Gmail.",
336
+ inputSchema: {
337
+ type: "object",
338
+ properties: {
339
+ to: { type: "string", description: "Recipient email address" },
340
+ subject: { type: "string", description: "Email subject" },
341
+ body: { type: "string", description: "Plain-text email body" },
342
+ from: { type: "string", description: "Sender name/address (optional)" }
343
+ },
344
+ required: ["to", "subject", "body"],
345
+ additionalProperties: false
346
+ }
347
+ },
348
+ {
349
+ name: "gws_calendar_list",
350
+ description: "List upcoming Google Calendar events.",
351
+ inputSchema: {
352
+ type: "object",
353
+ properties: {
354
+ calendar_id: { type: "string", description: "Calendar ID (default: primary)" },
355
+ max_results: { type: "number", description: "Max events (default 10)" },
356
+ time_min: { type: "string", description: "Start bound ISO 8601, e.g. '2026-04-10T00:00:00Z'" },
357
+ time_max: { type: "string", description: "End bound ISO 8601" }
358
+ },
359
+ additionalProperties: false
360
+ }
361
+ },
362
+ {
363
+ name: "gws_drive_list",
364
+ description: "List files in Google Drive. Supports Drive search query syntax.",
365
+ inputSchema: {
366
+ type: "object",
367
+ properties: {
368
+ query: { type: "string", description: "Drive search query, e.g. \"name contains 'report'\"" },
369
+ max_results: { type: "number", description: "Max files (default 10)" }
370
+ },
371
+ additionalProperties: false
372
+ }
373
+ },
374
+ // ── Filesystem tools ──
375
+ {
376
+ name: "fs_read",
377
+ description: "[DEPRECATED — use WebDAV: rclone cat dav:path (37x faster). Fallback only.] Read a file. Returns UTF-8 text with line numbers. Supports offset/limit for large files.",
378
+ inputSchema: {
379
+ type: "object",
380
+ properties: {
381
+ path: { type: "string", description: "Relative path within mount (e.g. 'packages/ui/src/index.ts')" },
382
+ mount: { type: "string", description: "Mount name (default: first mount)" },
383
+ offset: { type: "number", description: "Start line (0-based, default 0)" },
384
+ limit: { type: "number", description: "Max lines to return (default 500)" },
385
+ },
386
+ required: ["path"],
387
+ additionalProperties: false,
388
+ },
389
+ },
390
+ {
391
+ name: "fs_write",
392
+ description: "Write content to a file. Creates parent directories if needed. Overwrites existing file.",
393
+ inputSchema: {
394
+ type: "object",
395
+ properties: {
396
+ path: { type: "string", description: "Relative path within mount" },
397
+ content: { type: "string", description: "File content to write" },
398
+ mount: { type: "string", description: "Mount name (default: first mount)" },
399
+ },
400
+ required: ["path", "content"],
401
+ additionalProperties: false,
402
+ },
403
+ },
404
+ {
405
+ name: "fs_stat",
406
+ description: "[DEPRECATED — use WebDAV: rclone lsjson dav:path (faster). Fallback only.] Get file or directory metadata. Files include a SHA-256 hash for guarded edits.",
407
+ inputSchema: {
408
+ type: "object",
409
+ properties: {
410
+ path: { type: "string", description: "Relative path within mount" },
411
+ mount: { type: "string", description: "Mount name (default: first mount)" },
412
+ },
413
+ required: ["path"],
414
+ additionalProperties: false,
415
+ },
416
+ },
417
+ {
418
+ name: "fs_append",
419
+ description: "Append text to a file, optionally guarded by the current file SHA-256 hash.",
420
+ inputSchema: {
421
+ type: "object",
422
+ properties: {
423
+ path: { type: "string", description: "Relative path within mount" },
424
+ content: { type: "string", description: "Text to append" },
425
+ mount: { type: "string", description: "Mount name (default: first mount)" },
426
+ expected_sha256: { type: "string", description: "Optional current file SHA-256. If provided and the file exists, append only when it matches." },
427
+ },
428
+ required: ["path", "content"],
429
+ additionalProperties: false,
430
+ },
431
+ },
432
+ {
433
+ name: "fs_write_chunk",
434
+ description: "Stage chunked text writes and atomically publish when all chunks arrive. Use when single write arguments are too large.",
435
+ inputSchema: {
436
+ type: "object",
437
+ properties: {
438
+ upload_id: { type: "string", description: "Caller-chosen idempotency key for this file upload" },
439
+ path: { type: "string", description: "Relative path within mount" },
440
+ chunk_index: { type: "number", description: "Zero-based chunk index" },
441
+ total_chunks: { type: "number", description: "Total chunks expected" },
442
+ content: { type: "string", description: "Chunk text content" },
443
+ mount: { type: "string", description: "Mount name (default: first mount)" },
444
+ expected_sha256: { type: "string", description: "Optional SHA-256 of the final assembled file content" },
445
+ },
446
+ required: ["upload_id", "path", "chunk_index", "total_chunks", "content"],
447
+ additionalProperties: false,
448
+ },
449
+ },
450
+ {
451
+ name: "fs_ingest_url",
452
+ description: "Download text from an http(s) URL and atomically write it inside the mount. Useful for moving cloud files into the local filesystem.",
453
+ inputSchema: {
454
+ type: "object",
455
+ properties: {
456
+ url: { type: "string", description: "HTTPS or HTTP URL to fetch" },
457
+ path: { type: "string", description: "Relative output path within mount" },
458
+ mount: { type: "string", description: "Mount name (default: first mount)" },
459
+ max_bytes: { type: "number", description: "Maximum download size in bytes (default 5MB, hard max 25MB)" },
460
+ expected_sha256: { type: "string", description: "Optional SHA-256 of fetched content before writing" },
461
+ },
462
+ required: ["url", "path"],
463
+ additionalProperties: false,
464
+ },
465
+ },
466
+ {
467
+ name: "fs_import_git_files",
468
+ description: "Fetch a Git ref and restore only named files into the mounted repo, optionally committing just those files. Designed for Claude.ai GitHub uploads into dirty local monorepos.",
469
+ inputSchema: {
470
+ type: "object",
471
+ properties: {
472
+ remote: { type: "string", description: "Git remote name (default: origin)" },
473
+ ref: { type: "string", description: "Branch, tag, or commit to fetch/restore from" },
474
+ paths: { type: "array", items: { type: "string" }, description: "Repo-relative file paths to import" },
475
+ mode: { type: "string", enum: ["new_only", "overwrite"], description: "new_only refuses existing local paths. overwrite restores named paths only." },
476
+ commit: { type: "boolean", description: "When true, stage only imported paths and create a local commit. Never pushes." },
477
+ message: { type: "string", description: "Commit subject when commit=true" },
478
+ mount: { type: "string", description: "Mount name (default: first mount)" },
479
+ allowed_prefixes: { type: "array", items: { type: "string" }, description: "Optional path allowlist prefixes. Defaults to docs and agent corpus paths." },
480
+ },
481
+ required: ["ref", "paths"],
482
+ additionalProperties: false,
483
+ },
484
+ },
485
+ {
486
+ name: "fs_list",
487
+ description: "[DEPRECATED — use WebDAV: rclone ls/lsd dav:path (faster). Fallback only.] List directory contents with file type, size, and modification time.",
488
+ inputSchema: {
489
+ type: "object",
490
+ properties: {
491
+ path: { type: "string", description: "Relative directory path (default: mount root)" },
492
+ mount: { type: "string", description: "Mount name (default: first mount)" },
493
+ },
494
+ additionalProperties: false,
495
+ },
496
+ },
497
+ {
498
+ name: "fs_grep",
499
+ description: "Search file contents using ripgrep. Returns matching lines with context. Spawns rg process.",
500
+ inputSchema: {
501
+ type: "object",
502
+ properties: {
503
+ pattern: { type: "string", description: "Regex pattern to search for" },
504
+ path: { type: "string", description: "Relative path to search in (default: mount root)" },
505
+ glob: { type: "string", description: "File glob filter (e.g. '*.ts', '*.{js,tsx}')" },
506
+ context: { type: "number", description: "Lines of context around matches (default 0)" },
507
+ max_results: { type: "number", description: "Max matches (default 50)" },
508
+ mount: { type: "string", description: "Mount name (default: first mount)" },
509
+ },
510
+ required: ["pattern"],
511
+ additionalProperties: false,
512
+ },
513
+ },
514
+ {
515
+ name: "fs_glob",
516
+ description: "Find files by glob pattern. Returns matching file paths relative to mount.",
517
+ inputSchema: {
518
+ type: "object",
519
+ properties: {
520
+ pattern: { type: "string", description: "Glob pattern (e.g. '**/*.tsx', 'apps/*/package.json')" },
521
+ path: { type: "string", description: "Relative base path (default: mount root)" },
522
+ mount: { type: "string", description: "Mount name (default: first mount)" },
523
+ },
524
+ required: ["pattern"],
525
+ additionalProperties: false,
526
+ },
527
+ },
528
+ {
529
+ name: "fs_delete",
530
+ description: "Delete a file or empty directory.",
531
+ inputSchema: {
532
+ type: "object",
533
+ properties: {
534
+ path: { type: "string", description: "Relative path to delete" },
535
+ mount: { type: "string", description: "Mount name (default: first mount)" },
536
+ },
537
+ required: ["path"],
538
+ additionalProperties: false,
539
+ },
540
+ },
541
+ {
542
+ name: "fs_mkdir",
543
+ description: "[DEPRECATED — use WebDAV: rclone mkdir dav:path (faster). Fallback only.] Create a directory (recursive — creates parents if needed).",
544
+ inputSchema: {
545
+ type: "object",
546
+ properties: {
547
+ path: { type: "string", description: "Relative directory path to create" },
548
+ mount: { type: "string", description: "Mount name (default: first mount)" },
549
+ },
550
+ required: ["path"],
551
+ additionalProperties: false,
552
+ },
553
+ },
554
+ {
555
+ name: "fs_edit",
556
+ description: "[DEPRECATED — read via WebDAV (rclone cat) + fs_write instead. Fallback only.] Replace a unique string in a file. Like Edit in Claude Code: fails if old_string is not found or matches multiple times unless replace_all=true. Atomic write. Returns new file info including sha256.",
557
+ inputSchema: {
558
+ type: "object",
559
+ properties: {
560
+ path: { type: "string", description: "Relative path within mount" },
561
+ old_string: { type: "string", description: "Exact text to find (must be unique unless replace_all=true)" },
562
+ new_string: { type: "string", description: "Replacement text (must differ from old_string)" },
563
+ replace_all: { type: "boolean", description: "Replace every occurrence (default false)" },
564
+ expected_sha256: { type: "string", description: "Optional current file SHA-256 guard. If provided and mismatched, edit is rejected." },
565
+ mount: { type: "string", description: "Mount name (default: first mount)" },
566
+ },
567
+ required: ["path", "old_string", "new_string"],
568
+ additionalProperties: false,
569
+ },
570
+ },
571
+ {
572
+ name: "fs_move",
573
+ description: "Move or rename a file or directory within a mount. Single syscall when possible; falls back to copy+delete across devices. Refuses to overwrite existing destination unless overwrite=true.",
574
+ inputSchema: {
575
+ type: "object",
576
+ properties: {
577
+ from: { type: "string", description: "Source relative path within mount" },
578
+ to: { type: "string", description: "Destination relative path within mount" },
579
+ overwrite: { type: "boolean", description: "Overwrite destination if it exists (default false)" },
580
+ mount: { type: "string", description: "Mount name for both from and to (default: first mount)" },
581
+ },
582
+ required: ["from", "to"],
583
+ additionalProperties: false,
584
+ },
585
+ },
586
+ {
587
+ name: "fs_copy",
588
+ description: "Copy a file or directory tree within a mount. Recursive by default. Refuses to overwrite existing destination unless overwrite=true.",
589
+ inputSchema: {
590
+ type: "object",
591
+ properties: {
592
+ from: { type: "string", description: "Source relative path within mount" },
593
+ to: { type: "string", description: "Destination relative path within mount" },
594
+ overwrite: { type: "boolean", description: "Overwrite destination if it exists (default false)" },
595
+ mount: { type: "string", description: "Mount name for both from and to (default: first mount)" },
596
+ },
597
+ required: ["from", "to"],
598
+ additionalProperties: false,
599
+ },
600
+ },
601
+ {
602
+ name: "fs_mounts",
603
+ description: "List configured filesystem mounts (fileserver services from vault).",
604
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
605
+ },
606
+ {
607
+ name: "fs_repo_status",
608
+ description: "Get the current git state of the mounted repo in ONE call: branch, HEAD sha + subject, whether the tree is clean or dirty (with changed paths), staged paths, how far ahead/behind the remote you are, and whether a merge or rebase is in progress. Call this first so you KNOW what you are working with before committing — you never have to guess whether your edits or a previous push landed. Requires 'g' (git) access on the mount.",
609
+ inputSchema: {
610
+ type: "object",
611
+ properties: {
612
+ mount: { type: "string", description: "Mount name (default: first mount)" },
613
+ },
614
+ additionalProperties: false,
615
+ },
616
+ },
617
+ {
618
+ name: "fs_use_branch",
619
+ description: "[RETIRED — use fs_exec([\"git\", \"checkout\", \"branch\"]) instead. Known worktree bugs.] Switches to an existing branch, or creates a new one from the current HEAD when create=true. Requires 'g' (git) access on the mount.",
620
+ inputSchema: {
621
+ type: "object",
622
+ properties: {
623
+ branch: { type: "string", description: "Branch name to switch to (or create)" },
624
+ create: { type: "boolean", description: "Create a new branch from current HEAD instead of switching to an existing one (default false)" },
625
+ mount: { type: "string", description: "Mount name (default: first mount)" },
626
+ },
627
+ required: ["branch"],
628
+ additionalProperties: false,
629
+ },
630
+ },
631
+ {
632
+ name: "fs_commit",
633
+ description: "Commit and push to GitHub: stages changes, commits, and PUSHES by default — usually a single call is all you need after editing. Handles the messy cases: nothing-to-commit returns cleanly (no error); a merge/rebase in progress or a detached HEAD is refused with a clear message; if the branch is behind the remote it auto-rebases and retries the push, and on conflict it aborts the rebase (restoring your tree) and keeps the commit local so nothing is lost. Refuses to push protected branches (main/master/production) — those are promoted by a human. Returns the commit sha, the exact files committed, whether the push succeeded, and ahead/behind counts, so you never need a follow-up status call. Set dry_run=true first to preview what WOULD be committed/pushed without doing it. Set expected_head to refuse committing if the base moved under you. Push auth uses the vault github token directly (never exposed). Requires 'g' (git) access on the mount.",
634
+ inputSchema: {
635
+ type: "object",
636
+ properties: {
637
+ message: { type: "string", description: "Commit message. Required for a real commit; optional when dry_run=true." },
638
+ paths: { type: "array", items: { type: "string" }, description: "Repo-relative paths to commit. Omit to commit ALL current changes in the repo." },
639
+ push: { type: "boolean", description: "Push to the remote after committing (default true)" },
640
+ dry_run: { type: "boolean", description: "Preview only: returns the branch, the files that would be committed, and whether/where it would push — without staging, committing, or pushing. Use to confirm before a real push." },
641
+ expected_head: { type: "string", description: "Optimistic-concurrency guard: the commit SHA you believe HEAD is on. If HEAD has moved, the commit is refused so you don't build on a stale base." },
642
+ remote: { type: "string", description: "Git remote name (default: origin)" },
643
+ author_name: { type: "string", description: "Commit author name (default: clauth-fs)" },
644
+ author_email: { type: "string", description: "Commit author email (default: fs@clauth.local)" },
645
+ mount: { type: "string", description: "Mount name (default: first mount)" },
646
+ },
647
+ required: [],
648
+ additionalProperties: false,
649
+ },
650
+ },
651
+ {
652
+ name: "fs_diff",
653
+ description: "Show the unified diff of your changes so you can self-verify BEFORE committing/pushing — essential after large or multi-step edits. Default compares the working tree to the last commit (HEAD). Pass ref (e.g. 'origin/develop') to compare against a remote branch and catch staleness — i.e. see exactly how your working tree differs from what's on the remote, in one call. Pass staged=true to see only what's staged. Returns a --stat summary plus the patch (capped at 60KB; `truncated` flags when hit). Requires 'g' (git) access on the mount.",
654
+ inputSchema: {
655
+ type: "object",
656
+ properties: {
657
+ paths: { type: "array", items: { type: "string" }, description: "Repo-relative paths to limit the diff to. Omit for all changes." },
658
+ ref: { type: "string", description: "Compare the working tree against this ref instead of HEAD (e.g. 'origin/develop', a tag, or a commit sha). Use for remote/staleness comparison." },
659
+ staged: { type: "boolean", description: "Show only staged changes (index vs HEAD) instead of the full working-tree diff." },
660
+ mount: { type: "string", description: "Mount name (default: first mount)" },
661
+ },
662
+ additionalProperties: false,
663
+ },
664
+ },
665
+ {
666
+ name: "fs_exec",
667
+ description: "Run a command on the host machine. Command[0] must be in the server-side allowlist (git, pnpm, npx, node, python3, rclone, bash, cat, grep, find, wc, sha256sum, tsc, eslint). cwd is relative to mount root; path traversal rejected. Timeout enforced server-side. Every execution is audit-logged.",
668
+ inputSchema: {
669
+ type: "object",
670
+ properties: {
671
+ mount: { type: "string", description: "Mount name (default: first mount)" },
672
+ command: { type: "array", items: { type: "string" }, description: "Exec-style array: [\"git\", \"status\"]" },
673
+ cwd: { type: "string", description: "Working directory relative to mount root (default: mount root)" },
674
+ timeout_seconds: { type: "number", description: "Hard-kill on expiry (default 30, max 300)" },
675
+ shell: { type: "boolean", description: "If true, uses shell (enables pipes/redirects); if false, execFile (safer). Default false." },
676
+ env: { type: "object", description: "Additional env vars merged into process env" },
677
+ },
678
+ required: ["command"],
679
+ additionalProperties: false,
680
+ },
681
+ },
682
+ {
683
+ name: "fs_hash",
684
+ description: "Server-side file hashing. Hash files on the host without pulling them over the network. Returns sha256 (or md5/sha1) for up to 50 files in one call.",
685
+ inputSchema: {
686
+ type: "object",
687
+ properties: {
688
+ mount: { type: "string", description: "Mount name (default: first mount)" },
689
+ paths: { type: "array", items: { type: "string" }, description: "Paths relative to mount root (max 50)" },
690
+ algorithm: { type: "string", description: "Hash algorithm: sha256 (default), md5, sha1" },
691
+ },
692
+ required: ["paths"],
693
+ additionalProperties: false,
694
+ },
695
+ },
696
+ {
697
+ name: "fs_cat_lines",
698
+ description: "Read a specific line range from a file. Server-side extraction — only the requested lines are returned, not the whole file. 1-indexed.",
699
+ inputSchema: {
700
+ type: "object",
701
+ properties: {
702
+ mount: { type: "string", description: "Mount name (default: first mount)" },
703
+ path: { type: "string", description: "File path relative to mount root" },
704
+ start_line: { type: "number", description: "First line to return (1-indexed)" },
705
+ end_line: { type: "number", description: "Last line to return (default: EOF)" },
706
+ },
707
+ required: ["path", "start_line"],
708
+ additionalProperties: false,
709
+ },
710
+ },
711
+ {
712
+ name: "fs_grep_json",
713
+ description: "Structured grep results as JSON. Same server-side ripgrep execution as fs_grep, but returns structured [{file, line, match, before[], after[]}] instead of raw text.",
714
+ inputSchema: {
715
+ type: "object",
716
+ properties: {
717
+ mount: { type: "string", description: "Mount name (default: first mount)" },
718
+ pattern: { type: "string", description: "Regex pattern to search for" },
719
+ path: { type: "string", description: "Path to search in (default: mount root)" },
720
+ glob: { type: "string", description: "File glob filter (default: **/*)" },
721
+ max_results: { type: "number", description: "Max matches to return (default 50)" },
722
+ context_lines: { type: "number", description: "Lines of context before/after each match (default 0)" },
723
+ },
724
+ required: ["pattern"],
725
+ additionalProperties: false,
726
+ },
727
+ },
728
+ {
729
+ name: "fs_search",
730
+ description: "Combined glob + grep + stat in one call. Find files by type, modification date, and content pattern. Saves 2-3 MCP round-trips.",
731
+ inputSchema: {
732
+ type: "object",
733
+ properties: {
734
+ mount: { type: "string", description: "Mount name (default: first mount)" },
735
+ query: { type: "string", description: "Filename glob pattern (e.g. *.tsx)" },
736
+ file_types: { type: "array", items: { type: "string" }, description: "File extensions to include (e.g. [\".tsx\", \".ts\"])" },
737
+ modified_after: { type: "string", description: "ISO date — only files modified after this" },
738
+ modified_before: { type: "string", description: "ISO date — only files modified before this" },
739
+ content_pattern: { type: "string", description: "Regex to match in file content (optional)" },
740
+ path: { type: "string", description: "Subdirectory to search in (default: mount root)" },
741
+ max_results: { type: "number", description: "Max results (default 50)" },
742
+ },
743
+ additionalProperties: false,
744
+ },
745
+ },
746
+ {
747
+ name: "fs_put",
748
+ description: "Write binary content (base64-encoded) to a file. For text files prefer fs_write; use this for images, PDFs, DOCX, or any binary. Decodes server-side, writes atomically.",
749
+ inputSchema: {
750
+ type: "object",
751
+ properties: {
752
+ mount: { type: "string", description: "Mount name (default: first mount)" },
753
+ path: { type: "string", description: "File path relative to mount root" },
754
+ content_base64: { type: "string", description: "Base64-encoded file content" },
755
+ overwrite: { type: "boolean", description: "Overwrite if file exists (default false)" },
756
+ },
757
+ required: ["path", "content_base64"],
758
+ additionalProperties: false,
759
+ },
760
+ },
761
+ {
762
+ name: "fs_dav_setup",
763
+ description: "Start the WebDAV bridge and return a setup script that configures rclone in the sandbox. After running the script, use rclone commands directly: rclone lsd dav:corpus/ (list), rclone cat dav:corpus/file.md (read), rclone rcat dav:dev/file.txt (write). Each command is a stateless WebDAV call — no FUSE mount, no daemon, survives across turns.",
764
+ inputSchema: {
765
+ type: "object",
766
+ properties: {
767
+ ttl_seconds: { type: "number", description: "Hint for how long the mount will stay active (default 14400 = 4 h). Does not enforce expiry — informational only in v1." },
768
+ },
769
+ additionalProperties: false,
770
+ },
771
+ },
772
+ {
773
+ name: "clauth_ops_catalog",
774
+ description: "Return the Vultr deployment-control API catalog. Availability is still decided on Vultr.",
775
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
776
+ },
777
+ {
778
+ name: "clauth_ops_processes",
779
+ description: "Submit a scoped PM2 process-status query to the Vultr control plane. Only server-approved applications are returned.",
780
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
781
+ },
782
+ {
783
+ name: "clauth_ops_describe",
784
+ description: "Submit a scoped PM2 status query for one server-approved application.",
785
+ inputSchema: { type: "object", properties: { application: { type: "string" } }, required: ["application"], additionalProperties: false },
786
+ },
787
+ {
788
+ name: "clauth_ops_deploy",
789
+ description: "Submit one manifest-scoped development deployment to Vultr. The server chooses the repository, build, PM2 process, and allowed ref.",
790
+ inputSchema: { type: "object", properties: { application: { type: "string" }, ref: { type: "string" } }, required: ["application"], additionalProperties: false },
791
+ },
792
+ {
793
+ name: "clauth_ops_promote",
794
+ description: "Submit one server-allowlisted Coolify promotion and return its asynchronous job receipt.",
795
+ inputSchema: { type: "object", properties: { application_uuid: { type: "string" } }, required: ["application_uuid"], additionalProperties: false },
796
+ },
797
+ {
798
+ name: "clauth_ops_job",
799
+ description: "Read the terminal or in-progress receipt for a deployment-control job.",
800
+ inputSchema: { type: "object", properties: { job_id: { type: "string" } }, required: ["job_id"], additionalProperties: false },
801
+ },
802
+ {
803
+ name: "clauth_ops_run",
804
+ description: "Submit one PM2 operation through the Vultr control plane. The remote agent/admin profile decides whether the operation and target are available.",
805
+ inputSchema: { type: "object", properties: { operation: { type: "string" }, input: { type: "object" } }, required: ["operation"], additionalProperties: false },
806
+ },
807
+ ];
808
+
809
+ export const MCP_WRITE_TOOL_NAMES = new Set([
810
+ "clauth_enable",
811
+ "clauth_disable",
812
+ "clauth_set_project",
813
+ "clauth_generate_token",
814
+ "clauth_ops_deploy",
815
+ "clauth_ops_promote",
816
+ "clauth_ops_run",
817
+ ]);
818
+
819
+ export function filterMcpToolsForWriteMode(tools) {
820
+ if (process.env.CLAUTH_MCP_WRITE === "1") return tools;
821
+ return tools.filter(tool => !MCP_WRITE_TOOL_NAMES.has(tool.name));
822
+ }