@idea-base/mcp-server 1.1.0 → 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/README.md +12 -2
- package/package.json +1 -1
- package/src/index.js +179 -4
package/README.md
CHANGED
|
@@ -85,8 +85,18 @@ Ask Claude to manage your projects:
|
|
|
85
85
|
| Tool | Description |
|
|
86
86
|
|------|-------------|
|
|
87
87
|
| `log_time` | Log time against a task with notes |
|
|
88
|
-
| `start_working` | Mark yourself as actively working on a task |
|
|
89
|
-
| `stop_working` | Stop active work on a task |
|
|
88
|
+
| `start_working` | Mark yourself as actively working on a task (surfaces the saved resume context + recent work notes so a cold session re-orients) |
|
|
89
|
+
| `stop_working` | Stop active work on a task (optionally capture a `note` and/or `resume_context` on the way out) |
|
|
90
|
+
|
|
91
|
+
### Activity & Audit Trail
|
|
92
|
+
|
|
93
|
+
For AI agents, these leave a durable trail of *what was done and why* on each task — so a future session (or a human reviewer) can see the reasoning, not just the final state.
|
|
94
|
+
|
|
95
|
+
| Tool | Description |
|
|
96
|
+
|------|-------------|
|
|
97
|
+
| `add_work_note` | Append a timestamped progress note to a task's activity log (append-only journal) |
|
|
98
|
+
| `add_comment` | Add a comment to a task's discussion thread (customer-visible by default) |
|
|
99
|
+
| `set_resume_context` | Overwrite the task's pinned "where I left off" block, read first on `start_working` |
|
|
90
100
|
|
|
91
101
|
### Products
|
|
92
102
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -346,7 +346,7 @@ const tools = [
|
|
|
346
346
|
},
|
|
347
347
|
{
|
|
348
348
|
name: 'stop_working',
|
|
349
|
-
description: 'Mark that you have stopped working on a task.
|
|
349
|
+
description: 'Mark that you have stopped working on a task (break or switching tasks). Optionally capture your state on the way out: pass `note` to append a work note and/or `resume_context` to overwrite the resume block, so the next session can pick up where you left off. Both are recorded as author_kind="ai".',
|
|
350
350
|
inputSchema: {
|
|
351
351
|
type: 'object',
|
|
352
352
|
properties: {
|
|
@@ -354,10 +354,95 @@ const tools = [
|
|
|
354
354
|
type: 'number',
|
|
355
355
|
description: 'The ID of the task to stop working on',
|
|
356
356
|
},
|
|
357
|
+
note: {
|
|
358
|
+
type: 'string',
|
|
359
|
+
description: 'Optional work note to append before stopping (what you did, what is next).',
|
|
360
|
+
},
|
|
361
|
+
resume_context: {
|
|
362
|
+
type: 'string',
|
|
363
|
+
description: 'Optional resume-context block to overwrite before stopping (current state / next steps / where to look).',
|
|
364
|
+
},
|
|
357
365
|
},
|
|
358
366
|
required: ['task_id'],
|
|
359
367
|
},
|
|
360
368
|
},
|
|
369
|
+
{
|
|
370
|
+
name: 'add_work_note',
|
|
371
|
+
description: 'Append a timestamped work note (status update) to a task\'s activity log. Use this to record progress so a future session can pick up where you left off (e.g. "finished auth handler, tests green, next: wire the callback"). Notes are append-only and never overwrite each other. Set author_kind="human" ONLY when you are relaying a note dictated by the human user; use "ai" (the default) when leaving your own status update.',
|
|
372
|
+
inputSchema: {
|
|
373
|
+
type: 'object',
|
|
374
|
+
properties: {
|
|
375
|
+
task_id: {
|
|
376
|
+
type: 'number',
|
|
377
|
+
description: 'The ID of the task to add a work note to',
|
|
378
|
+
},
|
|
379
|
+
note: {
|
|
380
|
+
type: 'string',
|
|
381
|
+
description: 'The work note / status update text',
|
|
382
|
+
},
|
|
383
|
+
author_kind: {
|
|
384
|
+
type: 'string',
|
|
385
|
+
enum: ['ai', 'human'],
|
|
386
|
+
description: 'Who authored this note: "ai" (default, your own update) or "human" (you are relaying something the user said).',
|
|
387
|
+
},
|
|
388
|
+
is_internal: {
|
|
389
|
+
type: 'boolean',
|
|
390
|
+
description: 'Whether the note is internal-only (hidden from customers). Defaults to true for work notes.',
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
required: ['task_id', 'note'],
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'add_comment',
|
|
398
|
+
description: 'Add a comment to a task\'s discussion thread. Unlike work notes (progress journal), comments are for communication and are customer-visible by default. Set author_kind="human" when relaying the user\'s words; use "ai" (default) for your own.',
|
|
399
|
+
inputSchema: {
|
|
400
|
+
type: 'object',
|
|
401
|
+
properties: {
|
|
402
|
+
task_id: {
|
|
403
|
+
type: 'number',
|
|
404
|
+
description: 'The ID of the task to comment on',
|
|
405
|
+
},
|
|
406
|
+
comment: {
|
|
407
|
+
type: 'string',
|
|
408
|
+
description: 'The comment text',
|
|
409
|
+
},
|
|
410
|
+
author_kind: {
|
|
411
|
+
type: 'string',
|
|
412
|
+
enum: ['ai', 'human'],
|
|
413
|
+
description: 'Who authored this comment: "ai" (default) or "human" (relaying the user).',
|
|
414
|
+
},
|
|
415
|
+
is_internal: {
|
|
416
|
+
type: 'boolean',
|
|
417
|
+
description: 'Whether the comment is internal-only (hidden from customers). Defaults to false.',
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
required: ['task_id', 'comment'],
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'set_resume_context',
|
|
425
|
+
description: 'Set (overwrite) the task\'s resume context — a single pinned block describing the current state, what is done, what is next, and where to look. This is read first when a work session restarts cold (e.g. via start_working) so you can re-orient immediately. Update it at the end of a work session. Set author_kind="human" only when relaying the user\'s words; default "ai".',
|
|
426
|
+
inputSchema: {
|
|
427
|
+
type: 'object',
|
|
428
|
+
properties: {
|
|
429
|
+
task_id: {
|
|
430
|
+
type: 'number',
|
|
431
|
+
description: 'The ID of the task to set resume context for',
|
|
432
|
+
},
|
|
433
|
+
context: {
|
|
434
|
+
type: 'string',
|
|
435
|
+
description: 'The resume-context text (markdown ok): current state / next steps / where to look.',
|
|
436
|
+
},
|
|
437
|
+
author_kind: {
|
|
438
|
+
type: 'string',
|
|
439
|
+
enum: ['ai', 'human'],
|
|
440
|
+
description: 'Who authored this context: "ai" (default) or "human".',
|
|
441
|
+
},
|
|
442
|
+
},
|
|
443
|
+
required: ['task_id', 'context'],
|
|
444
|
+
},
|
|
445
|
+
},
|
|
361
446
|
// Products tools
|
|
362
447
|
{
|
|
363
448
|
name: 'list_products',
|
|
@@ -677,26 +762,116 @@ const toolHandlers = {
|
|
|
677
762
|
body: JSON.stringify({ is_active: true }),
|
|
678
763
|
});
|
|
679
764
|
|
|
765
|
+
// Orient a cold-starting session: surface the resume context + recent work notes.
|
|
766
|
+
let orientation = '';
|
|
767
|
+
try {
|
|
768
|
+
const task = await apiRequest(`/tasks/${task_id}`);
|
|
769
|
+
const lines = [];
|
|
770
|
+
if (task.resume_context) {
|
|
771
|
+
const by = task.resume_context_author_kind === 'ai' ? 'AI Agent' : 'human';
|
|
772
|
+
lines.push(`\n--- Resume context (last set by ${by}) ---\n${task.resume_context}`);
|
|
773
|
+
}
|
|
774
|
+
const notes = task.recent_work_notes || [];
|
|
775
|
+
if (notes.length) {
|
|
776
|
+
lines.push('\n--- Recent work notes (newest first) ---');
|
|
777
|
+
for (const n of notes) {
|
|
778
|
+
let text = '';
|
|
779
|
+
try { text = JSON.parse(n.event_data || '{}').note || ''; } catch {}
|
|
780
|
+
const who = n.author_kind === 'ai' ? 'AI' : (n.author_name || 'human');
|
|
781
|
+
lines.push(`• [${n.created_at}] (${who}) ${text}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
if (lines.length) {
|
|
785
|
+
orientation = `\n\nHere is where you left off:\n${lines.join('\n')}`;
|
|
786
|
+
} else {
|
|
787
|
+
orientation = `\n\nNo resume context or work notes yet — consider leaving some with add_work_note / set_resume_context before you stop.`;
|
|
788
|
+
}
|
|
789
|
+
} catch {
|
|
790
|
+
// Non-fatal: assignment already succeeded.
|
|
791
|
+
}
|
|
792
|
+
|
|
680
793
|
return {
|
|
681
794
|
content: [
|
|
682
795
|
{
|
|
683
796
|
type: 'text',
|
|
684
|
-
text: `Started working on task #${task_id}. Other team members can now see you are actively working on this task
|
|
797
|
+
text: `Started working on task #${task_id}. Other team members can now see you are actively working on this task.${orientation}`,
|
|
685
798
|
},
|
|
686
799
|
],
|
|
687
800
|
};
|
|
688
801
|
},
|
|
689
802
|
|
|
690
|
-
async stop_working({ task_id }) {
|
|
803
|
+
async stop_working({ task_id, note, resume_context }) {
|
|
804
|
+
const captured = [];
|
|
805
|
+
if (note && note.trim()) {
|
|
806
|
+
await apiRequest(`/tasks/${task_id}/activity`, {
|
|
807
|
+
method: 'POST',
|
|
808
|
+
body: JSON.stringify({ note, author_kind: 'ai', is_internal: true }),
|
|
809
|
+
});
|
|
810
|
+
captured.push('work note');
|
|
811
|
+
}
|
|
812
|
+
if (resume_context !== undefined && resume_context !== null) {
|
|
813
|
+
await apiRequest(`/tasks/${task_id}`, {
|
|
814
|
+
method: 'PUT',
|
|
815
|
+
body: JSON.stringify({ resume_context, resume_context_author_kind: 'ai' }),
|
|
816
|
+
});
|
|
817
|
+
captured.push('resume context');
|
|
818
|
+
}
|
|
819
|
+
|
|
691
820
|
await apiRequest(`/tasks/${task_id}/assignments?stop_only=true`, {
|
|
692
821
|
method: 'DELETE',
|
|
693
822
|
});
|
|
694
823
|
|
|
824
|
+
const capturedText = captured.length ? ` Saved ${captured.join(' and ')} for next time.` : '';
|
|
825
|
+
return {
|
|
826
|
+
content: [
|
|
827
|
+
{
|
|
828
|
+
type: 'text',
|
|
829
|
+
text: `Stopped working on task #${task_id}. You are still assigned to the task but no longer marked as actively working.${capturedText}`,
|
|
830
|
+
},
|
|
831
|
+
],
|
|
832
|
+
};
|
|
833
|
+
},
|
|
834
|
+
|
|
835
|
+
async add_work_note({ task_id, note, author_kind = 'ai', is_internal = true }) {
|
|
836
|
+
const saved = await apiRequest(`/tasks/${task_id}/activity`, {
|
|
837
|
+
method: 'POST',
|
|
838
|
+
body: JSON.stringify({ note, author_kind, is_internal }),
|
|
839
|
+
});
|
|
840
|
+
return {
|
|
841
|
+
content: [
|
|
842
|
+
{
|
|
843
|
+
type: 'text',
|
|
844
|
+
text: `Work note added to task #${task_id} (as ${author_kind === 'ai' ? 'AI Agent' : 'human'}). Note id: ${saved.id}.`,
|
|
845
|
+
},
|
|
846
|
+
],
|
|
847
|
+
};
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
async add_comment({ task_id, comment, author_kind = 'ai', is_internal = false }) {
|
|
851
|
+
const saved = await apiRequest(`/tasks/${task_id}/comments`, {
|
|
852
|
+
method: 'POST',
|
|
853
|
+
body: JSON.stringify({ comment, author_kind, is_internal }),
|
|
854
|
+
});
|
|
855
|
+
return {
|
|
856
|
+
content: [
|
|
857
|
+
{
|
|
858
|
+
type: 'text',
|
|
859
|
+
text: `Comment added to task #${task_id} (as ${author_kind === 'ai' ? 'AI Agent' : 'human'}). Comment id: ${saved.id}.`,
|
|
860
|
+
},
|
|
861
|
+
],
|
|
862
|
+
};
|
|
863
|
+
},
|
|
864
|
+
|
|
865
|
+
async set_resume_context({ task_id, context, author_kind = 'ai' }) {
|
|
866
|
+
await apiRequest(`/tasks/${task_id}`, {
|
|
867
|
+
method: 'PUT',
|
|
868
|
+
body: JSON.stringify({ resume_context: context, resume_context_author_kind: author_kind }),
|
|
869
|
+
});
|
|
695
870
|
return {
|
|
696
871
|
content: [
|
|
697
872
|
{
|
|
698
873
|
type: 'text',
|
|
699
|
-
text: `
|
|
874
|
+
text: `Resume context updated for task #${task_id} (as ${author_kind === 'ai' ? 'AI Agent' : 'human'}). The next session that calls start_working will read this first.`,
|
|
700
875
|
},
|
|
701
876
|
],
|
|
702
877
|
};
|