taskover-mcp 1.0.1 → 1.2.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.
@@ -0,0 +1,1146 @@
1
+ // mcp-server/tool-map.js
2
+ // Maps MCP tool names to cloud RPC method names, argument builders, and schemas.
3
+ //
4
+ // SECURITY: Only tools whose rpc method is in cloud-adapter's MCP_ALLOWED_METHODS will work.
5
+ // This file defines the mapping shape + schema -- the allowlist is enforced in cloud-adapter.js.
6
+ //
7
+ // IMPORTANT: Every mapping was verified against the actual RPC_REGISTRY in server.js.
8
+ // Do NOT add speculative mappings. If a method doesn't exist in the RPC registry,
9
+ // it must be added to server.js first (see Task 1).
10
+ //
11
+ // Schema validation runs BEFORE the RPC call. Unknown fields are stripped.
12
+ // Required fields cause an error if missing. Types are checked loosely (string, number, boolean, array, object).
13
+
14
+ // ── Schema helpers ──
15
+
16
+ function validateSchema(args, schema) {
17
+ const errors = [];
18
+ const cleaned = {};
19
+
20
+ // Check required fields
21
+ for (const field of (schema.required || [])) {
22
+ if (args[field] === undefined || args[field] === null) {
23
+ errors.push(`Missing required field: ${field}`);
24
+ }
25
+ }
26
+
27
+ // Copy allowed fields only (required + optional), with type checks
28
+ const allFields = { ...(schema.fields || {}) };
29
+ for (const [field, type] of Object.entries(allFields)) {
30
+ if (args[field] !== undefined && args[field] !== null) {
31
+ if (type && !checkType(args[field], type)) {
32
+ errors.push(`Field "${field}" must be ${type}, got ${typeof args[field]}`);
33
+ } else {
34
+ cleaned[field] = args[field];
35
+ }
36
+ }
37
+ }
38
+
39
+ // SECURITY: Unknown fields are silently dropped -- never forwarded to RPC
40
+ return { errors, cleaned };
41
+ }
42
+
43
+ function checkType(value, type) {
44
+ if (type === "string") return typeof value === "string";
45
+ if (type === "number") return typeof value === "number";
46
+ if (type === "boolean") return typeof value === "boolean";
47
+ if (type === "array") return Array.isArray(value);
48
+ if (type === "object") return typeof value === "object" && !Array.isArray(value) && value !== null;
49
+ if (type === "any") return true;
50
+ return true; // unknown type = allow
51
+ }
52
+
53
+ // ── Tool mappings ──
54
+ // Each entry:
55
+ // rpc: string -- RPC method name (must be in cloud-adapter allowlist)
56
+ // schema: object -- { required: string[], fields: { name: type } }
57
+ // args: (cleaned, raw) -- builds the RPC args array from validated+cleaned fields
58
+ // `raw` is passed for fields like project_id that are always
59
+ // present but not part of the "update" object
60
+ //
61
+ // For update methods, the `schema` defines the UPDATABLE fields only.
62
+ // The record ID and project ID come from `raw` (the original MCP args).
63
+
64
+ const TOOL_MAP = {
65
+
66
+ // ── Dashboard & context ──
67
+
68
+ taskovergg_dashboard: {
69
+ rpc: "dashboard",
70
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
71
+ args: (c, r) => [r.project_id],
72
+ },
73
+ taskovergg_context_export: {
74
+ rpc: "contextExport",
75
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
76
+ args: (c, r) => [r.project_id],
77
+ },
78
+ taskovergg_search: {
79
+ rpc: "search",
80
+ schema: { required: ["project_id", "query"], fields: { project_id: "string", query: "string" } },
81
+ args: (c, r) => [r.project_id, r.query],
82
+ },
83
+ taskovergg_list_projects: {
84
+ rpc: "listProjects",
85
+ schema: { required: [], fields: {} },
86
+ args: () => [],
87
+ },
88
+
89
+ // ── Tasks ──
90
+
91
+ taskovergg_get_tasks: {
92
+ rpc: "getTasks",
93
+ schema: {
94
+ required: ["project_id"],
95
+ fields: { project_id: "string", status: "string", phase: "string" },
96
+ },
97
+ args: (c, r) => [r.project_id, r.status, r.phase],
98
+ },
99
+ taskovergg_add_task: {
100
+ rpc: "addTask",
101
+ schema: {
102
+ required: ["project_id", "title"],
103
+ fields: {
104
+ project_id: "string", title: "string", description: "string",
105
+ status: "string", phase: "string", priority: "string",
106
+ tags: "string", checklist: "string",
107
+ },
108
+ },
109
+ // REVISED per M0: project:true add — single object with projectId inside
110
+ // NOTE: createdBy is intentionally HARDCODED and excluded from schema.fields.
111
+ // The RPC accepts it (matrix write allowlist), but the MCP tool does not expose it
112
+ // to callers. This prevents callers from impersonating other authors.
113
+ args: (c, r) => [{
114
+ projectId: r.project_id, title: c.title, description: c.description,
115
+ status: c.status || "todo", phase: c.phase, priority: c.priority || "medium",
116
+ tags: c.tags, checklist: c.checklist, createdBy: "Assistant",
117
+ }],
118
+ },
119
+ taskovergg_update_task: {
120
+ rpc: "updateTask",
121
+ schema: {
122
+ required: ["task_id"],
123
+ fields: {
124
+ task_id: "string", project_id: "string",
125
+ title: "string", description: "string", status: "string",
126
+ phase: "string", priority: "string", tags: "string", checklist: "string",
127
+ },
128
+ },
129
+ // SECURITY: explicit field allowlist -- only these fields are forwarded as updates
130
+ // REVISED per M0: record:tasks ownership — no projectId in args
131
+ args: (c, r) => [r.task_id, {
132
+ ...(c.title !== undefined && { title: c.title }),
133
+ ...(c.description !== undefined && { description: c.description }),
134
+ ...(c.status !== undefined && { status: c.status }),
135
+ ...(c.phase !== undefined && { phase: c.phase }),
136
+ ...(c.priority !== undefined && { priority: c.priority }),
137
+ ...(c.tags !== undefined && { tags: c.tags }),
138
+ ...(c.checklist !== undefined && { checklist: c.checklist }),
139
+ }],
140
+ },
141
+ taskovergg_move_task: {
142
+ rpc: "moveTask",
143
+ schema: {
144
+ required: ["task_id", "status"],
145
+ fields: { task_id: "string", project_id: "string", status: "string" },
146
+ },
147
+ // REVISED per M0: record:tasks ownership — [taskId, status]
148
+ args: (c, r) => [r.task_id, r.status],
149
+ },
150
+ taskovergg_add_task_comment: {
151
+ rpc: "addTaskComment",
152
+ schema: {
153
+ required: ["task_id", "text"],
154
+ fields: { task_id: "string", project_id: "string", text: "string" },
155
+ },
156
+ // REVISED per M0: record:tasks ownership — [taskId, text] (not wrapped in object)
157
+ args: (c, r) => [r.task_id, c.text],
158
+ },
159
+
160
+ // ── Systems ──
161
+
162
+ taskovergg_get_systems: {
163
+ rpc: "getSystems",
164
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
165
+ args: (c, r) => [r.project_id],
166
+ },
167
+ taskovergg_add_system: {
168
+ rpc: "addSystem",
169
+ schema: {
170
+ required: ["project_id", "name"],
171
+ fields: {
172
+ project_id: "string", name: "string", description: "string",
173
+ status: "string", key_files: "string", notes: "string", critical_rules: "string",
174
+ },
175
+ },
176
+ // REVISED per M0: project:true add — single object with projectId inside
177
+ args: (c, r) => [{
178
+ projectId: r.project_id, name: c.name, description: c.description,
179
+ status: c.status || "planned", keyFiles: c.key_files, notes: c.notes,
180
+ criticalRules: c.critical_rules,
181
+ }],
182
+ },
183
+ taskovergg_update_system: {
184
+ rpc: "updateSystem",
185
+ schema: {
186
+ required: ["system_id"],
187
+ fields: {
188
+ system_id: "string", project_id: "string",
189
+ name: "string", description: "string", status: "string",
190
+ key_files: "string", notes: "string", critical_rules: "string",
191
+ },
192
+ },
193
+ // REVISED per M0: record:systems ownership — no projectId in args
194
+ args: (c, r) => [r.system_id, {
195
+ ...(c.name !== undefined && { name: c.name }),
196
+ ...(c.description !== undefined && { description: c.description }),
197
+ ...(c.status !== undefined && { status: c.status }),
198
+ ...(c.key_files !== undefined && { keyFiles: c.key_files }),
199
+ ...(c.notes !== undefined && { notes: c.notes }),
200
+ ...(c.critical_rules !== undefined && { criticalRules: c.critical_rules }),
201
+ }],
202
+ },
203
+
204
+ // ── Sessions ──
205
+
206
+ taskovergg_get_sessions: {
207
+ rpc: "getSessions",
208
+ schema: {
209
+ required: ["project_id"],
210
+ fields: { project_id: "string", limit: "number" },
211
+ },
212
+ args: (c, r) => [r.project_id, r.limit],
213
+ },
214
+ taskovergg_log_session: {
215
+ rpc: "logSession",
216
+ schema: {
217
+ required: ["project_id", "title"],
218
+ fields: {
219
+ project_id: "string", title: "string", summary: "string",
220
+ what_we_did: "string", where_we_left_off: "string",
221
+ next_steps: "string", systems_worked_on: "string",
222
+ },
223
+ },
224
+ // REVISED per M0: project:true add — single object with projectId inside
225
+ args: (c, r) => [{
226
+ projectId: r.project_id, title: c.title, summary: c.summary,
227
+ whatWeDid: c.what_we_did, whereWeLeftOff: c.where_we_left_off,
228
+ nextSteps: c.next_steps, systemsWorkedOn: c.systems_worked_on,
229
+ }],
230
+ },
231
+
232
+ // ── Changelog ──
233
+
234
+ taskovergg_get_changelog: {
235
+ rpc: "getChangelog",
236
+ schema: {
237
+ required: ["project_id"],
238
+ fields: { project_id: "string", limit: "number" },
239
+ },
240
+ args: (c, r) => [r.project_id, r.limit],
241
+ },
242
+ taskovergg_add_changelog: {
243
+ rpc: "addChangelog",
244
+ schema: {
245
+ required: ["project_id", "title", "changes"],
246
+ fields: {
247
+ project_id: "string", title: "string",
248
+ changes: "string", systems_affected: "string",
249
+ },
250
+ },
251
+ // REVISED per M0: project:true add — single object with projectId inside
252
+ args: (c, r) => [{
253
+ projectId: r.project_id, title: c.title, changes: c.changes,
254
+ systemsAffected: c.systems_affected,
255
+ }],
256
+ },
257
+
258
+ // ── Bugs ──
259
+
260
+ taskovergg_get_bugs: {
261
+ rpc: "getBugs",
262
+ schema: {
263
+ required: ["project_id"],
264
+ fields: { project_id: "string", status: "string" },
265
+ },
266
+ args: (c, r) => [r.project_id, r.status],
267
+ },
268
+ taskovergg_add_bug: {
269
+ rpc: "addBug",
270
+ schema: {
271
+ required: ["project_id", "title"],
272
+ fields: {
273
+ project_id: "string", title: "string", system_id: "string",
274
+ description: "string", priority: "string", steps_to_reproduce: "string",
275
+ },
276
+ },
277
+ // REVISED per M0: project:true add — single object with projectId inside
278
+ args: (c, r) => [{
279
+ projectId: r.project_id, systemId: c.system_id, title: c.title,
280
+ description: c.description, priority: c.priority,
281
+ stepsToReproduce: c.steps_to_reproduce,
282
+ }],
283
+ },
284
+ taskovergg_fix_bug: {
285
+ rpc: "fixBug",
286
+ schema: {
287
+ required: ["bug_id"],
288
+ fields: { bug_id: "string", project_id: "string", fix_description: "string" },
289
+ },
290
+ // REVISED per M0: record:bugs ownership — [bugId, fixDescription]
291
+ args: (c, r) => [r.bug_id, c.fix_description],
292
+ },
293
+
294
+ // ── Decisions ──
295
+
296
+ taskovergg_get_decisions: {
297
+ rpc: "getDecisions",
298
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
299
+ args: (c, r) => [r.project_id],
300
+ },
301
+ taskovergg_log_decision: {
302
+ rpc: "logDecision",
303
+ schema: {
304
+ required: ["project_id", "decision"],
305
+ fields: {
306
+ project_id: "string", decision: "string", context: "string",
307
+ system_id: "string", alternatives: "string",
308
+ },
309
+ },
310
+ // REVISED per M0: project:true add — single object with projectId inside
311
+ args: (c, r) => [{
312
+ projectId: r.project_id, decision: c.decision, context: c.context,
313
+ systemId: c.system_id, alternatives: c.alternatives,
314
+ }],
315
+ },
316
+
317
+ // ── Blueprints ──
318
+
319
+ taskovergg_get_blueprints: {
320
+ rpc: "getBlueprints",
321
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
322
+ args: (c, r) => [r.project_id],
323
+ },
324
+ taskovergg_add_blueprint: {
325
+ rpc: "addBlueprint",
326
+ schema: {
327
+ required: ["project_id", "name"],
328
+ fields: {
329
+ project_id: "string", name: "string", path: "string",
330
+ parent_class: "string", system_id: "string", description: "string",
331
+ variables: "string", key_functions: "string",
332
+ },
333
+ },
334
+ // REVISED per M0: project:true add — single object with projectId inside
335
+ args: (c, r) => [{
336
+ projectId: r.project_id, name: c.name, path: c.path,
337
+ parentClass: c.parent_class, systemId: c.system_id,
338
+ description: c.description, variables: c.variables,
339
+ keyFunctions: c.key_functions,
340
+ }],
341
+ },
342
+ taskovergg_update_blueprint: {
343
+ rpc: "updateBlueprint",
344
+ schema: {
345
+ required: ["blueprint_id"],
346
+ fields: {
347
+ blueprint_id: "string", project_id: "string",
348
+ name: "string", path: "string", parent_class: "string",
349
+ description: "string", variables: "string", key_functions: "string",
350
+ },
351
+ },
352
+ // REVISED per M0: record:blueprints ownership — no projectId in args
353
+ args: (c, r) => [r.blueprint_id, {
354
+ ...(c.name !== undefined && { name: c.name }),
355
+ ...(c.path !== undefined && { path: c.path }),
356
+ ...(c.parent_class !== undefined && { parentClass: c.parent_class }),
357
+ ...(c.description !== undefined && { description: c.description }),
358
+ ...(c.variables !== undefined && { variables: c.variables }),
359
+ ...(c.key_functions !== undefined && { keyFunctions: c.key_functions }),
360
+ }],
361
+ },
362
+ // REMOVED per M0: taskovergg_get_blueprint_graphs — DEFERRED (ANOMALY-1: ownership mismatch)
363
+ // REMOVED per M0: taskovergg_set_blueprint_graph — DEFERRED (ANOMALY-1: ownership mismatch)
364
+ // REMOVED per M0: taskovergg_add_graph_nodes — DEFERRED (ANOMALY-1: ownership mismatch)
365
+ // Unblock path: fix RPC registry project:true → record:"blueprints" for all 3 methods
366
+
367
+ // ── Notes ──
368
+
369
+ taskovergg_get_notes: {
370
+ rpc: "getNotes",
371
+ schema: {
372
+ required: ["project_id"],
373
+ fields: { project_id: "string", parent_type: "string", parent_id: "string" },
374
+ },
375
+ args: (c, r) => [r.project_id, r.parent_type, r.parent_id],
376
+ },
377
+ taskovergg_add_note: {
378
+ rpc: "addNote",
379
+ schema: {
380
+ required: ["project_id", "content"],
381
+ fields: {
382
+ project_id: "string", content: "string",
383
+ parent_type: "string", parent_id: "string", title: "string",
384
+ },
385
+ },
386
+ // REVISED per M0: project:true add — single object with projectId inside
387
+ args: (c, r) => [{
388
+ projectId: r.project_id, parentType: c.parent_type,
389
+ parentId: c.parent_id, title: c.title, content: c.content,
390
+ }],
391
+ },
392
+
393
+ // ── Backups ──
394
+
395
+ taskovergg_log_backup: {
396
+ rpc: "logBackup",
397
+ schema: {
398
+ required: ["project_id"],
399
+ fields: { project_id: "string", title: "string", description: "string" },
400
+ },
401
+ // REVISED per M0: project:true add — single object with projectId inside
402
+ args: (c, r) => [{ projectId: r.project_id, title: c.title, description: c.description }],
403
+ },
404
+
405
+ // ── Levels ──
406
+
407
+ taskovergg_get_levels: {
408
+ rpc: "getLevels",
409
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
410
+ args: (c, r) => [r.project_id],
411
+ },
412
+ taskovergg_add_level: {
413
+ rpc: "addLevel",
414
+ schema: {
415
+ required: ["project_id", "name"],
416
+ fields: {
417
+ project_id: "string", name: "string",
418
+ map: "string", actor_count: "number", status: "string",
419
+ },
420
+ },
421
+ // REVISED per M0: project:true add — single object with projectId inside
422
+ args: (c, r) => [{
423
+ projectId: r.project_id, name: c.name, map: c.map,
424
+ actorCount: c.actor_count, status: c.status,
425
+ }],
426
+ },
427
+ taskovergg_update_level: {
428
+ rpc: "updateLevel",
429
+ schema: {
430
+ required: ["level_id"],
431
+ fields: {
432
+ level_id: "string", project_id: "string",
433
+ name: "string", map: "string", actor_count: "number", status: "string",
434
+ },
435
+ },
436
+ // REVISED per M0: record:levels ownership — no projectId in args
437
+ args: (c, r) => [r.level_id, {
438
+ ...(c.name !== undefined && { name: c.name }),
439
+ ...(c.map !== undefined && { map: c.map }),
440
+ ...(c.actor_count !== undefined && { actorCount: c.actor_count }),
441
+ ...(c.status !== undefined && { status: c.status }),
442
+ }],
443
+ },
444
+ taskovergg_add_actor: {
445
+ rpc: "addActor",
446
+ schema: {
447
+ required: ["level_id", "name"],
448
+ fields: {
449
+ level_id: "string", project_id: "string", name: "string",
450
+ x: "number", y: "number", z: "number", notes: "string",
451
+ },
452
+ },
453
+ // REVISED per M0: record:levels ownership — [levelId, actorData]
454
+ args: (c, r) => [r.level_id, {
455
+ name: c.name, x: c.x, y: c.y, z: c.z, notes: c.notes,
456
+ }],
457
+ },
458
+ taskovergg_remove_actor: {
459
+ rpc: "removeActor",
460
+ schema: {
461
+ required: ["level_id", "actor_index"],
462
+ fields: { level_id: "string", project_id: "string", actor_index: "number" },
463
+ },
464
+ // REVISED per M0: record:levels ownership — [levelId, actorIndex]
465
+ args: (c, r) => [r.level_id, r.actor_index],
466
+ },
467
+
468
+ // ── Plugins ──
469
+
470
+ taskovergg_get_plugins: {
471
+ rpc: "getPlugins",
472
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
473
+ args: (c, r) => [r.project_id],
474
+ },
475
+ taskovergg_add_plugin: {
476
+ rpc: "addPlugin",
477
+ schema: {
478
+ required: ["project_id", "name"],
479
+ fields: {
480
+ project_id: "string", name: "string",
481
+ version: "string", source: "string", dependents: "string",
482
+ },
483
+ },
484
+ // REVISED per M0: project:true add — single object with projectId inside
485
+ args: (c, r) => [{
486
+ projectId: r.project_id, name: c.name, version: c.version,
487
+ source: c.source, dependents: c.dependents,
488
+ }],
489
+ },
490
+ taskovergg_update_plugin: {
491
+ rpc: "updatePlugin",
492
+ schema: {
493
+ required: ["plugin_id"],
494
+ fields: {
495
+ plugin_id: "string", project_id: "string",
496
+ name: "string", version: "string", source: "string", dependents: "string",
497
+ },
498
+ },
499
+ // REVISED per M0: record:plugins ownership — no projectId in args
500
+ args: (c, r) => [r.plugin_id, {
501
+ ...(c.name !== undefined && { name: c.name }),
502
+ ...(c.version !== undefined && { version: c.version }),
503
+ ...(c.source !== undefined && { source: c.source }),
504
+ ...(c.dependents !== undefined && { dependents: c.dependents }),
505
+ }],
506
+ },
507
+ // taskovergg_delete_plugin -- DEFERRED: delete ops blocked in beta
508
+
509
+ // ── Build Errors ──
510
+
511
+ taskovergg_get_build_errors: {
512
+ rpc: "getBuildErrors",
513
+ schema: {
514
+ required: ["project_id"],
515
+ fields: { project_id: "string", status: "string" },
516
+ },
517
+ args: (c, r) => [r.project_id, r.status],
518
+ },
519
+ taskovergg_add_build_error: {
520
+ rpc: "addBuildError",
521
+ schema: {
522
+ required: ["project_id", "error"],
523
+ fields: { project_id: "string", error: "string", bp: "string" },
524
+ },
525
+ // REVISED per M0: project:true add — single object with projectId inside
526
+ args: (c, r) => [{ projectId: r.project_id, error: c.error, bp: c.bp }],
527
+ },
528
+ taskovergg_fix_build_error: {
529
+ rpc: "fixBuildError",
530
+ schema: {
531
+ required: ["error_id"],
532
+ fields: { error_id: "string", project_id: "string", fix: "string" },
533
+ },
534
+ // REVISED per M0: record:build_errors ownership — [errorId, fix]
535
+ args: (c, r) => [r.error_id, c.fix],
536
+ },
537
+
538
+ // ── Optimize ──
539
+
540
+ taskovergg_get_optimize_items: {
541
+ rpc: "getOptimizeItems",
542
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
543
+ args: (c, r) => [r.project_id],
544
+ },
545
+ taskovergg_add_optimize_item: {
546
+ rpc: "addOptimizeItem",
547
+ schema: {
548
+ required: ["project_id", "title"],
549
+ fields: {
550
+ project_id: "string", title: "string",
551
+ category: "string", status: "string", savings: "string",
552
+ },
553
+ },
554
+ // REVISED per M0: project:true add — single object with projectId inside
555
+ args: (c, r) => [{
556
+ projectId: r.project_id, title: c.title, category: c.category,
557
+ status: c.status, savings: c.savings,
558
+ }],
559
+ },
560
+ taskovergg_update_optimize_item: {
561
+ rpc: "updateOptimizeItem",
562
+ schema: {
563
+ required: ["item_id"],
564
+ fields: {
565
+ item_id: "string", project_id: "string",
566
+ title: "string", category: "string", status: "string", savings: "string",
567
+ },
568
+ },
569
+ // REVISED per M0: record:optimize_items — no projectId in args
570
+ args: (c, r) => [r.item_id, {
571
+ ...(c.title !== undefined && { title: c.title }),
572
+ ...(c.category !== undefined && { category: c.category }),
573
+ ...(c.status !== undefined && { status: c.status }),
574
+ ...(c.savings !== undefined && { savings: c.savings }),
575
+ }],
576
+ },
577
+
578
+ // ── Perf Budget ──
579
+
580
+ taskovergg_get_perf_budget: {
581
+ rpc: "getPerfBudget",
582
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
583
+ args: (c, r) => [r.project_id],
584
+ },
585
+ taskovergg_add_perf_budget: {
586
+ rpc: "addPerfBudget",
587
+ schema: {
588
+ required: ["project_id", "scene"],
589
+ fields: {
590
+ project_id: "string", scene: "string",
591
+ fps: "string", gpu: "string", memory: "string", notes: "string",
592
+ },
593
+ },
594
+ // REVISED per M0: project:true add — single object with projectId inside
595
+ args: (c, r) => [{
596
+ projectId: r.project_id, scene: c.scene, fps: c.fps,
597
+ gpu: c.gpu, memory: c.memory, notes: c.notes,
598
+ }],
599
+ },
600
+
601
+ // ── Playtests ──
602
+
603
+ taskovergg_get_playtests: {
604
+ rpc: "getPlaytests",
605
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
606
+ args: (c, r) => [r.project_id],
607
+ },
608
+ taskovergg_add_playtest: {
609
+ rpc: "addPlaytest",
610
+ schema: {
611
+ required: ["project_id"],
612
+ fields: {
613
+ project_id: "string", duration: "string", issues: "string",
614
+ rating: "number", notes: "string", tester: "string", build_version: "string",
615
+ },
616
+ },
617
+ // REVISED per M0: project:true add — single object with projectId inside
618
+ args: (c, r) => [{
619
+ projectId: r.project_id, duration: c.duration, issues: c.issues,
620
+ rating: c.rating, notes: c.notes, tester: c.tester,
621
+ buildVersion: c.build_version,
622
+ }],
623
+ },
624
+
625
+ // ── Milestones ──
626
+
627
+ taskovergg_get_milestones: {
628
+ rpc: "getMilestones",
629
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
630
+ args: (c, r) => [r.project_id],
631
+ },
632
+ taskovergg_add_milestone: {
633
+ rpc: "addMilestone",
634
+ schema: {
635
+ required: ["project_id", "title"],
636
+ fields: {
637
+ project_id: "string", title: "string",
638
+ date: "string", status: "string", notes: "string",
639
+ },
640
+ },
641
+ // REVISED per M0: project:true add — single object with projectId inside
642
+ args: (c, r) => [{
643
+ projectId: r.project_id, title: c.title, date: c.date,
644
+ status: c.status, notes: c.notes,
645
+ }],
646
+ },
647
+ taskovergg_update_milestone: {
648
+ rpc: "updateMilestone",
649
+ schema: {
650
+ required: ["milestone_id"],
651
+ fields: {
652
+ milestone_id: "string", project_id: "string",
653
+ title: "string", date: "string", status: "string", notes: "string",
654
+ },
655
+ },
656
+ // REVISED per M0: record:milestones — no projectId in args
657
+ args: (c, r) => [r.milestone_id, {
658
+ ...(c.title !== undefined && { title: c.title }),
659
+ ...(c.date !== undefined && { date: c.date }),
660
+ ...(c.status !== undefined && { status: c.status }),
661
+ ...(c.notes !== undefined && { notes: c.notes }),
662
+ }],
663
+ },
664
+
665
+ // ── Iterations ──
666
+
667
+ taskovergg_get_iterations: {
668
+ rpc: "getIterations",
669
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
670
+ args: (c, r) => [r.project_id],
671
+ },
672
+ taskovergg_add_iteration: {
673
+ rpc: "addIteration",
674
+ schema: {
675
+ required: ["project_id", "feature"],
676
+ fields: {
677
+ project_id: "string", feature: "string",
678
+ version: "string", change: "string", reason: "string",
679
+ },
680
+ },
681
+ // REVISED per M0: project:true add — single object with projectId inside
682
+ args: (c, r) => [{
683
+ projectId: r.project_id, feature: c.feature, version: c.version,
684
+ change: c.change, reason: c.reason,
685
+ }],
686
+ },
687
+
688
+ // ── Dialogues ──
689
+
690
+ taskovergg_get_dialogues: {
691
+ rpc: "getDialogues",
692
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
693
+ args: (c, r) => [r.project_id],
694
+ },
695
+ taskovergg_add_dialogue: {
696
+ rpc: "addDialogue",
697
+ schema: {
698
+ required: ["project_id", "npc"],
699
+ fields: {
700
+ project_id: "string", npc: "string", line: "string",
701
+ choices: "string", next_node: "string", notes: "string",
702
+ },
703
+ },
704
+ // REVISED per M0: project:true add — single object with projectId inside
705
+ args: (c, r) => [{
706
+ projectId: r.project_id, npc: c.npc, line: c.line,
707
+ choices: c.choices, nextNode: c.next_node, notes: c.notes,
708
+ }],
709
+ },
710
+ taskovergg_update_dialogue: {
711
+ rpc: "updateDialogue",
712
+ schema: {
713
+ required: ["dialogue_id"],
714
+ fields: {
715
+ dialogue_id: "string", project_id: "string",
716
+ npc: "string", line: "string", choices: "string",
717
+ next_node: "string", notes: "string",
718
+ },
719
+ },
720
+ // REVISED per M0: record:dialogues — no projectId in args
721
+ args: (c, r) => [r.dialogue_id, {
722
+ ...(c.npc !== undefined && { npc: c.npc }),
723
+ ...(c.line !== undefined && { line: c.line }),
724
+ ...(c.choices !== undefined && { choices: c.choices }),
725
+ ...(c.next_node !== undefined && { nextNode: c.next_node }),
726
+ ...(c.notes !== undefined && { notes: c.notes }),
727
+ }],
728
+ },
729
+
730
+ // ── Sounds ──
731
+
732
+ taskovergg_get_sounds: {
733
+ rpc: "getSounds",
734
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
735
+ args: (c, r) => [r.project_id],
736
+ },
737
+ taskovergg_add_sound: {
738
+ rpc: "addSound",
739
+ schema: {
740
+ required: ["project_id", "name"],
741
+ fields: {
742
+ project_id: "string", name: "string", actor: "string",
743
+ trigger: "string", spatial: "string", attenuation: "string", notes: "string",
744
+ },
745
+ },
746
+ // REVISED per M0: project:true add — single object with projectId inside
747
+ args: (c, r) => [{
748
+ projectId: r.project_id, name: c.name, actor: c.actor,
749
+ trigger: c.trigger, spatial: c.spatial,
750
+ attenuation: c.attenuation, notes: c.notes,
751
+ }],
752
+ },
753
+ taskovergg_update_sound: {
754
+ rpc: "updateSound",
755
+ schema: {
756
+ required: ["sound_id"],
757
+ fields: {
758
+ sound_id: "string", project_id: "string",
759
+ name: "string", actor: "string", trigger: "string",
760
+ spatial: "string", attenuation: "string", notes: "string",
761
+ },
762
+ },
763
+ // REVISED per M0: record:sounds — no projectId in args
764
+ args: (c, r) => [r.sound_id, {
765
+ ...(c.name !== undefined && { name: c.name }),
766
+ ...(c.actor !== undefined && { actor: c.actor }),
767
+ ...(c.trigger !== undefined && { trigger: c.trigger }),
768
+ ...(c.spatial !== undefined && { spatial: c.spatial }),
769
+ ...(c.attenuation !== undefined && { attenuation: c.attenuation }),
770
+ ...(c.notes !== undefined && { notes: c.notes }),
771
+ }],
772
+ },
773
+
774
+ // ── Controls ──
775
+
776
+ taskovergg_get_controls: {
777
+ rpc: "getControls",
778
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
779
+ args: (c, r) => [r.project_id],
780
+ },
781
+ taskovergg_add_control: {
782
+ rpc: "addControl",
783
+ schema: {
784
+ required: ["project_id", "key", "action"],
785
+ fields: {
786
+ project_id: "string", key: "string", action: "string",
787
+ context: "string", condition: "string",
788
+ },
789
+ },
790
+ // REVISED per M0: project:true add — single object with projectId inside
791
+ args: (c, r) => [{
792
+ projectId: r.project_id, key: c.key, action: c.action,
793
+ context: c.context, condition: c.condition,
794
+ }],
795
+ },
796
+ taskovergg_update_control: {
797
+ rpc: "updateControl",
798
+ schema: {
799
+ required: ["control_id"],
800
+ fields: {
801
+ control_id: "string", project_id: "string",
802
+ key: "string", action: "string", context: "string", condition: "string",
803
+ },
804
+ },
805
+ // REVISED per M0: record:controls — no projectId in args
806
+ args: (c, r) => [r.control_id, {
807
+ ...(c.key !== undefined && { key: c.key }),
808
+ ...(c.action !== undefined && { action: c.action }),
809
+ ...(c.context !== undefined && { context: c.context }),
810
+ ...(c.condition !== undefined && { condition: c.condition }),
811
+ }],
812
+ },
813
+
814
+ // ── Assets ──
815
+
816
+ taskovergg_get_assets: {
817
+ rpc: "getAssets",
818
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
819
+ args: (c, r) => [r.project_id],
820
+ },
821
+ taskovergg_add_asset: {
822
+ rpc: "addAsset",
823
+ schema: {
824
+ required: ["project_id", "name"],
825
+ fields: {
826
+ project_id: "string", name: "string", type: "string",
827
+ path: "string", size: "string", status: "string", used_by: "string",
828
+ },
829
+ },
830
+ // REVISED per M0: project:true add — single object with projectId inside
831
+ args: (c, r) => [{
832
+ projectId: r.project_id, name: c.name, type: c.type,
833
+ path: c.path, size: c.size, status: c.status, usedBy: c.used_by,
834
+ }],
835
+ },
836
+ taskovergg_update_asset: {
837
+ rpc: "updateAsset",
838
+ schema: {
839
+ required: ["asset_id"],
840
+ fields: {
841
+ asset_id: "string", project_id: "string",
842
+ name: "string", type: "string", path: "string",
843
+ size: "string", status: "string", used_by: "string",
844
+ },
845
+ },
846
+ // REVISED per M0: record:assets — no projectId in args
847
+ args: (c, r) => [r.asset_id, {
848
+ ...(c.name !== undefined && { name: c.name }),
849
+ ...(c.type !== undefined && { type: c.type }),
850
+ ...(c.path !== undefined && { path: c.path }),
851
+ ...(c.size !== undefined && { size: c.size }),
852
+ ...(c.status !== undefined && { status: c.status }),
853
+ ...(c.used_by !== undefined && { usedBy: c.used_by }),
854
+ }],
855
+ },
856
+
857
+ // ── References ──
858
+
859
+ taskovergg_get_refs: {
860
+ rpc: "getRefs",
861
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
862
+ args: (c, r) => [r.project_id],
863
+ },
864
+ taskovergg_add_ref: {
865
+ rpc: "addRef",
866
+ schema: {
867
+ required: ["project_id", "title"],
868
+ fields: {
869
+ project_id: "string", title: "string",
870
+ type: "string", url: "string", notes: "string",
871
+ },
872
+ },
873
+ // REVISED per M0: project:true add — single object with projectId inside
874
+ args: (c, r) => [{
875
+ projectId: r.project_id, title: c.title, type: c.type,
876
+ url: c.url, notes: c.notes,
877
+ }],
878
+ },
879
+
880
+ // ── Marketing ──
881
+
882
+ taskovergg_get_marketing_items: {
883
+ rpc: "getMarketingItems",
884
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
885
+ args: (c, r) => [r.project_id],
886
+ },
887
+ taskovergg_add_marketing_item: {
888
+ rpc: "addMarketingItem",
889
+ schema: {
890
+ required: ["project_id", "item"],
891
+ fields: {
892
+ project_id: "string", item: "string",
893
+ category: "string", status: "string", notes: "string",
894
+ },
895
+ },
896
+ // REVISED per M0: project:true add — single object with projectId inside
897
+ args: (c, r) => [{
898
+ projectId: r.project_id, item: c.item, category: c.category,
899
+ status: c.status, notes: c.notes,
900
+ }],
901
+ },
902
+ taskovergg_update_marketing_item: {
903
+ rpc: "updateMarketingItem",
904
+ schema: {
905
+ required: ["marketing_id"],
906
+ fields: {
907
+ marketing_id: "string", project_id: "string",
908
+ item: "string", category: "string", status: "string", notes: "string",
909
+ },
910
+ },
911
+ // REVISED per M0: record:marketing_items — no projectId in args
912
+ args: (c, r) => [r.marketing_id, {
913
+ ...(c.item !== undefined && { item: c.item }),
914
+ ...(c.category !== undefined && { category: c.category }),
915
+ ...(c.status !== undefined && { status: c.status }),
916
+ ...(c.notes !== undefined && { notes: c.notes }),
917
+ }],
918
+ },
919
+
920
+ // ── Wiki ──
921
+
922
+ taskovergg_get_wiki_pages: {
923
+ rpc: "getWikiPages",
924
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
925
+ args: (c, r) => [r.project_id],
926
+ },
927
+ taskovergg_add_wiki_page: {
928
+ rpc: "addWikiPage",
929
+ schema: {
930
+ required: ["project_id", "title"],
931
+ fields: { project_id: "string", title: "string", category: "string", content: "string" },
932
+ },
933
+ // REVISED per M0: project:true add — single object with projectId inside
934
+ args: (c, r) => [{
935
+ projectId: r.project_id, title: c.title, category: c.category,
936
+ content: c.content,
937
+ }],
938
+ },
939
+ taskovergg_update_wiki_page: {
940
+ rpc: "updateWikiPage",
941
+ schema: {
942
+ required: ["wiki_id"],
943
+ fields: {
944
+ wiki_id: "string", project_id: "string",
945
+ title: "string", category: "string", content: "string",
946
+ },
947
+ },
948
+ // REVISED per M0: record:wiki_pages — no projectId in args
949
+ args: (c, r) => [r.wiki_id, {
950
+ ...(c.title !== undefined && { title: c.title }),
951
+ ...(c.category !== undefined && { category: c.category }),
952
+ ...(c.content !== undefined && { content: c.content }),
953
+ }],
954
+ },
955
+
956
+ // ── Ship Readiness ──
957
+
958
+ taskovergg_get_ship_checked: {
959
+ rpc: "getShipChecked",
960
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
961
+ args: (c, r) => [r.project_id],
962
+ },
963
+ taskovergg_toggle_ship_check: {
964
+ rpc: "toggleShipCheck",
965
+ schema: {
966
+ required: ["project_id", "step_id"],
967
+ fields: { project_id: "string", step_id: "string" },
968
+ },
969
+ args: (c, r) => [r.project_id, r.step_id],
970
+ },
971
+
972
+ // ── Open Questions ──
973
+
974
+ taskovergg_get_open_questions: {
975
+ rpc: "getOpenQuestions",
976
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
977
+ args: (c, r) => [r.project_id],
978
+ },
979
+ taskovergg_add_open_question: {
980
+ rpc: "addOpenQuestion",
981
+ schema: {
982
+ required: ["project_id", "text"],
983
+ fields: { project_id: "string", text: "string" },
984
+ },
985
+ // REVISED per M0: project:true add — single object with projectId inside
986
+ args: (c, r) => [{ projectId: r.project_id, text: c.text }],
987
+ },
988
+ taskovergg_toggle_question: {
989
+ rpc: "toggleQuestion",
990
+ schema: {
991
+ required: ["question_id"],
992
+ fields: { question_id: "string", project_id: "string" },
993
+ },
994
+ // REVISED per M0: record:open_questions — [questionId] only
995
+ args: (c, r) => [r.question_id],
996
+ },
997
+
998
+ // ── Board ──
999
+
1000
+ // REMOVED per M0: taskovergg_add_board_column — DEFERRED (ANOMALY-2: no RPC method)
1001
+ // Unblock path: add dedicated addBoardColumn RPC method or allowlist customBoardCols in updateProjectField
1002
+
1003
+ // ── Stories ──
1004
+
1005
+ taskovergg_get_stories: {
1006
+ rpc: "getStories",
1007
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
1008
+ args: (c, r) => [r.project_id],
1009
+ },
1010
+ taskovergg_add_story: {
1011
+ rpc: "addStory",
1012
+ schema: {
1013
+ required: ["project_id", "section_id", "section"],
1014
+ fields: {
1015
+ project_id: "string", section_id: "string",
1016
+ section: "string", content: "string",
1017
+ },
1018
+ },
1019
+ // REVISED per M0: project:true add — single object with projectId inside
1020
+ args: (c, r) => [{
1021
+ projectId: r.project_id, sectionId: c.section_id,
1022
+ section: c.section, content: c.content,
1023
+ }],
1024
+ },
1025
+ taskovergg_update_story: {
1026
+ rpc: "updateStory",
1027
+ schema: {
1028
+ required: ["story_id"],
1029
+ fields: {
1030
+ story_id: "string", project_id: "string",
1031
+ section: "string", content: "string",
1032
+ },
1033
+ },
1034
+ // REVISED per M0: record:stories — no projectId in args
1035
+ args: (c, r) => [r.story_id, {
1036
+ ...(c.section !== undefined && { section: c.section }),
1037
+ ...(c.content !== undefined && { content: c.content }),
1038
+ }],
1039
+ },
1040
+
1041
+ // ── Scenes ──
1042
+
1043
+ taskovergg_get_scenes: {
1044
+ rpc: "getScenes",
1045
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
1046
+ args: (c, r) => [r.project_id],
1047
+ },
1048
+ taskovergg_add_scene: {
1049
+ rpc: "addScene",
1050
+ schema: {
1051
+ required: ["project_id", "title"],
1052
+ fields: {
1053
+ project_id: "string", title: "string", number: "number",
1054
+ location: "string", characters: "string", mood: "string",
1055
+ round: "string", content: "string",
1056
+ },
1057
+ },
1058
+ // REVISED per M0: project:true add — single object with projectId inside
1059
+ args: (c, r) => [{
1060
+ projectId: r.project_id, number: c.number, title: c.title,
1061
+ location: c.location, characters: c.characters,
1062
+ mood: c.mood, round: c.round, content: c.content,
1063
+ }],
1064
+ },
1065
+ taskovergg_update_scene: {
1066
+ rpc: "updateScene",
1067
+ schema: {
1068
+ required: ["scene_id"],
1069
+ fields: {
1070
+ scene_id: "string", project_id: "string",
1071
+ title: "string", number: "number", location: "string",
1072
+ characters: "string", mood: "string", round: "string", content: "string",
1073
+ },
1074
+ },
1075
+ // REVISED per M0: record:scenes — no projectId in args
1076
+ args: (c, r) => [r.scene_id, {
1077
+ ...(c.title !== undefined && { title: c.title }),
1078
+ ...(c.number !== undefined && { number: c.number }),
1079
+ ...(c.location !== undefined && { location: c.location }),
1080
+ ...(c.characters !== undefined && { characters: c.characters }),
1081
+ ...(c.mood !== undefined && { mood: c.mood }),
1082
+ ...(c.round !== undefined && { round: c.round }),
1083
+ ...(c.content !== undefined && { content: c.content }),
1084
+ }],
1085
+ },
1086
+ // taskovergg_delete_scene -- DEFERRED: delete ops blocked in beta
1087
+
1088
+ // ── Story Bible ──
1089
+
1090
+ taskovergg_get_story_bible: {
1091
+ rpc: "getStoryBible",
1092
+ schema: { required: ["project_id"], fields: { project_id: "string" } },
1093
+ args: (c, r) => [r.project_id],
1094
+ },
1095
+ taskovergg_update_story_bible: {
1096
+ rpc: "updateStoryBible",
1097
+ schema: {
1098
+ required: ["project_id"],
1099
+ fields: {
1100
+ project_id: "string", title: "string", logline: "string",
1101
+ genre: "string", tone: "string", script: "string",
1102
+ },
1103
+ },
1104
+ args: (c, r) => [r.project_id, {
1105
+ ...(c.title !== undefined && { title: c.title }),
1106
+ ...(c.logline !== undefined && { logline: c.logline }),
1107
+ ...(c.genre !== undefined && { genre: c.genre }),
1108
+ ...(c.tone !== undefined && { tone: c.tone }),
1109
+ ...(c.script !== undefined && { script: c.script }),
1110
+ }],
1111
+ },
1112
+ taskovergg_add_story_character: {
1113
+ rpc: "addStoryCharacter",
1114
+ schema: {
1115
+ required: ["project_id", "name"],
1116
+ fields: {
1117
+ project_id: "string", name: "string",
1118
+ role: "string", description: "string",
1119
+ },
1120
+ },
1121
+ args: (c, r) => [r.project_id, {
1122
+ name: c.name, role: c.role, description: c.description,
1123
+ }],
1124
+ },
1125
+ // REMOVED per M0: taskovergg_update_story_character — DEFERRED (ANOMALY-3: ownership mismatch)
1126
+ // Unblock path: fix RPC registry record:"story_characters" → project:true
1127
+
1128
+ // ── Scene Content ──
1129
+
1130
+ taskovergg_get_scene_content: {
1131
+ rpc: "getSceneContent",
1132
+ schema: {
1133
+ required: ["scene_id"],
1134
+ fields: { scene_id: "string", project_id: "string" },
1135
+ },
1136
+ // REVISED per M0: record:scenes ownership — cloud adapter sends [sceneId] only.
1137
+ // RPC handler resolves projectId internally via SELECT project_id FROM scenes WHERE id = ?
1138
+ args: (c, r) => [r.scene_id],
1139
+ },
1140
+ // REMOVED per M0: taskovergg_update_scene_content — DEFERRED (ANOMALY-3: ownership mismatch)
1141
+ // Unblock path: fix RPC registry record:"scenes" → project:true
1142
+
1143
+ // REMOVED: taskovergg_get_activity_feed — not in M0 matrix (no MCP tool in TOOLS array)
1144
+ };
1145
+
1146
+ module.exports = { TOOL_MAP, validateSchema };