@contextstream/mcp-server 0.4.49 → 0.4.50
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 +27 -0
- package/dist/hooks/post-write.js +0 -0
- package/dist/hooks/pre-compact.js +100 -11
- package/dist/index.js +477 -119
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -157,6 +157,33 @@ claude mcp update contextstream -e CONTEXTSTREAM_API_KEY=your_key
|
|
|
157
157
|
|
|
158
158
|
</details>
|
|
159
159
|
|
|
160
|
+
<details>
|
|
161
|
+
<summary><b>GitHub Copilot CLI</b></summary>
|
|
162
|
+
|
|
163
|
+
Use the Copilot CLI to interactively add the MCP server:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
/mcp add
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Or add to `~/.copilot/mcp-config.json`:
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"mcpServers": {
|
|
174
|
+
"contextstream": {
|
|
175
|
+
"command": "npx",
|
|
176
|
+
"args": ["-y", "@contextstream/mcp-server"],
|
|
177
|
+
"env": { "CONTEXTSTREAM_API_KEY": "your_key" }
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
For more information, see the [GitHub Copilot CLI documentation](https://docs.github.com/en/copilot/concepts/agents/about-copilot-cli).
|
|
184
|
+
|
|
185
|
+
</details>
|
|
186
|
+
|
|
160
187
|
---
|
|
161
188
|
|
|
162
189
|
## Links
|
package/dist/hooks/post-write.js
CHANGED
|
File without changes
|
|
@@ -68,6 +68,9 @@ function parseTranscript(transcriptPath) {
|
|
|
68
68
|
const activeFiles = /* @__PURE__ */ new Set();
|
|
69
69
|
const recentMessages = [];
|
|
70
70
|
const toolCalls = [];
|
|
71
|
+
const messages = [];
|
|
72
|
+
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
73
|
+
let firstTimestamp = true;
|
|
71
74
|
try {
|
|
72
75
|
const content = fs.readFileSync(transcriptPath, "utf-8");
|
|
73
76
|
const lines = content.split("\n");
|
|
@@ -76,6 +79,11 @@ function parseTranscript(transcriptPath) {
|
|
|
76
79
|
try {
|
|
77
80
|
const entry = JSON.parse(line);
|
|
78
81
|
const msgType = entry.type || "";
|
|
82
|
+
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
83
|
+
if (firstTimestamp && entry.timestamp) {
|
|
84
|
+
startedAt = entry.timestamp;
|
|
85
|
+
firstTimestamp = false;
|
|
86
|
+
}
|
|
79
87
|
if (msgType === "tool_use") {
|
|
80
88
|
const toolName = entry.name || "";
|
|
81
89
|
const toolInput = entry.input || {};
|
|
@@ -91,11 +99,40 @@ function parseTranscript(transcriptPath) {
|
|
|
91
99
|
activeFiles.add(`[glob:${pattern}]`);
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
messages.push({
|
|
103
|
+
role: "assistant",
|
|
104
|
+
content: `[Tool: ${toolName}]`,
|
|
105
|
+
timestamp,
|
|
106
|
+
tool_calls: { name: toolName, input: toolInput }
|
|
107
|
+
});
|
|
108
|
+
} else if (msgType === "tool_result") {
|
|
109
|
+
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
110
|
+
messages.push({
|
|
111
|
+
role: "tool",
|
|
112
|
+
content: resultContent,
|
|
113
|
+
timestamp,
|
|
114
|
+
tool_results: { name: entry.name }
|
|
115
|
+
});
|
|
116
|
+
} else if (msgType === "user" || entry.role === "user") {
|
|
117
|
+
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
118
|
+
if (userContent) {
|
|
119
|
+
messages.push({
|
|
120
|
+
role: "user",
|
|
121
|
+
content: userContent,
|
|
122
|
+
timestamp
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
126
|
+
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
127
|
+
if (assistantContent) {
|
|
128
|
+
messages.push({
|
|
129
|
+
role: "assistant",
|
|
130
|
+
content: assistantContent,
|
|
131
|
+
timestamp
|
|
132
|
+
});
|
|
133
|
+
if (assistantContent.length > 50) {
|
|
134
|
+
recentMessages.push(assistantContent.slice(0, 500));
|
|
135
|
+
}
|
|
99
136
|
}
|
|
100
137
|
}
|
|
101
138
|
} catch {
|
|
@@ -108,11 +145,57 @@ function parseTranscript(transcriptPath) {
|
|
|
108
145
|
activeFiles: Array.from(activeFiles).slice(-20),
|
|
109
146
|
// Last 20 files
|
|
110
147
|
toolCallCount: toolCalls.length,
|
|
111
|
-
messageCount:
|
|
112
|
-
lastTools: toolCalls.slice(-10).map((t) => t.name)
|
|
148
|
+
messageCount: messages.length,
|
|
149
|
+
lastTools: toolCalls.slice(-10).map((t) => t.name),
|
|
113
150
|
// Last 10 tool names
|
|
151
|
+
messages,
|
|
152
|
+
startedAt
|
|
114
153
|
};
|
|
115
154
|
}
|
|
155
|
+
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
156
|
+
if (!API_KEY) {
|
|
157
|
+
return { success: false, message: "No API key configured" };
|
|
158
|
+
}
|
|
159
|
+
if (transcriptData.messages.length === 0) {
|
|
160
|
+
return { success: false, message: "No messages to save" };
|
|
161
|
+
}
|
|
162
|
+
const payload = {
|
|
163
|
+
session_id: sessionId,
|
|
164
|
+
messages: transcriptData.messages,
|
|
165
|
+
started_at: transcriptData.startedAt,
|
|
166
|
+
source_type: "pre_compact",
|
|
167
|
+
title: `Pre-compaction save (${trigger})`,
|
|
168
|
+
metadata: {
|
|
169
|
+
trigger,
|
|
170
|
+
active_files: transcriptData.activeFiles,
|
|
171
|
+
tool_call_count: transcriptData.toolCallCount
|
|
172
|
+
},
|
|
173
|
+
tags: ["pre_compaction", trigger]
|
|
174
|
+
};
|
|
175
|
+
if (WORKSPACE_ID) {
|
|
176
|
+
payload.workspace_id = WORKSPACE_ID;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const controller = new AbortController();
|
|
180
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
181
|
+
const response = await fetch(`${API_URL}/api/v1/transcripts`, {
|
|
182
|
+
method: "POST",
|
|
183
|
+
headers: {
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
"X-API-Key": API_KEY
|
|
186
|
+
},
|
|
187
|
+
body: JSON.stringify(payload),
|
|
188
|
+
signal: controller.signal
|
|
189
|
+
});
|
|
190
|
+
clearTimeout(timeoutId);
|
|
191
|
+
if (response.ok) {
|
|
192
|
+
return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
|
|
193
|
+
}
|
|
194
|
+
return { success: false, message: `API error: ${response.status}` };
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return { success: false, message: String(error) };
|
|
197
|
+
}
|
|
198
|
+
}
|
|
116
199
|
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
117
200
|
if (!API_KEY) {
|
|
118
201
|
return { success: false, message: "No API key configured" };
|
|
@@ -193,13 +276,19 @@ async function runPreCompactHook() {
|
|
|
193
276
|
}
|
|
194
277
|
let autoSaveStatus = "";
|
|
195
278
|
if (AUTO_SAVE && API_KEY) {
|
|
196
|
-
const
|
|
197
|
-
if (success) {
|
|
279
|
+
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
280
|
+
if (transcriptResult.success) {
|
|
198
281
|
autoSaveStatus = `
|
|
199
|
-
[ContextStream:
|
|
282
|
+
[ContextStream: ${transcriptResult.message}]`;
|
|
200
283
|
} else {
|
|
201
|
-
|
|
284
|
+
const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
|
|
285
|
+
if (success) {
|
|
286
|
+
autoSaveStatus = `
|
|
287
|
+
[ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
|
|
288
|
+
} else {
|
|
289
|
+
autoSaveStatus = `
|
|
202
290
|
[ContextStream: Auto-save failed - ${message}]`;
|
|
291
|
+
}
|
|
203
292
|
}
|
|
204
293
|
}
|
|
205
294
|
const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
|
package/dist/index.js
CHANGED
|
@@ -2825,6 +2825,9 @@ function parseTranscript(transcriptPath) {
|
|
|
2825
2825
|
const activeFiles = /* @__PURE__ */ new Set();
|
|
2826
2826
|
const recentMessages = [];
|
|
2827
2827
|
const toolCalls = [];
|
|
2828
|
+
const messages = [];
|
|
2829
|
+
let startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2830
|
+
let firstTimestamp = true;
|
|
2828
2831
|
try {
|
|
2829
2832
|
const content = fs10.readFileSync(transcriptPath, "utf-8");
|
|
2830
2833
|
const lines = content.split("\n");
|
|
@@ -2833,6 +2836,11 @@ function parseTranscript(transcriptPath) {
|
|
|
2833
2836
|
try {
|
|
2834
2837
|
const entry = JSON.parse(line);
|
|
2835
2838
|
const msgType = entry.type || "";
|
|
2839
|
+
const timestamp = entry.timestamp || (/* @__PURE__ */ new Date()).toISOString();
|
|
2840
|
+
if (firstTimestamp && entry.timestamp) {
|
|
2841
|
+
startedAt = entry.timestamp;
|
|
2842
|
+
firstTimestamp = false;
|
|
2843
|
+
}
|
|
2836
2844
|
if (msgType === "tool_use") {
|
|
2837
2845
|
const toolName = entry.name || "";
|
|
2838
2846
|
const toolInput = entry.input || {};
|
|
@@ -2848,11 +2856,40 @@ function parseTranscript(transcriptPath) {
|
|
|
2848
2856
|
activeFiles.add(`[glob:${pattern}]`);
|
|
2849
2857
|
}
|
|
2850
2858
|
}
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2859
|
+
messages.push({
|
|
2860
|
+
role: "assistant",
|
|
2861
|
+
content: `[Tool: ${toolName}]`,
|
|
2862
|
+
timestamp,
|
|
2863
|
+
tool_calls: { name: toolName, input: toolInput }
|
|
2864
|
+
});
|
|
2865
|
+
} else if (msgType === "tool_result") {
|
|
2866
|
+
const resultContent = typeof entry.content === "string" ? entry.content.slice(0, 2e3) : JSON.stringify(entry.content || {}).slice(0, 2e3);
|
|
2867
|
+
messages.push({
|
|
2868
|
+
role: "tool",
|
|
2869
|
+
content: resultContent,
|
|
2870
|
+
timestamp,
|
|
2871
|
+
tool_results: { name: entry.name }
|
|
2872
|
+
});
|
|
2873
|
+
} else if (msgType === "user" || entry.role === "user") {
|
|
2874
|
+
const userContent = typeof entry.content === "string" ? entry.content : "";
|
|
2875
|
+
if (userContent) {
|
|
2876
|
+
messages.push({
|
|
2877
|
+
role: "user",
|
|
2878
|
+
content: userContent,
|
|
2879
|
+
timestamp
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
} else if (msgType === "assistant" || entry.role === "assistant") {
|
|
2883
|
+
const assistantContent = typeof entry.content === "string" ? entry.content : "";
|
|
2884
|
+
if (assistantContent) {
|
|
2885
|
+
messages.push({
|
|
2886
|
+
role: "assistant",
|
|
2887
|
+
content: assistantContent,
|
|
2888
|
+
timestamp
|
|
2889
|
+
});
|
|
2890
|
+
if (assistantContent.length > 50) {
|
|
2891
|
+
recentMessages.push(assistantContent.slice(0, 500));
|
|
2892
|
+
}
|
|
2856
2893
|
}
|
|
2857
2894
|
}
|
|
2858
2895
|
} catch {
|
|
@@ -2865,11 +2902,57 @@ function parseTranscript(transcriptPath) {
|
|
|
2865
2902
|
activeFiles: Array.from(activeFiles).slice(-20),
|
|
2866
2903
|
// Last 20 files
|
|
2867
2904
|
toolCallCount: toolCalls.length,
|
|
2868
|
-
messageCount:
|
|
2869
|
-
lastTools: toolCalls.slice(-10).map((t) => t.name)
|
|
2905
|
+
messageCount: messages.length,
|
|
2906
|
+
lastTools: toolCalls.slice(-10).map((t) => t.name),
|
|
2870
2907
|
// Last 10 tool names
|
|
2908
|
+
messages,
|
|
2909
|
+
startedAt
|
|
2871
2910
|
};
|
|
2872
2911
|
}
|
|
2912
|
+
async function saveFullTranscript(sessionId, transcriptData, trigger) {
|
|
2913
|
+
if (!API_KEY2) {
|
|
2914
|
+
return { success: false, message: "No API key configured" };
|
|
2915
|
+
}
|
|
2916
|
+
if (transcriptData.messages.length === 0) {
|
|
2917
|
+
return { success: false, message: "No messages to save" };
|
|
2918
|
+
}
|
|
2919
|
+
const payload = {
|
|
2920
|
+
session_id: sessionId,
|
|
2921
|
+
messages: transcriptData.messages,
|
|
2922
|
+
started_at: transcriptData.startedAt,
|
|
2923
|
+
source_type: "pre_compact",
|
|
2924
|
+
title: `Pre-compaction save (${trigger})`,
|
|
2925
|
+
metadata: {
|
|
2926
|
+
trigger,
|
|
2927
|
+
active_files: transcriptData.activeFiles,
|
|
2928
|
+
tool_call_count: transcriptData.toolCallCount
|
|
2929
|
+
},
|
|
2930
|
+
tags: ["pre_compaction", trigger]
|
|
2931
|
+
};
|
|
2932
|
+
if (WORKSPACE_ID) {
|
|
2933
|
+
payload.workspace_id = WORKSPACE_ID;
|
|
2934
|
+
}
|
|
2935
|
+
try {
|
|
2936
|
+
const controller = new AbortController();
|
|
2937
|
+
const timeoutId = setTimeout(() => controller.abort(), 1e4);
|
|
2938
|
+
const response = await fetch(`${API_URL2}/api/v1/transcripts`, {
|
|
2939
|
+
method: "POST",
|
|
2940
|
+
headers: {
|
|
2941
|
+
"Content-Type": "application/json",
|
|
2942
|
+
"X-API-Key": API_KEY2
|
|
2943
|
+
},
|
|
2944
|
+
body: JSON.stringify(payload),
|
|
2945
|
+
signal: controller.signal
|
|
2946
|
+
});
|
|
2947
|
+
clearTimeout(timeoutId);
|
|
2948
|
+
if (response.ok) {
|
|
2949
|
+
return { success: true, message: `Transcript saved (${transcriptData.messages.length} messages)` };
|
|
2950
|
+
}
|
|
2951
|
+
return { success: false, message: `API error: ${response.status}` };
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
return { success: false, message: String(error) };
|
|
2954
|
+
}
|
|
2955
|
+
}
|
|
2873
2956
|
async function saveSnapshot(sessionId, transcriptData, trigger) {
|
|
2874
2957
|
if (!API_KEY2) {
|
|
2875
2958
|
return { success: false, message: "No API key configured" };
|
|
@@ -2950,13 +3033,19 @@ async function runPreCompactHook() {
|
|
|
2950
3033
|
}
|
|
2951
3034
|
let autoSaveStatus = "";
|
|
2952
3035
|
if (AUTO_SAVE && API_KEY2) {
|
|
2953
|
-
const
|
|
2954
|
-
if (success) {
|
|
3036
|
+
const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
|
|
3037
|
+
if (transcriptResult.success) {
|
|
2955
3038
|
autoSaveStatus = `
|
|
2956
|
-
[ContextStream:
|
|
3039
|
+
[ContextStream: ${transcriptResult.message}]`;
|
|
2957
3040
|
} else {
|
|
2958
|
-
|
|
3041
|
+
const { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
|
|
3042
|
+
if (success) {
|
|
3043
|
+
autoSaveStatus = `
|
|
3044
|
+
[ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files (transcript save failed: ${transcriptResult.message})]`;
|
|
3045
|
+
} else {
|
|
3046
|
+
autoSaveStatus = `
|
|
2959
3047
|
[ContextStream: Auto-save failed - ${message}]`;
|
|
3048
|
+
}
|
|
2960
3049
|
}
|
|
2961
3050
|
}
|
|
2962
3051
|
const filesList = transcriptData.activeFiles.slice(0, 5).join(", ") || "none detected";
|
|
@@ -9935,6 +10024,35 @@ var ContextStreamClient = class {
|
|
|
9935
10024
|
}
|
|
9936
10025
|
});
|
|
9937
10026
|
}
|
|
10027
|
+
/**
|
|
10028
|
+
* Capture a memory event with a direct event_type.
|
|
10029
|
+
* Used for auto-save session snapshots and other system events.
|
|
10030
|
+
*/
|
|
10031
|
+
async captureMemoryEvent(params) {
|
|
10032
|
+
const withDefaults = this.withDefaults(params);
|
|
10033
|
+
const metadata = {
|
|
10034
|
+
...params.metadata || {}
|
|
10035
|
+
};
|
|
10036
|
+
if (params.tags && params.tags.length > 0) {
|
|
10037
|
+
metadata.tags = params.tags;
|
|
10038
|
+
}
|
|
10039
|
+
if (!metadata.captured_at) {
|
|
10040
|
+
metadata.captured_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10041
|
+
}
|
|
10042
|
+
if (!metadata.source) {
|
|
10043
|
+
metadata.source = "mcp_auto_capture";
|
|
10044
|
+
}
|
|
10045
|
+
return this.createMemoryEvent({
|
|
10046
|
+
workspace_id: withDefaults.workspace_id,
|
|
10047
|
+
project_id: withDefaults.project_id,
|
|
10048
|
+
event_type: params.event_type,
|
|
10049
|
+
title: params.title,
|
|
10050
|
+
content: params.content,
|
|
10051
|
+
provenance: params.provenance,
|
|
10052
|
+
code_refs: params.code_refs,
|
|
10053
|
+
metadata
|
|
10054
|
+
});
|
|
10055
|
+
}
|
|
9938
10056
|
submitContextFeedback(body) {
|
|
9939
10057
|
return request(this.config, "/context/smart/feedback", { body: this.withDefaults(body) });
|
|
9940
10058
|
}
|
|
@@ -10502,7 +10620,11 @@ var ContextStreamClient = class {
|
|
|
10502
10620
|
notice_inline: false,
|
|
10503
10621
|
// Session token tracking for context pressure
|
|
10504
10622
|
...params.session_tokens !== void 0 && { session_tokens: params.session_tokens },
|
|
10505
|
-
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold }
|
|
10623
|
+
...params.context_threshold !== void 0 && { context_threshold: params.context_threshold },
|
|
10624
|
+
// Transcript save parameters
|
|
10625
|
+
...params.save_exchange !== void 0 && { save_exchange: params.save_exchange },
|
|
10626
|
+
...params.session_id !== void 0 && { session_id: params.session_id },
|
|
10627
|
+
...params.client_name !== void 0 && { client_name: params.client_name }
|
|
10506
10628
|
}
|
|
10507
10629
|
});
|
|
10508
10630
|
const data = unwrapApiResponse(apiResult);
|
|
@@ -12064,6 +12186,95 @@ ${context}`;
|
|
|
12064
12186
|
uuidSchema.parse(params.doc_id);
|
|
12065
12187
|
return request(this.config, `/docs/${params.doc_id}`, { method: "DELETE" });
|
|
12066
12188
|
}
|
|
12189
|
+
// -------------------------------------------------------------------------
|
|
12190
|
+
// Transcript methods (conversation session storage)
|
|
12191
|
+
// -------------------------------------------------------------------------
|
|
12192
|
+
/**
|
|
12193
|
+
* List transcripts for a workspace/project
|
|
12194
|
+
*/
|
|
12195
|
+
async listTranscripts(params) {
|
|
12196
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12197
|
+
const query = new URLSearchParams();
|
|
12198
|
+
if (withDefaults.workspace_id) query.set("workspace_id", withDefaults.workspace_id);
|
|
12199
|
+
if (withDefaults.project_id) query.set("project_id", withDefaults.project_id);
|
|
12200
|
+
if (params?.session_id) query.set("session_id", params.session_id);
|
|
12201
|
+
if (params?.client_name) query.set("client_name", params.client_name);
|
|
12202
|
+
if (params?.started_after) query.set("started_after", params.started_after);
|
|
12203
|
+
if (params?.started_before) query.set("started_before", params.started_before);
|
|
12204
|
+
if (params?.limit) query.set("per_page", String(params.limit));
|
|
12205
|
+
if (params?.page) query.set("page", String(params.page));
|
|
12206
|
+
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
12207
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
12208
|
+
return request(this.config, `/transcripts${suffix}`, { method: "GET" });
|
|
12209
|
+
}
|
|
12210
|
+
/**
|
|
12211
|
+
* Get a specific transcript by ID
|
|
12212
|
+
*/
|
|
12213
|
+
async getTranscript(transcript_id) {
|
|
12214
|
+
uuidSchema.parse(transcript_id);
|
|
12215
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "GET" });
|
|
12216
|
+
}
|
|
12217
|
+
/**
|
|
12218
|
+
* Search transcripts by content
|
|
12219
|
+
*/
|
|
12220
|
+
async searchTranscripts(params) {
|
|
12221
|
+
const withDefaults = this.withDefaults(params);
|
|
12222
|
+
const queryParams = new URLSearchParams();
|
|
12223
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12224
|
+
queryParams.set("query", params.query);
|
|
12225
|
+
if (params.limit) queryParams.set("limit", String(params.limit));
|
|
12226
|
+
return request(this.config, `/transcripts/search?${queryParams.toString()}`, { method: "GET" });
|
|
12227
|
+
}
|
|
12228
|
+
/**
|
|
12229
|
+
* Delete a transcript
|
|
12230
|
+
*/
|
|
12231
|
+
async deleteTranscript(transcript_id) {
|
|
12232
|
+
uuidSchema.parse(transcript_id);
|
|
12233
|
+
return request(this.config, `/transcripts/${transcript_id}`, { method: "DELETE" });
|
|
12234
|
+
}
|
|
12235
|
+
// -------------------------------------------------------------------------
|
|
12236
|
+
// Suggested Rules methods (ML-generated rule suggestions)
|
|
12237
|
+
// -------------------------------------------------------------------------
|
|
12238
|
+
/**
|
|
12239
|
+
* List suggested rules
|
|
12240
|
+
*/
|
|
12241
|
+
async listSuggestedRules(params) {
|
|
12242
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12243
|
+
const queryParams = new URLSearchParams();
|
|
12244
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12245
|
+
if (params?.status) queryParams.set("status", params.status);
|
|
12246
|
+
if (params?.source_type) queryParams.set("source_type", params.source_type);
|
|
12247
|
+
if (params?.min_confidence) queryParams.set("min_confidence", String(params.min_confidence));
|
|
12248
|
+
if (params?.limit) queryParams.set("limit", String(params.limit));
|
|
12249
|
+
if (params?.offset) queryParams.set("offset", String(params.offset));
|
|
12250
|
+
return request(this.config, `/suggested-rules?${queryParams.toString()}`, { method: "GET" });
|
|
12251
|
+
}
|
|
12252
|
+
/**
|
|
12253
|
+
* Get pending suggested rules count
|
|
12254
|
+
*/
|
|
12255
|
+
async getSuggestedRulesPendingCount(params) {
|
|
12256
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12257
|
+
const queryParams = new URLSearchParams();
|
|
12258
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12259
|
+
return request(this.config, `/suggested-rules/pending-count?${queryParams.toString()}`, { method: "GET" });
|
|
12260
|
+
}
|
|
12261
|
+
/**
|
|
12262
|
+
* Get suggested rules feedback stats
|
|
12263
|
+
*/
|
|
12264
|
+
async getSuggestedRulesStats(params) {
|
|
12265
|
+
const withDefaults = this.withDefaults(params || {});
|
|
12266
|
+
const queryParams = new URLSearchParams();
|
|
12267
|
+
if (withDefaults.workspace_id) queryParams.set("workspace_id", withDefaults.workspace_id);
|
|
12268
|
+
return request(this.config, `/suggested-rules/stats?${queryParams.toString()}`, { method: "GET" });
|
|
12269
|
+
}
|
|
12270
|
+
/**
|
|
12271
|
+
* Perform action on suggested rule (accept/reject/modify)
|
|
12272
|
+
*/
|
|
12273
|
+
async suggestedRuleAction(params) {
|
|
12274
|
+
uuidSchema.parse(params.rule_id);
|
|
12275
|
+
const { rule_id, ...body } = params;
|
|
12276
|
+
return request(this.config, `/suggested-rules/${rule_id}/action`, { method: "POST", body });
|
|
12277
|
+
}
|
|
12067
12278
|
};
|
|
12068
12279
|
|
|
12069
12280
|
// src/tools.ts
|
|
@@ -13110,111 +13321,84 @@ function trackToolTokenSavings(client, tool, contextText, params, extraMetadata)
|
|
|
13110
13321
|
}
|
|
13111
13322
|
}
|
|
13112
13323
|
|
|
13113
|
-
// src/
|
|
13114
|
-
var SESSION_INIT_TIPS = [
|
|
13115
|
-
"AI work that doesn't disappear.",
|
|
13116
|
-
"Every conversation builds context. Every artifact persists.",
|
|
13117
|
-
"Your AI remembers. Now you can see what it knows.",
|
|
13118
|
-
"Context that survives sessions.",
|
|
13119
|
-
"The bridge between AI conversations and human artifacts."
|
|
13120
|
-
];
|
|
13324
|
+
// src/microcopy.ts
|
|
13121
13325
|
function getSessionInitTip(sessionId) {
|
|
13122
|
-
|
|
13123
|
-
|
|
13124
|
-
}
|
|
13125
|
-
var CAPTURE_HINTS = {
|
|
13126
|
-
// Core event types
|
|
13127
|
-
decision: "Future you will thank present you.",
|
|
13128
|
-
preference: "Noted. This will inform future suggestions.",
|
|
13129
|
-
insight: "Captured for future reference.",
|
|
13130
|
-
note: "Saved. Won't disappear when the chat does.",
|
|
13131
|
-
implementation: "Implementation recorded.",
|
|
13132
|
-
task: "Task tracked.",
|
|
13133
|
-
bug: "Bug logged for tracking.",
|
|
13134
|
-
feature: "Feature request captured.",
|
|
13135
|
-
plan: "This plan will be here when you come back.",
|
|
13136
|
-
correction: "Correction noted. Learning from this.",
|
|
13137
|
-
lesson: "Learn once, remember forever.",
|
|
13138
|
-
warning: "Warning logged.",
|
|
13139
|
-
frustration: "Feedback captured. We're listening.",
|
|
13140
|
-
conversation: "Conversation preserved.",
|
|
13141
|
-
session_snapshot: "Session state saved. Ready to resume anytime."
|
|
13142
|
-
};
|
|
13326
|
+
return `Session ${sessionId.slice(0, 8)}... initialized. Use context(user_message="...") to get relevant context.`;
|
|
13327
|
+
}
|
|
13143
13328
|
function getCaptureHint(eventType) {
|
|
13144
|
-
|
|
13145
|
-
|
|
13146
|
-
|
|
13147
|
-
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
|
|
13156
|
-
|
|
13157
|
-
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
|
|
13161
|
-
|
|
13162
|
-
|
|
13163
|
-
|
|
13164
|
-
|
|
13165
|
-
|
|
13166
|
-
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
|
|
13172
|
-
|
|
13329
|
+
switch (eventType) {
|
|
13330
|
+
case "decision":
|
|
13331
|
+
return "Decision captured. It will surface in future context() calls when relevant.";
|
|
13332
|
+
case "preference":
|
|
13333
|
+
return "Preference saved. Future sessions will respect this preference.";
|
|
13334
|
+
case "insight":
|
|
13335
|
+
return "Insight captured. Use recall() to retrieve it later.";
|
|
13336
|
+
case "task":
|
|
13337
|
+
return "Task captured. Use list_tasks() to view all tasks.";
|
|
13338
|
+
case "lesson":
|
|
13339
|
+
return "Lesson saved. It will warn you before similar mistakes.";
|
|
13340
|
+
case "session_snapshot":
|
|
13341
|
+
return "Session state saved. Use restore_context() after compaction.";
|
|
13342
|
+
default:
|
|
13343
|
+
return "Event captured. Use recall() to retrieve related events.";
|
|
13344
|
+
}
|
|
13345
|
+
}
|
|
13346
|
+
function getEmptyStateHint(operation) {
|
|
13347
|
+
switch (operation) {
|
|
13348
|
+
case "get_lessons":
|
|
13349
|
+
return "No lessons found. Lessons are captured when mistakes occur.";
|
|
13350
|
+
case "recall":
|
|
13351
|
+
return "No memories found. Use capture() or remember() to save context.";
|
|
13352
|
+
case "list_plans":
|
|
13353
|
+
return "No plans found. Use capture_plan() to create an implementation plan.";
|
|
13354
|
+
case "list_events":
|
|
13355
|
+
return "No events found. Events are captured as you work.";
|
|
13356
|
+
case "list_tasks":
|
|
13357
|
+
return "No tasks found. Use create_task() to add tasks.";
|
|
13358
|
+
case "list_todos":
|
|
13359
|
+
return "No todos found. Use create_todo() to add quick todos.";
|
|
13360
|
+
case "list_diagrams":
|
|
13361
|
+
return "No diagrams found. Use create_diagram() to save a Mermaid diagram.";
|
|
13362
|
+
case "list_docs":
|
|
13363
|
+
return "No docs found. Use create_doc() to save documentation.";
|
|
13364
|
+
default:
|
|
13365
|
+
return "No results found.";
|
|
13366
|
+
}
|
|
13173
13367
|
}
|
|
13174
|
-
var POST_COMPACT_HINTS = {
|
|
13175
|
-
restored: "Picked up where you left off. Session ended, memory didn't.",
|
|
13176
|
-
restored_with_session: (sessionId) => `Restored from session ${sessionId}. Session ended, memory didn't.`,
|
|
13177
|
-
no_snapshot: "No prior session found. Fresh start\u2014context will build as you work.",
|
|
13178
|
-
failed: "Couldn't restore previous session. Use context to retrieve what you need."
|
|
13179
|
-
};
|
|
13180
|
-
var PLAN_HINTS = {
|
|
13181
|
-
created: "Plan saved. It will be here when you come back.",
|
|
13182
|
-
activated: "Plan is now active. Track progress across sessions.",
|
|
13183
|
-
completed: "Plan completed. The journey is preserved for future reference.",
|
|
13184
|
-
abandoned: "Plan archived. Abandoned plans still teach.",
|
|
13185
|
-
updated: "Plan updated. Changes are preserved."
|
|
13186
|
-
};
|
|
13187
13368
|
function getPlanStatusHint(status) {
|
|
13188
|
-
|
|
13189
|
-
draft:
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13369
|
+
switch (status) {
|
|
13370
|
+
case "draft":
|
|
13371
|
+
return "Plan saved as draft. Update status to 'active' when ready.";
|
|
13372
|
+
case "active":
|
|
13373
|
+
return "Plan is now active. Create tasks to track implementation.";
|
|
13374
|
+
case "completed":
|
|
13375
|
+
return "Plan completed. Great work!";
|
|
13376
|
+
case "archived":
|
|
13377
|
+
return "Plan archived. It will still appear in searches.";
|
|
13378
|
+
case "abandoned":
|
|
13379
|
+
return "Plan abandoned. Consider capturing lessons learned.";
|
|
13380
|
+
default:
|
|
13381
|
+
return "Plan updated. Changes are preserved.";
|
|
13382
|
+
}
|
|
13196
13383
|
}
|
|
13197
|
-
var
|
|
13198
|
-
|
|
13199
|
-
|
|
13200
|
-
|
|
13201
|
-
|
|
13384
|
+
var POST_COMPACT_HINTS = {
|
|
13385
|
+
restored: "Context restored from pre-compaction snapshot.",
|
|
13386
|
+
restored_with_session: (sessionId) => `Context restored from session ${sessionId.slice(0, 8)}... snapshot.`,
|
|
13387
|
+
no_snapshot: "No snapshot found. Session state may be incomplete.",
|
|
13388
|
+
failed: "Failed to restore context. Try recall() to find relevant memories."
|
|
13202
13389
|
};
|
|
13203
13390
|
var INTEGRATION_HINTS = {
|
|
13204
|
-
|
|
13205
|
-
|
|
13206
|
-
|
|
13207
|
-
|
|
13391
|
+
connected: "Integration connected and syncing.",
|
|
13392
|
+
not_connected: "Integration not connected. Connect at:",
|
|
13393
|
+
sync_in_progress: "Sync in progress. Results may be incomplete.",
|
|
13394
|
+
sync_complete: "Sync complete. All data is current."
|
|
13395
|
+
};
|
|
13396
|
+
var TASK_HINTS = {
|
|
13397
|
+
created: "Task created. Use update_task() to change status.",
|
|
13398
|
+
completed: "Task completed. Well done!",
|
|
13399
|
+
blocked: "Task blocked. Add blocked_reason for context.",
|
|
13400
|
+
linked_to_plan: "Task linked to plan. Progress will be tracked."
|
|
13208
13401
|
};
|
|
13209
|
-
function hashString(str) {
|
|
13210
|
-
let hash = 0;
|
|
13211
|
-
for (let i = 0; i < str.length; i++) {
|
|
13212
|
-
const char = str.charCodeAt(i);
|
|
13213
|
-
hash = (hash << 5) - hash + char;
|
|
13214
|
-
hash = hash & hash;
|
|
13215
|
-
}
|
|
13216
|
-
return hash;
|
|
13217
|
-
}
|
|
13218
13402
|
|
|
13219
13403
|
// src/tools.ts
|
|
13220
13404
|
var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
|
|
@@ -14880,7 +15064,10 @@ function registerTools(server, client, sessionManager) {
|
|
|
14880
15064
|
"notion_stats",
|
|
14881
15065
|
"notion_activity",
|
|
14882
15066
|
"notion_knowledge",
|
|
14883
|
-
"notion_summary"
|
|
15067
|
+
"notion_summary",
|
|
15068
|
+
// Media operations (credit-metered)
|
|
15069
|
+
"media_index",
|
|
15070
|
+
"media_search"
|
|
14884
15071
|
]);
|
|
14885
15072
|
const proTools = (() => {
|
|
14886
15073
|
const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
|
|
@@ -18577,7 +18764,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18577
18764
|
mode: external_exports.enum(["standard", "pack"]).optional().describe("Context pack mode (default: pack when enabled)"),
|
|
18578
18765
|
distill: external_exports.boolean().optional().describe("Use distillation for context pack (default: true)"),
|
|
18579
18766
|
session_tokens: external_exports.number().optional().describe("Cumulative session token count for context pressure calculation"),
|
|
18580
|
-
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)")
|
|
18767
|
+
context_threshold: external_exports.number().optional().describe("Custom context window threshold (defaults to 70k)"),
|
|
18768
|
+
save_exchange: external_exports.boolean().optional().describe("Save this exchange to the transcript for later search (background task)"),
|
|
18769
|
+
session_id: external_exports.string().optional().describe("Session ID for transcript association (required if save_exchange is true)"),
|
|
18770
|
+
client_name: external_exports.string().optional().describe("Client name for transcript metadata (e.g., 'claude', 'cursor')")
|
|
18581
18771
|
})
|
|
18582
18772
|
},
|
|
18583
18773
|
async (input) => {
|
|
@@ -18653,6 +18843,14 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18653
18843
|
logDebug(`Failed to restore post-compact context: ${err}`);
|
|
18654
18844
|
}
|
|
18655
18845
|
}
|
|
18846
|
+
let sessionId = input.session_id;
|
|
18847
|
+
if (!sessionId && sessionManager && input.save_exchange) {
|
|
18848
|
+
sessionId = sessionManager.getSessionId();
|
|
18849
|
+
}
|
|
18850
|
+
let clientName = input.client_name;
|
|
18851
|
+
if (!clientName && detectedClientInfo) {
|
|
18852
|
+
clientName = detectedClientInfo.name;
|
|
18853
|
+
}
|
|
18656
18854
|
const result = await client.getSmartContext({
|
|
18657
18855
|
user_message: input.user_message,
|
|
18658
18856
|
workspace_id: workspaceId,
|
|
@@ -18662,7 +18860,10 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
18662
18860
|
mode: input.mode,
|
|
18663
18861
|
distill: input.distill,
|
|
18664
18862
|
session_tokens: sessionTokens,
|
|
18665
|
-
context_threshold: contextThreshold
|
|
18863
|
+
context_threshold: contextThreshold,
|
|
18864
|
+
save_exchange: input.save_exchange,
|
|
18865
|
+
session_id: sessionId,
|
|
18866
|
+
client_name: clientName
|
|
18666
18867
|
});
|
|
18667
18868
|
if (sessionManager && result.token_estimate) {
|
|
18668
18869
|
sessionManager.addTokens(result.token_estimate);
|
|
@@ -19959,7 +20160,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19959
20160
|
"session",
|
|
19960
20161
|
{
|
|
19961
20162
|
title: "Session",
|
|
19962
|
-
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
20163
|
+
description: `Session management operations. Actions: capture (save decision/insight), capture_lesson (save lesson from mistake), get_lessons (retrieve lessons), recall (natural language recall), remember (quick save), user_context (get preferences), summary (workspace summary), compress (compress chat), delta (changes since timestamp), smart_search (context-enriched search), decision_trace (trace decision provenance), restore_context (restore state after compaction). Plan actions: capture_plan (save implementation plan), get_plan (retrieve plan with tasks), update_plan (modify plan), list_plans (list all plans). Suggested rules actions: list_suggested_rules (view ML-generated rule suggestions), suggested_rule_action (accept/reject/modify a suggestion), suggested_rules_stats (view ML accuracy stats). Team actions (team plans only): team_decisions (team-wide decisions), team_lessons (team-wide lessons), team_plans (plans across team workspaces).`,
|
|
19963
20164
|
inputSchema: external_exports.object({
|
|
19964
20165
|
action: external_exports.enum([
|
|
19965
20166
|
"capture",
|
|
@@ -19983,7 +20184,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
19983
20184
|
// Team actions (team plans only)
|
|
19984
20185
|
"team_decisions",
|
|
19985
20186
|
"team_lessons",
|
|
19986
|
-
"team_plans"
|
|
20187
|
+
"team_plans",
|
|
20188
|
+
// Suggested rules actions (ML-generated)
|
|
20189
|
+
"list_suggested_rules",
|
|
20190
|
+
"suggested_rule_action",
|
|
20191
|
+
"suggested_rules_stats"
|
|
19987
20192
|
]).describe("Action to perform"),
|
|
19988
20193
|
workspace_id: external_exports.string().uuid().optional(),
|
|
19989
20194
|
project_id: external_exports.string().uuid().optional(),
|
|
@@ -20060,7 +20265,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20060
20265
|
is_personal: external_exports.boolean().optional().describe("Mark plan as personal (only visible to creator). For capture_plan/list_plans."),
|
|
20061
20266
|
// Restore context params
|
|
20062
20267
|
snapshot_id: external_exports.string().uuid().optional().describe("Specific snapshot ID to restore (defaults to most recent)"),
|
|
20063
|
-
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)")
|
|
20268
|
+
max_snapshots: external_exports.number().optional().default(1).describe("Number of recent snapshots to consider (default: 1)"),
|
|
20269
|
+
// Suggested rules params
|
|
20270
|
+
rule_id: external_exports.string().uuid().optional().describe("Suggested rule ID for actions"),
|
|
20271
|
+
rule_action: external_exports.enum(["accept", "reject", "modify"]).optional().describe("Action to perform on suggested rule"),
|
|
20272
|
+
modified_keywords: external_exports.array(external_exports.string()).optional().describe("Modified keywords when action is modify"),
|
|
20273
|
+
modified_instruction: external_exports.string().optional().describe("Modified instruction when action is modify"),
|
|
20274
|
+
min_confidence: external_exports.number().optional().describe("Minimum confidence threshold for listing rules")
|
|
20064
20275
|
})
|
|
20065
20276
|
},
|
|
20066
20277
|
async (input) => {
|
|
@@ -20636,6 +20847,81 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20636
20847
|
]
|
|
20637
20848
|
};
|
|
20638
20849
|
}
|
|
20850
|
+
case "list_suggested_rules": {
|
|
20851
|
+
const result = await client.listSuggestedRules({
|
|
20852
|
+
workspace_id: workspaceId,
|
|
20853
|
+
status: input.status,
|
|
20854
|
+
min_confidence: input.min_confidence,
|
|
20855
|
+
limit: input.limit
|
|
20856
|
+
});
|
|
20857
|
+
const rules = result?.data?.items || result?.items || [];
|
|
20858
|
+
if (rules.length === 0) {
|
|
20859
|
+
return {
|
|
20860
|
+
content: [
|
|
20861
|
+
{
|
|
20862
|
+
type: "text",
|
|
20863
|
+
text: formatContent({
|
|
20864
|
+
suggested_rules: [],
|
|
20865
|
+
hint: "No pending rule suggestions. The ML system learns from your lessons and will suggest rules when patterns are detected."
|
|
20866
|
+
})
|
|
20867
|
+
}
|
|
20868
|
+
]
|
|
20869
|
+
};
|
|
20870
|
+
}
|
|
20871
|
+
return {
|
|
20872
|
+
content: [
|
|
20873
|
+
{
|
|
20874
|
+
type: "text",
|
|
20875
|
+
text: formatContent({
|
|
20876
|
+
suggested_rules: rules,
|
|
20877
|
+
total: result?.data?.total || rules.length,
|
|
20878
|
+
hint: "Use suggested_rule_action to accept, reject, or modify these suggestions."
|
|
20879
|
+
})
|
|
20880
|
+
}
|
|
20881
|
+
]
|
|
20882
|
+
};
|
|
20883
|
+
}
|
|
20884
|
+
case "suggested_rule_action": {
|
|
20885
|
+
if (!input.rule_id || !input.rule_action) {
|
|
20886
|
+
return errorResult("suggested_rule_action requires: rule_id, rule_action (accept/reject/modify)");
|
|
20887
|
+
}
|
|
20888
|
+
const result = await client.suggestedRuleAction({
|
|
20889
|
+
rule_id: input.rule_id,
|
|
20890
|
+
action: input.rule_action,
|
|
20891
|
+
modified_keywords: input.modified_keywords,
|
|
20892
|
+
modified_instruction: input.modified_instruction
|
|
20893
|
+
});
|
|
20894
|
+
const actionVerb = input.rule_action === "accept" ? "accepted" : input.rule_action === "reject" ? "rejected" : "modified";
|
|
20895
|
+
return {
|
|
20896
|
+
content: [
|
|
20897
|
+
{
|
|
20898
|
+
type: "text",
|
|
20899
|
+
text: formatContent({
|
|
20900
|
+
success: true,
|
|
20901
|
+
message: `Rule ${actionVerb} successfully`,
|
|
20902
|
+
rule: result?.data || result,
|
|
20903
|
+
hint: input.rule_action === "accept" ? "This rule will now be applied to future context() calls." : input.rule_action === "reject" ? "This pattern will have reduced confidence for future suggestions." : "The modified rule will be applied to future context() calls."
|
|
20904
|
+
})
|
|
20905
|
+
}
|
|
20906
|
+
]
|
|
20907
|
+
};
|
|
20908
|
+
}
|
|
20909
|
+
case "suggested_rules_stats": {
|
|
20910
|
+
const result = await client.getSuggestedRulesStats({
|
|
20911
|
+
workspace_id: workspaceId
|
|
20912
|
+
});
|
|
20913
|
+
return {
|
|
20914
|
+
content: [
|
|
20915
|
+
{
|
|
20916
|
+
type: "text",
|
|
20917
|
+
text: formatContent({
|
|
20918
|
+
stats: result?.data || result,
|
|
20919
|
+
hint: "These stats show ML vs Grok accuracy. The blend weight auto-adjusts based on these metrics."
|
|
20920
|
+
})
|
|
20921
|
+
}
|
|
20922
|
+
]
|
|
20923
|
+
};
|
|
20924
|
+
}
|
|
20639
20925
|
default:
|
|
20640
20926
|
return errorResult(`Unknown action: ${input.action}`);
|
|
20641
20927
|
}
|
|
@@ -20645,7 +20931,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20645
20931
|
"memory",
|
|
20646
20932
|
{
|
|
20647
20933
|
title: "Memory",
|
|
20648
|
-
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
20934
|
+
description: `Memory operations for events and nodes. Event actions: create_event, get_event, update_event, delete_event, list_events, distill_event, import_batch (bulk import array of events). Node actions: create_node, get_node, update_node, delete_node, list_nodes, supersede_node. Query actions: search, decisions, timeline, summary. Task actions: create_task (create task, optionally linked to plan), get_task, update_task (can link/unlink task to plan via plan_id), delete_task, list_tasks, reorder_tasks. Todo actions: create_todo, list_todos, get_todo, update_todo, delete_todo, complete_todo. Diagram actions: create_diagram, list_diagrams, get_diagram, update_diagram, delete_diagram. Doc actions: create_doc, list_docs, get_doc, update_doc, delete_doc, create_roadmap. Transcript actions: list_transcripts (list saved conversations), get_transcript (get full transcript by ID), search_transcripts (semantic search across conversations), delete_transcript. Team actions (team plans only): team_tasks, team_todos, team_diagrams, team_docs.`,
|
|
20649
20935
|
inputSchema: external_exports.object({
|
|
20650
20936
|
action: external_exports.enum([
|
|
20651
20937
|
"create_event",
|
|
@@ -20693,6 +20979,11 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20693
20979
|
"update_doc",
|
|
20694
20980
|
"delete_doc",
|
|
20695
20981
|
"create_roadmap",
|
|
20982
|
+
// Transcript actions
|
|
20983
|
+
"list_transcripts",
|
|
20984
|
+
"get_transcript",
|
|
20985
|
+
"search_transcripts",
|
|
20986
|
+
"delete_transcript",
|
|
20696
20987
|
// Team actions
|
|
20697
20988
|
"team_tasks",
|
|
20698
20989
|
"team_todos",
|
|
@@ -20798,7 +21089,13 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
|
|
|
20798
21089
|
})
|
|
20799
21090
|
).optional().describe("Milestones for create_roadmap action"),
|
|
20800
21091
|
// Personal items param
|
|
20801
|
-
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs.")
|
|
21092
|
+
is_personal: external_exports.boolean().optional().describe("Mark as personal (only visible to creator). For create/list actions on todos, diagrams, docs."),
|
|
21093
|
+
// Transcript params
|
|
21094
|
+
transcript_id: external_exports.string().uuid().optional().describe("Transcript ID for get_transcript/delete_transcript"),
|
|
21095
|
+
session_id: external_exports.string().optional().describe("Session ID filter for list_transcripts"),
|
|
21096
|
+
client_name: external_exports.string().optional().describe("Client name filter for list_transcripts (e.g., 'claude', 'cursor')"),
|
|
21097
|
+
started_after: external_exports.string().optional().describe("ISO timestamp - filter transcripts started after this time"),
|
|
21098
|
+
started_before: external_exports.string().optional().describe("ISO timestamp - filter transcripts started before this time")
|
|
20802
21099
|
})
|
|
20803
21100
|
},
|
|
20804
21101
|
async (input) => {
|
|
@@ -21572,6 +21869,54 @@ ${formatContent(result)}`
|
|
|
21572
21869
|
]
|
|
21573
21870
|
};
|
|
21574
21871
|
}
|
|
21872
|
+
// Transcript actions
|
|
21873
|
+
case "list_transcripts": {
|
|
21874
|
+
const result = await client.listTranscripts({
|
|
21875
|
+
workspace_id: workspaceId,
|
|
21876
|
+
project_id: projectId,
|
|
21877
|
+
session_id: input.session_id,
|
|
21878
|
+
client_name: input.client_name,
|
|
21879
|
+
started_after: input.started_after,
|
|
21880
|
+
started_before: input.started_before,
|
|
21881
|
+
limit: input.limit
|
|
21882
|
+
});
|
|
21883
|
+
const transcripts = result?.data?.items || result?.items || result?.data || [];
|
|
21884
|
+
const resultWithHint = Array.isArray(transcripts) && transcripts.length === 0 ? { ...result, hint: "No transcripts found. Enable save_exchange in context() calls to save conversations." } : result;
|
|
21885
|
+
return {
|
|
21886
|
+
content: [{ type: "text", text: formatContent(resultWithHint) }]
|
|
21887
|
+
};
|
|
21888
|
+
}
|
|
21889
|
+
case "get_transcript": {
|
|
21890
|
+
if (!input.transcript_id) {
|
|
21891
|
+
return errorResult("get_transcript requires: transcript_id");
|
|
21892
|
+
}
|
|
21893
|
+
const result = await client.getTranscript(input.transcript_id);
|
|
21894
|
+
return {
|
|
21895
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21896
|
+
};
|
|
21897
|
+
}
|
|
21898
|
+
case "search_transcripts": {
|
|
21899
|
+
if (!input.query) {
|
|
21900
|
+
return errorResult("search_transcripts requires: query");
|
|
21901
|
+
}
|
|
21902
|
+
const result = await client.searchTranscripts({
|
|
21903
|
+
workspace_id: workspaceId,
|
|
21904
|
+
query: input.query,
|
|
21905
|
+
limit: input.limit
|
|
21906
|
+
});
|
|
21907
|
+
return {
|
|
21908
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21909
|
+
};
|
|
21910
|
+
}
|
|
21911
|
+
case "delete_transcript": {
|
|
21912
|
+
if (!input.transcript_id) {
|
|
21913
|
+
return errorResult("delete_transcript requires: transcript_id");
|
|
21914
|
+
}
|
|
21915
|
+
const result = await client.deleteTranscript(input.transcript_id);
|
|
21916
|
+
return {
|
|
21917
|
+
content: [{ type: "text", text: formatContent(result) }]
|
|
21918
|
+
};
|
|
21919
|
+
}
|
|
21575
21920
|
default:
|
|
21576
21921
|
return errorResult(`Unknown action: ${input.action}`);
|
|
21577
21922
|
}
|
|
@@ -22860,6 +23205,8 @@ Example workflow:
|
|
|
22860
23205
|
const projectId = resolveProjectId(input.project_id);
|
|
22861
23206
|
switch (input.action) {
|
|
22862
23207
|
case "index": {
|
|
23208
|
+
const indexGate = await gateIfProTool("media_index");
|
|
23209
|
+
if (indexGate) return indexGate;
|
|
22863
23210
|
if (!input.file_path && !input.external_url) {
|
|
22864
23211
|
return errorResult("index requires: file_path or external_url");
|
|
22865
23212
|
}
|
|
@@ -23024,6 +23371,8 @@ Created: ${content.created_at}`
|
|
|
23024
23371
|
}
|
|
23025
23372
|
}
|
|
23026
23373
|
case "search": {
|
|
23374
|
+
const searchGate = await gateIfProTool("media_search");
|
|
23375
|
+
if (searchGate) return searchGate;
|
|
23027
23376
|
if (!input.query) {
|
|
23028
23377
|
return errorResult("search requires: query");
|
|
23029
23378
|
}
|
|
@@ -24244,6 +24593,7 @@ function registerPrompts(server) {
|
|
|
24244
24593
|
}
|
|
24245
24594
|
|
|
24246
24595
|
// src/session-manager.ts
|
|
24596
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
24247
24597
|
var SessionManager = class _SessionManager {
|
|
24248
24598
|
constructor(server, client) {
|
|
24249
24599
|
this.server = server;
|
|
@@ -24275,12 +24625,20 @@ var SessionManager = class _SessionManager {
|
|
|
24275
24625
|
this.lastHighPressureAt = null;
|
|
24276
24626
|
this.lastHighPressureTokens = 0;
|
|
24277
24627
|
this.postCompactRestoreCompleted = false;
|
|
24628
|
+
this.sessionId = `mcp-${randomUUID2()}`;
|
|
24278
24629
|
}
|
|
24279
24630
|
static {
|
|
24280
24631
|
// Each conversation turn typically includes: user message (~500), AI response (~1500),
|
|
24281
24632
|
// system prompt overhead (~500), and reasoning (~1500). Conservative estimate: 3000/turn
|
|
24282
24633
|
this.TOKENS_PER_TURN_ESTIMATE = 3e3;
|
|
24283
24634
|
}
|
|
24635
|
+
/**
|
|
24636
|
+
* Get the unique session ID for this MCP connection.
|
|
24637
|
+
* Used for transcript saving and session association.
|
|
24638
|
+
*/
|
|
24639
|
+
getSessionId() {
|
|
24640
|
+
return this.sessionId;
|
|
24641
|
+
}
|
|
24284
24642
|
/**
|
|
24285
24643
|
* Check if session has been auto-initialized
|
|
24286
24644
|
*/
|
|
@@ -24815,7 +25173,7 @@ var SessionManager = class _SessionManager {
|
|
|
24815
25173
|
|
|
24816
25174
|
// src/http-gateway.ts
|
|
24817
25175
|
import { createServer } from "node:http";
|
|
24818
|
-
import { randomUUID as
|
|
25176
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
24819
25177
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
24820
25178
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
24821
25179
|
var HOST = process.env.MCP_HTTP_HOST || "0.0.0.0";
|
|
@@ -24992,7 +25350,7 @@ async function createSession() {
|
|
|
24992
25350
|
registerPrompts(server);
|
|
24993
25351
|
}
|
|
24994
25352
|
const transport = new StreamableHTTPServerTransport({
|
|
24995
|
-
sessionIdGenerator: () =>
|
|
25353
|
+
sessionIdGenerator: () => randomUUID3(),
|
|
24996
25354
|
enableJsonResponse: ENABLE_JSON_RESPONSE,
|
|
24997
25355
|
onsessionclosed: (sessionId) => {
|
|
24998
25356
|
sessions.delete(sessionId);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contextstream/mcp-server",
|
|
3
3
|
"mcpName": "io.github.contextstreamio/mcp-server",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.50",
|
|
5
5
|
"description": "ContextStream MCP server - v0.4.x with consolidated domain tools (~11 tools, ~75% token reduction). Code context, memory, search, and AI tools.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"license": "MIT",
|