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.
package/index.js CHANGED
@@ -1,2068 +1,2294 @@
1
- #!/usr/bin/env node
2
-
3
- const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
4
- const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
5
- const {
6
- ListToolsRequestSchema,
7
- CallToolRequestSchema,
8
- } = require("@modelcontextprotocol/sdk/types.js");
9
-
10
- // SECURITY: Mode selection -- cloud is the customer path, local is dev-only.
11
- const CLOUD_MODE = !!process.env.TASKOVER_API_KEY;
12
- const LOCAL_MODE = !CLOUD_MODE && process.env.TASKOVER_LOCAL === "1";
13
-
14
- if (!CLOUD_MODE && !LOCAL_MODE) {
15
- console.error("[ERROR] No API key found.");
16
- console.error("Set TASKOVER_API_KEY environment variable.");
17
- console.error("Get your key at taskover.gg -> Settings -> Security.");
18
- process.exit(1);
19
- }
20
-
21
- if (LOCAL_MODE) {
22
- console.error("[DEV MODE] Using local SQLite database. This mode is not for production use.");
23
- console.error("[DEV MODE] Set TASKOVER_API_KEY for cloud mode.");
24
- }
25
-
26
- // SECURITY: Refuse API key passed as CLI argument
27
- if (process.argv.some(arg => arg.startsWith("tok_"))) {
28
- console.error("[SECURITY] API keys must not be passed as command-line arguments.");
29
- console.error("Use the TASKOVER_API_KEY environment variable instead.");
30
- process.exit(1);
31
- }
32
-
33
- let store, initDb, reloadFromDisk;
34
- let cloudAdapter, toolMap, validateSchema;
35
-
36
- if (CLOUD_MODE) {
37
- cloudAdapter = require("./cloud-adapter.js");
38
- const tm = require("./tool-map.js");
39
- toolMap = tm.TOOL_MAP;
40
- validateSchema = tm.validateSchema;
41
- cloudAdapter.init(process.env.TASKOVER_API_KEY);
42
- } else {
43
- store = require("./data-store.js");
44
- ({ initDb, reloadFromDisk } = require("./db"));
45
- }
46
-
47
- const server = new Server(
48
- { name: "taskover.gg", version: "1.0.0" },
49
- { capabilities: { tools: {} } }
50
- );
51
-
52
- // ===== TOOL DEFINITIONS =====
53
-
54
- const TOOLS = [
55
- // --- Dashboard & Context ---
56
- {
57
- name: "taskovergg_dashboard",
58
- description: "Get full project dashboard: system counts, open bugs, task board, last session. Use at start of every new chat.",
59
- inputSchema: {
60
- type: "object",
61
- properties: {
62
- project_id: { type: "string", description: "Project ID (e.g. 'village', 'room-06')" },
63
- },
64
- required: ["project_id"],
65
- },
66
- },
67
- {
68
- name: "taskovergg_context_export",
69
- description: "Generate full context dump — all systems, bugs, tasks, last session, decisions. Paste into new chats.",
70
- inputSchema: {
71
- type: "object",
72
- properties: { project_id: { type: "string" } },
73
- required: ["project_id"],
74
- },
75
- },
76
- {
77
- name: "taskovergg_search",
78
- description: "Search across all project data — tasks, systems, sessions, bugs, blueprints, notes.",
79
- inputSchema: {
80
- type: "object",
81
- properties: {
82
- project_id: { type: "string" },
83
- query: { type: "string", description: "Search term" },
84
- },
85
- required: ["project_id", "query"],
86
- },
87
- },
88
-
89
- // --- Projects ---
90
- {
91
- name: "taskovergg_list_projects",
92
- description: "List all projects.",
93
- inputSchema: { type: "object", properties: {} },
94
- },
95
- {
96
- name: "taskovergg_add_project",
97
- description: `DO NOT call this tool directly. Projects must be created by the user on the TaskOver website.
98
-
99
- Instead of calling this tool, instruct the user with these steps:
100
- 1. Go to taskover.gg and log in
101
- 2. Click the project dropdown in the top-left of the sidebar
102
- 3. Click "+ New Project" at the bottom of the dropdown
103
- 4. Follow the 5-step project creation wizard:
104
- - Step 1: Enter the project name and description
105
- - Step 2: Choose a project color/icon
106
- - Step 3: Set up initial systems (or skip)
107
- - Step 4: Configure project settings
108
- - Step 5: Review and confirm
109
- 5. Once the project appears in the project list, tell the AI the project name so it can begin adding tasks, systems, and other data to it via MCP.
110
-
111
- After the user confirms the project exists, use taskovergg_list_projects to find the project_id, then use other tools (taskovergg_add_task, taskovergg_add_system, etc.) to populate it.`,
112
- inputSchema: {
113
- type: "object",
114
- properties: {
115
- name: { type: "string" },
116
- description: { type: "string" },
117
- trello_board_id: { type: "string" },
118
- },
119
- required: ["name"],
120
- },
121
- },
122
-
123
- // --- Tasks (replaces To Do / Doing / Done / Scrapped) ---
124
- {
125
- name: "taskovergg_get_tasks",
126
- description: "Get tasks filtered by status and/or phase. Statuses: todo, doing, done, scrapped.",
127
- inputSchema: {
128
- type: "object",
129
- properties: {
130
- project_id: { type: "string" },
131
- status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
132
- phase: { type: "string" },
133
- },
134
- required: ["project_id"],
135
- },
136
- },
137
- {
138
- name: "taskovergg_add_task",
139
- description: "Add a task (idea, feature, to-do item).",
140
- inputSchema: {
141
- type: "object",
142
- properties: {
143
- project_id: { type: "string" },
144
- title: { type: "string" },
145
- description: { type: "string" },
146
- status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
147
- phase: { type: "string" },
148
- priority: { type: "string", enum: ["high", "medium", "low"] },
149
- tags: { type: "array", items: { type: "string" } },
150
- checklist: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
151
- },
152
- required: ["project_id", "title"],
153
- },
154
- },
155
- {
156
- name: "taskovergg_update_task",
157
- description: "Update a task's fields.",
158
- inputSchema: {
159
- type: "object",
160
- properties: {
161
- task_id: { type: "string" },
162
- title: { type: "string" },
163
- description: { type: "string" },
164
- status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
165
- phase: { type: "string" },
166
- priority: { type: "string", enum: ["high", "medium", "low"] },
167
- tags: { type: "array", items: { type: "string" } },
168
- checklist: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
169
- },
170
- required: ["task_id"],
171
- },
172
- },
173
- {
174
- name: "taskovergg_move_task",
175
- description: "Move task between columns (todo/doing/done/scrapped).",
176
- inputSchema: {
177
- type: "object",
178
- properties: {
179
- task_id: { type: "string" },
180
- status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
181
- },
182
- required: ["task_id", "status"],
183
- },
184
- },
185
- {
186
- name: "taskovergg_add_task_comment",
187
- description: "Add a comment to a task (replaces Trello card comments).",
188
- inputSchema: {
189
- type: "object",
190
- properties: {
191
- task_id: { type: "string" },
192
- text: { type: "string" },
193
- },
194
- required: ["task_id", "text"],
195
- },
196
- },
197
-
198
- // --- Systems (replaces Claude Memory documentation cards) ---
199
- {
200
- name: "taskovergg_get_systems",
201
- description: "Get all systems/features for a project with status, files, rules, notes.",
202
- inputSchema: {
203
- type: "object",
204
- properties: { project_id: { type: "string" } },
205
- required: ["project_id"],
206
- },
207
- },
208
- {
209
- name: "taskovergg_add_system",
210
- description: "Document a new system/feature. Include critical rules and notes.",
211
- inputSchema: {
212
- type: "object",
213
- properties: {
214
- project_id: { type: "string" },
215
- name: { type: "string" },
216
- description: { type: "string" },
217
- status: { type: "string", enum: ["working", "wip", "broken", "planned"] },
218
- key_files: { type: "string" },
219
- notes: { type: "string" },
220
- critical_rules: { type: "string" },
221
- },
222
- required: ["project_id", "name"],
223
- },
224
- },
225
- {
226
- name: "taskovergg_update_system",
227
- description: "Update a system's status, description, notes, or rules.",
228
- inputSchema: {
229
- type: "object",
230
- properties: {
231
- system_id: { type: "string" },
232
- name: { type: "string" },
233
- description: { type: "string" },
234
- status: { type: "string", enum: ["working", "wip", "broken", "planned"] },
235
- key_files: { type: "string" },
236
- notes: { type: "string" },
237
- critical_rules: { type: "string" },
238
- },
239
- required: ["system_id"],
240
- },
241
- },
242
-
243
- // --- Sessions (replaces Claude Chat Memory) ---
244
- {
245
- name: "taskovergg_get_sessions",
246
- description: "Get recent work sessions with what was done and where we left off.",
247
- inputSchema: {
248
- type: "object",
249
- properties: {
250
- project_id: { type: "string" },
251
- limit: { type: "number" },
252
- },
253
- required: ["project_id"],
254
- },
255
- },
256
- {
257
- name: "taskovergg_log_session",
258
- description: "Log a work session. Call at end of each chat (replaces !chatsave). Include what was done, where we left off, and next steps.",
259
- inputSchema: {
260
- type: "object",
261
- properties: {
262
- project_id: { type: "string" },
263
- title: { type: "string" },
264
- summary: { type: "string" },
265
- what_we_did: { type: "array", items: { type: "string" } },
266
- where_we_left_off: { type: "string" },
267
- next_steps: { type: "array", items: { type: "string" } },
268
- systems_worked_on: { type: "array", items: { type: "string" } },
269
- },
270
- required: ["project_id", "title"],
271
- },
272
- },
273
-
274
- // --- Changelog ---
275
- {
276
- name: "taskovergg_get_changelog",
277
- description: "Get recent changelog entries.",
278
- inputSchema: {
279
- type: "object",
280
- properties: {
281
- project_id: { type: "string" },
282
- limit: { type: "number" },
283
- },
284
- required: ["project_id"],
285
- },
286
- },
287
- {
288
- name: "taskovergg_add_changelog",
289
- description: "Log changes made. Include list of specific changes and affected systems.",
290
- inputSchema: {
291
- type: "object",
292
- properties: {
293
- project_id: { type: "string" },
294
- title: { type: "string" },
295
- changes: { type: "array", items: { type: "string" } },
296
- systems_affected: { type: "array", items: { type: "string" } },
297
- },
298
- required: ["project_id", "title", "changes"],
299
- },
300
- },
301
-
302
- // --- Bugs ---
303
- {
304
- name: "taskovergg_get_bugs",
305
- description: "Get bugs, optionally filtered by status (open/fixed/wontfix).",
306
- inputSchema: {
307
- type: "object",
308
- properties: {
309
- project_id: { type: "string" },
310
- status: { type: "string", enum: ["open", "fixed", "wontfix"] },
311
- },
312
- required: ["project_id"],
313
- },
314
- },
315
- {
316
- name: "taskovergg_add_bug",
317
- description: "Report a bug with priority and steps to reproduce.",
318
- inputSchema: {
319
- type: "object",
320
- properties: {
321
- project_id: { type: "string" },
322
- system_id: { type: "string" },
323
- title: { type: "string" },
324
- description: { type: "string" },
325
- priority: { type: "string", enum: ["high", "medium", "low"] },
326
- steps_to_reproduce: { type: "string" },
327
- },
328
- required: ["project_id", "title"],
329
- },
330
- },
331
- {
332
- name: "taskovergg_fix_bug",
333
- description: "Mark a bug as fixed with a description of the fix.",
334
- inputSchema: {
335
- type: "object",
336
- properties: {
337
- bug_id: { type: "string" },
338
- fix_description: { type: "string" },
339
- },
340
- required: ["bug_id"],
341
- },
342
- },
343
-
344
- // --- Decisions ---
345
- {
346
- name: "taskovergg_get_decisions",
347
- description: "Get design decisions for a project.",
348
- inputSchema: {
349
- type: "object",
350
- properties: { project_id: { type: "string" } },
351
- required: ["project_id"],
352
- },
353
- },
354
- {
355
- name: "taskovergg_log_decision",
356
- description: "Log a design decision with context and alternatives considered.",
357
- inputSchema: {
358
- type: "object",
359
- properties: {
360
- project_id: { type: "string" },
361
- decision: { type: "string" },
362
- context: { type: "string" },
363
- system_id: { type: "string" },
364
- alternatives: { type: "string" },
365
- },
366
- required: ["project_id", "decision"],
367
- },
368
- },
369
-
370
- // --- Blueprints ---
371
- {
372
- name: "taskovergg_get_blueprints",
373
- description: "Get all registered Blueprints for a project.",
374
- inputSchema: {
375
- type: "object",
376
- properties: { project_id: { type: "string" } },
377
- required: ["project_id"],
378
- },
379
- },
380
- {
381
- name: "taskovergg_add_blueprint",
382
- description: "Register a Blueprint with path, parent class, variables, and key functions.",
383
- inputSchema: {
384
- type: "object",
385
- properties: {
386
- project_id: { type: "string" },
387
- name: { type: "string" },
388
- path: { type: "string" },
389
- parent_class: { type: "string" },
390
- system_id: { type: "string" },
391
- description: { type: "string" },
392
- variables: { type: "array", items: { type: "string" } },
393
- key_functions: { type: "array", items: { type: "string" } },
394
- },
395
- required: ["project_id", "name"],
396
- },
397
- },
398
- {
399
- name: "taskovergg_update_blueprint",
400
- description: "Update a registered Blueprint.",
401
- inputSchema: {
402
- type: "object",
403
- properties: {
404
- blueprint_id: { type: "string" },
405
- name: { type: "string" },
406
- path: { type: "string" },
407
- parent_class: { type: "string" },
408
- description: { type: "string" },
409
- variables: { type: "array", items: { type: "string" } },
410
- key_functions: { type: "array", items: { type: "string" } },
411
- },
412
- required: ["blueprint_id"],
413
- },
414
- },
415
-
416
- // --- Blueprint Graphs ---
417
- {
418
- name: "taskovergg_get_blueprint_graphs",
419
- description: "Get all visual diagram graphs for a blueprint, including nodes, connections, and layout.",
420
- inputSchema: {
421
- type: "object",
422
- properties: { blueprint_id: { type: "string" } },
423
- required: ["blueprint_id"],
424
- },
425
- },
426
- {
427
- name: "taskovergg_set_blueprint_graph",
428
- description: "Set a complete visual diagram graph on a blueprint. Creates or replaces a named graph with nodes, pins, and connections.",
429
- inputSchema: {
430
- type: "object",
431
- properties: {
432
- blueprint_id: { type: "string" },
433
- graph_name: { type: "string", description: "Graph name, e.g. 'EventGraph'" },
434
- graph_type: { type: "string", enum: ["event", "function", "macro"] },
435
- nodes: {
436
- type: "array",
437
- items: {
438
- type: "object",
439
- properties: {
440
- id: { type: "string" }, type: { type: "string" }, title: { type: "string" },
441
- subtitle: { type: "string" }, x: { type: "number" }, y: { type: "number" },
442
- comment: { type: "string" }, pure: { type: "boolean" },
443
- pins: { type: "array", items: { type: "object",
444
- properties: { id: { type: "string" }, name: { type: "string" }, direction: { type: "string" }, type: { type: "string" } },
445
- required: ["id", "name", "direction", "type"] } },
446
- },
447
- required: ["id", "type", "title", "x", "y", "pins"],
448
- },
449
- },
450
- connections: {
451
- type: "array",
452
- items: { type: "object",
453
- properties: { id: { type: "string" }, sourceNodeId: { type: "string" }, sourcePinId: { type: "string" }, targetNodeId: { type: "string" }, targetPinId: { type: "string" } },
454
- required: ["id", "sourceNodeId", "sourcePinId", "targetNodeId", "targetPinId"] },
455
- },
456
- },
457
- required: ["blueprint_id", "graph_name", "nodes", "connections"],
458
- },
459
- },
460
- {
461
- name: "taskovergg_add_graph_nodes",
462
- description: "Add nodes and connections to an existing blueprint graph without replacing existing data.",
463
- inputSchema: {
464
- type: "object",
465
- properties: {
466
- blueprint_id: { type: "string" },
467
- graph_name: { type: "string" },
468
- nodes: { type: "array", items: { type: "object" } },
469
- connections: { type: "array", items: { type: "object" } },
470
- },
471
- required: ["blueprint_id", "graph_name"],
472
- },
473
- },
474
-
475
- // --- Notes ---
476
- {
477
- name: "taskovergg_get_notes",
478
- description: "Get notes, optionally filtered by parent type (project/system/task/blueprint).",
479
- inputSchema: {
480
- type: "object",
481
- properties: {
482
- project_id: { type: "string" },
483
- parent_type: { type: "string", enum: ["project", "system", "task", "blueprint"] },
484
- parent_id: { type: "string" },
485
- },
486
- required: ["project_id"],
487
- },
488
- },
489
- {
490
- name: "taskovergg_add_note",
491
- description: "Add a note/document to the project or attached to a system/task/blueprint.",
492
- inputSchema: {
493
- type: "object",
494
- properties: {
495
- project_id: { type: "string" },
496
- parent_type: { type: "string", enum: ["project", "system", "task", "blueprint"] },
497
- parent_id: { type: "string" },
498
- title: { type: "string" },
499
- content: { type: "string" },
500
- },
501
- required: ["project_id", "content"],
502
- },
503
- },
504
-
505
- // --- Backups ---
506
- {
507
- name: "taskovergg_log_backup",
508
- description: "Log that a project backup was created (replaces !backup).",
509
- inputSchema: {
510
- type: "object",
511
- properties: {
512
- project_id: { type: "string" },
513
- title: { type: "string" },
514
- description: { type: "string" },
515
- },
516
- required: ["project_id"],
517
- },
518
- },
519
-
520
- // --- Levels ---
521
- {
522
- name: "taskovergg_get_levels",
523
- description: "Get all levels/scenes for a project with actors and status.",
524
- inputSchema: {
525
- type: "object",
526
- properties: { project_id: { type: "string" } },
527
- required: ["project_id"],
528
- },
529
- },
530
- {
531
- name: "taskovergg_add_level",
532
- description: "Add a level/scene with name, map, and actor count.",
533
- inputSchema: {
534
- type: "object",
535
- properties: {
536
- project_id: { type: "string" },
537
- name: { type: "string" },
538
- map: { type: "string", description: "Map/file path" },
539
- actor_count: { type: "number" },
540
- status: { type: "string", enum: ["planned", "wip", "testing", "done"] },
541
- },
542
- required: ["project_id", "name"],
543
- },
544
- },
545
- {
546
- name: "taskovergg_update_level",
547
- description: "Update a level's status, name, map, or actor count.",
548
- inputSchema: {
549
- type: "object",
550
- properties: {
551
- level_id: { type: "string" },
552
- name: { type: "string" },
553
- map: { type: "string" },
554
- actor_count: { type: "number" },
555
- status: { type: "string", enum: ["planned", "wip", "testing", "done"] },
556
- },
557
- required: ["level_id"],
558
- },
559
- },
560
- {
561
- name: "taskovergg_add_actor",
562
- description: "Add an actor to a level with XYZ coordinates.",
563
- inputSchema: {
564
- type: "object",
565
- properties: {
566
- level_id: { type: "string" },
567
- name: { type: "string" },
568
- x: { type: "number" }, y: { type: "number" }, z: { type: "number" },
569
- notes: { type: "string" },
570
- },
571
- required: ["level_id", "name"],
572
- },
573
- },
574
- {
575
- name: "taskovergg_remove_actor",
576
- description: "Remove an actor from a level by index.",
577
- inputSchema: {
578
- type: "object",
579
- properties: {
580
- level_id: { type: "string" },
581
- actor_index: { type: "number" },
582
- },
583
- required: ["level_id", "actor_index"],
584
- },
585
- },
586
-
587
- // --- Plugins ---
588
- {
589
- name: "taskovergg_get_plugins",
590
- description: "Get all plugins/dependencies for a project.",
591
- inputSchema: {
592
- type: "object",
593
- properties: { project_id: { type: "string" } },
594
- required: ["project_id"],
595
- },
596
- },
597
- {
598
- name: "taskovergg_add_plugin",
599
- description: "Track a plugin/dependency with version and source.",
600
- inputSchema: {
601
- type: "object",
602
- properties: {
603
- project_id: { type: "string" },
604
- name: { type: "string" },
605
- version: { type: "string" },
606
- source: { type: "string", description: "Marketplace, GitHub, Custom, etc." },
607
- dependents: { type: "array", items: { type: "string" } },
608
- },
609
- required: ["project_id", "name"],
610
- },
611
- },
612
- {
613
- name: "taskovergg_update_plugin",
614
- description: "Update a plugin's version, source, or dependents.",
615
- inputSchema: {
616
- type: "object",
617
- properties: {
618
- plugin_id: { type: "string" },
619
- name: { type: "string" },
620
- version: { type: "string" },
621
- source: { type: "string" },
622
- dependents: { type: "array", items: { type: "string" } },
623
- },
624
- required: ["plugin_id"],
625
- },
626
- },
627
- {
628
- name: "taskovergg_delete_plugin",
629
- description: "Remove a plugin from the tracker.",
630
- inputSchema: {
631
- type: "object",
632
- properties: { plugin_id: { type: "string" } },
633
- required: ["plugin_id"],
634
- },
635
- },
636
-
637
- // --- Build Errors ---
638
- {
639
- name: "taskovergg_get_build_errors",
640
- description: "Get build/compile errors, optionally filtered by status (open/fixed).",
641
- inputSchema: {
642
- type: "object",
643
- properties: {
644
- project_id: { type: "string" },
645
- status: { type: "string", enum: ["open", "fixed"] },
646
- },
647
- required: ["project_id"],
648
- },
649
- },
650
- {
651
- name: "taskovergg_add_build_error",
652
- description: "Log a build/compile error tagged to a blueprint.",
653
- inputSchema: {
654
- type: "object",
655
- properties: {
656
- project_id: { type: "string" },
657
- error: { type: "string", description: "Error message/description" },
658
- bp: { type: "string", description: "Related blueprint name" },
659
- },
660
- required: ["project_id", "error"],
661
- },
662
- },
663
- {
664
- name: "taskovergg_fix_build_error",
665
- description: "Mark a build error as fixed with a description of the fix.",
666
- inputSchema: {
667
- type: "object",
668
- properties: {
669
- error_id: { type: "string" },
670
- fix: { type: "string", description: "How the error was fixed" },
671
- },
672
- required: ["error_id"],
673
- },
674
- },
675
-
676
- // --- Optimize Items ---
677
- {
678
- name: "taskovergg_get_optimize_items",
679
- description: "Get optimization issues (unused assets, oversized files, performance problems).",
680
- inputSchema: {
681
- type: "object",
682
- properties: { project_id: { type: "string" } },
683
- required: ["project_id"],
684
- },
685
- },
686
- {
687
- name: "taskovergg_add_optimize_item",
688
- description: "Log an optimization issue with category and estimated savings.",
689
- inputSchema: {
690
- type: "object",
691
- properties: {
692
- project_id: { type: "string" },
693
- title: { type: "string" },
694
- category: { type: "string", enum: ["unused", "textures", "meshes", "performance", "memory", "other"] },
695
- status: { type: "string", enum: ["open", "fixed", "ok"] },
696
- savings: { type: "string", description: "Estimated savings (e.g. '50MB', '15ms')" },
697
- },
698
- required: ["project_id", "title"],
699
- },
700
- },
701
- {
702
- name: "taskovergg_update_optimize_item",
703
- description: "Update an optimization item's status or details.",
704
- inputSchema: {
705
- type: "object",
706
- properties: {
707
- item_id: { type: "string" },
708
- title: { type: "string" },
709
- category: { type: "string" },
710
- status: { type: "string", enum: ["open", "fixed", "ok"] },
711
- savings: { type: "string" },
712
- },
713
- required: ["item_id"],
714
- },
715
- },
716
-
717
- // --- Perf Budget ---
718
- {
719
- name: "taskovergg_get_perf_budget",
720
- description: "Get performance budget readings (FPS, GPU, memory per scene).",
721
- inputSchema: {
722
- type: "object",
723
- properties: { project_id: { type: "string" } },
724
- required: ["project_id"],
725
- },
726
- },
727
- {
728
- name: "taskovergg_add_perf_budget",
729
- description: "Add a performance budget reading for a scene.",
730
- inputSchema: {
731
- type: "object",
732
- properties: {
733
- project_id: { type: "string" },
734
- scene: { type: "string" },
735
- fps: { type: "number" },
736
- gpu: { type: "number", description: "GPU usage percentage" },
737
- memory: { type: "number", description: "Memory usage in MB" },
738
- notes: { type: "string" },
739
- },
740
- required: ["project_id", "scene"],
741
- },
742
- },
743
-
744
- // --- Playtests ---
745
- {
746
- name: "taskovergg_get_playtests",
747
- description: "Get playtest sessions for a project.",
748
- inputSchema: {
749
- type: "object",
750
- properties: { project_id: { type: "string" } },
751
- required: ["project_id"],
752
- },
753
- },
754
- {
755
- name: "taskovergg_add_playtest",
756
- description: "Log a playtest session with rating, tester, issues, and build version.",
757
- inputSchema: {
758
- type: "object",
759
- properties: {
760
- project_id: { type: "string" },
761
- duration: { type: "string", description: "e.g. '45 min'" },
762
- issues: { type: "array", items: { type: "string" } },
763
- rating: { type: "number", description: "1-5 star rating" },
764
- notes: { type: "string" },
765
- tester: { type: "string" },
766
- build_version: { type: "string" },
767
- },
768
- required: ["project_id"],
769
- },
770
- },
771
-
772
- // --- Milestones ---
773
- {
774
- name: "taskovergg_get_milestones",
775
- description: "Get project milestones/timeline.",
776
- inputSchema: {
777
- type: "object",
778
- properties: { project_id: { type: "string" } },
779
- required: ["project_id"],
780
- },
781
- },
782
- {
783
- name: "taskovergg_add_milestone",
784
- description: "Add a project milestone with target date.",
785
- inputSchema: {
786
- type: "object",
787
- properties: {
788
- project_id: { type: "string" },
789
- title: { type: "string" },
790
- date: { type: "string", description: "Target date (YYYY-MM-DD)" },
791
- status: { type: "string", enum: ["upcoming", "active", "done", "missed"] },
792
- notes: { type: "string" },
793
- },
794
- required: ["project_id", "title"],
795
- },
796
- },
797
- {
798
- name: "taskovergg_update_milestone",
799
- description: "Update a milestone's status, date, or notes.",
800
- inputSchema: {
801
- type: "object",
802
- properties: {
803
- milestone_id: { type: "string" },
804
- title: { type: "string" },
805
- date: { type: "string" },
806
- status: { type: "string", enum: ["upcoming", "active", "done", "missed"] },
807
- notes: { type: "string" },
808
- },
809
- required: ["milestone_id"],
810
- },
811
- },
812
-
813
- // --- Iterations ---
814
- {
815
- name: "taskovergg_get_iterations",
816
- description: "Get feature iteration history.",
817
- inputSchema: {
818
- type: "object",
819
- properties: { project_id: { type: "string" } },
820
- required: ["project_id"],
821
- },
822
- },
823
- {
824
- name: "taskovergg_add_iteration",
825
- description: "Log a feature iteration (version change with reason).",
826
- inputSchema: {
827
- type: "object",
828
- properties: {
829
- project_id: { type: "string" },
830
- feature: { type: "string" },
831
- version: { type: "string", description: "e.g. 'v2', 'v3'" },
832
- change: { type: "string", description: "What changed" },
833
- reason: { type: "string", description: "Why it changed" },
834
- },
835
- required: ["project_id", "feature"],
836
- },
837
- },
838
-
839
- // --- Dialogues ---
840
- {
841
- name: "taskovergg_get_dialogues",
842
- description: "Get NPC dialogue nodes for a project.",
843
- inputSchema: {
844
- type: "object",
845
- properties: { project_id: { type: "string" } },
846
- required: ["project_id"],
847
- },
848
- },
849
- {
850
- name: "taskovergg_add_dialogue",
851
- description: "Add an NPC dialogue node with choices and branching.",
852
- inputSchema: {
853
- type: "object",
854
- properties: {
855
- project_id: { type: "string" },
856
- npc: { type: "string" },
857
- line: { type: "string", description: "Dialogue text" },
858
- choices: { type: "array", items: { type: "string" } },
859
- next_node: { type: "string", description: "Next dialogue node ID" },
860
- notes: { type: "string" },
861
- },
862
- required: ["project_id", "npc"],
863
- },
864
- },
865
- {
866
- name: "taskovergg_update_dialogue",
867
- description: "Update a dialogue node.",
868
- inputSchema: {
869
- type: "object",
870
- properties: {
871
- dialogue_id: { type: "string" },
872
- npc: { type: "string" },
873
- line: { type: "string" },
874
- choices: { type: "array", items: { type: "string" } },
875
- next_node: { type: "string" },
876
- notes: { type: "string" },
877
- },
878
- required: ["dialogue_id"],
879
- },
880
- },
881
-
882
- // --- Sounds ---
883
- {
884
- name: "taskovergg_get_sounds",
885
- description: "Get sound cues for a project.",
886
- inputSchema: {
887
- type: "object",
888
- properties: { project_id: { type: "string" } },
889
- required: ["project_id"],
890
- },
891
- },
892
- {
893
- name: "taskovergg_add_sound",
894
- description: "Track a sound cue with actor, trigger, and spatial settings.",
895
- inputSchema: {
896
- type: "object",
897
- properties: {
898
- project_id: { type: "string" },
899
- name: { type: "string" },
900
- actor: { type: "string" },
901
- trigger: { type: "string" },
902
- spatial: { type: "boolean" },
903
- attenuation: { type: "string" },
904
- notes: { type: "string" },
905
- },
906
- required: ["project_id", "name"],
907
- },
908
- },
909
- {
910
- name: "taskovergg_update_sound",
911
- description: "Update a sound cue's settings.",
912
- inputSchema: {
913
- type: "object",
914
- properties: {
915
- sound_id: { type: "string" },
916
- name: { type: "string" },
917
- actor: { type: "string" },
918
- trigger: { type: "string" },
919
- spatial: { type: "boolean" },
920
- attenuation: { type: "string" },
921
- notes: { type: "string" },
922
- },
923
- required: ["sound_id"],
924
- },
925
- },
926
-
927
- // --- Controls ---
928
- {
929
- name: "taskovergg_get_controls",
930
- description: "Get input control bindings for a project.",
931
- inputSchema: {
932
- type: "object",
933
- properties: { project_id: { type: "string" } },
934
- required: ["project_id"],
935
- },
936
- },
937
- {
938
- name: "taskovergg_add_control",
939
- description: "Add an input control binding (key action).",
940
- inputSchema: {
941
- type: "object",
942
- properties: {
943
- project_id: { type: "string" },
944
- key: { type: "string", description: "Input key/button" },
945
- action: { type: "string", description: "Game action" },
946
- context: { type: "string", description: "Input context (e.g. 'on foot', 'in vehicle')" },
947
- condition: { type: "string" },
948
- },
949
- required: ["project_id", "key", "action"],
950
- },
951
- },
952
- {
953
- name: "taskovergg_update_control",
954
- description: "Update a control binding.",
955
- inputSchema: {
956
- type: "object",
957
- properties: {
958
- control_id: { type: "string" },
959
- key: { type: "string" },
960
- action: { type: "string" },
961
- context: { type: "string" },
962
- condition: { type: "string" },
963
- },
964
- required: ["control_id"],
965
- },
966
- },
967
-
968
- // --- Assets ---
969
- {
970
- name: "taskovergg_get_assets",
971
- description: "Get assets in the pipeline for a project.",
972
- inputSchema: {
973
- type: "object",
974
- properties: { project_id: { type: "string" } },
975
- required: ["project_id"],
976
- },
977
- },
978
- {
979
- name: "taskovergg_add_asset",
980
- description: "Track an asset in the pipeline with type, status, and usage.",
981
- inputSchema: {
982
- type: "object",
983
- properties: {
984
- project_id: { type: "string" },
985
- name: { type: "string" },
986
- type: { type: "string", description: "Asset type (Mesh, Texture, Material, Sound, etc.)" },
987
- path: { type: "string" },
988
- size: { type: "string" },
989
- status: { type: "string", enum: ["concept", "imported", "integrated", "tested", "final"] },
990
- used_by: { type: "string" },
991
- },
992
- required: ["project_id", "name"],
993
- },
994
- },
995
- {
996
- name: "taskovergg_update_asset",
997
- description: "Update an asset's status or details.",
998
- inputSchema: {
999
- type: "object",
1000
- properties: {
1001
- asset_id: { type: "string" },
1002
- name: { type: "string" },
1003
- type: { type: "string" },
1004
- path: { type: "string" },
1005
- size: { type: "string" },
1006
- status: { type: "string", enum: ["concept", "imported", "integrated", "tested", "final"] },
1007
- used_by: { type: "string" },
1008
- },
1009
- required: ["asset_id"],
1010
- },
1011
- },
1012
-
1013
- // --- References ---
1014
- {
1015
- name: "taskovergg_get_refs",
1016
- description: "Get reference/inspiration items for a project.",
1017
- inputSchema: {
1018
- type: "object",
1019
- properties: { project_id: { type: "string" } },
1020
- required: ["project_id"],
1021
- },
1022
- },
1023
- {
1024
- name: "taskovergg_add_ref",
1025
- description: "Add a reference/inspiration item (game, art, video, article).",
1026
- inputSchema: {
1027
- type: "object",
1028
- properties: {
1029
- project_id: { type: "string" },
1030
- title: { type: "string" },
1031
- type: { type: "string", enum: ["Game", "Art", "Video", "Article"] },
1032
- url: { type: "string" },
1033
- notes: { type: "string" },
1034
- },
1035
- required: ["project_id", "title"],
1036
- },
1037
- },
1038
-
1039
- // --- Marketing ---
1040
- {
1041
- name: "taskovergg_get_marketing_items",
1042
- description: "Get marketing/launch prep items for a project.",
1043
- inputSchema: {
1044
- type: "object",
1045
- properties: { project_id: { type: "string" } },
1046
- required: ["project_id"],
1047
- },
1048
- },
1049
- {
1050
- name: "taskovergg_add_marketing_item",
1051
- description: "Add a marketing/launch prep item (Steam page, trailer, social, press).",
1052
- inputSchema: {
1053
- type: "object",
1054
- properties: {
1055
- project_id: { type: "string" },
1056
- item: { type: "string" },
1057
- category: { type: "string", enum: ["Steam", "Trailer", "Social", "Press"] },
1058
- status: { type: "string", enum: ["todo", "doing", "done"] },
1059
- notes: { type: "string" },
1060
- },
1061
- required: ["project_id", "item"],
1062
- },
1063
- },
1064
- {
1065
- name: "taskovergg_update_marketing_item",
1066
- description: "Update a marketing item's status or details.",
1067
- inputSchema: {
1068
- type: "object",
1069
- properties: {
1070
- marketing_id: { type: "string" },
1071
- item: { type: "string" },
1072
- category: { type: "string" },
1073
- status: { type: "string", enum: ["todo", "doing", "done"] },
1074
- notes: { type: "string" },
1075
- },
1076
- required: ["marketing_id"],
1077
- },
1078
- },
1079
-
1080
- // --- Wiki ---
1081
- {
1082
- name: "taskovergg_get_wiki_pages",
1083
- description: "Get internal wiki/knowledge base pages for a project.",
1084
- inputSchema: {
1085
- type: "object",
1086
- properties: { project_id: { type: "string" } },
1087
- required: ["project_id"],
1088
- },
1089
- },
1090
- {
1091
- name: "taskovergg_add_wiki_page",
1092
- description: "Add a wiki/knowledge base page with category and content.",
1093
- inputSchema: {
1094
- type: "object",
1095
- properties: {
1096
- project_id: { type: "string" },
1097
- title: { type: "string" },
1098
- category: { type: "string", enum: ["Systems", "Workflows", "Guides", "Reference"] },
1099
- content: { type: "string" },
1100
- },
1101
- required: ["project_id", "title"],
1102
- },
1103
- },
1104
- {
1105
- name: "taskovergg_update_wiki_page",
1106
- description: "Update a wiki page's title, category, or content.",
1107
- inputSchema: {
1108
- type: "object",
1109
- properties: {
1110
- wiki_id: { type: "string" },
1111
- title: { type: "string" },
1112
- category: { type: "string" },
1113
- content: { type: "string" },
1114
- },
1115
- required: ["wiki_id"],
1116
- },
1117
- },
1118
-
1119
- // --- Ship Readiness ---
1120
- {
1121
- name: "taskovergg_get_ship_checked",
1122
- description: "Get completed ship readiness checklist items for a project.",
1123
- inputSchema: {
1124
- type: "object",
1125
- properties: { project_id: { type: "string" } },
1126
- required: ["project_id"],
1127
- },
1128
- },
1129
- {
1130
- name: "taskovergg_toggle_ship_check",
1131
- description: "Toggle a ship readiness checklist step as complete/incomplete.",
1132
- inputSchema: {
1133
- type: "object",
1134
- properties: {
1135
- project_id: { type: "string" },
1136
- step_id: { type: "string", description: "Checklist step ID (e.g. 'p1s1', 'p2s3')" },
1137
- },
1138
- required: ["project_id", "step_id"],
1139
- },
1140
- },
1141
-
1142
- // --- Open Questions ---
1143
- {
1144
- name: "taskovergg_get_open_questions",
1145
- description: "Get open design/development questions for a project.",
1146
- inputSchema: {
1147
- type: "object",
1148
- properties: { project_id: { type: "string" } },
1149
- required: ["project_id"],
1150
- },
1151
- },
1152
- {
1153
- name: "taskovergg_add_open_question",
1154
- description: "Add an open question that needs answering.",
1155
- inputSchema: {
1156
- type: "object",
1157
- properties: {
1158
- project_id: { type: "string" },
1159
- text: { type: "string" },
1160
- },
1161
- required: ["project_id", "text"],
1162
- },
1163
- },
1164
- {
1165
- name: "taskovergg_toggle_question",
1166
- description: "Toggle an open question as resolved/unresolved.",
1167
- inputSchema: {
1168
- type: "object",
1169
- properties: { question_id: { type: "string" } },
1170
- required: ["question_id"],
1171
- },
1172
- },
1173
-
1174
- // --- Board Columns (Blocks) ---
1175
- {
1176
- name: "taskovergg_add_board_column",
1177
- description: "Add a new custom column (block) to the Kanban task board. Use this when the user asks to create a new block, column, or category on their task board. This does NOT create a task/card — it creates a new column that cards can be placed into.",
1178
- inputSchema: {
1179
- type: "object",
1180
- properties: {
1181
- name: { type: "string", description: "The name/label for the new column (e.g. 'Review Later', 'Blocked', 'QA Testing')" },
1182
- color: { type: "string", description: "Hex color for the column dot (e.g. '#FDE68A'). Defaults to '#9B95A0' if not provided." },
1183
- },
1184
- required: ["name"],
1185
- },
1186
- },
1187
- // --- Stories (Narrative Bible) ---
1188
- {
1189
- name: "taskovergg_get_stories",
1190
- description: "Get all narrative bible / story sections for a project.",
1191
- inputSchema: {
1192
- type: "object",
1193
- properties: { project_id: { type: "string" } },
1194
- required: ["project_id"],
1195
- },
1196
- },
1197
- {
1198
- name: "taskovergg_add_story",
1199
- description: "Add a narrative bible section (synopsis, characters, lore, plot points, or endings).",
1200
- inputSchema: {
1201
- type: "object",
1202
- properties: {
1203
- project_id: { type: "string" },
1204
- section_id: { type: "string", description: "Section key: s_synopsis, s_characters, s_lore, s_plot, s_endings" },
1205
- section: { type: "string", description: "Display label for the section" },
1206
- content: { type: "string", description: "Section content text" },
1207
- },
1208
- required: ["project_id", "section_id", "section"],
1209
- },
1210
- },
1211
- {
1212
- name: "taskovergg_update_story",
1213
- description: "Update a narrative bible story section's content.",
1214
- inputSchema: {
1215
- type: "object",
1216
- properties: {
1217
- story_id: { type: "string" },
1218
- section: { type: "string" },
1219
- content: { type: "string" },
1220
- },
1221
- required: ["story_id"],
1222
- },
1223
- },
1224
- // --- Scenes ---
1225
- {
1226
- name: "taskovergg_get_scenes",
1227
- description: "Get all scenes for a project's narrative bible.",
1228
- inputSchema: {
1229
- type: "object",
1230
- properties: { project_id: { type: "string" } },
1231
- required: ["project_id"],
1232
- },
1233
- },
1234
- {
1235
- name: "taskovergg_add_scene",
1236
- description: "Add a new scene to a project's story.",
1237
- inputSchema: {
1238
- type: "object",
1239
- properties: {
1240
- project_id: { type: "string" },
1241
- number: { type: "number", description: "Scene number" },
1242
- title: { type: "string" },
1243
- location: { type: "string" },
1244
- characters: { type: "array", items: { type: "string" }, description: "Character names in this scene" },
1245
- mood: { type: "string" },
1246
- round: { type: "string", description: "Story round/act (e.g. Intro, Act 1)" },
1247
- content: { type: "string" },
1248
- },
1249
- required: ["project_id", "title"],
1250
- },
1251
- },
1252
- {
1253
- name: "taskovergg_update_scene",
1254
- description: "Update a scene's fields.",
1255
- inputSchema: {
1256
- type: "object",
1257
- properties: {
1258
- scene_id: { type: "string" },
1259
- number: { type: "number" },
1260
- title: { type: "string" },
1261
- location: { type: "string" },
1262
- characters: { type: "array", items: { type: "string" } },
1263
- mood: { type: "string" },
1264
- round: { type: "string" },
1265
- content: { type: "string" },
1266
- },
1267
- required: ["scene_id"],
1268
- },
1269
- },
1270
- {
1271
- name: "taskovergg_delete_scene",
1272
- description: "Delete a scene from the narrative bible.",
1273
- inputSchema: {
1274
- type: "object",
1275
- properties: { scene_id: { type: "string" } },
1276
- required: ["scene_id"],
1277
- },
1278
- },
1279
- // --- Story Bible (Story NEW tab — full project story data) ---
1280
- {
1281
- name: "taskovergg_get_story_bible",
1282
- description: "Get the full Story Bible for a project — includes title, logline, genre, tone, characters, chapters, scenes with word counts, and script. This is the primary tool for reading story data from the Story NEW writing environment.",
1283
- inputSchema: {
1284
- type: "object",
1285
- properties: { project_id: { type: "string" } },
1286
- required: ["project_id"],
1287
- },
1288
- },
1289
- {
1290
- name: "taskovergg_update_story_bible",
1291
- description: "Update the Story Bible metadata (title, logline, genre, tone, script).",
1292
- inputSchema: {
1293
- type: "object",
1294
- properties: {
1295
- project_id: { type: "string" },
1296
- title: { type: "string" },
1297
- logline: { type: "string" },
1298
- genre: { type: "string" },
1299
- tone: { type: "string" },
1300
- script: { type: "string", description: "Full script/source material text" },
1301
- },
1302
- required: ["project_id"],
1303
- },
1304
- },
1305
- {
1306
- name: "taskovergg_add_story_character",
1307
- description: "Add a character to the Story Bible.",
1308
- inputSchema: {
1309
- type: "object",
1310
- properties: {
1311
- project_id: { type: "string" },
1312
- name: { type: "string" },
1313
- role: { type: "string", description: "Character role: Player, Protagonist, Antagonist, NPC, Supporting, Mentioned" },
1314
- description: { type: "string" },
1315
- },
1316
- required: ["project_id", "name"],
1317
- },
1318
- },
1319
- {
1320
- name: "taskovergg_update_story_character",
1321
- description: "Update a character in the Story Bible.",
1322
- inputSchema: {
1323
- type: "object",
1324
- properties: {
1325
- project_id: { type: "string" },
1326
- character_id: { type: "string" },
1327
- name: { type: "string" },
1328
- role: { type: "string" },
1329
- description: { type: "string" },
1330
- },
1331
- required: ["project_id", "character_id"],
1332
- },
1333
- },
1334
- {
1335
- name: "taskovergg_get_scene_content",
1336
- description: "Get the full content and details of a specific scene by ID.",
1337
- inputSchema: {
1338
- type: "object",
1339
- properties: { scene_id: { type: "string" } },
1340
- required: ["scene_id"],
1341
- },
1342
- },
1343
- {
1344
- name: "taskovergg_update_scene_content",
1345
- description: "Update a scene's fields in the Story NEW writing environment (title, location, mood, content, chapter, characters, objectives).",
1346
- inputSchema: {
1347
- type: "object",
1348
- properties: {
1349
- project_id: { type: "string" },
1350
- scene_id: { type: "string" },
1351
- title: { type: "string" },
1352
- location: { type: "string" },
1353
- mood: { type: "string" },
1354
- content: { type: "string", description: "Scene content (HTML supported)" },
1355
- round: { type: "string", description: "Chapter name this scene belongs to" },
1356
- characters: { type: "array", items: { type: "string" }, description: "Character IDs assigned to this scene" },
1357
- objectives: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
1358
- },
1359
- required: ["project_id", "scene_id"],
1360
- },
1361
- },
1362
- ];
1363
-
1364
- // ===== REGISTER TOOLS =====
1365
-
1366
- server.setRequestHandler(ListToolsRequestSchema, async () => {
1367
- return { tools: TOOLS };
1368
- });
1369
-
1370
- // ===== PROJECT SCOPE GUARD =====
1371
- const EXEMPT_TOOLS = new Set(["taskovergg_list_projects", "taskovergg_add_project"]);
1372
-
1373
- function getActiveProject() {
1374
- return store.getSetting("active_project");
1375
- }
1376
-
1377
- // ===== HANDLE TOOL CALLS =====
1378
-
1379
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
1380
- const { name, arguments: args } = request.params;
1381
-
1382
- // ── Cloud mode: validate schema, then route through cloud adapter ──
1383
- if (CLOUD_MODE) {
1384
- const mapping = toolMap[name];
1385
- if (!mapping) {
1386
- return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
1387
- }
1388
- if (!cloudAdapter.isAllowed(mapping.rpc)) {
1389
- return { content: [{ type: "text", text: `Method "${mapping.rpc}" is not available in cloud mode` }] };
1390
- }
1391
-
1392
- // SECURITY: Schema validation -- strips unknown fields, checks required + types
1393
- const raw = args || {};
1394
- const { errors, cleaned } = validateSchema(raw, mapping.schema);
1395
- if (errors.length > 0) {
1396
- return { content: [{ type: "text", text: `Validation error: ${errors.join("; ")}` }] };
1397
- }
1398
-
1399
- try {
1400
- const rpcArgs = mapping.args(cleaned, raw);
1401
- const result = await cloudAdapter.callRpc(mapping.rpc, rpcArgs);
1402
- return { content: [{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2) }] };
1403
- } catch (err) {
1404
- if (err.message === "AUTH_FAILED") {
1405
- console.error("[ERROR] API key rejected. Shutting down.");
1406
- process.exit(1);
1407
- }
1408
- if (err.message.startsWith("RATE_LIMITED:")) {
1409
- const secs = err.message.split(":")[1];
1410
- return { content: [{ type: "text", text: `Rate limited. Try again in ${secs} seconds.` }] };
1411
- }
1412
- return { content: [{ type: "text", text: `Error: ${err.message}` }] };
1413
- }
1414
- }
1415
-
1416
- // ── Local mode: existing switch/case block (unchanged) ──
1417
-
1418
- // Reload database from disk so we pick up changes made by the Electron app
1419
- reloadFromDisk();
1420
-
1421
- // Auto-resolve project_id: if a tool needs a project_id, use the active project from TaskOver
1422
- if (!EXEMPT_TOOLS.has(name)) {
1423
- const activeProject = getActiveProject();
1424
- if (activeProject) {
1425
- // Always use the active project — no guessing needed from the AI
1426
- args.project_id = activeProject;
1427
- }
1428
- }
1429
-
1430
- try {
1431
- let result;
1432
-
1433
- switch (name) {
1434
- // Dashboard & Context
1435
- case "taskovergg_dashboard":
1436
- result = store.dashboard(args.project_id);
1437
- break;
1438
- case "taskovergg_context_export":
1439
- result = store.contextExport(args.project_id);
1440
- break;
1441
- case "taskovergg_search":
1442
- result = store.search(args.project_id, args.query);
1443
- break;
1444
-
1445
- // Projects
1446
- case "taskovergg_list_projects":
1447
- result = store.listProjects();
1448
- break;
1449
- case "taskovergg_add_project":
1450
- result = {
1451
- error: "Projects must be created on the TaskOver website — not via MCP.",
1452
- instructions: [
1453
- "1. Go to taskover.gg and log in",
1454
- "2. Click the project dropdown in the top-left of the sidebar",
1455
- "3. Click '+ New Project' at the bottom of the dropdown",
1456
- "4. Follow the 5-step project creation wizard (name, color/icon, systems, settings, review)",
1457
- "5. Once the project appears in the list, come back and tell me the project name",
1458
- "6. I'll then use taskovergg_list_projects to get the project_id and start adding tasks, systems, etc."
1459
- ],
1460
- hint: "After the user creates the project, use taskovergg_list_projects to find the project_id."
1461
- };
1462
- break;
1463
-
1464
- // Tasks
1465
- case "taskovergg_get_tasks":
1466
- result = store.getTasks(args.project_id, args.status, args.phase);
1467
- break;
1468
- case "taskovergg_add_task": {
1469
- const DEFAULT_STATUSES = new Set(["todo", "doing", "done", "scrapped"]);
1470
- const taskStatus = (args.status || "todo").toLowerCase();
1471
-
1472
- // If the status matches a custom column, add as a card to that column instead
1473
- if (!DEFAULT_STATUSES.has(taskStatus) && args.project_id) {
1474
- const proj = store.getProject(args.project_id);
1475
- const customCols = proj?.customBoardCols || [];
1476
- const matchCol = customCols.find(cc =>
1477
- cc.name.toLowerCase() === taskStatus ||
1478
- cc.tag === taskStatus ||
1479
- String(cc.id) === taskStatus
1480
- );
1481
- if (matchCol) {
1482
- const newCard = {
1483
- id: Date.now(),
1484
- title: args.title,
1485
- description: args.description || "",
1486
- priority: args.priority || "medium",
1487
- tags: args.tags || [],
1488
- createdBy: "Assistant",
1489
- createdAt: new Date().toISOString(),
1490
- };
1491
- const updCols = customCols.map(cc =>
1492
- String(cc.id) === String(matchCol.id) ? { ...cc, cards: [...(cc.cards || []), newCard] } : cc
1493
- );
1494
- store.updateProjectField(args.project_id, "customBoardCols", updCols);
1495
- result = { ...newCard, column: matchCol.name };
1496
- break;
1497
- }
1498
- }
1499
-
1500
- result = store.addTask({
1501
- projectId: args.project_id, title: args.title, description: args.description,
1502
- status: args.status, phase: args.phase, priority: args.priority,
1503
- tags: args.tags, checklist: args.checklist, createdBy: "Assistant",
1504
- });
1505
- break;
1506
- }
1507
- case "taskovergg_update_task":
1508
- if (!args.task_id) { result = { error: "task_id is required" }; break; }
1509
- result = store.updateTask(args.task_id, {
1510
- title: args.title, description: args.description, status: args.status,
1511
- phase: args.phase, priority: args.priority, tags: args.tags, checklist: args.checklist,
1512
- });
1513
- if (!result) { result = { error: `Task not found: ${args.task_id}` }; }
1514
- break;
1515
- case "taskovergg_move_task":
1516
- result = store.moveTask(args.task_id, args.status);
1517
- break;
1518
- case "taskovergg_add_task_comment":
1519
- result = store.addTaskComment(args.task_id, args.text);
1520
- break;
1521
-
1522
- // Systems
1523
- case "taskovergg_get_systems":
1524
- result = store.getSystems(args.project_id);
1525
- break;
1526
- case "taskovergg_add_system":
1527
- result = store.addSystem({
1528
- projectId: args.project_id, name: args.name, description: args.description,
1529
- status: args.status, keyFiles: args.key_files, notes: args.notes,
1530
- criticalRules: args.critical_rules,
1531
- });
1532
- break;
1533
- case "taskovergg_update_system":
1534
- result = store.updateSystem(args.system_id, {
1535
- name: args.name, description: args.description, status: args.status,
1536
- keyFiles: args.key_files, notes: args.notes, criticalRules: args.critical_rules,
1537
- });
1538
- break;
1539
-
1540
- // Sessions
1541
- case "taskovergg_get_sessions":
1542
- result = store.getSessions(args.project_id, args.limit);
1543
- break;
1544
- case "taskovergg_log_session":
1545
- result = store.logSession({
1546
- projectId: args.project_id, title: args.title, summary: args.summary,
1547
- whatWeDid: args.what_we_did, whereWeLeftOff: args.where_we_left_off,
1548
- nextSteps: args.next_steps, systemsWorkedOn: args.systems_worked_on,
1549
- });
1550
- break;
1551
-
1552
- // Changelog
1553
- case "taskovergg_get_changelog":
1554
- result = store.getChangelog(args.project_id, args.limit);
1555
- break;
1556
- case "taskovergg_add_changelog":
1557
- result = store.addChangelog({
1558
- projectId: args.project_id, title: args.title,
1559
- changes: args.changes, systemsAffected: args.systems_affected,
1560
- });
1561
- break;
1562
-
1563
- // Bugs
1564
- case "taskovergg_get_bugs":
1565
- result = store.getBugs(args.project_id, args.status);
1566
- break;
1567
- case "taskovergg_add_bug":
1568
- result = store.addBug({
1569
- projectId: args.project_id, systemId: args.system_id, title: args.title,
1570
- description: args.description, priority: args.priority,
1571
- stepsToReproduce: args.steps_to_reproduce,
1572
- });
1573
- break;
1574
- case "taskovergg_fix_bug":
1575
- result = store.fixBug(args.bug_id, args.fix_description);
1576
- break;
1577
-
1578
- // Decisions
1579
- case "taskovergg_get_decisions":
1580
- result = store.getDecisions(args.project_id);
1581
- break;
1582
- case "taskovergg_log_decision":
1583
- result = store.logDecision({
1584
- projectId: args.project_id, decision: args.decision, context: args.context,
1585
- systemId: args.system_id, alternatives: args.alternatives,
1586
- });
1587
- break;
1588
-
1589
- // Blueprints
1590
- case "taskovergg_get_blueprints":
1591
- result = store.getBlueprints(args.project_id);
1592
- break;
1593
- case "taskovergg_add_blueprint":
1594
- result = store.addBlueprint({
1595
- projectId: args.project_id, name: args.name, path: args.path,
1596
- parentClass: args.parent_class, systemId: args.system_id,
1597
- description: args.description, variables: args.variables,
1598
- keyFunctions: args.key_functions,
1599
- });
1600
- break;
1601
- case "taskovergg_update_blueprint":
1602
- result = store.updateBlueprint(args.blueprint_id, {
1603
- name: args.name, path: args.path, parentClass: args.parent_class,
1604
- description: args.description, variables: args.variables,
1605
- keyFunctions: args.key_functions,
1606
- });
1607
- break;
1608
-
1609
- // Blueprint Graphs
1610
- case "taskovergg_get_blueprint_graphs":
1611
- result = store.getBlueprintGraphs(args.blueprint_id);
1612
- break;
1613
- case "taskovergg_set_blueprint_graph":
1614
- result = store.setBlueprintGraph(args.blueprint_id, {
1615
- name: args.graph_name, type: args.graph_type || "event",
1616
- nodes: args.nodes, connections: args.connections,
1617
- });
1618
- break;
1619
- case "taskovergg_add_graph_nodes":
1620
- result = store.addGraphNodes(args.blueprint_id, args.graph_name, {
1621
- nodes: args.nodes || [], connections: args.connections || [],
1622
- });
1623
- break;
1624
-
1625
- // Notes
1626
- case "taskovergg_get_notes":
1627
- result = store.getNotes(args.project_id, args.parent_type, args.parent_id);
1628
- break;
1629
- case "taskovergg_add_note":
1630
- result = store.addNote({
1631
- projectId: args.project_id, parentType: args.parent_type,
1632
- parentId: args.parent_id, title: args.title, content: args.content,
1633
- });
1634
- break;
1635
-
1636
- // Backups
1637
- case "taskovergg_log_backup":
1638
- result = store.logBackup({
1639
- projectId: args.project_id, title: args.title, description: args.description,
1640
- });
1641
- break;
1642
-
1643
- // Levels
1644
- case "taskovergg_get_levels":
1645
- result = store.getLevels(args.project_id);
1646
- break;
1647
- case "taskovergg_add_level":
1648
- result = store.addLevel({
1649
- projectId: args.project_id, name: args.name, map: args.map,
1650
- actorCount: args.actor_count, status: args.status,
1651
- });
1652
- break;
1653
- case "taskovergg_update_level":
1654
- result = store.updateLevel(args.level_id, {
1655
- name: args.name, map: args.map, actorCount: args.actor_count, status: args.status,
1656
- });
1657
- break;
1658
- case "taskovergg_add_actor":
1659
- result = store.addActor(args.level_id, {
1660
- name: args.name, x: args.x, y: args.y, z: args.z, notes: args.notes,
1661
- });
1662
- break;
1663
- case "taskovergg_remove_actor":
1664
- result = store.removeActor(args.level_id, args.actor_index);
1665
- break;
1666
-
1667
- // Plugins
1668
- case "taskovergg_get_plugins":
1669
- result = store.getPlugins(args.project_id);
1670
- break;
1671
- case "taskovergg_add_plugin":
1672
- result = store.addPlugin({
1673
- projectId: args.project_id, name: args.name, version: args.version,
1674
- source: args.source, dependents: args.dependents,
1675
- });
1676
- break;
1677
- case "taskovergg_update_plugin":
1678
- result = store.updatePlugin(args.plugin_id, {
1679
- name: args.name, version: args.version, source: args.source, dependents: args.dependents,
1680
- });
1681
- break;
1682
- case "taskovergg_delete_plugin":
1683
- result = store.deletePlugin(args.plugin_id);
1684
- break;
1685
-
1686
- // Build Errors
1687
- case "taskovergg_get_build_errors":
1688
- result = store.getBuildErrors(args.project_id, args.status);
1689
- break;
1690
- case "taskovergg_add_build_error":
1691
- result = store.addBuildError({
1692
- projectId: args.project_id, error: args.error, bp: args.bp,
1693
- });
1694
- break;
1695
- case "taskovergg_fix_build_error":
1696
- result = store.fixBuildError(args.error_id, args.fix);
1697
- break;
1698
-
1699
- // Optimize Items
1700
- case "taskovergg_get_optimize_items":
1701
- result = store.getOptimizeItems(args.project_id);
1702
- break;
1703
- case "taskovergg_add_optimize_item":
1704
- result = store.addOptimizeItem({
1705
- projectId: args.project_id, title: args.title, category: args.category,
1706
- status: args.status, savings: args.savings,
1707
- });
1708
- break;
1709
- case "taskovergg_update_optimize_item":
1710
- result = store.updateOptimizeItem(args.item_id, {
1711
- title: args.title, category: args.category, status: args.status, savings: args.savings,
1712
- });
1713
- break;
1714
-
1715
- // Perf Budget
1716
- case "taskovergg_get_perf_budget":
1717
- result = store.getPerfBudget(args.project_id);
1718
- break;
1719
- case "taskovergg_add_perf_budget":
1720
- result = store.addPerfBudget({
1721
- projectId: args.project_id, scene: args.scene, fps: args.fps,
1722
- gpu: args.gpu, memory: args.memory, notes: args.notes,
1723
- });
1724
- break;
1725
-
1726
- // Playtests
1727
- case "taskovergg_get_playtests":
1728
- result = store.getPlaytests(args.project_id);
1729
- break;
1730
- case "taskovergg_add_playtest":
1731
- result = store.addPlaytest({
1732
- projectId: args.project_id, duration: args.duration, issues: args.issues,
1733
- rating: args.rating, notes: args.notes, tester: args.tester,
1734
- buildVersion: args.build_version,
1735
- });
1736
- break;
1737
-
1738
- // Milestones
1739
- case "taskovergg_get_milestones":
1740
- result = store.getMilestones(args.project_id);
1741
- break;
1742
- case "taskovergg_add_milestone":
1743
- result = store.addMilestone({
1744
- projectId: args.project_id, title: args.title, date: args.date,
1745
- status: args.status, notes: args.notes,
1746
- });
1747
- break;
1748
- case "taskovergg_update_milestone":
1749
- result = store.updateMilestone(args.milestone_id, {
1750
- title: args.title, date: args.date, status: args.status, notes: args.notes,
1751
- });
1752
- break;
1753
-
1754
- // Iterations
1755
- case "taskovergg_get_iterations":
1756
- result = store.getIterations(args.project_id);
1757
- break;
1758
- case "taskovergg_add_iteration":
1759
- result = store.addIteration({
1760
- projectId: args.project_id, feature: args.feature, version: args.version,
1761
- change: args.change, reason: args.reason,
1762
- });
1763
- break;
1764
-
1765
- // Dialogues
1766
- case "taskovergg_get_dialogues":
1767
- result = store.getDialogues(args.project_id);
1768
- break;
1769
- case "taskovergg_add_dialogue":
1770
- result = store.addDialogue({
1771
- projectId: args.project_id, npc: args.npc, line: args.line,
1772
- choices: args.choices, nextNode: args.next_node, notes: args.notes,
1773
- });
1774
- break;
1775
- case "taskovergg_update_dialogue":
1776
- result = store.updateDialogue(args.dialogue_id, {
1777
- npc: args.npc, line: args.line, choices: args.choices,
1778
- nextNode: args.next_node, notes: args.notes,
1779
- });
1780
- break;
1781
-
1782
- // Sounds
1783
- case "taskovergg_get_sounds":
1784
- result = store.getSounds(args.project_id);
1785
- break;
1786
- case "taskovergg_add_sound":
1787
- result = store.addSound({
1788
- projectId: args.project_id, name: args.name, actor: args.actor,
1789
- trigger: args.trigger, spatial: args.spatial, attenuation: args.attenuation,
1790
- notes: args.notes,
1791
- });
1792
- break;
1793
- case "taskovergg_update_sound":
1794
- result = store.updateSound(args.sound_id, {
1795
- name: args.name, actor: args.actor, trigger: args.trigger,
1796
- spatial: args.spatial, attenuation: args.attenuation, notes: args.notes,
1797
- });
1798
- break;
1799
-
1800
- // Controls
1801
- case "taskovergg_get_controls":
1802
- result = store.getControls(args.project_id);
1803
- break;
1804
- case "taskovergg_add_control":
1805
- result = store.addControl({
1806
- projectId: args.project_id, key: args.key, action: args.action,
1807
- context: args.context, condition: args.condition,
1808
- });
1809
- break;
1810
- case "taskovergg_update_control":
1811
- result = store.updateControl(args.control_id, {
1812
- key: args.key, action: args.action, context: args.context, condition: args.condition,
1813
- });
1814
- break;
1815
-
1816
- // Assets
1817
- case "taskovergg_get_assets":
1818
- result = store.getAssets(args.project_id);
1819
- break;
1820
- case "taskovergg_add_asset":
1821
- result = store.addAsset({
1822
- projectId: args.project_id, name: args.name, type: args.type,
1823
- path: args.path, size: args.size, status: args.status, usedBy: args.used_by,
1824
- });
1825
- break;
1826
- case "taskovergg_update_asset":
1827
- result = store.updateAsset(args.asset_id, {
1828
- name: args.name, type: args.type, path: args.path,
1829
- size: args.size, status: args.status, usedBy: args.used_by,
1830
- });
1831
- break;
1832
-
1833
- // References
1834
- case "taskovergg_get_refs":
1835
- result = store.getRefs(args.project_id);
1836
- break;
1837
- case "taskovergg_add_ref":
1838
- result = store.addRef({
1839
- projectId: args.project_id, title: args.title, type: args.type,
1840
- url: args.url, notes: args.notes,
1841
- });
1842
- break;
1843
-
1844
- // Marketing
1845
- case "taskovergg_get_marketing_items":
1846
- result = store.getMarketingItems(args.project_id);
1847
- break;
1848
- case "taskovergg_add_marketing_item":
1849
- result = store.addMarketingItem({
1850
- projectId: args.project_id, item: args.item, category: args.category,
1851
- status: args.status, notes: args.notes,
1852
- });
1853
- break;
1854
- case "taskovergg_update_marketing_item":
1855
- result = store.updateMarketingItem(args.marketing_id, {
1856
- item: args.item, category: args.category, status: args.status, notes: args.notes,
1857
- });
1858
- break;
1859
-
1860
- // Wiki
1861
- case "taskovergg_get_wiki_pages":
1862
- result = store.getWikiPages(args.project_id);
1863
- break;
1864
- case "taskovergg_add_wiki_page":
1865
- result = store.addWikiPage({
1866
- projectId: args.project_id, title: args.title, category: args.category,
1867
- content: args.content,
1868
- });
1869
- break;
1870
- case "taskovergg_update_wiki_page":
1871
- result = store.updateWikiPage(args.wiki_id, {
1872
- title: args.title, category: args.category, content: args.content,
1873
- });
1874
- break;
1875
-
1876
- // Ship Readiness
1877
- case "taskovergg_get_ship_checked":
1878
- result = store.getShipChecked(args.project_id);
1879
- break;
1880
- case "taskovergg_toggle_ship_check":
1881
- result = store.toggleShipCheck(args.project_id, args.step_id);
1882
- break;
1883
-
1884
- // Open Questions
1885
- case "taskovergg_get_open_questions":
1886
- result = store.getOpenQuestions(args.project_id);
1887
- break;
1888
- case "taskovergg_add_open_question":
1889
- result = store.addOpenQuestion({
1890
- projectId: args.project_id, text: args.text,
1891
- });
1892
- break;
1893
- case "taskovergg_toggle_question":
1894
- result = store.toggleQuestion(args.question_id);
1895
- break;
1896
-
1897
- // Board Columns (Blocks)
1898
- case "taskovergg_add_board_column": {
1899
- const pid = args.project_id;
1900
- if (!pid) { result = { error: "No active project" }; break; }
1901
- const proj = store.getProject(pid);
1902
- if (!proj) { result = { error: `Project not found: ${pid}` }; break; }
1903
- const newCol = {
1904
- id: Date.now(),
1905
- name: args.name,
1906
- color: args.color || "#9B95A0",
1907
- tag: args.name.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
1908
- cards: [],
1909
- };
1910
- const existingCols = proj.customBoardCols || [];
1911
- store.updateProjectField(pid, "customBoardCols", [...existingCols, newCol]);
1912
- result = newCol;
1913
- break;
1914
- }
1915
-
1916
- // Stories (Narrative Bible)
1917
- case "taskovergg_get_stories":
1918
- result = store.getStories(args.project_id);
1919
- break;
1920
- case "taskovergg_add_story":
1921
- result = store.addStory({
1922
- projectId: args.project_id, sectionId: args.section_id,
1923
- section: args.section, content: args.content,
1924
- });
1925
- break;
1926
- case "taskovergg_update_story":
1927
- result = store.updateStory(args.story_id, {
1928
- section: args.section, content: args.content,
1929
- });
1930
- break;
1931
-
1932
- // Scenes
1933
- case "taskovergg_get_scenes":
1934
- result = store.getScenes(args.project_id);
1935
- break;
1936
- case "taskovergg_add_scene":
1937
- result = store.addScene({
1938
- projectId: args.project_id, number: args.number, title: args.title,
1939
- location: args.location, characters: args.characters,
1940
- mood: args.mood, round: args.round, content: args.content,
1941
- });
1942
- break;
1943
- case "taskovergg_update_scene":
1944
- result = store.updateScene(args.scene_id, {
1945
- number: args.number, title: args.title, location: args.location,
1946
- characters: args.characters, mood: args.mood, round: args.round, content: args.content,
1947
- });
1948
- break;
1949
- case "taskovergg_delete_scene":
1950
- result = store.deleteScene(args.scene_id);
1951
- break;
1952
-
1953
- // Story Bible (Story NEW tab)
1954
- case "taskovergg_get_story_bible":
1955
- result = store.getStoryBible(args.project_id);
1956
- break;
1957
- case "taskovergg_update_story_bible":
1958
- result = store.updateStoryBible(args.project_id, {
1959
- title: args.title, logline: args.logline, genre: args.genre,
1960
- tone: args.tone, script: args.script,
1961
- });
1962
- break;
1963
- case "taskovergg_add_story_character":
1964
- result = store.addStoryCharacter(args.project_id, {
1965
- name: args.name, role: args.role, description: args.description,
1966
- });
1967
- break;
1968
- case "taskovergg_update_story_character":
1969
- result = store.updateStoryCharacter(args.project_id, args.character_id, {
1970
- name: args.name, role: args.role, description: args.description,
1971
- });
1972
- break;
1973
- case "taskovergg_get_scene_content":
1974
- result = store.getSceneContent(args.scene_id);
1975
- break;
1976
- case "taskovergg_update_scene_content":
1977
- result = store.updateSceneContent(args.project_id, args.scene_id, {
1978
- title: args.title, location: args.location, mood: args.mood,
1979
- content: args.content, round: args.round,
1980
- characters: args.characters, objectives: args.objectives,
1981
- });
1982
- break;
1983
-
1984
- default:
1985
- return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
1986
- }
1987
-
1988
- return {
1989
- content: [{
1990
- type: "text",
1991
- text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
1992
- }],
1993
- };
1994
-
1995
- } catch (err) {
1996
- return {
1997
- content: [{ type: "text", text: `Error: ${err && err.message ? err.message : String(err)}` }],
1998
- isError: true,
1999
- };
2000
- }
2001
- });
2002
-
2003
- // ===== START =====
2004
-
2005
- const MCP_TRANSPORT = (process.env.MCP_TRANSPORT || "stdio").toLowerCase();
2006
- const MCP_PORT = parseInt(process.env.MCP_PORT || "8484", 10);
2007
-
2008
- async function startStdio() {
2009
- const transport = new StdioServerTransport();
2010
- await server.connect(transport);
2011
- console.error("TaskOver MCP server running (stdio)");
2012
- }
2013
-
2014
- async function startHttp() {
2015
- const http = require("http");
2016
- const { randomUUID } = require("crypto");
2017
- const { StreamableHTTPServerTransport } = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
2018
-
2019
- const transport = new StreamableHTTPServerTransport({
2020
- sessionIdGenerator: () => randomUUID(),
2021
- });
2022
- await server.connect(transport);
2023
-
2024
- const httpServer = http.createServer(async (req, res) => {
2025
- // Only bind to /mcp endpoint
2026
- const url = req.url.split("?")[0];
2027
- if (url === "/mcp") {
2028
- await transport.handleRequest(req, res);
2029
- } else if (url === "/health") {
2030
- res.writeHead(200, { "Content-Type": "application/json" });
2031
- res.end(JSON.stringify({ status: "ok", transport: "http", tools: TOOLS.length }));
2032
- } else {
2033
- res.writeHead(404);
2034
- res.end("Not found");
2035
- }
2036
- });
2037
-
2038
- httpServer.listen(MCP_PORT, "127.0.0.1", () => {
2039
- console.error(`TaskOver MCP server running (http) on http://127.0.0.1:${MCP_PORT}/mcp`);
2040
- });
2041
- }
2042
-
2043
- async function main() {
2044
- if (CLOUD_MODE) {
2045
- // Validate key before accepting connections
2046
- const check = await cloudAdapter.validateKey();
2047
- if (!check.valid) {
2048
- console.error(`[ERROR] ${check.error}`);
2049
- process.exit(1);
2050
- }
2051
- console.error(`[CLOUD] Connected to api.taskover.gg as ${check.displayName}`);
2052
- } else {
2053
- await initDb();
2054
- }
2055
-
2056
- if (MCP_TRANSPORT === "http" || MCP_TRANSPORT === "sse") {
2057
- await startHttp();
2058
- } else {
2059
- await startStdio();
2060
- }
2061
- }
2062
-
2063
- main().catch((err) => {
2064
- // SECURITY: Never log the API key in crash output
2065
- const msg = err.message || String(err);
2066
- console.error(`[FATAL] ${msg.replace(/tok_[a-zA-Z0-9]+/g, "tok_[REDACTED]")}`);
2067
- process.exit(1);
2068
- });
1
+ #!/usr/bin/env node
2
+
3
+ const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
4
+ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const {
6
+ ListToolsRequestSchema,
7
+ CallToolRequestSchema,
8
+ } = require("@modelcontextprotocol/sdk/types.js");
9
+
10
+ // SECURITY: Mode selection -- cloud is the customer path, local is dev-only.
11
+ // Priority: CLOUD > LOCAL > BROWSER_AUTH (default)
12
+ const CLOUD_MODE = !!process.env.TASKOVER_API_KEY;
13
+ const LOCAL_MODE = !CLOUD_MODE && process.env.TASKOVER_LOCAL === "1";
14
+ const BROWSER_AUTH_MODE = !CLOUD_MODE && !LOCAL_MODE;
15
+
16
+ if (LOCAL_MODE) {
17
+ console.error("[DEV] Using local SQLite database. Not for production use.");
18
+ }
19
+
20
+ // SECURITY: Refuse API key passed as CLI argument
21
+ if (process.argv.some(arg => arg.startsWith("tok_"))) {
22
+ console.error("[SECURITY] API keys must not be passed as command-line arguments.");
23
+ console.error("Use the TASKOVER_API_KEY environment variable instead.");
24
+ process.exit(1);
25
+ }
26
+
27
+ let store, initDb, reloadFromDisk;
28
+ let cloudAdapter, toolMap, validateSchema;
29
+ let authFlow;
30
+
31
+ if (CLOUD_MODE || BROWSER_AUTH_MODE) {
32
+ cloudAdapter = require("./cloud-adapter.js");
33
+ const tm = require("./tool-map.js");
34
+ toolMap = tm.TOOL_MAP;
35
+ validateSchema = tm.validateSchema;
36
+ if (CLOUD_MODE) {
37
+ cloudAdapter.init(process.env.TASKOVER_API_KEY);
38
+ }
39
+ if (BROWSER_AUTH_MODE) {
40
+ authFlow = require("./auth-flow.js");
41
+ }
42
+ } else {
43
+ store = require("./data-store.js");
44
+ ({ initDb, reloadFromDisk } = require("./db"));
45
+ }
46
+
47
+ const server = new Server(
48
+ { name: "taskover.com", version: "1.0.0" },
49
+ { capabilities: { tools: {} } }
50
+ );
51
+
52
+ // ===== TOOL DEFINITIONS =====
53
+
54
+ const TOOLS = [
55
+ // --- Dashboard & Context ---
56
+ {
57
+ name: "taskovergg_dashboard",
58
+ description: "Get full project dashboard: system counts, open bugs, task board, last session. Use at start of every new chat.",
59
+ inputSchema: {
60
+ type: "object",
61
+ properties: {
62
+ project_id: { type: "string", description: "Project ID (e.g. 'village', 'room-06')" },
63
+ },
64
+ required: ["project_id"],
65
+ },
66
+ },
67
+ {
68
+ name: "taskovergg_context_export",
69
+ description: "Generate full context dump — all systems, bugs, tasks, last session, decisions. Paste into new chats.",
70
+ inputSchema: {
71
+ type: "object",
72
+ properties: { project_id: { type: "string" } },
73
+ required: ["project_id"],
74
+ },
75
+ },
76
+ {
77
+ name: "taskovergg_search",
78
+ description: "Search across all project data — tasks, systems, sessions, bugs, blueprints, notes.",
79
+ inputSchema: {
80
+ type: "object",
81
+ properties: {
82
+ project_id: { type: "string" },
83
+ query: { type: "string", description: "Search term" },
84
+ },
85
+ required: ["project_id", "query"],
86
+ },
87
+ },
88
+
89
+ // --- Projects ---
90
+ {
91
+ name: "taskovergg_list_projects",
92
+ description: "List all projects.",
93
+ inputSchema: { type: "object", properties: {} },
94
+ },
95
+ {
96
+ name: "taskovergg_add_project",
97
+ description: `DO NOT call this tool directly. Projects must be created by the user on the TaskOver website.
98
+
99
+ Instead of calling this tool, instruct the user with these steps:
100
+ 1. Go to taskover.com and log in
101
+ 2. Click the project dropdown in the top-left of the sidebar
102
+ 3. Click "+ New Project" at the bottom of the dropdown
103
+ 4. Follow the 5-step project creation wizard:
104
+ - Step 1: Enter the project name and description
105
+ - Step 2: Choose a project color/icon
106
+ - Step 3: Set up initial systems (or skip)
107
+ - Step 4: Configure project settings
108
+ - Step 5: Review and confirm
109
+ 5. Once the project appears in the project list, tell the AI the project name so it can begin adding tasks, systems, and other data to it via MCP.
110
+
111
+ After the user confirms the project exists, use taskovergg_list_projects to find the project_id, then use other tools (taskovergg_add_task, taskovergg_add_system, etc.) to populate it.`,
112
+ inputSchema: {
113
+ type: "object",
114
+ properties: {
115
+ name: { type: "string" },
116
+ description: { type: "string" },
117
+ trello_board_id: { type: "string" },
118
+ },
119
+ required: ["name"],
120
+ },
121
+ },
122
+
123
+ // --- Tasks (replaces To Do / Doing / Done / Scrapped) ---
124
+ {
125
+ name: "taskovergg_get_tasks",
126
+ description: "Get tasks filtered by status and/or phase. Statuses: todo, doing, done, scrapped.",
127
+ inputSchema: {
128
+ type: "object",
129
+ properties: {
130
+ project_id: { type: "string" },
131
+ status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
132
+ phase: { type: "string" },
133
+ },
134
+ required: ["project_id"],
135
+ },
136
+ },
137
+ {
138
+ name: "taskovergg_add_task",
139
+ description: "Add a task (idea, feature, to-do item).",
140
+ inputSchema: {
141
+ type: "object",
142
+ properties: {
143
+ project_id: { type: "string" },
144
+ title: { type: "string" },
145
+ description: { type: "string" },
146
+ status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
147
+ phase: { type: "string" },
148
+ priority: { type: "string", enum: ["high", "medium", "low"] },
149
+ tags: { type: "array", items: { type: "string" } },
150
+ checklist: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
151
+ },
152
+ required: ["project_id", "title"],
153
+ },
154
+ },
155
+ {
156
+ name: "taskovergg_update_task",
157
+ description: "Update a task's fields.",
158
+ inputSchema: {
159
+ type: "object",
160
+ properties: {
161
+ task_id: { type: "string" },
162
+ title: { type: "string" },
163
+ description: { type: "string" },
164
+ status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
165
+ phase: { type: "string" },
166
+ priority: { type: "string", enum: ["high", "medium", "low"] },
167
+ tags: { type: "array", items: { type: "string" } },
168
+ checklist: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
169
+ },
170
+ required: ["task_id"],
171
+ },
172
+ },
173
+ {
174
+ name: "taskovergg_move_task",
175
+ description: "Move task between columns (todo/doing/done/scrapped).",
176
+ inputSchema: {
177
+ type: "object",
178
+ properties: {
179
+ task_id: { type: "string" },
180
+ status: { type: "string", enum: ["todo", "doing", "done", "scrapped"] },
181
+ },
182
+ required: ["task_id", "status"],
183
+ },
184
+ },
185
+ {
186
+ name: "taskovergg_add_task_comment",
187
+ description: "Add a comment to a task (replaces Trello card comments).",
188
+ inputSchema: {
189
+ type: "object",
190
+ properties: {
191
+ task_id: { type: "string" },
192
+ text: { type: "string" },
193
+ },
194
+ required: ["task_id", "text"],
195
+ },
196
+ },
197
+
198
+ // --- Systems (replaces Claude Memory documentation cards) ---
199
+ {
200
+ name: "taskovergg_get_systems",
201
+ description: "Get all systems/features for a project with status, files, rules, notes.",
202
+ inputSchema: {
203
+ type: "object",
204
+ properties: { project_id: { type: "string" } },
205
+ required: ["project_id"],
206
+ },
207
+ },
208
+ {
209
+ name: "taskovergg_add_system",
210
+ description: "Document a new system/feature. Include critical rules and notes.",
211
+ inputSchema: {
212
+ type: "object",
213
+ properties: {
214
+ project_id: { type: "string" },
215
+ name: { type: "string" },
216
+ description: { type: "string" },
217
+ status: { type: "string", enum: ["working", "wip", "broken", "planned"] },
218
+ key_files: { type: "string" },
219
+ notes: { type: "string" },
220
+ critical_rules: { type: "string" },
221
+ },
222
+ required: ["project_id", "name"],
223
+ },
224
+ },
225
+ {
226
+ name: "taskovergg_update_system",
227
+ description: "Update a system's status, description, notes, or rules.",
228
+ inputSchema: {
229
+ type: "object",
230
+ properties: {
231
+ system_id: { type: "string" },
232
+ name: { type: "string" },
233
+ description: { type: "string" },
234
+ status: { type: "string", enum: ["working", "wip", "broken", "planned"] },
235
+ key_files: { type: "string" },
236
+ notes: { type: "string" },
237
+ critical_rules: { type: "string" },
238
+ },
239
+ required: ["system_id"],
240
+ },
241
+ },
242
+
243
+ // --- Sessions (replaces Claude Chat Memory) ---
244
+ {
245
+ name: "taskovergg_get_sessions",
246
+ description: "Get recent work sessions with what was done and where we left off.",
247
+ inputSchema: {
248
+ type: "object",
249
+ properties: {
250
+ project_id: { type: "string" },
251
+ limit: { type: "number" },
252
+ },
253
+ required: ["project_id"],
254
+ },
255
+ },
256
+ {
257
+ name: "taskovergg_log_session",
258
+ description: "Log a work session. Call at end of each chat (replaces !chatsave). Include what was done, where we left off, and next steps.",
259
+ inputSchema: {
260
+ type: "object",
261
+ properties: {
262
+ project_id: { type: "string" },
263
+ title: { type: "string" },
264
+ summary: { type: "string" },
265
+ what_we_did: { type: "array", items: { type: "string" } },
266
+ where_we_left_off: { type: "string" },
267
+ next_steps: { type: "array", items: { type: "string" } },
268
+ systems_worked_on: { type: "array", items: { type: "string" } },
269
+ },
270
+ required: ["project_id", "title"],
271
+ },
272
+ },
273
+
274
+ // --- Changelog ---
275
+ {
276
+ name: "taskovergg_get_changelog",
277
+ description: "Get recent changelog entries.",
278
+ inputSchema: {
279
+ type: "object",
280
+ properties: {
281
+ project_id: { type: "string" },
282
+ limit: { type: "number" },
283
+ },
284
+ required: ["project_id"],
285
+ },
286
+ },
287
+ {
288
+ name: "taskovergg_add_changelog",
289
+ description: "Log changes made. Include list of specific changes and affected systems.",
290
+ inputSchema: {
291
+ type: "object",
292
+ properties: {
293
+ project_id: { type: "string" },
294
+ title: { type: "string" },
295
+ changes: { type: "array", items: { type: "string" } },
296
+ systems_affected: { type: "array", items: { type: "string" } },
297
+ },
298
+ required: ["project_id", "title", "changes"],
299
+ },
300
+ },
301
+
302
+ // --- Bugs ---
303
+ {
304
+ name: "taskovergg_get_bugs",
305
+ description: "Get bugs, optionally filtered by status (open/fixed/wontfix).",
306
+ inputSchema: {
307
+ type: "object",
308
+ properties: {
309
+ project_id: { type: "string" },
310
+ status: { type: "string", enum: ["open", "fixed", "wontfix"] },
311
+ },
312
+ required: ["project_id"],
313
+ },
314
+ },
315
+ {
316
+ name: "taskovergg_add_bug",
317
+ description: "Report a bug with priority and steps to reproduce.",
318
+ inputSchema: {
319
+ type: "object",
320
+ properties: {
321
+ project_id: { type: "string" },
322
+ system_id: { type: "string" },
323
+ title: { type: "string" },
324
+ description: { type: "string" },
325
+ priority: { type: "string", enum: ["high", "medium", "low"] },
326
+ steps_to_reproduce: { type: "string" },
327
+ },
328
+ required: ["project_id", "title"],
329
+ },
330
+ },
331
+ {
332
+ name: "taskovergg_fix_bug",
333
+ description: "Mark a bug as fixed with a description of the fix.",
334
+ inputSchema: {
335
+ type: "object",
336
+ properties: {
337
+ bug_id: { type: "string" },
338
+ fix_description: { type: "string" },
339
+ },
340
+ required: ["bug_id"],
341
+ },
342
+ },
343
+
344
+ // --- Decisions ---
345
+ {
346
+ name: "taskovergg_get_decisions",
347
+ description: "Get design decisions for a project.",
348
+ inputSchema: {
349
+ type: "object",
350
+ properties: { project_id: { type: "string" } },
351
+ required: ["project_id"],
352
+ },
353
+ },
354
+ {
355
+ name: "taskovergg_log_decision",
356
+ description: "Log a design decision with context and alternatives considered.",
357
+ inputSchema: {
358
+ type: "object",
359
+ properties: {
360
+ project_id: { type: "string" },
361
+ decision: { type: "string" },
362
+ context: { type: "string" },
363
+ system_id: { type: "string" },
364
+ alternatives: { type: "string" },
365
+ },
366
+ required: ["project_id", "decision"],
367
+ },
368
+ },
369
+
370
+ // --- Blueprints ---
371
+ {
372
+ name: "taskovergg_get_blueprints",
373
+ description: "Get all registered Blueprints for a project.",
374
+ inputSchema: {
375
+ type: "object",
376
+ properties: { project_id: { type: "string" } },
377
+ required: ["project_id"],
378
+ },
379
+ },
380
+ {
381
+ name: "taskovergg_add_blueprint",
382
+ description: "Register a Blueprint with path, parent class, variables, and key functions.",
383
+ inputSchema: {
384
+ type: "object",
385
+ properties: {
386
+ project_id: { type: "string" },
387
+ name: { type: "string" },
388
+ path: { type: "string" },
389
+ parent_class: { type: "string" },
390
+ system_id: { type: "string" },
391
+ description: { type: "string" },
392
+ variables: { type: "array", items: { type: "string" } },
393
+ key_functions: { type: "array", items: { type: "string" } },
394
+ },
395
+ required: ["project_id", "name"],
396
+ },
397
+ },
398
+ {
399
+ name: "taskovergg_update_blueprint",
400
+ description: "Update a registered Blueprint.",
401
+ inputSchema: {
402
+ type: "object",
403
+ properties: {
404
+ blueprint_id: { type: "string" },
405
+ name: { type: "string" },
406
+ path: { type: "string" },
407
+ parent_class: { type: "string" },
408
+ description: { type: "string" },
409
+ variables: { type: "array", items: { type: "string" } },
410
+ key_functions: { type: "array", items: { type: "string" } },
411
+ },
412
+ required: ["blueprint_id"],
413
+ },
414
+ },
415
+
416
+ // --- Blueprint Graphs ---
417
+ {
418
+ name: "taskovergg_get_blueprint_graphs",
419
+ description: "Get all visual diagram graphs for a blueprint, including nodes, connections, and layout.",
420
+ inputSchema: {
421
+ type: "object",
422
+ properties: { blueprint_id: { type: "string" } },
423
+ required: ["blueprint_id"],
424
+ },
425
+ },
426
+ {
427
+ name: "taskovergg_set_blueprint_graph",
428
+ description: "Set a complete visual diagram graph on a blueprint. Creates or replaces a named graph with nodes, pins, and connections.",
429
+ inputSchema: {
430
+ type: "object",
431
+ properties: {
432
+ blueprint_id: { type: "string" },
433
+ graph_name: { type: "string", description: "Graph name, e.g. 'EventGraph'" },
434
+ graph_type: { type: "string", enum: ["event", "function", "macro"] },
435
+ nodes: {
436
+ type: "array",
437
+ items: {
438
+ type: "object",
439
+ properties: {
440
+ id: { type: "string" }, type: { type: "string" }, title: { type: "string" },
441
+ subtitle: { type: "string" }, x: { type: "number" }, y: { type: "number" },
442
+ comment: { type: "string" }, pure: { type: "boolean" },
443
+ pins: { type: "array", items: { type: "object",
444
+ properties: { id: { type: "string" }, name: { type: "string" }, direction: { type: "string" }, type: { type: "string" } },
445
+ required: ["id", "name", "direction", "type"] } },
446
+ },
447
+ required: ["id", "type", "title", "x", "y", "pins"],
448
+ },
449
+ },
450
+ connections: {
451
+ type: "array",
452
+ items: { type: "object",
453
+ properties: { id: { type: "string" }, sourceNodeId: { type: "string" }, sourcePinId: { type: "string" }, targetNodeId: { type: "string" }, targetPinId: { type: "string" } },
454
+ required: ["id", "sourceNodeId", "sourcePinId", "targetNodeId", "targetPinId"] },
455
+ },
456
+ },
457
+ required: ["blueprint_id", "graph_name", "nodes", "connections"],
458
+ },
459
+ },
460
+ {
461
+ name: "taskovergg_add_graph_nodes",
462
+ description: "Add nodes and connections to an existing blueprint graph without replacing existing data.",
463
+ inputSchema: {
464
+ type: "object",
465
+ properties: {
466
+ blueprint_id: { type: "string" },
467
+ graph_name: { type: "string" },
468
+ nodes: { type: "array", items: { type: "object" } },
469
+ connections: { type: "array", items: { type: "object" } },
470
+ },
471
+ required: ["blueprint_id", "graph_name"],
472
+ },
473
+ },
474
+
475
+ // --- Blueprint Intelligence ---
476
+ {
477
+ name: "taskovergg_sync_blueprint_data",
478
+ description: "Sync full Blueprint graph data from UE5 plugin. Pushes variables, functions, graphs with nodes/pins/connections. Max 50 BPs per call — chunk larger projects.",
479
+ inputSchema: {
480
+ type: "object",
481
+ properties: {
482
+ project_id: { type: "string", description: "Target project ID" },
483
+ schema_version: { type: "number", description: "Payload schema version (must be 1)" },
484
+ blueprints: {
485
+ type: "array",
486
+ description: "Array of blueprint data objects",
487
+ items: {
488
+ type: "object",
489
+ properties: {
490
+ name: { type: "string" }, path: { type: "string" },
491
+ parent_class: { type: "string" }, description: { type: "string" },
492
+ variables: { type: "array", items: { type: "object" } },
493
+ key_functions: { type: "array", items: { type: "object" } },
494
+ graphs: { type: "array", items: {
495
+ type: "object",
496
+ properties: {
497
+ name: { type: "string" }, type: { type: "string" },
498
+ nodes: { type: "array", items: { type: "object" } },
499
+ connections: { type: "array", items: { type: "object" } },
500
+ },
501
+ }},
502
+ },
503
+ required: ["name", "path"],
504
+ },
505
+ },
506
+ },
507
+ required: ["project_id", "schema_version", "blueprints"],
508
+ },
509
+ },
510
+ {
511
+ name: "taskovergg_sync_debug",
512
+ description: "Get sync debug info for a project: last sync time, per-BP hash, node/connection counts, sync status.",
513
+ inputSchema: {
514
+ type: "object",
515
+ properties: { project_id: { type: "string" } },
516
+ required: ["project_id"],
517
+ },
518
+ },
519
+ {
520
+ name: "taskovergg_get_discovered_bp_detail",
521
+ description: "Get full detail for a discovered Blueprint including parsed graph data.",
522
+ inputSchema: {
523
+ type: "object",
524
+ properties: { blueprint_id: { type: "string" } },
525
+ required: ["blueprint_id"],
526
+ },
527
+ },
528
+ {
529
+ name: "taskovergg_blueprint_complexity",
530
+ description: "Get estimated complexity score (heuristic v1) for one blueprint or all blueprints in a project. Returns per-metric breakdown with data confidence level.",
531
+ inputSchema: {
532
+ type: "object",
533
+ properties: {
534
+ project_id: { type: "string", description: "Get health overview for entire project" },
535
+ blueprint_id: { type: "string", description: "Get complexity for specific blueprint" },
536
+ },
537
+ },
538
+ },
539
+ {
540
+ name: "taskovergg_blueprint_timeline",
541
+ description: "Get change history for a blueprint. Shows structural changes over time with diffs and summaries.",
542
+ inputSchema: {
543
+ type: "object",
544
+ properties: {
545
+ blueprint_id: { type: "string" },
546
+ limit: { type: "number", description: "Max entries to return (default 20)" },
547
+ include_layout: { type: "boolean", description: "Include layout-only changes (default false)" },
548
+ },
549
+ required: ["blueprint_id"],
550
+ },
551
+ },
552
+ {
553
+ name: "taskovergg_blueprint_summary",
554
+ description: "Generate an auto-generated overview of a blueprint including execution paths, variables, and performance notes. This is a draft summary — verify against the actual Blueprint.",
555
+ inputSchema: {
556
+ type: "object",
557
+ properties: { blueprint_id: { type: "string" } },
558
+ required: ["blueprint_id"],
559
+ },
560
+ },
561
+ {
562
+ name: "taskovergg_blueprint_review",
563
+ description: "Run AI heuristic review on a blueprint. Returns findings (high-confidence warnings) and signals (informational hints). Checks for tick issues, cast chains, deep nesting, spaghetti, etc.",
564
+ inputSchema: {
565
+ type: "object",
566
+ properties: {
567
+ project_id: { type: "string", description: "Review all blueprints in project" },
568
+ blueprint_id: { type: "string", description: "Review single blueprint" },
569
+ },
570
+ },
571
+ },
572
+ {
573
+ name: "taskovergg_blueprint_standards",
574
+ description: "Manage blueprint coding standards for a project. Get standards list, toggle rules, or evaluate all blueprints against enabled standards.",
575
+ inputSchema: {
576
+ type: "object",
577
+ properties: {
578
+ project_id: { type: "string" },
579
+ action: { type: "string", enum: ["get", "toggle", "evaluate"], description: "get=list standards, toggle=enable/disable a rule, evaluate=run all standards" },
580
+ rule_id: { type: "string", description: "For toggle action" },
581
+ enabled: { type: "boolean", description: "For toggle action" },
582
+ },
583
+ required: ["project_id", "action"],
584
+ },
585
+ },
586
+ {
587
+ name: "taskovergg_blueprint_impact",
588
+ description: "Analyze detected impact radius for a blueprint change. Shows which other blueprints reference this one via casts, variables, and function calls. Heuristic v1 — does not include C++, interfaces, or runtime connections.",
589
+ inputSchema: {
590
+ type: "object",
591
+ properties: {
592
+ project_id: { type: "string" },
593
+ blueprint_id: { type: "string" },
594
+ },
595
+ required: ["project_id", "blueprint_id"],
596
+ },
597
+ },
598
+ {
599
+ name: "taskovergg_blueprint_dependencies",
600
+ description: "Build or retrieve the full blueprint dependency graph for a project. Shows cast, variable, and function references between blueprints.",
601
+ inputSchema: {
602
+ type: "object",
603
+ properties: { project_id: { type: "string" } },
604
+ required: ["project_id"],
605
+ },
606
+ },
607
+
608
+ // --- Notes ---
609
+ {
610
+ name: "taskovergg_get_notes",
611
+ description: "Get notes, optionally filtered by parent type (project/system/task/blueprint).",
612
+ inputSchema: {
613
+ type: "object",
614
+ properties: {
615
+ project_id: { type: "string" },
616
+ parent_type: { type: "string", enum: ["project", "system", "task", "blueprint"] },
617
+ parent_id: { type: "string" },
618
+ },
619
+ required: ["project_id"],
620
+ },
621
+ },
622
+ {
623
+ name: "taskovergg_add_note",
624
+ description: "Add a note/document to the project or attached to a system/task/blueprint.",
625
+ inputSchema: {
626
+ type: "object",
627
+ properties: {
628
+ project_id: { type: "string" },
629
+ parent_type: { type: "string", enum: ["project", "system", "task", "blueprint"] },
630
+ parent_id: { type: "string" },
631
+ title: { type: "string" },
632
+ content: { type: "string" },
633
+ },
634
+ required: ["project_id", "content"],
635
+ },
636
+ },
637
+
638
+ // --- Backups ---
639
+ {
640
+ name: "taskovergg_log_backup",
641
+ description: "Log that a project backup was created (replaces !backup).",
642
+ inputSchema: {
643
+ type: "object",
644
+ properties: {
645
+ project_id: { type: "string" },
646
+ title: { type: "string" },
647
+ description: { type: "string" },
648
+ },
649
+ required: ["project_id"],
650
+ },
651
+ },
652
+
653
+ // --- Levels ---
654
+ {
655
+ name: "taskovergg_get_levels",
656
+ description: "Get all levels/scenes for a project with actors and status.",
657
+ inputSchema: {
658
+ type: "object",
659
+ properties: { project_id: { type: "string" } },
660
+ required: ["project_id"],
661
+ },
662
+ },
663
+ {
664
+ name: "taskovergg_add_level",
665
+ description: "Add a level/scene with name, map, and actor count.",
666
+ inputSchema: {
667
+ type: "object",
668
+ properties: {
669
+ project_id: { type: "string" },
670
+ name: { type: "string" },
671
+ map: { type: "string", description: "Map/file path" },
672
+ actor_count: { type: "number" },
673
+ status: { type: "string", enum: ["planned", "wip", "testing", "done"] },
674
+ },
675
+ required: ["project_id", "name"],
676
+ },
677
+ },
678
+ {
679
+ name: "taskovergg_update_level",
680
+ description: "Update a level's status, name, map, or actor count.",
681
+ inputSchema: {
682
+ type: "object",
683
+ properties: {
684
+ level_id: { type: "string" },
685
+ name: { type: "string" },
686
+ map: { type: "string" },
687
+ actor_count: { type: "number" },
688
+ status: { type: "string", enum: ["planned", "wip", "testing", "done"] },
689
+ },
690
+ required: ["level_id"],
691
+ },
692
+ },
693
+ {
694
+ name: "taskovergg_add_actor",
695
+ description: "Add an actor to a level with XYZ coordinates.",
696
+ inputSchema: {
697
+ type: "object",
698
+ properties: {
699
+ level_id: { type: "string" },
700
+ name: { type: "string" },
701
+ x: { type: "number" }, y: { type: "number" }, z: { type: "number" },
702
+ notes: { type: "string" },
703
+ },
704
+ required: ["level_id", "name"],
705
+ },
706
+ },
707
+ {
708
+ name: "taskovergg_remove_actor",
709
+ description: "Remove an actor from a level by index.",
710
+ inputSchema: {
711
+ type: "object",
712
+ properties: {
713
+ level_id: { type: "string" },
714
+ actor_index: { type: "number" },
715
+ },
716
+ required: ["level_id", "actor_index"],
717
+ },
718
+ },
719
+
720
+ // --- Plugins ---
721
+ {
722
+ name: "taskovergg_get_plugins",
723
+ description: "Get all plugins/dependencies for a project.",
724
+ inputSchema: {
725
+ type: "object",
726
+ properties: { project_id: { type: "string" } },
727
+ required: ["project_id"],
728
+ },
729
+ },
730
+ {
731
+ name: "taskovergg_add_plugin",
732
+ description: "Track a plugin/dependency with version and source.",
733
+ inputSchema: {
734
+ type: "object",
735
+ properties: {
736
+ project_id: { type: "string" },
737
+ name: { type: "string" },
738
+ version: { type: "string" },
739
+ source: { type: "string", description: "Marketplace, GitHub, Custom, etc." },
740
+ dependents: { type: "array", items: { type: "string" } },
741
+ },
742
+ required: ["project_id", "name"],
743
+ },
744
+ },
745
+ {
746
+ name: "taskovergg_update_plugin",
747
+ description: "Update a plugin's version, source, or dependents.",
748
+ inputSchema: {
749
+ type: "object",
750
+ properties: {
751
+ plugin_id: { type: "string" },
752
+ name: { type: "string" },
753
+ version: { type: "string" },
754
+ source: { type: "string" },
755
+ dependents: { type: "array", items: { type: "string" } },
756
+ },
757
+ required: ["plugin_id"],
758
+ },
759
+ },
760
+ {
761
+ name: "taskovergg_delete_plugin",
762
+ description: "Remove a plugin from the tracker.",
763
+ inputSchema: {
764
+ type: "object",
765
+ properties: { plugin_id: { type: "string" } },
766
+ required: ["plugin_id"],
767
+ },
768
+ },
769
+
770
+ // --- Build Errors ---
771
+ {
772
+ name: "taskovergg_get_build_errors",
773
+ description: "Get build/compile errors, optionally filtered by status (open/fixed).",
774
+ inputSchema: {
775
+ type: "object",
776
+ properties: {
777
+ project_id: { type: "string" },
778
+ status: { type: "string", enum: ["open", "fixed"] },
779
+ },
780
+ required: ["project_id"],
781
+ },
782
+ },
783
+ {
784
+ name: "taskovergg_add_build_error",
785
+ description: "Log a build/compile error tagged to a blueprint.",
786
+ inputSchema: {
787
+ type: "object",
788
+ properties: {
789
+ project_id: { type: "string" },
790
+ error: { type: "string", description: "Error message/description" },
791
+ bp: { type: "string", description: "Related blueprint name" },
792
+ },
793
+ required: ["project_id", "error"],
794
+ },
795
+ },
796
+ {
797
+ name: "taskovergg_fix_build_error",
798
+ description: "Mark a build error as fixed with a description of the fix.",
799
+ inputSchema: {
800
+ type: "object",
801
+ properties: {
802
+ error_id: { type: "string" },
803
+ fix: { type: "string", description: "How the error was fixed" },
804
+ },
805
+ required: ["error_id"],
806
+ },
807
+ },
808
+
809
+ // --- Optimize Items ---
810
+ {
811
+ name: "taskovergg_get_optimize_items",
812
+ description: "Get optimization issues (unused assets, oversized files, performance problems).",
813
+ inputSchema: {
814
+ type: "object",
815
+ properties: { project_id: { type: "string" } },
816
+ required: ["project_id"],
817
+ },
818
+ },
819
+ {
820
+ name: "taskovergg_add_optimize_item",
821
+ description: "Log an optimization issue with category and estimated savings.",
822
+ inputSchema: {
823
+ type: "object",
824
+ properties: {
825
+ project_id: { type: "string" },
826
+ title: { type: "string" },
827
+ category: { type: "string", enum: ["unused", "textures", "meshes", "performance", "memory", "other"] },
828
+ status: { type: "string", enum: ["open", "fixed", "ok"] },
829
+ savings: { type: "string", description: "Estimated savings (e.g. '50MB', '15ms')" },
830
+ },
831
+ required: ["project_id", "title"],
832
+ },
833
+ },
834
+ {
835
+ name: "taskovergg_update_optimize_item",
836
+ description: "Update an optimization item's status or details.",
837
+ inputSchema: {
838
+ type: "object",
839
+ properties: {
840
+ item_id: { type: "string" },
841
+ title: { type: "string" },
842
+ category: { type: "string" },
843
+ status: { type: "string", enum: ["open", "fixed", "ok"] },
844
+ savings: { type: "string" },
845
+ },
846
+ required: ["item_id"],
847
+ },
848
+ },
849
+
850
+ // --- Perf Budget ---
851
+ {
852
+ name: "taskovergg_get_perf_budget",
853
+ description: "Get performance budget readings (FPS, GPU, memory per scene).",
854
+ inputSchema: {
855
+ type: "object",
856
+ properties: { project_id: { type: "string" } },
857
+ required: ["project_id"],
858
+ },
859
+ },
860
+ {
861
+ name: "taskovergg_add_perf_budget",
862
+ description: "Add a performance budget reading for a scene.",
863
+ inputSchema: {
864
+ type: "object",
865
+ properties: {
866
+ project_id: { type: "string" },
867
+ scene: { type: "string" },
868
+ fps: { type: "number" },
869
+ gpu: { type: "number", description: "GPU usage percentage" },
870
+ memory: { type: "number", description: "Memory usage in MB" },
871
+ notes: { type: "string" },
872
+ },
873
+ required: ["project_id", "scene"],
874
+ },
875
+ },
876
+
877
+ // --- Playtests ---
878
+ {
879
+ name: "taskovergg_get_playtests",
880
+ description: "Get playtest sessions for a project.",
881
+ inputSchema: {
882
+ type: "object",
883
+ properties: { project_id: { type: "string" } },
884
+ required: ["project_id"],
885
+ },
886
+ },
887
+ {
888
+ name: "taskovergg_add_playtest",
889
+ description: "Log a playtest session with rating, tester, issues, and build version.",
890
+ inputSchema: {
891
+ type: "object",
892
+ properties: {
893
+ project_id: { type: "string" },
894
+ duration: { type: "string", description: "e.g. '45 min'" },
895
+ issues: { type: "array", items: { type: "string" } },
896
+ rating: { type: "number", description: "1-5 star rating" },
897
+ notes: { type: "string" },
898
+ tester: { type: "string" },
899
+ build_version: { type: "string" },
900
+ },
901
+ required: ["project_id"],
902
+ },
903
+ },
904
+
905
+ // --- Milestones ---
906
+ {
907
+ name: "taskovergg_get_milestones",
908
+ description: "Get project milestones/timeline.",
909
+ inputSchema: {
910
+ type: "object",
911
+ properties: { project_id: { type: "string" } },
912
+ required: ["project_id"],
913
+ },
914
+ },
915
+ {
916
+ name: "taskovergg_add_milestone",
917
+ description: "Add a project milestone with target date.",
918
+ inputSchema: {
919
+ type: "object",
920
+ properties: {
921
+ project_id: { type: "string" },
922
+ title: { type: "string" },
923
+ date: { type: "string", description: "Target date (YYYY-MM-DD)" },
924
+ status: { type: "string", enum: ["upcoming", "active", "done", "missed"] },
925
+ notes: { type: "string" },
926
+ },
927
+ required: ["project_id", "title"],
928
+ },
929
+ },
930
+ {
931
+ name: "taskovergg_update_milestone",
932
+ description: "Update a milestone's status, date, or notes.",
933
+ inputSchema: {
934
+ type: "object",
935
+ properties: {
936
+ milestone_id: { type: "string" },
937
+ title: { type: "string" },
938
+ date: { type: "string" },
939
+ status: { type: "string", enum: ["upcoming", "active", "done", "missed"] },
940
+ notes: { type: "string" },
941
+ },
942
+ required: ["milestone_id"],
943
+ },
944
+ },
945
+
946
+ // --- Iterations ---
947
+ {
948
+ name: "taskovergg_get_iterations",
949
+ description: "Get feature iteration history.",
950
+ inputSchema: {
951
+ type: "object",
952
+ properties: { project_id: { type: "string" } },
953
+ required: ["project_id"],
954
+ },
955
+ },
956
+ {
957
+ name: "taskovergg_add_iteration",
958
+ description: "Log a feature iteration (version change with reason).",
959
+ inputSchema: {
960
+ type: "object",
961
+ properties: {
962
+ project_id: { type: "string" },
963
+ feature: { type: "string" },
964
+ version: { type: "string", description: "e.g. 'v2', 'v3'" },
965
+ change: { type: "string", description: "What changed" },
966
+ reason: { type: "string", description: "Why it changed" },
967
+ },
968
+ required: ["project_id", "feature"],
969
+ },
970
+ },
971
+
972
+ // --- Dialogues ---
973
+ {
974
+ name: "taskovergg_get_dialogues",
975
+ description: "Get NPC dialogue nodes for a project.",
976
+ inputSchema: {
977
+ type: "object",
978
+ properties: { project_id: { type: "string" } },
979
+ required: ["project_id"],
980
+ },
981
+ },
982
+ {
983
+ name: "taskovergg_add_dialogue",
984
+ description: "Add an NPC dialogue node with choices and branching.",
985
+ inputSchema: {
986
+ type: "object",
987
+ properties: {
988
+ project_id: { type: "string" },
989
+ npc: { type: "string" },
990
+ line: { type: "string", description: "Dialogue text" },
991
+ choices: { type: "array", items: { type: "string" } },
992
+ next_node: { type: "string", description: "Next dialogue node ID" },
993
+ notes: { type: "string" },
994
+ },
995
+ required: ["project_id", "npc"],
996
+ },
997
+ },
998
+ {
999
+ name: "taskovergg_update_dialogue",
1000
+ description: "Update a dialogue node.",
1001
+ inputSchema: {
1002
+ type: "object",
1003
+ properties: {
1004
+ dialogue_id: { type: "string" },
1005
+ npc: { type: "string" },
1006
+ line: { type: "string" },
1007
+ choices: { type: "array", items: { type: "string" } },
1008
+ next_node: { type: "string" },
1009
+ notes: { type: "string" },
1010
+ },
1011
+ required: ["dialogue_id"],
1012
+ },
1013
+ },
1014
+
1015
+ // --- Sounds ---
1016
+ {
1017
+ name: "taskovergg_get_sounds",
1018
+ description: "Get sound cues for a project.",
1019
+ inputSchema: {
1020
+ type: "object",
1021
+ properties: { project_id: { type: "string" } },
1022
+ required: ["project_id"],
1023
+ },
1024
+ },
1025
+ {
1026
+ name: "taskovergg_add_sound",
1027
+ description: "Track a sound cue with actor, trigger, and spatial settings.",
1028
+ inputSchema: {
1029
+ type: "object",
1030
+ properties: {
1031
+ project_id: { type: "string" },
1032
+ name: { type: "string" },
1033
+ actor: { type: "string" },
1034
+ trigger: { type: "string" },
1035
+ spatial: { type: "boolean" },
1036
+ attenuation: { type: "string" },
1037
+ notes: { type: "string" },
1038
+ },
1039
+ required: ["project_id", "name"],
1040
+ },
1041
+ },
1042
+ {
1043
+ name: "taskovergg_update_sound",
1044
+ description: "Update a sound cue's settings.",
1045
+ inputSchema: {
1046
+ type: "object",
1047
+ properties: {
1048
+ sound_id: { type: "string" },
1049
+ name: { type: "string" },
1050
+ actor: { type: "string" },
1051
+ trigger: { type: "string" },
1052
+ spatial: { type: "boolean" },
1053
+ attenuation: { type: "string" },
1054
+ notes: { type: "string" },
1055
+ },
1056
+ required: ["sound_id"],
1057
+ },
1058
+ },
1059
+
1060
+ // --- Controls ---
1061
+ {
1062
+ name: "taskovergg_get_controls",
1063
+ description: "Get input control bindings for a project.",
1064
+ inputSchema: {
1065
+ type: "object",
1066
+ properties: { project_id: { type: "string" } },
1067
+ required: ["project_id"],
1068
+ },
1069
+ },
1070
+ {
1071
+ name: "taskovergg_add_control",
1072
+ description: "Add an input control binding (key → action).",
1073
+ inputSchema: {
1074
+ type: "object",
1075
+ properties: {
1076
+ project_id: { type: "string" },
1077
+ key: { type: "string", description: "Input key/button" },
1078
+ action: { type: "string", description: "Game action" },
1079
+ context: { type: "string", description: "Input context (e.g. 'on foot', 'in vehicle')" },
1080
+ condition: { type: "string" },
1081
+ },
1082
+ required: ["project_id", "key", "action"],
1083
+ },
1084
+ },
1085
+ {
1086
+ name: "taskovergg_update_control",
1087
+ description: "Update a control binding.",
1088
+ inputSchema: {
1089
+ type: "object",
1090
+ properties: {
1091
+ control_id: { type: "string" },
1092
+ key: { type: "string" },
1093
+ action: { type: "string" },
1094
+ context: { type: "string" },
1095
+ condition: { type: "string" },
1096
+ },
1097
+ required: ["control_id"],
1098
+ },
1099
+ },
1100
+
1101
+ // --- Assets ---
1102
+ {
1103
+ name: "taskovergg_get_assets",
1104
+ description: "Get assets in the pipeline for a project.",
1105
+ inputSchema: {
1106
+ type: "object",
1107
+ properties: { project_id: { type: "string" } },
1108
+ required: ["project_id"],
1109
+ },
1110
+ },
1111
+ {
1112
+ name: "taskovergg_add_asset",
1113
+ description: "Track an asset in the pipeline with type, status, and usage.",
1114
+ inputSchema: {
1115
+ type: "object",
1116
+ properties: {
1117
+ project_id: { type: "string" },
1118
+ name: { type: "string" },
1119
+ type: { type: "string", description: "Asset type (Mesh, Texture, Material, Sound, etc.)" },
1120
+ path: { type: "string" },
1121
+ size: { type: "string" },
1122
+ status: { type: "string", enum: ["concept", "imported", "integrated", "tested", "final"] },
1123
+ used_by: { type: "string" },
1124
+ },
1125
+ required: ["project_id", "name"],
1126
+ },
1127
+ },
1128
+ {
1129
+ name: "taskovergg_update_asset",
1130
+ description: "Update an asset's status or details.",
1131
+ inputSchema: {
1132
+ type: "object",
1133
+ properties: {
1134
+ asset_id: { type: "string" },
1135
+ name: { type: "string" },
1136
+ type: { type: "string" },
1137
+ path: { type: "string" },
1138
+ size: { type: "string" },
1139
+ status: { type: "string", enum: ["concept", "imported", "integrated", "tested", "final"] },
1140
+ used_by: { type: "string" },
1141
+ },
1142
+ required: ["asset_id"],
1143
+ },
1144
+ },
1145
+
1146
+ // --- References ---
1147
+ {
1148
+ name: "taskovergg_get_refs",
1149
+ description: "Get reference/inspiration items for a project.",
1150
+ inputSchema: {
1151
+ type: "object",
1152
+ properties: { project_id: { type: "string" } },
1153
+ required: ["project_id"],
1154
+ },
1155
+ },
1156
+ {
1157
+ name: "taskovergg_add_ref",
1158
+ description: "Add a reference/inspiration item (game, art, video, article).",
1159
+ inputSchema: {
1160
+ type: "object",
1161
+ properties: {
1162
+ project_id: { type: "string" },
1163
+ title: { type: "string" },
1164
+ type: { type: "string", enum: ["Game", "Art", "Video", "Article"] },
1165
+ url: { type: "string" },
1166
+ notes: { type: "string" },
1167
+ },
1168
+ required: ["project_id", "title"],
1169
+ },
1170
+ },
1171
+
1172
+ // --- Marketing ---
1173
+ {
1174
+ name: "taskovergg_get_marketing_items",
1175
+ description: "Get marketing/launch prep items for a project.",
1176
+ inputSchema: {
1177
+ type: "object",
1178
+ properties: { project_id: { type: "string" } },
1179
+ required: ["project_id"],
1180
+ },
1181
+ },
1182
+ {
1183
+ name: "taskovergg_add_marketing_item",
1184
+ description: "Add a marketing/launch prep item (Steam page, trailer, social, press).",
1185
+ inputSchema: {
1186
+ type: "object",
1187
+ properties: {
1188
+ project_id: { type: "string" },
1189
+ item: { type: "string" },
1190
+ category: { type: "string", enum: ["Steam", "Trailer", "Social", "Press"] },
1191
+ status: { type: "string", enum: ["todo", "doing", "done"] },
1192
+ notes: { type: "string" },
1193
+ },
1194
+ required: ["project_id", "item"],
1195
+ },
1196
+ },
1197
+ {
1198
+ name: "taskovergg_update_marketing_item",
1199
+ description: "Update a marketing item's status or details.",
1200
+ inputSchema: {
1201
+ type: "object",
1202
+ properties: {
1203
+ marketing_id: { type: "string" },
1204
+ item: { type: "string" },
1205
+ category: { type: "string" },
1206
+ status: { type: "string", enum: ["todo", "doing", "done"] },
1207
+ notes: { type: "string" },
1208
+ },
1209
+ required: ["marketing_id"],
1210
+ },
1211
+ },
1212
+
1213
+ // --- Wiki ---
1214
+ {
1215
+ name: "taskovergg_get_wiki_pages",
1216
+ description: "Get internal wiki/knowledge base pages for a project.",
1217
+ inputSchema: {
1218
+ type: "object",
1219
+ properties: { project_id: { type: "string" } },
1220
+ required: ["project_id"],
1221
+ },
1222
+ },
1223
+ {
1224
+ name: "taskovergg_add_wiki_page",
1225
+ description: "Add a wiki/knowledge base page with category and content.",
1226
+ inputSchema: {
1227
+ type: "object",
1228
+ properties: {
1229
+ project_id: { type: "string" },
1230
+ title: { type: "string" },
1231
+ category: { type: "string", enum: ["Systems", "Workflows", "Guides", "Reference"] },
1232
+ content: { type: "string" },
1233
+ },
1234
+ required: ["project_id", "title"],
1235
+ },
1236
+ },
1237
+ {
1238
+ name: "taskovergg_update_wiki_page",
1239
+ description: "Update a wiki page's title, category, or content.",
1240
+ inputSchema: {
1241
+ type: "object",
1242
+ properties: {
1243
+ wiki_id: { type: "string" },
1244
+ title: { type: "string" },
1245
+ category: { type: "string" },
1246
+ content: { type: "string" },
1247
+ },
1248
+ required: ["wiki_id"],
1249
+ },
1250
+ },
1251
+
1252
+ // --- Ship Readiness ---
1253
+ {
1254
+ name: "taskovergg_get_ship_checked",
1255
+ description: "Get completed ship readiness checklist items for a project.",
1256
+ inputSchema: {
1257
+ type: "object",
1258
+ properties: { project_id: { type: "string" } },
1259
+ required: ["project_id"],
1260
+ },
1261
+ },
1262
+ {
1263
+ name: "taskovergg_toggle_ship_check",
1264
+ description: "Toggle a ship readiness checklist step as complete/incomplete.",
1265
+ inputSchema: {
1266
+ type: "object",
1267
+ properties: {
1268
+ project_id: { type: "string" },
1269
+ step_id: { type: "string", description: "Checklist step ID (e.g. 'p1s1', 'p2s3')" },
1270
+ },
1271
+ required: ["project_id", "step_id"],
1272
+ },
1273
+ },
1274
+
1275
+ // --- Open Questions ---
1276
+ {
1277
+ name: "taskovergg_get_open_questions",
1278
+ description: "Get open design/development questions for a project.",
1279
+ inputSchema: {
1280
+ type: "object",
1281
+ properties: { project_id: { type: "string" } },
1282
+ required: ["project_id"],
1283
+ },
1284
+ },
1285
+ {
1286
+ name: "taskovergg_add_open_question",
1287
+ description: "Add an open question that needs answering.",
1288
+ inputSchema: {
1289
+ type: "object",
1290
+ properties: {
1291
+ project_id: { type: "string" },
1292
+ text: { type: "string" },
1293
+ },
1294
+ required: ["project_id", "text"],
1295
+ },
1296
+ },
1297
+ {
1298
+ name: "taskovergg_toggle_question",
1299
+ description: "Toggle an open question as resolved/unresolved.",
1300
+ inputSchema: {
1301
+ type: "object",
1302
+ properties: { question_id: { type: "string" } },
1303
+ required: ["question_id"],
1304
+ },
1305
+ },
1306
+
1307
+ // --- Board Columns (Blocks) ---
1308
+ {
1309
+ name: "taskovergg_add_board_column",
1310
+ description: "Add a new custom column (block) to the Kanban task board. Use this when the user asks to create a new block, column, or category on their task board. This does NOT create a task/card — it creates a new column that cards can be placed into.",
1311
+ inputSchema: {
1312
+ type: "object",
1313
+ properties: {
1314
+ name: { type: "string", description: "The name/label for the new column (e.g. 'Review Later', 'Blocked', 'QA Testing')" },
1315
+ color: { type: "string", description: "Hex color for the column dot (e.g. '#FDE68A'). Defaults to '#9B95A0' if not provided." },
1316
+ },
1317
+ required: ["name"],
1318
+ },
1319
+ },
1320
+ // --- Stories (Narrative Bible) ---
1321
+ {
1322
+ name: "taskovergg_get_stories",
1323
+ description: "Get all narrative bible / story sections for a project.",
1324
+ inputSchema: {
1325
+ type: "object",
1326
+ properties: { project_id: { type: "string" } },
1327
+ required: ["project_id"],
1328
+ },
1329
+ },
1330
+ {
1331
+ name: "taskovergg_add_story",
1332
+ description: "Add a narrative bible section (synopsis, characters, lore, plot points, or endings).",
1333
+ inputSchema: {
1334
+ type: "object",
1335
+ properties: {
1336
+ project_id: { type: "string" },
1337
+ section_id: { type: "string", description: "Section key: s_synopsis, s_characters, s_lore, s_plot, s_endings" },
1338
+ section: { type: "string", description: "Display label for the section" },
1339
+ content: { type: "string", description: "Section content text" },
1340
+ },
1341
+ required: ["project_id", "section_id", "section"],
1342
+ },
1343
+ },
1344
+ {
1345
+ name: "taskovergg_update_story",
1346
+ description: "Update a narrative bible story section's content.",
1347
+ inputSchema: {
1348
+ type: "object",
1349
+ properties: {
1350
+ story_id: { type: "string" },
1351
+ section: { type: "string" },
1352
+ content: { type: "string" },
1353
+ },
1354
+ required: ["story_id"],
1355
+ },
1356
+ },
1357
+ // --- Scenes ---
1358
+ {
1359
+ name: "taskovergg_get_scenes",
1360
+ description: "Get all scenes for a project's narrative bible.",
1361
+ inputSchema: {
1362
+ type: "object",
1363
+ properties: { project_id: { type: "string" } },
1364
+ required: ["project_id"],
1365
+ },
1366
+ },
1367
+ {
1368
+ name: "taskovergg_add_scene",
1369
+ description: "Add a new scene to a project's story.",
1370
+ inputSchema: {
1371
+ type: "object",
1372
+ properties: {
1373
+ project_id: { type: "string" },
1374
+ number: { type: "number", description: "Scene number" },
1375
+ title: { type: "string" },
1376
+ location: { type: "string" },
1377
+ characters: { type: "array", items: { type: "string" }, description: "Character names in this scene" },
1378
+ mood: { type: "string" },
1379
+ round: { type: "string", description: "Story round/act (e.g. Intro, Act 1)" },
1380
+ content: { type: "string" },
1381
+ },
1382
+ required: ["project_id", "title"],
1383
+ },
1384
+ },
1385
+ {
1386
+ name: "taskovergg_update_scene",
1387
+ description: "Update a scene's fields.",
1388
+ inputSchema: {
1389
+ type: "object",
1390
+ properties: {
1391
+ scene_id: { type: "string" },
1392
+ number: { type: "number" },
1393
+ title: { type: "string" },
1394
+ location: { type: "string" },
1395
+ characters: { type: "array", items: { type: "string" } },
1396
+ mood: { type: "string" },
1397
+ round: { type: "string" },
1398
+ content: { type: "string" },
1399
+ },
1400
+ required: ["scene_id"],
1401
+ },
1402
+ },
1403
+ {
1404
+ name: "taskovergg_delete_scene",
1405
+ description: "Delete a scene from the narrative bible.",
1406
+ inputSchema: {
1407
+ type: "object",
1408
+ properties: { scene_id: { type: "string" } },
1409
+ required: ["scene_id"],
1410
+ },
1411
+ },
1412
+ // --- Story Bible (Story NEW tab full project story data) ---
1413
+ {
1414
+ name: "taskovergg_get_story_bible",
1415
+ description: "Get the full Story Bible for a project — includes title, logline, genre, tone, characters, chapters, scenes with word counts, and script. This is the primary tool for reading story data from the Story NEW writing environment.",
1416
+ inputSchema: {
1417
+ type: "object",
1418
+ properties: { project_id: { type: "string" } },
1419
+ required: ["project_id"],
1420
+ },
1421
+ },
1422
+ {
1423
+ name: "taskovergg_update_story_bible",
1424
+ description: "Update the Story Bible metadata (title, logline, genre, tone, script).",
1425
+ inputSchema: {
1426
+ type: "object",
1427
+ properties: {
1428
+ project_id: { type: "string" },
1429
+ title: { type: "string" },
1430
+ logline: { type: "string" },
1431
+ genre: { type: "string" },
1432
+ tone: { type: "string" },
1433
+ script: { type: "string", description: "Full script/source material text" },
1434
+ },
1435
+ required: ["project_id"],
1436
+ },
1437
+ },
1438
+ {
1439
+ name: "taskovergg_add_story_character",
1440
+ description: "Add a character to the Story Bible.",
1441
+ inputSchema: {
1442
+ type: "object",
1443
+ properties: {
1444
+ project_id: { type: "string" },
1445
+ name: { type: "string" },
1446
+ role: { type: "string", description: "Character role: Player, Protagonist, Antagonist, NPC, Supporting, Mentioned" },
1447
+ description: { type: "string" },
1448
+ },
1449
+ required: ["project_id", "name"],
1450
+ },
1451
+ },
1452
+ {
1453
+ name: "taskovergg_update_story_character",
1454
+ description: "Update a character in the Story Bible.",
1455
+ inputSchema: {
1456
+ type: "object",
1457
+ properties: {
1458
+ project_id: { type: "string" },
1459
+ character_id: { type: "string" },
1460
+ name: { type: "string" },
1461
+ role: { type: "string" },
1462
+ description: { type: "string" },
1463
+ },
1464
+ required: ["project_id", "character_id"],
1465
+ },
1466
+ },
1467
+ {
1468
+ name: "taskovergg_get_scene_content",
1469
+ description: "Get the full content and details of a specific scene by ID.",
1470
+ inputSchema: {
1471
+ type: "object",
1472
+ properties: { scene_id: { type: "string" } },
1473
+ required: ["scene_id"],
1474
+ },
1475
+ },
1476
+ {
1477
+ name: "taskovergg_update_scene_content",
1478
+ description: "Update a scene's fields in the Story NEW writing environment (title, location, mood, content, chapter, characters, objectives).",
1479
+ inputSchema: {
1480
+ type: "object",
1481
+ properties: {
1482
+ project_id: { type: "string" },
1483
+ scene_id: { type: "string" },
1484
+ title: { type: "string" },
1485
+ location: { type: "string" },
1486
+ mood: { type: "string" },
1487
+ content: { type: "string", description: "Scene content (HTML supported)" },
1488
+ round: { type: "string", description: "Chapter name this scene belongs to" },
1489
+ characters: { type: "array", items: { type: "string" }, description: "Character IDs assigned to this scene" },
1490
+ objectives: { type: "array", items: { type: "object", properties: { text: { type: "string" }, done: { type: "boolean" } } } },
1491
+ },
1492
+ required: ["project_id", "scene_id"],
1493
+ },
1494
+ },
1495
+ ];
1496
+
1497
+ // ===== REGISTER TOOLS =====
1498
+
1499
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
1500
+ return { tools: TOOLS };
1501
+ });
1502
+
1503
+ // ===== PROJECT SCOPE GUARD =====
1504
+ const EXEMPT_TOOLS = new Set(["taskovergg_list_projects", "taskovergg_add_project"]);
1505
+
1506
+ function getActiveProject() {
1507
+ return store.getSetting("active_project");
1508
+ }
1509
+
1510
+ // ===== HANDLE TOOL CALLS =====
1511
+
1512
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1513
+ const { name, arguments: args } = request.params;
1514
+
1515
+ // ── Cloud/Browser-auth mode: validate schema, then route through cloud adapter ──
1516
+ if (CLOUD_MODE || BROWSER_AUTH_MODE) {
1517
+ const mapping = toolMap[name];
1518
+ if (!mapping) {
1519
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }] };
1520
+ }
1521
+ if (!cloudAdapter.isAllowed(mapping.rpc)) {
1522
+ return { content: [{ type: "text", text: `Method "${mapping.rpc}" is not available in cloud mode` }] };
1523
+ }
1524
+
1525
+ // SECURITY: Schema validation -- strips unknown fields, checks required + types
1526
+ const raw = args || {};
1527
+ const { errors, cleaned } = validateSchema(raw, mapping.schema);
1528
+ if (errors.length > 0) {
1529
+ return { content: [{ type: "text", text: `Validation error: ${errors.join("; ")}` }] };
1530
+ }
1531
+
1532
+ try {
1533
+ const rpcArgs = mapping.args(cleaned, raw);
1534
+ const result = await cloudAdapter.callRpc(mapping.rpc, rpcArgs);
1535
+ return { content: [{ type: "text", text: typeof result === "string" ? result : JSON.stringify(result, null, 2) }] };
1536
+ } catch (err) {
1537
+ // Boot grace: MCP host auto-called a tool before the user did anything.
1538
+ // Return a passive, non-alarming message — no browser popup was opened.
1539
+ if (err.message && err.message.startsWith("AUTH_NOT_READY:")) {
1540
+ const detail = err.message.slice("AUTH_NOT_READY:".length);
1541
+ return {
1542
+ content: [{
1543
+ type: "text",
1544
+ text: detail
1545
+ }],
1546
+ };
1547
+ }
1548
+ // JIT auth: auth was declined, timed out, or failed after browser flow.
1549
+ // The held-request already waited — this is a final failure, user must retry.
1550
+ if (err.message && err.message.startsWith("AUTH_FAILED_RETRY:")) {
1551
+ const detail = err.message.slice("AUTH_FAILED_RETRY:".length);
1552
+ return {
1553
+ content: [{
1554
+ type: "text",
1555
+ text: `Authorization required. ${detail} Please try your request again.`
1556
+ }],
1557
+ isError: true,
1558
+ };
1559
+ }
1560
+ // Hard auth failure (invalid API key in CLOUD_MODE)
1561
+ if (err.message === "AUTH_FAILED") {
1562
+ return {
1563
+ content: [{
1564
+ type: "text",
1565
+ text: "Authorization failed. Your API key or session is invalid. Check your TASKOVER_API_KEY or restart the MCP server."
1566
+ }],
1567
+ isError: true,
1568
+ };
1569
+ }
1570
+ if (err.message.startsWith("RATE_LIMITED:")) {
1571
+ const secs = err.message.split(":")[1];
1572
+ return { content: [{ type: "text", text: `Rate limited. Try again in ${secs} seconds.` }] };
1573
+ }
1574
+ return { content: [{ type: "text", text: `Error: ${err.message}` }] };
1575
+ }
1576
+ }
1577
+
1578
+ // ── Local mode: existing switch/case block (unchanged) ──
1579
+
1580
+ // Reload database from disk so we pick up changes made by the Electron app
1581
+ reloadFromDisk();
1582
+
1583
+ // Auto-resolve project_id: if a tool needs a project_id, use the active project from TaskOver
1584
+ if (!EXEMPT_TOOLS.has(name)) {
1585
+ const activeProject = getActiveProject();
1586
+ if (activeProject) {
1587
+ // Always use the active project — no guessing needed from the AI
1588
+ args.project_id = activeProject;
1589
+ }
1590
+ }
1591
+
1592
+ try {
1593
+ let result;
1594
+
1595
+ switch (name) {
1596
+ // Dashboard & Context
1597
+ case "taskovergg_dashboard":
1598
+ result = store.dashboard(args.project_id);
1599
+ break;
1600
+ case "taskovergg_context_export":
1601
+ result = store.contextExport(args.project_id);
1602
+ break;
1603
+ case "taskovergg_search":
1604
+ result = store.search(args.project_id, args.query);
1605
+ break;
1606
+
1607
+ // Projects
1608
+ case "taskovergg_list_projects":
1609
+ result = store.listProjects();
1610
+ break;
1611
+ case "taskovergg_add_project":
1612
+ result = {
1613
+ error: "Projects must be created on the TaskOver website — not via MCP.",
1614
+ instructions: [
1615
+ "1. Go to taskover.com and log in",
1616
+ "2. Click the project dropdown in the top-left of the sidebar",
1617
+ "3. Click '+ New Project' at the bottom of the dropdown",
1618
+ "4. Follow the 5-step project creation wizard (name, color/icon, systems, settings, review)",
1619
+ "5. Once the project appears in the list, come back and tell me the project name",
1620
+ "6. I'll then use taskovergg_list_projects to get the project_id and start adding tasks, systems, etc."
1621
+ ],
1622
+ hint: "After the user creates the project, use taskovergg_list_projects to find the project_id."
1623
+ };
1624
+ break;
1625
+
1626
+ // Tasks
1627
+ case "taskovergg_get_tasks":
1628
+ result = store.getTasks(args.project_id, args.status, args.phase);
1629
+ break;
1630
+ case "taskovergg_add_task": {
1631
+ const DEFAULT_STATUSES = new Set(["todo", "doing", "done", "scrapped"]);
1632
+ const taskStatus = (args.status || "todo").toLowerCase();
1633
+
1634
+ // If the status matches a custom column, add as a card to that column instead
1635
+ if (!DEFAULT_STATUSES.has(taskStatus) && args.project_id) {
1636
+ const proj = store.getProject(args.project_id);
1637
+ const customCols = proj?.customBoardCols || [];
1638
+ const matchCol = customCols.find(cc =>
1639
+ cc.name.toLowerCase() === taskStatus ||
1640
+ cc.tag === taskStatus ||
1641
+ String(cc.id) === taskStatus
1642
+ );
1643
+ if (matchCol) {
1644
+ const newCard = {
1645
+ id: Date.now(),
1646
+ title: args.title,
1647
+ description: args.description || "",
1648
+ priority: args.priority || "medium",
1649
+ tags: args.tags || [],
1650
+ createdBy: "Assistant",
1651
+ createdAt: new Date().toISOString(),
1652
+ };
1653
+ const updCols = customCols.map(cc =>
1654
+ String(cc.id) === String(matchCol.id) ? { ...cc, cards: [...(cc.cards || []), newCard] } : cc
1655
+ );
1656
+ store.updateProjectField(args.project_id, "customBoardCols", updCols);
1657
+ result = { ...newCard, column: matchCol.name };
1658
+ break;
1659
+ }
1660
+ }
1661
+
1662
+ result = store.addTask({
1663
+ projectId: args.project_id, title: args.title, description: args.description,
1664
+ status: args.status, phase: args.phase, priority: args.priority,
1665
+ tags: args.tags, checklist: args.checklist, createdBy: "Assistant",
1666
+ });
1667
+ break;
1668
+ }
1669
+ case "taskovergg_update_task":
1670
+ if (!args.task_id) { result = { error: "task_id is required" }; break; }
1671
+ result = store.updateTask(args.task_id, {
1672
+ title: args.title, description: args.description, status: args.status,
1673
+ phase: args.phase, priority: args.priority, tags: args.tags, checklist: args.checklist,
1674
+ });
1675
+ if (!result) { result = { error: `Task not found: ${args.task_id}` }; }
1676
+ break;
1677
+ case "taskovergg_move_task":
1678
+ result = store.moveTask(args.task_id, args.status);
1679
+ break;
1680
+ case "taskovergg_add_task_comment":
1681
+ result = store.addTaskComment(args.task_id, args.text);
1682
+ break;
1683
+
1684
+ // Systems
1685
+ case "taskovergg_get_systems":
1686
+ result = store.getSystems(args.project_id);
1687
+ break;
1688
+ case "taskovergg_add_system":
1689
+ result = store.addSystem({
1690
+ projectId: args.project_id, name: args.name, description: args.description,
1691
+ status: args.status, keyFiles: args.key_files, notes: args.notes,
1692
+ criticalRules: args.critical_rules,
1693
+ });
1694
+ break;
1695
+ case "taskovergg_update_system":
1696
+ result = store.updateSystem(args.system_id, {
1697
+ name: args.name, description: args.description, status: args.status,
1698
+ keyFiles: args.key_files, notes: args.notes, criticalRules: args.critical_rules,
1699
+ });
1700
+ break;
1701
+
1702
+ // Sessions
1703
+ case "taskovergg_get_sessions":
1704
+ result = store.getSessions(args.project_id, args.limit);
1705
+ break;
1706
+ case "taskovergg_log_session":
1707
+ result = store.logSession({
1708
+ projectId: args.project_id, title: args.title, summary: args.summary,
1709
+ whatWeDid: args.what_we_did, whereWeLeftOff: args.where_we_left_off,
1710
+ nextSteps: args.next_steps, systemsWorkedOn: args.systems_worked_on,
1711
+ });
1712
+ break;
1713
+
1714
+ // Changelog
1715
+ case "taskovergg_get_changelog":
1716
+ result = store.getChangelog(args.project_id, args.limit);
1717
+ break;
1718
+ case "taskovergg_add_changelog":
1719
+ result = store.addChangelog({
1720
+ projectId: args.project_id, title: args.title,
1721
+ changes: args.changes, systemsAffected: args.systems_affected,
1722
+ });
1723
+ break;
1724
+
1725
+ // Bugs
1726
+ case "taskovergg_get_bugs":
1727
+ result = store.getBugs(args.project_id, args.status);
1728
+ break;
1729
+ case "taskovergg_add_bug":
1730
+ result = store.addBug({
1731
+ projectId: args.project_id, systemId: args.system_id, title: args.title,
1732
+ description: args.description, priority: args.priority,
1733
+ stepsToReproduce: args.steps_to_reproduce,
1734
+ });
1735
+ break;
1736
+ case "taskovergg_fix_bug":
1737
+ result = store.fixBug(args.bug_id, args.fix_description);
1738
+ break;
1739
+
1740
+ // Decisions
1741
+ case "taskovergg_get_decisions":
1742
+ result = store.getDecisions(args.project_id);
1743
+ break;
1744
+ case "taskovergg_log_decision":
1745
+ result = store.logDecision({
1746
+ projectId: args.project_id, decision: args.decision, context: args.context,
1747
+ systemId: args.system_id, alternatives: args.alternatives,
1748
+ });
1749
+ break;
1750
+
1751
+ // Blueprints
1752
+ case "taskovergg_get_blueprints":
1753
+ result = store.getBlueprints(args.project_id);
1754
+ break;
1755
+ case "taskovergg_add_blueprint":
1756
+ result = store.addBlueprint({
1757
+ projectId: args.project_id, name: args.name, path: args.path,
1758
+ parentClass: args.parent_class, systemId: args.system_id,
1759
+ description: args.description, variables: args.variables,
1760
+ keyFunctions: args.key_functions,
1761
+ });
1762
+ break;
1763
+ case "taskovergg_update_blueprint":
1764
+ result = store.updateBlueprint(args.blueprint_id, {
1765
+ name: args.name, path: args.path, parentClass: args.parent_class,
1766
+ description: args.description, variables: args.variables,
1767
+ keyFunctions: args.key_functions,
1768
+ });
1769
+ break;
1770
+
1771
+ // Blueprint Graphs
1772
+ case "taskovergg_get_blueprint_graphs":
1773
+ result = store.getBlueprintGraphs(args.blueprint_id);
1774
+ break;
1775
+ case "taskovergg_set_blueprint_graph":
1776
+ result = store.setBlueprintGraph(args.blueprint_id, {
1777
+ name: args.graph_name, type: args.graph_type || "event",
1778
+ nodes: args.nodes, connections: args.connections,
1779
+ });
1780
+ break;
1781
+ case "taskovergg_add_graph_nodes":
1782
+ result = store.addGraphNodes(args.blueprint_id, args.graph_name, {
1783
+ nodes: args.nodes || [], connections: args.connections || [],
1784
+ });
1785
+ break;
1786
+
1787
+ // Blueprint Intelligence
1788
+ case "taskovergg_sync_blueprint_data":
1789
+ result = store.syncDiscoveredBps(args.project_id, args.blueprints, args.schema_version);
1790
+ break;
1791
+ case "taskovergg_sync_debug":
1792
+ result = store.getSyncDebugInfo(args.project_id);
1793
+ break;
1794
+ case "taskovergg_get_discovered_bp_detail":
1795
+ result = store.getDiscoveredBpDetail(args.blueprint_id);
1796
+ break;
1797
+ case "taskovergg_blueprint_complexity":
1798
+ if (args.blueprint_id) {
1799
+ result = store.computeBlueprintComplexity(args.blueprint_id);
1800
+ } else if (args.project_id) {
1801
+ result = store.computeBlueprintHealth(args.project_id);
1802
+ } else {
1803
+ result = { error: "Provide either project_id or blueprint_id" };
1804
+ }
1805
+ break;
1806
+ case "taskovergg_blueprint_timeline":
1807
+ result = store.getBlueprintTimeline(args.blueprint_id, args.limit, args.include_layout);
1808
+ break;
1809
+ case "taskovergg_blueprint_summary":
1810
+ result = store.generateBlueprintSummary(args.blueprint_id);
1811
+ break;
1812
+ case "taskovergg_blueprint_review":
1813
+ if (args.blueprint_id) {
1814
+ result = store.computeBlueprintReview(args.blueprint_id);
1815
+ } else if (args.project_id) {
1816
+ result = store.computeProjectBlueprintReview(args.project_id);
1817
+ } else {
1818
+ result = { error: "Provide either project_id or blueprint_id" };
1819
+ }
1820
+ break;
1821
+ case "taskovergg_blueprint_standards":
1822
+ if (args.action === "get") {
1823
+ result = store.getBlueprintStandards(args.project_id);
1824
+ } else if (args.action === "toggle") {
1825
+ result = store.toggleBlueprintStandard(args.project_id, args.rule_id, args.enabled);
1826
+ } else if (args.action === "evaluate") {
1827
+ result = store.evaluateBlueprintStandards(args.project_id);
1828
+ } else {
1829
+ result = { error: "Unknown action. Use: get, toggle, evaluate" };
1830
+ }
1831
+ break;
1832
+ case "taskovergg_blueprint_impact":
1833
+ result = store.computeImpactAnalysis(args.project_id, args.blueprint_id);
1834
+ break;
1835
+ case "taskovergg_blueprint_dependencies":
1836
+ result = store.buildBlueprintDependencyGraph(args.project_id);
1837
+ break;
1838
+
1839
+ // Notes
1840
+ case "taskovergg_get_notes":
1841
+ result = store.getNotes(args.project_id, args.parent_type, args.parent_id);
1842
+ break;
1843
+ case "taskovergg_add_note":
1844
+ result = store.addNote({
1845
+ projectId: args.project_id, parentType: args.parent_type,
1846
+ parentId: args.parent_id, title: args.title, content: args.content,
1847
+ });
1848
+ break;
1849
+
1850
+ // Backups
1851
+ case "taskovergg_log_backup":
1852
+ result = store.logBackup({
1853
+ projectId: args.project_id, title: args.title, description: args.description,
1854
+ });
1855
+ break;
1856
+
1857
+ // Levels
1858
+ case "taskovergg_get_levels":
1859
+ result = store.getLevels(args.project_id);
1860
+ break;
1861
+ case "taskovergg_add_level":
1862
+ result = store.addLevel({
1863
+ projectId: args.project_id, name: args.name, map: args.map,
1864
+ actorCount: args.actor_count, status: args.status,
1865
+ });
1866
+ break;
1867
+ case "taskovergg_update_level":
1868
+ result = store.updateLevel(args.level_id, {
1869
+ name: args.name, map: args.map, actorCount: args.actor_count, status: args.status,
1870
+ });
1871
+ break;
1872
+ case "taskovergg_add_actor":
1873
+ result = store.addActor(args.level_id, {
1874
+ name: args.name, x: args.x, y: args.y, z: args.z, notes: args.notes,
1875
+ });
1876
+ break;
1877
+ case "taskovergg_remove_actor":
1878
+ result = store.removeActor(args.level_id, args.actor_index);
1879
+ break;
1880
+
1881
+ // Plugins
1882
+ case "taskovergg_get_plugins":
1883
+ result = store.getPlugins(args.project_id);
1884
+ break;
1885
+ case "taskovergg_add_plugin":
1886
+ result = store.addPlugin({
1887
+ projectId: args.project_id, name: args.name, version: args.version,
1888
+ source: args.source, dependents: args.dependents,
1889
+ });
1890
+ break;
1891
+ case "taskovergg_update_plugin":
1892
+ result = store.updatePlugin(args.plugin_id, {
1893
+ name: args.name, version: args.version, source: args.source, dependents: args.dependents,
1894
+ });
1895
+ break;
1896
+ case "taskovergg_delete_plugin":
1897
+ result = store.deletePlugin(args.plugin_id);
1898
+ break;
1899
+
1900
+ // Build Errors
1901
+ case "taskovergg_get_build_errors":
1902
+ result = store.getBuildErrors(args.project_id, args.status);
1903
+ break;
1904
+ case "taskovergg_add_build_error":
1905
+ result = store.addBuildError({
1906
+ projectId: args.project_id, error: args.error, bp: args.bp,
1907
+ });
1908
+ break;
1909
+ case "taskovergg_fix_build_error":
1910
+ result = store.fixBuildError(args.error_id, args.fix);
1911
+ break;
1912
+
1913
+ // Optimize Items
1914
+ case "taskovergg_get_optimize_items":
1915
+ result = store.getOptimizeItems(args.project_id);
1916
+ break;
1917
+ case "taskovergg_add_optimize_item":
1918
+ result = store.addOptimizeItem({
1919
+ projectId: args.project_id, title: args.title, category: args.category,
1920
+ status: args.status, savings: args.savings,
1921
+ });
1922
+ break;
1923
+ case "taskovergg_update_optimize_item":
1924
+ result = store.updateOptimizeItem(args.item_id, {
1925
+ title: args.title, category: args.category, status: args.status, savings: args.savings,
1926
+ });
1927
+ break;
1928
+
1929
+ // Perf Budget
1930
+ case "taskovergg_get_perf_budget":
1931
+ result = store.getPerfBudget(args.project_id);
1932
+ break;
1933
+ case "taskovergg_add_perf_budget":
1934
+ result = store.addPerfBudget({
1935
+ projectId: args.project_id, scene: args.scene, fps: args.fps,
1936
+ gpu: args.gpu, memory: args.memory, notes: args.notes,
1937
+ });
1938
+ break;
1939
+
1940
+ // Playtests
1941
+ case "taskovergg_get_playtests":
1942
+ result = store.getPlaytests(args.project_id);
1943
+ break;
1944
+ case "taskovergg_add_playtest":
1945
+ result = store.addPlaytest({
1946
+ projectId: args.project_id, duration: args.duration, issues: args.issues,
1947
+ rating: args.rating, notes: args.notes, tester: args.tester,
1948
+ buildVersion: args.build_version,
1949
+ });
1950
+ break;
1951
+
1952
+ // Milestones
1953
+ case "taskovergg_get_milestones":
1954
+ result = store.getMilestones(args.project_id);
1955
+ break;
1956
+ case "taskovergg_add_milestone":
1957
+ result = store.addMilestone({
1958
+ projectId: args.project_id, title: args.title, date: args.date,
1959
+ status: args.status, notes: args.notes,
1960
+ });
1961
+ break;
1962
+ case "taskovergg_update_milestone":
1963
+ result = store.updateMilestone(args.milestone_id, {
1964
+ title: args.title, date: args.date, status: args.status, notes: args.notes,
1965
+ });
1966
+ break;
1967
+
1968
+ // Iterations
1969
+ case "taskovergg_get_iterations":
1970
+ result = store.getIterations(args.project_id);
1971
+ break;
1972
+ case "taskovergg_add_iteration":
1973
+ result = store.addIteration({
1974
+ projectId: args.project_id, feature: args.feature, version: args.version,
1975
+ change: args.change, reason: args.reason,
1976
+ });
1977
+ break;
1978
+
1979
+ // Dialogues
1980
+ case "taskovergg_get_dialogues":
1981
+ result = store.getDialogues(args.project_id);
1982
+ break;
1983
+ case "taskovergg_add_dialogue":
1984
+ result = store.addDialogue({
1985
+ projectId: args.project_id, npc: args.npc, line: args.line,
1986
+ choices: args.choices, nextNode: args.next_node, notes: args.notes,
1987
+ });
1988
+ break;
1989
+ case "taskovergg_update_dialogue":
1990
+ result = store.updateDialogue(args.dialogue_id, {
1991
+ npc: args.npc, line: args.line, choices: args.choices,
1992
+ nextNode: args.next_node, notes: args.notes,
1993
+ });
1994
+ break;
1995
+
1996
+ // Sounds
1997
+ case "taskovergg_get_sounds":
1998
+ result = store.getSounds(args.project_id);
1999
+ break;
2000
+ case "taskovergg_add_sound":
2001
+ result = store.addSound({
2002
+ projectId: args.project_id, name: args.name, actor: args.actor,
2003
+ trigger: args.trigger, spatial: args.spatial, attenuation: args.attenuation,
2004
+ notes: args.notes,
2005
+ });
2006
+ break;
2007
+ case "taskovergg_update_sound":
2008
+ result = store.updateSound(args.sound_id, {
2009
+ name: args.name, actor: args.actor, trigger: args.trigger,
2010
+ spatial: args.spatial, attenuation: args.attenuation, notes: args.notes,
2011
+ });
2012
+ break;
2013
+
2014
+ // Controls
2015
+ case "taskovergg_get_controls":
2016
+ result = store.getControls(args.project_id);
2017
+ break;
2018
+ case "taskovergg_add_control":
2019
+ result = store.addControl({
2020
+ projectId: args.project_id, key: args.key, action: args.action,
2021
+ context: args.context, condition: args.condition,
2022
+ });
2023
+ break;
2024
+ case "taskovergg_update_control":
2025
+ result = store.updateControl(args.control_id, {
2026
+ key: args.key, action: args.action, context: args.context, condition: args.condition,
2027
+ });
2028
+ break;
2029
+
2030
+ // Assets
2031
+ case "taskovergg_get_assets":
2032
+ result = store.getAssets(args.project_id);
2033
+ break;
2034
+ case "taskovergg_add_asset":
2035
+ result = store.addAsset({
2036
+ projectId: args.project_id, name: args.name, type: args.type,
2037
+ path: args.path, size: args.size, status: args.status, usedBy: args.used_by,
2038
+ });
2039
+ break;
2040
+ case "taskovergg_update_asset":
2041
+ result = store.updateAsset(args.asset_id, {
2042
+ name: args.name, type: args.type, path: args.path,
2043
+ size: args.size, status: args.status, usedBy: args.used_by,
2044
+ });
2045
+ break;
2046
+
2047
+ // References
2048
+ case "taskovergg_get_refs":
2049
+ result = store.getRefs(args.project_id);
2050
+ break;
2051
+ case "taskovergg_add_ref":
2052
+ result = store.addRef({
2053
+ projectId: args.project_id, title: args.title, type: args.type,
2054
+ url: args.url, notes: args.notes,
2055
+ });
2056
+ break;
2057
+
2058
+ // Marketing
2059
+ case "taskovergg_get_marketing_items":
2060
+ result = store.getMarketingItems(args.project_id);
2061
+ break;
2062
+ case "taskovergg_add_marketing_item":
2063
+ result = store.addMarketingItem({
2064
+ projectId: args.project_id, item: args.item, category: args.category,
2065
+ status: args.status, notes: args.notes,
2066
+ });
2067
+ break;
2068
+ case "taskovergg_update_marketing_item":
2069
+ result = store.updateMarketingItem(args.marketing_id, {
2070
+ item: args.item, category: args.category, status: args.status, notes: args.notes,
2071
+ });
2072
+ break;
2073
+
2074
+ // Wiki
2075
+ case "taskovergg_get_wiki_pages":
2076
+ result = store.getWikiPages(args.project_id);
2077
+ break;
2078
+ case "taskovergg_add_wiki_page":
2079
+ result = store.addWikiPage({
2080
+ projectId: args.project_id, title: args.title, category: args.category,
2081
+ content: args.content,
2082
+ });
2083
+ break;
2084
+ case "taskovergg_update_wiki_page":
2085
+ result = store.updateWikiPage(args.wiki_id, {
2086
+ title: args.title, category: args.category, content: args.content,
2087
+ });
2088
+ break;
2089
+
2090
+ // Ship Readiness
2091
+ case "taskovergg_get_ship_checked":
2092
+ result = store.getShipChecked(args.project_id);
2093
+ break;
2094
+ case "taskovergg_toggle_ship_check":
2095
+ result = store.toggleShipCheck(args.project_id, args.step_id);
2096
+ break;
2097
+
2098
+ // Open Questions
2099
+ case "taskovergg_get_open_questions":
2100
+ result = store.getOpenQuestions(args.project_id);
2101
+ break;
2102
+ case "taskovergg_add_open_question":
2103
+ result = store.addOpenQuestion({
2104
+ projectId: args.project_id, text: args.text,
2105
+ });
2106
+ break;
2107
+ case "taskovergg_toggle_question":
2108
+ result = store.toggleQuestion(args.question_id);
2109
+ break;
2110
+
2111
+ // Board Columns (Blocks)
2112
+ case "taskovergg_add_board_column": {
2113
+ const pid = args.project_id;
2114
+ if (!pid) { result = { error: "No active project" }; break; }
2115
+ const proj = store.getProject(pid);
2116
+ if (!proj) { result = { error: `Project not found: ${pid}` }; break; }
2117
+ const newCol = {
2118
+ id: Date.now(),
2119
+ name: args.name,
2120
+ color: args.color || "#9B95A0",
2121
+ tag: args.name.toLowerCase().replace(/[^a-z0-9]+/g, "-"),
2122
+ cards: [],
2123
+ };
2124
+ const existingCols = proj.customBoardCols || [];
2125
+ store.updateProjectField(pid, "customBoardCols", [...existingCols, newCol]);
2126
+ result = newCol;
2127
+ break;
2128
+ }
2129
+
2130
+ // Stories (Narrative Bible)
2131
+ case "taskovergg_get_stories":
2132
+ result = store.getStories(args.project_id);
2133
+ break;
2134
+ case "taskovergg_add_story":
2135
+ result = store.addStory({
2136
+ projectId: args.project_id, sectionId: args.section_id,
2137
+ section: args.section, content: args.content,
2138
+ });
2139
+ break;
2140
+ case "taskovergg_update_story":
2141
+ result = store.updateStory(args.story_id, {
2142
+ section: args.section, content: args.content,
2143
+ });
2144
+ break;
2145
+
2146
+ // Scenes
2147
+ case "taskovergg_get_scenes":
2148
+ result = store.getScenes(args.project_id);
2149
+ break;
2150
+ case "taskovergg_add_scene":
2151
+ result = store.addScene({
2152
+ projectId: args.project_id, number: args.number, title: args.title,
2153
+ location: args.location, characters: args.characters,
2154
+ mood: args.mood, round: args.round, content: args.content,
2155
+ });
2156
+ break;
2157
+ case "taskovergg_update_scene":
2158
+ result = store.updateScene(args.scene_id, {
2159
+ number: args.number, title: args.title, location: args.location,
2160
+ characters: args.characters, mood: args.mood, round: args.round, content: args.content,
2161
+ });
2162
+ break;
2163
+ case "taskovergg_delete_scene":
2164
+ result = store.deleteScene(args.scene_id);
2165
+ break;
2166
+
2167
+ // Story Bible (Story NEW tab)
2168
+ case "taskovergg_get_story_bible":
2169
+ result = store.getStoryBible(args.project_id);
2170
+ break;
2171
+ case "taskovergg_update_story_bible":
2172
+ result = store.updateStoryBible(args.project_id, {
2173
+ title: args.title, logline: args.logline, genre: args.genre,
2174
+ tone: args.tone, script: args.script,
2175
+ });
2176
+ break;
2177
+ case "taskovergg_add_story_character":
2178
+ result = store.addStoryCharacter(args.project_id, {
2179
+ name: args.name, role: args.role, description: args.description,
2180
+ });
2181
+ break;
2182
+ case "taskovergg_update_story_character":
2183
+ result = store.updateStoryCharacter(args.project_id, args.character_id, {
2184
+ name: args.name, role: args.role, description: args.description,
2185
+ });
2186
+ break;
2187
+ case "taskovergg_get_scene_content":
2188
+ result = store.getSceneContent(args.scene_id);
2189
+ break;
2190
+ case "taskovergg_update_scene_content":
2191
+ result = store.updateSceneContent(args.project_id, args.scene_id, {
2192
+ title: args.title, location: args.location, mood: args.mood,
2193
+ content: args.content, round: args.round,
2194
+ characters: args.characters, objectives: args.objectives,
2195
+ });
2196
+ break;
2197
+
2198
+ default:
2199
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
2200
+ }
2201
+
2202
+ return {
2203
+ content: [{
2204
+ type: "text",
2205
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
2206
+ }],
2207
+ };
2208
+
2209
+ } catch (err) {
2210
+ return {
2211
+ content: [{ type: "text", text: `Error: ${err && err.message ? err.message : String(err)}` }],
2212
+ isError: true,
2213
+ };
2214
+ }
2215
+ });
2216
+
2217
+ // ===== START =====
2218
+
2219
+ const MCP_TRANSPORT = (process.env.MCP_TRANSPORT || "stdio").toLowerCase();
2220
+ const MCP_PORT = parseInt(process.env.MCP_PORT || "8484", 10);
2221
+
2222
+ async function startStdio() {
2223
+ const transport = new StdioServerTransport();
2224
+ await server.connect(transport);
2225
+ console.error("TaskOver MCP server running (stdio)");
2226
+ }
2227
+
2228
+ async function startHttp() {
2229
+ const http = require("http");
2230
+ const { randomUUID } = require("crypto");
2231
+ const { StreamableHTTPServerTransport } = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
2232
+
2233
+ const transport = new StreamableHTTPServerTransport({
2234
+ sessionIdGenerator: () => randomUUID(),
2235
+ });
2236
+ await server.connect(transport);
2237
+
2238
+ const httpServer = http.createServer(async (req, res) => {
2239
+ // Only bind to /mcp endpoint
2240
+ const url = req.url.split("?")[0];
2241
+ if (url === "/mcp") {
2242
+ await transport.handleRequest(req, res);
2243
+ } else if (url === "/health") {
2244
+ res.writeHead(200, { "Content-Type": "application/json" });
2245
+ res.end(JSON.stringify({ status: "ok", transport: "http", tools: TOOLS.length }));
2246
+ } else {
2247
+ res.writeHead(404);
2248
+ res.end("Not found");
2249
+ }
2250
+ });
2251
+
2252
+ httpServer.listen(MCP_PORT, "127.0.0.1", () => {
2253
+ console.error(`TaskOver MCP server running (http) on http://127.0.0.1:${MCP_PORT}/mcp`);
2254
+ });
2255
+ }
2256
+
2257
+ async function main() {
2258
+ if (CLOUD_MODE) {
2259
+ const check = await cloudAdapter.validateKey();
2260
+ if (!check.valid) {
2261
+ console.error(`[ERROR] ${check.error}`);
2262
+ process.exit(1);
2263
+ }
2264
+ console.error("[CLOUD] Connected to api.taskover.gg (API key mode)");
2265
+ } else if (BROWSER_AUTH_MODE) {
2266
+ // JIT auth: try loading cached tokens silently, but never force browser auth at startup.
2267
+ // If no cached tokens, the server boots unauthenticated. Auth happens on first tool use.
2268
+ const authGate = require("./auth-gate.js");
2269
+ const hasCached = await authGate.tryLoadCachedTokens();
2270
+ if (hasCached) {
2271
+ console.error("[AUTH] Ready (cached credentials loaded)");
2272
+ } else {
2273
+ console.error("[AUTH] Ready (will authenticate on first tool use)");
2274
+ }
2275
+ } else {
2276
+ await initDb();
2277
+ }
2278
+
2279
+ if (MCP_TRANSPORT === "http" || MCP_TRANSPORT === "sse") {
2280
+ await startHttp();
2281
+ } else {
2282
+ await startStdio();
2283
+ }
2284
+ }
2285
+
2286
+ main().catch((err) => {
2287
+ // SECURITY: Never log secrets in crash output
2288
+ const msg = err.message || String(err);
2289
+ const redacted = msg
2290
+ .replace(/tok_[a-zA-Z0-9]+/g, "tok_[REDACTED]")
2291
+ .replace(/ht_[a-zA-Z0-9]+/g, "ht_[REDACTED]");
2292
+ console.error(`[FATAL] ${redacted}`);
2293
+ process.exit(1);
2294
+ });