@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 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
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
- if (msgType === "assistant" && entry.content) {
96
- const content2 = entry.content;
97
- if (typeof content2 === "string" && content2.length > 50) {
98
- recentMessages.push(content2.slice(0, 500));
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: recentMessages.length,
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 { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
197
- if (success) {
279
+ const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
280
+ if (transcriptResult.success) {
198
281
  autoSaveStatus = `
199
- [ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files]`;
282
+ [ContextStream: ${transcriptResult.message}]`;
200
283
  } else {
201
- autoSaveStatus = `
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
- if (msgType === "assistant" && entry.content) {
2853
- const content2 = entry.content;
2854
- if (typeof content2 === "string" && content2.length > 50) {
2855
- recentMessages.push(content2.slice(0, 500));
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: recentMessages.length,
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 { success, message } = await saveSnapshot(sessionId, transcriptData, trigger);
2954
- if (success) {
3036
+ const transcriptResult = await saveFullTranscript(sessionId, transcriptData, trigger);
3037
+ if (transcriptResult.success) {
2955
3038
  autoSaveStatus = `
2956
- [ContextStream: Auto-saved snapshot with ${transcriptData.activeFiles.length} active files]`;
3039
+ [ContextStream: ${transcriptResult.message}]`;
2957
3040
  } else {
2958
- autoSaveStatus = `
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/educational-microcopy.ts
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
- const index = sessionId ? Math.abs(hashString(sessionId)) % SESSION_INIT_TIPS.length : Math.floor(Math.random() * SESSION_INIT_TIPS.length);
13123
- return SESSION_INIT_TIPS[index];
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
- return CAPTURE_HINTS[eventType] || "Captured. This will survive when the chat disappears.";
13145
- }
13146
- var EMPTY_STATE_HINTS = {
13147
- // Plans
13148
- list_plans: "No plans yet. Plans live here, not in chat transcripts.",
13149
- get_plan: "Plan not found. Create one to track implementation across sessions.",
13150
- // Memory & Search
13151
- recall: "Nothing found yet. Context builds over time.",
13152
- search: "No matches. Try a different query or let context accumulate.",
13153
- list_events: "No events yet. Start capturing decisions and insights.",
13154
- decisions: "No decisions captured yet. Record the 'why' behind your choices.",
13155
- timeline: "Timeline empty. Events will appear as you work.",
13156
- // Lessons
13157
- get_lessons: "No lessons yet. Capture mistakes to learn from them.",
13158
- // Tasks
13159
- list_tasks: "No tasks yet. Break plans into trackable tasks.",
13160
- // Todos
13161
- list_todos: "No todos yet. Action items will appear here.",
13162
- // Diagrams
13163
- list_diagrams: "No diagrams yet. AI-generated diagrams persist here.",
13164
- // Docs
13165
- list_docs: "No docs yet. Turn AI explanations into shareable documentation.",
13166
- // Reminders
13167
- list_reminders: "No reminders set. Schedule follow-ups that won't be forgotten.",
13168
- // Generic
13169
- default: "Nothing yet. Start a conversation\u2014context builds over time."
13170
- };
13171
- function getEmptyStateHint(action) {
13172
- return EMPTY_STATE_HINTS[action] || EMPTY_STATE_HINTS.default;
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
- const statusMap = {
13189
- draft: PLAN_HINTS.created,
13190
- active: PLAN_HINTS.activated,
13191
- completed: PLAN_HINTS.completed,
13192
- abandoned: PLAN_HINTS.abandoned,
13193
- archived: PLAN_HINTS.abandoned
13194
- };
13195
- return statusMap[status] || PLAN_HINTS.updated;
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 TASK_HINTS = {
13198
- created: "Task tracked. It won't disappear when the chat does.",
13199
- completed: "Task done. Progress is preserved.",
13200
- blocked: "Task blocked. Capture what's blocking for future reference.",
13201
- cancelled: "Task cancelled. The history remains."
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
- not_connected: "Connect integrations to sync context where your team already works.",
13205
- slack: "Context from Slack conversations, connected.",
13206
- github: "Issues and PRs linked to the decisions behind them.",
13207
- notion: "Decisions synced to where your team documents."
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 randomUUID2 } from "node:crypto";
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: () => randomUUID2(),
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.49",
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",