@idea-base/mcp-server 1.3.0 → 2.0.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/package.json +1 -1
- package/src/index.js +29 -30
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -53,6 +53,19 @@ function resolveBaseUrl() {
|
|
|
53
53
|
const API_BASE_URL = resolveBaseUrl();
|
|
54
54
|
const API_KEY = (process.env.IDEA_BASE_API_KEY || '').trim();
|
|
55
55
|
|
|
56
|
+
// The agent identity this server declares on every request (X-On-Behalf-Of).
|
|
57
|
+
//
|
|
58
|
+
// It answers "who did this?" and nothing else — it carries no permission, no
|
|
59
|
+
// delegation and no elevation. Authorization stays entirely with the API key and its
|
|
60
|
+
// owner. Without it, work done by an agent is recorded as the key owner, which makes
|
|
61
|
+
// the owner the actor of his own notifications and silently suppresses them.
|
|
62
|
+
//
|
|
63
|
+
// Freeform by design, so a session may declare a different agent. The identity must
|
|
64
|
+
// already exist server-side as a non-human user, or EVERY call fails closed with 403
|
|
65
|
+
// AGENT_IDENTITY_NOT_RESOLVABLE / AGENT_IDENTITY_NOT_PERMITTED — deliberately loud,
|
|
66
|
+
// never a silent fallback to the key owner.
|
|
67
|
+
const AGENT_ID = (process.env.IDEA_BASE_AGENT_ID || 'claude_ai').trim();
|
|
68
|
+
|
|
56
69
|
if (!API_KEY) {
|
|
57
70
|
console.error('Error: IDEA_BASE_API_KEY environment variable is required');
|
|
58
71
|
process.exit(1);
|
|
@@ -190,6 +203,7 @@ async function apiRequest(endpoint, options = {}) {
|
|
|
190
203
|
signal: controller.signal,
|
|
191
204
|
headers: {
|
|
192
205
|
'Authorization': `Bearer ${API_KEY}`,
|
|
206
|
+
'X-On-Behalf-Of': AGENT_ID,
|
|
193
207
|
'Content-Type': 'application/json',
|
|
194
208
|
'Accept': 'application/json',
|
|
195
209
|
...options.headers,
|
|
@@ -622,7 +636,7 @@ const tools = [
|
|
|
622
636
|
},
|
|
623
637
|
{
|
|
624
638
|
name: 'stop_working',
|
|
625
|
-
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.
|
|
639
|
+
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.',
|
|
626
640
|
inputSchema: {
|
|
627
641
|
type: 'object',
|
|
628
642
|
properties: {
|
|
@@ -644,7 +658,7 @@ const tools = [
|
|
|
644
658
|
},
|
|
645
659
|
{
|
|
646
660
|
name: 'add_work_note',
|
|
647
|
-
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.
|
|
661
|
+
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.',
|
|
648
662
|
inputSchema: {
|
|
649
663
|
type: 'object',
|
|
650
664
|
properties: {
|
|
@@ -656,11 +670,6 @@ const tools = [
|
|
|
656
670
|
type: 'string',
|
|
657
671
|
description: 'The work note / status update text',
|
|
658
672
|
},
|
|
659
|
-
author_kind: {
|
|
660
|
-
type: 'string',
|
|
661
|
-
enum: ['ai', 'human'],
|
|
662
|
-
description: 'Who authored this note: "ai" (default, your own update) or "human" (you are relaying something the user said).',
|
|
663
|
-
},
|
|
664
673
|
is_internal: {
|
|
665
674
|
type: 'boolean',
|
|
666
675
|
description: 'Whether the note is internal-only (hidden from customers). Defaults to true for work notes.',
|
|
@@ -671,7 +680,7 @@ const tools = [
|
|
|
671
680
|
},
|
|
672
681
|
{
|
|
673
682
|
name: 'add_comment',
|
|
674
|
-
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.
|
|
683
|
+
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.',
|
|
675
684
|
inputSchema: {
|
|
676
685
|
type: 'object',
|
|
677
686
|
properties: {
|
|
@@ -683,11 +692,6 @@ const tools = [
|
|
|
683
692
|
type: 'string',
|
|
684
693
|
description: 'The comment text',
|
|
685
694
|
},
|
|
686
|
-
author_kind: {
|
|
687
|
-
type: 'string',
|
|
688
|
-
enum: ['ai', 'human'],
|
|
689
|
-
description: 'Who authored this comment: "ai" (default) or "human" (relaying the user).',
|
|
690
|
-
},
|
|
691
695
|
is_internal: {
|
|
692
696
|
type: 'boolean',
|
|
693
697
|
description: 'Whether the comment is internal-only (hidden from customers). Defaults to false.',
|
|
@@ -698,7 +702,7 @@ const tools = [
|
|
|
698
702
|
},
|
|
699
703
|
{
|
|
700
704
|
name: 'set_resume_context',
|
|
701
|
-
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.
|
|
705
|
+
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.',
|
|
702
706
|
inputSchema: {
|
|
703
707
|
type: 'object',
|
|
704
708
|
properties: {
|
|
@@ -710,11 +714,6 @@ const tools = [
|
|
|
710
714
|
type: 'string',
|
|
711
715
|
description: 'The resume-context text (markdown ok): current state / next steps / where to look.',
|
|
712
716
|
},
|
|
713
|
-
author_kind: {
|
|
714
|
-
type: 'string',
|
|
715
|
-
enum: ['ai', 'human'],
|
|
716
|
-
description: 'Who authored this context: "ai" (default) or "human".',
|
|
717
|
-
},
|
|
718
717
|
},
|
|
719
718
|
required: ['task_id', 'context'],
|
|
720
719
|
},
|
|
@@ -1100,14 +1099,14 @@ const toolHandlers = {
|
|
|
1100
1099
|
if (note && note.trim()) {
|
|
1101
1100
|
await apiRequest(`/tasks/${task_id}/activity`, {
|
|
1102
1101
|
method: 'POST',
|
|
1103
|
-
body: JSON.stringify({ note,
|
|
1102
|
+
body: JSON.stringify({ note, is_internal: true }),
|
|
1104
1103
|
});
|
|
1105
1104
|
captured.push('work note');
|
|
1106
1105
|
}
|
|
1107
1106
|
if (resume_context !== undefined && resume_context !== null) {
|
|
1108
1107
|
await apiRequest(`/tasks/${task_id}`, {
|
|
1109
1108
|
method: 'PUT',
|
|
1110
|
-
body: JSON.stringify({ resume_context
|
|
1109
|
+
body: JSON.stringify({ resume_context }),
|
|
1111
1110
|
});
|
|
1112
1111
|
captured.push('resume context');
|
|
1113
1112
|
}
|
|
@@ -1127,46 +1126,46 @@ const toolHandlers = {
|
|
|
1127
1126
|
};
|
|
1128
1127
|
},
|
|
1129
1128
|
|
|
1130
|
-
async add_work_note({ task_id, note,
|
|
1129
|
+
async add_work_note({ task_id, note, is_internal = true }) {
|
|
1131
1130
|
const saved = await apiRequest(`/tasks/${task_id}/activity`, {
|
|
1132
1131
|
method: 'POST',
|
|
1133
|
-
body: JSON.stringify({ note,
|
|
1132
|
+
body: JSON.stringify({ note, is_internal }),
|
|
1134
1133
|
});
|
|
1135
1134
|
return {
|
|
1136
1135
|
content: [
|
|
1137
1136
|
{
|
|
1138
1137
|
type: 'text',
|
|
1139
|
-
text: `Work note added to task #${task_id} (as ${
|
|
1138
|
+
text: `Work note added to task #${task_id} (as ${AGENT_ID}). Note id: ${saved.id}.`,
|
|
1140
1139
|
},
|
|
1141
1140
|
],
|
|
1142
1141
|
};
|
|
1143
1142
|
},
|
|
1144
1143
|
|
|
1145
|
-
async add_comment({ task_id, comment,
|
|
1144
|
+
async add_comment({ task_id, comment, is_internal = false }) {
|
|
1146
1145
|
const saved = await apiRequest(`/tasks/${task_id}/comments`, {
|
|
1147
1146
|
method: 'POST',
|
|
1148
|
-
body: JSON.stringify({ comment,
|
|
1147
|
+
body: JSON.stringify({ comment, is_internal }),
|
|
1149
1148
|
});
|
|
1150
1149
|
return {
|
|
1151
1150
|
content: [
|
|
1152
1151
|
{
|
|
1153
1152
|
type: 'text',
|
|
1154
|
-
text: `Comment added to task #${task_id} (as ${
|
|
1153
|
+
text: `Comment added to task #${task_id} (as ${AGENT_ID}). Comment id: ${saved.id}.`,
|
|
1155
1154
|
},
|
|
1156
1155
|
],
|
|
1157
1156
|
};
|
|
1158
1157
|
},
|
|
1159
1158
|
|
|
1160
|
-
async set_resume_context({ task_id, context
|
|
1159
|
+
async set_resume_context({ task_id, context }) {
|
|
1161
1160
|
await apiRequest(`/tasks/${task_id}`, {
|
|
1162
1161
|
method: 'PUT',
|
|
1163
|
-
body: JSON.stringify({ resume_context: context
|
|
1162
|
+
body: JSON.stringify({ resume_context: context }),
|
|
1164
1163
|
});
|
|
1165
1164
|
return {
|
|
1166
1165
|
content: [
|
|
1167
1166
|
{
|
|
1168
1167
|
type: 'text',
|
|
1169
|
-
text: `Resume context updated for task #${task_id} (as ${
|
|
1168
|
+
text: `Resume context updated for task #${task_id} (as ${AGENT_ID}). The next session that calls start_working will read this first.`,
|
|
1170
1169
|
},
|
|
1171
1170
|
],
|
|
1172
1171
|
};
|