@contextstream/mcp-server 0.4.58 → 0.4.59

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/dist/index.js CHANGED
@@ -958,7 +958,7 @@ var init_rules_templates = __esm({
958
958
  "generate_rules",
959
959
  // Consolidated domain tools (v0.4.x default)
960
960
  "search",
961
- // Modes: semantic, hybrid, keyword, pattern
961
+ // Modes: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern
962
962
  "session",
963
963
  // Actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace
964
964
  "memory",
@@ -982,7 +982,7 @@ var init_rules_templates = __esm({
982
982
  |---------|----------|
983
983
  | **1st message** | \`init()\` \u2192 \`context(user_message="...")\` |
984
984
  | **Every message** | \`context(user_message="...")\` FIRST |
985
- | **Before file search** | \`search(mode="hybrid")\` BEFORE Glob/Grep/Read |
985
+ | **Before file search** | \`search(mode="auto")\` BEFORE Glob/Grep/Read |
986
986
  </contextstream_rules>
987
987
 
988
988
  **Why?** \`context()\` delivers task-specific rules, lessons from past mistakes, and relevant decisions. Skip it = fly blind.
@@ -1005,7 +1005,7 @@ v${RULES_VERSION}
1005
1005
  | 1st message | \`init()\` \u2192 \`context(user_message="<msg>")\` |
1006
1006
  | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
1007
1007
 
1008
- **BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="hybrid")\` FIRST
1008
+ **BEFORE Glob/Grep/Read/Search:** \u2192 \`search(mode="auto")\` FIRST
1009
1009
  </contextstream_rules>
1010
1010
 
1011
1011
  **This block MUST appear at the start of EVERY response.** Failing to call \`context()\` means missing rules, lessons, and relevant context.
@@ -1057,7 +1057,7 @@ v${RULES_VERSION}
1057
1057
  - Example: \`[CONTEXTSTREAM RULES] 1. BEFORE Glob/Grep... [END RULES]\`
1058
1058
 
1059
1059
  2. **PreToolUse blocking** - If you try to use Glob/Grep/Search/Explore:
1060
- - Hook returns error: \`STOP: Use mcp__contextstream__search(mode="hybrid") instead\`
1060
+ - Hook returns error: \`STOP: Use mcp__contextstream__search(mode="auto") instead\`
1061
1061
  - **You MUST use the suggested ContextStream tool instead**
1062
1062
  - Local tools are only allowed if project is not indexed or ContextStream returns 0 results
1063
1063
 
@@ -1081,7 +1081,7 @@ Set environment variable: \`CONTEXTSTREAM_HOOK_ENABLED=false\`
1081
1081
 
1082
1082
  **BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
1083
1083
  \`\`\`
1084
- STOP \u2192 Call search(mode="hybrid", query="...") FIRST
1084
+ STOP \u2192 Call search(mode="auto", query="...") FIRST
1085
1085
  \`\`\`
1086
1086
 
1087
1087
  **Note:** PreToolUse hooks block these tools when ContextStream is available.
@@ -1090,11 +1090,11 @@ STOP \u2192 Call search(mode="hybrid", query="...") FIRST
1090
1090
  \u274C **NEVER DO THIS:**
1091
1091
  - \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
1092
1092
  - \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
1093
- - \`Read(file)\` for discovery \u2192 Use \`search(mode="hybrid", query="...")\` instead
1094
- - \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="hybrid")\` instead
1093
+ - \`Read(file)\` for discovery \u2192 Use \`search(mode="auto", query="...")\` instead
1094
+ - \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="auto")\` instead
1095
1095
 
1096
1096
  \u2705 **ALWAYS DO THIS:**
1097
- 1. \`search(mode="hybrid", query="what you're looking for")\`
1097
+ 1. \`search(mode="auto", query="what you're looking for")\`
1098
1098
  2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
1099
1099
  3. Use Read ONLY for exact file edits after you know the file path
1100
1100
 
@@ -1165,7 +1165,7 @@ Rules Version: ${RULES_VERSION}
1165
1165
  |---------|----------|
1166
1166
  | **1st message** | \`init()\` \u2192 \`context(user_message="<msg>")\` |
1167
1167
  | **EVERY message after** | \`context(user_message="<msg>")\` **FIRST** |
1168
- | **Before file search** | \`search(mode="hybrid")\` FIRST |
1168
+ | **Before file search** | \`search(mode="auto")\` FIRST |
1169
1169
  | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
1170
1170
  | **User correction** | \`session(action="capture_lesson", ...)\` |
1171
1171
 
@@ -1185,7 +1185,7 @@ Rules Version: ${RULES_VERSION}
1185
1185
  |------|--------------|
1186
1186
  | **1st message** | \`init(folder_path="...", context_hint="<msg>")\`, then \`context(...)\` |
1187
1187
  | **2nd+ messages** | \`context(user_message="<msg>", format="minified", max_tokens=400)\` |
1188
- | **Code search** | \`search(mode="hybrid", query="...")\` \u2014 BEFORE Glob/Grep/Read |
1188
+ | **Code search** | \`search(mode="auto", query="...")\` \u2014 BEFORE Glob/Grep/Read |
1189
1189
  | **After significant work** | \`session(action="capture", event_type="decision", ...)\` |
1190
1190
  | **User correction** | \`session(action="capture_lesson", ...)\` |
1191
1191
  | **\u26A0\uFE0F When warnings received** | **STOP**, acknowledge, explain mitigation, then proceed |
@@ -1212,7 +1212,7 @@ v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode
1212
1212
 
1213
1213
  | Domain | Actions/Modes | Example |
1214
1214
  |--------|---------------|---------|
1215
- | **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="hybrid", query="auth implementation", limit=3)\` |
1215
+ | **\`search\`** | mode: auto (recommended), semantic, hybrid (legacy alias), keyword, pattern | \`search(mode="auto", query="auth implementation", limit=3)\` |
1216
1216
  | **\`session\`** | action: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search, decision_trace | \`session(action="capture", event_type="decision", title="Use JWT", content="...")\` |
1217
1217
  | **\`memory\`** | action: create_event, get_event, update_event, delete_event, list_events, distill_event, create_node, get_node, update_node, delete_node, list_nodes, supersede_node, search, decisions, timeline, summary | \`memory(action="list_events", limit=10)\` |
1218
1218
  | **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
@@ -1358,7 +1358,7 @@ session(action="capture", event_type="session_snapshot", title="Pre-compaction s
1358
1358
 
1359
1359
  ### Search & Code Intelligence (ContextStream-first)
1360
1360
 
1361
- \u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="hybrid")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
1361
+ \u26A0\uFE0F **STOP: Before using Search/Glob/Grep/Read/Explore** \u2192 Call \`search(mode="auto")\` FIRST. Use local tools ONLY if ContextStream returns 0 results.
1362
1362
 
1363
1363
  **\u274C WRONG workflow (wastes tokens, slow):**
1364
1364
  \`\`\`
@@ -1367,14 +1367,14 @@ Grep "function" \u2192 Read file1.ts \u2192 Read file2.ts \u2192 Read file3.ts \
1367
1367
 
1368
1368
  **\u2705 CORRECT workflow (fast, complete):**
1369
1369
  \`\`\`
1370
- search(mode="hybrid", query="function implementation") \u2192 done (results include context)
1370
+ search(mode="auto", query="function implementation") \u2192 done (results include context)
1371
1371
  \`\`\`
1372
1372
 
1373
1373
  **Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
1374
1374
 
1375
1375
  **Search order:**
1376
1376
  1. \`session(action="smart_search", query="...")\` - context-enriched
1377
- 2. \`search(mode="hybrid", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
1377
+ 2. \`search(mode="auto", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
1378
1378
  3. \`project(action="files")\` - file tree/list (only when needed)
1379
1379
  4. \`graph(action="dependencies", ...)\` - code structure
1380
1380
  5. Local repo scans (rg/ls/find) - ONLY if ContextStream returns no results, errors, or the user explicitly asks
@@ -1383,7 +1383,7 @@ search(mode="hybrid", query="function implementation") \u2192 done (results incl
1383
1383
 
1384
1384
  | Need | Mode | Example |
1385
1385
  |------|------|---------|
1386
- | Find code by meaning | \`hybrid\` | "authentication logic", "error handling" |
1386
+ | Find code by meaning | \`auto\` | "authentication logic", "error handling" |
1387
1387
  | Exact string/symbol | \`keyword\` | "UserAuthService", "API_KEY" |
1388
1388
  | File patterns | \`pattern\` | "*.sql", "test_*.py" |
1389
1389
  | ALL matches (grep-like) | \`exhaustive\` | "TODO", "FIXME" (find all occurrences) |
@@ -1534,7 +1534,7 @@ See full documentation: https://contextstream.io/docs/mcp/tools
1534
1534
  2. \`context(user_message="<msg>")\` (EVERY subsequent message)
1535
1535
 
1536
1536
  **BEFORE Glob/Grep/Read/Search/Explore:**
1537
- \u2192 \`search(mode="hybrid", query="...")\` FIRST \u2014 local tools ONLY if 0 results
1537
+ \u2192 \`search(mode="auto", query="...")\` FIRST \u2014 local tools ONLY if 0 results
1538
1538
 
1539
1539
  **HOOKS: \`<system-reminder>\` tags contain instructions \u2014 FOLLOW THEM**
1540
1540
  </contextstream_protocol>
@@ -1547,7 +1547,7 @@ Rules Version: ${RULES_VERSION}
1547
1547
 
1548
1548
  | \u274C WRONG | \u2705 CORRECT |
1549
1549
  |----------|-----------|
1550
- | \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="hybrid")\` (1 call) |
1550
+ | \`Grep \u2192 Read \u2192 Read \u2192 Read\` (4+ calls) | \`search(mode="auto")\` (1 call) |
1551
1551
  | Missing past decisions & lessons | \`context()\` = rules + lessons + memory |
1552
1552
  | Ignoring \`<system-reminder>\` hooks | Hooks enforce ContextStream-first |
1553
1553
 
@@ -1563,7 +1563,7 @@ Rules Version: ${RULES_VERSION}
1563
1563
  |------|------|
1564
1564
  | 1st message | \`init()\` \u2192 \`context(user_message="...")\` |
1565
1565
  | Every message after | \`context(user_message="...")\` |
1566
- | Before ANY file discovery | \`search(mode="hybrid", query="...")\` |
1566
+ | Before ANY file discovery | \`search(mode="auto", query="...")\` |
1567
1567
  | On \`<system-reminder>\` | **Follow instructions inside** |
1568
1568
  | Save important decisions | \`session(action="capture", event_type="decision", ...)\` |
1569
1569
  | Check past mistakes | \`session(action="get_lessons", query="...")\` |
@@ -1572,7 +1572,8 @@ Rules Version: ${RULES_VERSION}
1572
1572
 
1573
1573
  | Mode | When |
1574
1574
  |------|------|
1575
- | \`hybrid\` | Default \u2014 semantic + keyword |
1575
+ | \`auto\` | Default \u2014 semantic + keyword |
1576
+ | \`hybrid\` | Legacy alias for auto |
1576
1577
  | \`keyword\` | Exact symbol match |
1577
1578
  | \`exhaustive\` | Find ALL occurrences |
1578
1579
  | \`semantic\` | Conceptual questions |
@@ -1752,7 +1753,7 @@ This tells you:
1752
1753
 
1753
1754
  **IF project is indexed and fresh:**
1754
1755
  \`\`\`
1755
- search(mode="hybrid", query="what you're looking for")
1756
+ search(mode="auto", query="what you're looking for")
1756
1757
  \`\`\`
1757
1758
 
1758
1759
  **IF project is NOT indexed or very stale (>7 days):**
@@ -2829,7 +2830,7 @@ def main():
2829
2830
  if tool == "Glob":
2830
2831
  pattern = inp.get("pattern", "")
2831
2832
  if is_discovery_glob(pattern):
2832
- print(f"STOP: Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
2833
+ print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of Glob.", file=sys.stderr)
2833
2834
  sys.exit(2)
2834
2835
 
2835
2836
  elif tool == "Grep" or tool == "Search":
@@ -2841,12 +2842,12 @@ def main():
2841
2842
  # Specific file - suggest Read instead
2842
2843
  print(f"STOP: Use Read(\\"{path}\\") to view file content, or mcp__contextstream__search(mode=\\"keyword\\", query=\\"{pattern}\\") for codebase search.", file=sys.stderr)
2843
2844
  else:
2844
- print(f"STOP: Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
2845
+ print(f"STOP: Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}.", file=sys.stderr)
2845
2846
  sys.exit(2)
2846
2847
 
2847
2848
  elif tool == "Task":
2848
2849
  if inp.get("subagent_type", "").lower() == "explore":
2849
- print("STOP: Use mcp__contextstream__search(mode=\\"hybrid\\") instead of Task(Explore).", file=sys.stderr)
2850
+ print("STOP: Use mcp__contextstream__search(mode=\\"auto\\") instead of Task(Explore).", file=sys.stderr)
2850
2851
  sys.exit(2)
2851
2852
  if inp.get("subagent_type", "").lower() == "plan":
2852
2853
  print("STOP: Use mcp__contextstream__session(action=\\"capture_plan\\") for planning. ContextStream plans persist across sessions.", file=sys.stderr)
@@ -2873,7 +2874,7 @@ import os
2873
2874
  ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
2874
2875
 
2875
2876
  REMINDER = """[CONTEXTSTREAM RULES]
2876
- 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="hybrid") FIRST
2877
+ 1. BEFORE Glob/Grep/Read/Search: mcp__contextstream__search(mode="auto") FIRST
2877
2878
  2. Call context_smart at start of EVERY response
2878
2879
  3. Local tools ONLY if ContextStream returns 0 results
2879
2880
  [END RULES]"""
@@ -3309,7 +3310,7 @@ def main():
3309
3310
  pattern = params.get("path", "") or params.get("regex", "")
3310
3311
  if is_discovery_glob(pattern) or is_discovery_grep(pattern):
3311
3312
  output_block(
3312
- f"Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}. "
3313
+ f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
3313
3314
  "ContextStream search is indexed and faster. Only use local tools if ContextStream returns 0 results.",
3314
3315
  "[CONTEXTSTREAM] Use ContextStream search for code discovery."
3315
3316
  )
@@ -3336,7 +3337,7 @@ import os
3336
3337
  ENABLED = os.environ.get("CONTEXTSTREAM_REMINDER_ENABLED", "true").lower() == "true"
3337
3338
 
3338
3339
  REMINDER = """[CONTEXTSTREAM RULES]
3339
- 1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="hybrid") FIRST
3340
+ 1. BEFORE list_files/search_files/read_file: mcp__contextstream__search(mode="auto") FIRST
3340
3341
  2. Call context_smart at start of EVERY response
3341
3342
  3. Local tools ONLY if ContextStream returns 0 results
3342
3343
  [END RULES]"""
@@ -3487,7 +3488,7 @@ def main():
3487
3488
  pattern = params.get("pattern", "") or params.get("path", "")
3488
3489
  if is_discovery_glob(pattern):
3489
3490
  output_deny(
3490
- f"Use mcp__contextstream__search(mode=\\"hybrid\\", query=\\"{pattern}\\") instead of {tool}. "
3491
+ f"Use mcp__contextstream__search(mode=\\"auto\\", query=\\"{pattern}\\") instead of {tool}. "
3491
3492
  "ContextStream search is indexed and faster."
3492
3493
  )
3493
3494
 
@@ -4059,7 +4060,7 @@ async function runPreToolUseHook() {
4059
4060
  fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Glob pattern=${pattern}, isDiscovery=${isDiscoveryGlob(pattern)}
4060
4061
  `);
4061
4062
  if (isDiscoveryGlob(pattern)) {
4062
- const msg = `STOP: Use mcp__contextstream__search(mode="hybrid", query="${pattern}") instead of Glob.`;
4063
+ const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of Glob.`;
4063
4064
  fs9.appendFileSync(DEBUG_FILE, `[PreToolUse] Intercepting discovery glob: ${msg}
4064
4065
  `);
4065
4066
  if (editorFormat === "cline") {
@@ -4082,7 +4083,7 @@ async function runPreToolUseHook() {
4082
4083
  }
4083
4084
  blockClaudeCode(msg);
4084
4085
  } else {
4085
- const msg = `STOP: Use mcp__contextstream__search(mode="hybrid", query="${pattern}") instead of ${tool}.`;
4086
+ const msg = `STOP: Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}.`;
4086
4087
  if (editorFormat === "cline") {
4087
4088
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4088
4089
  } else if (editorFormat === "cursor") {
@@ -4094,7 +4095,7 @@ async function runPreToolUseHook() {
4094
4095
  } else if (tool === "Task") {
4095
4096
  const subagentType = toolInput?.subagent_type?.toLowerCase() || "";
4096
4097
  if (subagentType === "explore") {
4097
- const msg = 'STOP: Use mcp__contextstream__search(mode="hybrid") instead of Task(Explore).';
4098
+ const msg = 'STOP: Use mcp__contextstream__search(mode="auto") instead of Task(Explore).';
4098
4099
  if (editorFormat === "cline") {
4099
4100
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4100
4101
  } else if (editorFormat === "cursor") {
@@ -4123,7 +4124,7 @@ async function runPreToolUseHook() {
4123
4124
  if (tool === "list_files" || tool === "search_files") {
4124
4125
  const pattern = toolInput?.path || toolInput?.regex || "";
4125
4126
  if (isDiscoveryGlob(pattern) || isDiscoveryGrep(pattern)) {
4126
- const msg = `Use mcp__contextstream__search(mode="hybrid", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
4127
+ const msg = `Use mcp__contextstream__search(mode="auto", query="${pattern}") instead of ${tool}. ContextStream search is indexed and faster.`;
4127
4128
  if (editorFormat === "cline") {
4128
4129
  outputClineBlock(msg, "[CONTEXTSTREAM] Use ContextStream search for code discovery.");
4129
4130
  } else if (editorFormat === "cursor") {
@@ -4381,6 +4382,34 @@ async function saveLastExchange(exchange, cwd, clientName) {
4381
4382
  } catch {
4382
4383
  }
4383
4384
  }
4385
+ async function fetchHookContext() {
4386
+ if (!API_KEY2) return null;
4387
+ try {
4388
+ const controller = new AbortController();
4389
+ const timeoutId = setTimeout(() => controller.abort(), 2e3);
4390
+ const url = `${API_URL2}/api/v1/context/hook`;
4391
+ const body = {};
4392
+ if (WORKSPACE_ID) body.workspace_id = WORKSPACE_ID;
4393
+ if (PROJECT_ID) body.project_id = PROJECT_ID;
4394
+ const response = await fetch(url, {
4395
+ method: "POST",
4396
+ headers: {
4397
+ "X-API-Key": API_KEY2,
4398
+ "Content-Type": "application/json"
4399
+ },
4400
+ body: JSON.stringify(body),
4401
+ signal: controller.signal
4402
+ });
4403
+ clearTimeout(timeoutId);
4404
+ if (response.ok) {
4405
+ const data = await response.json();
4406
+ return data?.data?.context || null;
4407
+ }
4408
+ return null;
4409
+ } catch {
4410
+ return null;
4411
+ }
4412
+ }
4384
4413
  async function fetchSessionContext() {
4385
4414
  if (!API_KEY2) return null;
4386
4415
  try {
@@ -4459,26 +4488,6 @@ function transformSmartContextResponse(data) {
4459
4488
  return null;
4460
4489
  }
4461
4490
  }
4462
- function buildClaudeReminder(ctx, versionNotice) {
4463
- const parts = [];
4464
- if (versionNotice?.behind) {
4465
- const versionInfo = getVersionNoticeForHook(versionNotice);
4466
- if (versionInfo) {
4467
- parts.push(versionInfo);
4468
- parts.push("");
4469
- }
4470
- }
4471
- const highImportancePrefs = ctx?.preferences?.filter((p) => p.importance === "high") || [];
4472
- if (highImportancePrefs.length > 0) {
4473
- parts.push(`[USER PREFERENCES - Always respect these]`);
4474
- for (const pref of highImportancePrefs.slice(0, 5)) {
4475
- parts.push(`\u2022 ${pref.title}: ${pref.content}`);
4476
- }
4477
- parts.push("");
4478
- }
4479
- parts.push(REMINDER);
4480
- return parts.join("\n");
4481
- }
4482
4491
  function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
4483
4492
  const parts = [ENHANCED_REMINDER_HEADER];
4484
4493
  if (versionNotice?.behind) {
@@ -4496,7 +4505,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
4496
4505
  2. Wait for indexing: if \`init\` returns \`indexing_status: "started"\`, files are being indexed
4497
4506
  3. Generate a unique session_id (e.g., "session-" + timestamp or UUID) - use this for ALL context() calls
4498
4507
  4. Call \`context(user_message="...", save_exchange=true, session_id="<your-session-id>")\` for task-specific context
4499
- 5. Use \`search(mode="hybrid")\` for code discovery (not Glob/Grep/Read)
4508
+ 5. Use \`search(mode="auto")\` for code discovery (not Glob/Grep/Read)
4500
4509
 
4501
4510
  `);
4502
4511
  }
@@ -4537,7 +4546,7 @@ function buildEnhancedReminder(ctx, isNewSession2, versionNotice) {
4537
4546
  parts.push("");
4538
4547
  }
4539
4548
  parts.push("---\n");
4540
- parts.push(REMINDER);
4549
+ parts.push(FULL_REMINDER);
4541
4550
  parts.push(`
4542
4551
 
4543
4552
  ---
@@ -4554,7 +4563,7 @@ Returns: \`indexed\` (true/false), \`last_indexed_at\`, \`file_count\`
4554
4563
  ### \u{1F50D} Search Decision Tree:
4555
4564
 
4556
4565
  **IF indexed=true AND last_indexed_at is recent:**
4557
- \u2192 Use \`search(mode="hybrid", query="...")\`
4566
+ \u2192 Use \`search(mode="auto", query="...")\`
4558
4567
 
4559
4568
  **IF indexed=false OR last_indexed_at is stale (>7 days):**
4560
4569
  \u2192 Use local tools (Glob/Grep/Read) directly
@@ -4625,61 +4634,58 @@ async function runUserPromptSubmitHook() {
4625
4634
  }
4626
4635
  const editorFormat = detectEditorFormat2(input);
4627
4636
  const cwd = input.cwd || process.cwd();
4628
- loadConfigFromMcpJson(cwd);
4629
- const versionNoticePromise = getUpdateNotice();
4630
- const lastExchange = extractLastExchange(input, editorFormat);
4631
- const clientName = editorFormat === "claude" ? "claude-code" : editorFormat;
4632
- const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
4633
4637
  if (editorFormat === "claude") {
4634
- saveExchangePromise.catch(() => {
4635
- });
4636
- const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise]);
4637
- const claudeReminder = buildClaudeReminder(ctx, versionNotice);
4638
+ loadConfigFromMcpJson(cwd);
4639
+ let context = REMINDER;
4640
+ if (API_KEY2) {
4641
+ try {
4642
+ const hookContext = await fetchHookContext();
4643
+ if (hookContext) {
4644
+ context = hookContext;
4645
+ }
4646
+ } catch {
4647
+ }
4648
+ }
4638
4649
  console.log(
4639
4650
  JSON.stringify({
4640
4651
  hookSpecificOutput: {
4641
4652
  hookEventName: "UserPromptSubmit",
4642
- additionalContext: claudeReminder
4653
+ additionalContext: context
4643
4654
  }
4644
4655
  })
4645
4656
  );
4646
- } else if (editorFormat === "cline") {
4647
- const newSession = isNewSession(input, editorFormat);
4648
- const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
4649
- const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
4650
- console.log(
4651
- JSON.stringify({
4652
- cancel: false,
4653
- contextModification: enhancedReminder
4654
- })
4655
- );
4656
- } else if (editorFormat === "cursor") {
4657
+ } else {
4658
+ loadConfigFromMcpJson(cwd);
4659
+ const versionNoticePromise = getUpdateNotice();
4660
+ const lastExchange = extractLastExchange(input, editorFormat);
4661
+ const clientName = editorFormat;
4662
+ const saveExchangePromise = lastExchange ? saveLastExchange(lastExchange, cwd, clientName) : Promise.resolve();
4657
4663
  const newSession = isNewSession(input, editorFormat);
4658
4664
  const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
4659
- let cursorReminder = ctx?.lessons?.length ? `[CONTEXTSTREAM] \u26A0\uFE0F ${ctx.lessons.length} lessons from past mistakes. Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="hybrid") before Glob/Grep. After file edits: project(action="index").` : `[CONTEXTSTREAM] Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="hybrid") before Glob/Grep/Read. After file edits: project(action="index").`;
4660
- if (versionNotice?.behind) {
4661
- cursorReminder += ` [UPDATE v${versionNotice.current}\u2192${versionNotice.latest}]`;
4665
+ if (editorFormat === "cursor") {
4666
+ let cursorReminder = ctx?.lessons?.length ? `[CONTEXTSTREAM] \u26A0\uFE0F ${ctx.lessons.length} lessons from past mistakes. Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="auto") before Glob/Grep. After file edits: project(action="index").` : `[CONTEXTSTREAM] Call context(save_exchange=true, session_id="...") FIRST. Use search(mode="auto") before Glob/Grep/Read. After file edits: project(action="index").`;
4667
+ if (versionNotice?.behind) {
4668
+ cursorReminder += ` [UPDATE v${versionNotice.current}\u2192${versionNotice.latest}]`;
4669
+ }
4670
+ console.log(
4671
+ JSON.stringify({
4672
+ continue: true,
4673
+ user_message: cursorReminder
4674
+ })
4675
+ );
4676
+ } else {
4677
+ const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
4678
+ console.log(
4679
+ JSON.stringify({
4680
+ cancel: false,
4681
+ contextModification: enhancedReminder
4682
+ })
4683
+ );
4662
4684
  }
4663
- console.log(
4664
- JSON.stringify({
4665
- continue: true,
4666
- user_message: cursorReminder
4667
- })
4668
- );
4669
- } else if (editorFormat === "antigravity") {
4670
- const newSession = isNewSession(input, editorFormat);
4671
- const [ctx, versionNotice] = await Promise.all([fetchSessionContext(), versionNoticePromise, saveExchangePromise]);
4672
- const enhancedReminder = buildEnhancedReminder(ctx, newSession, versionNotice);
4673
- console.log(
4674
- JSON.stringify({
4675
- cancel: false,
4676
- contextModification: enhancedReminder
4677
- })
4678
- );
4679
4685
  }
4680
4686
  process.exit(0);
4681
4687
  }
4682
- var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun3;
4688
+ var ENABLED3, API_URL2, API_KEY2, WORKSPACE_ID, PROJECT_ID, REMINDER, FULL_REMINDER, ENHANCED_REMINDER_HEADER, isDirectRun3;
4683
4689
  var init_user_prompt_submit = __esm({
4684
4690
  "src/hooks/user-prompt-submit.ts"() {
4685
4691
  "use strict";
@@ -4689,7 +4695,9 @@ var init_user_prompt_submit = __esm({
4689
4695
  API_KEY2 = process.env.CONTEXTSTREAM_API_KEY || "";
4690
4696
  WORKSPACE_ID = null;
4691
4697
  PROJECT_ID = null;
4692
- REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
4698
+ REMINDER = `[CONTEXTSTREAM] Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") FIRST before any other tool. Response contains dynamic rules, lessons, preferences. For search: use search(mode="auto") if indexed, else local tools.
4699
+ [END]`;
4700
+ FULL_REMINDER = `[CONTEXTSTREAM RULES - MANDATORY]
4693
4701
 
4694
4702
  1. FIRST: Call mcp__contextstream__context(user_message="...", save_exchange=true, session_id="<session-id>") before ANY other tool
4695
4703
  - Returns: dynamic rules, lessons from past mistakes, relevant context
@@ -4699,7 +4707,7 @@ var init_user_prompt_submit = __esm({
4699
4707
 
4700
4708
  2. FOR CODE SEARCH: Check index status, then search appropriately
4701
4709
  \u26A0\uFE0F BEFORE searching: mcp__contextstream__project(action="index_status")
4702
- \u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="hybrid", query="...")
4710
+ \u2705 IF indexed & fresh: Use mcp__contextstream__search(mode="auto", query="...")
4703
4711
  \u2705 IF NOT indexed OR stale: Use local tools (Glob/Grep/Read) directly
4704
4712
  \u2705 IF search returns 0 results: Fallback to local tools (Glob/Grep/Read)
4705
4713
 
@@ -11311,7 +11319,16 @@ async function request(config, path22, options = {}) {
11311
11319
  const authOverride = getAuthOverride();
11312
11320
  const apiKey = authOverride?.apiKey ?? config.apiKey;
11313
11321
  const jwt = authOverride?.jwt ?? config.jwt;
11314
- const apiPath = path22.startsWith("/api/") ? path22 : `/api/v1${path22}`;
11322
+ const rawPath = path22.startsWith("/") ? path22 : `/${path22}`;
11323
+ const apiPath = rawPath.startsWith("/api/") ? rawPath : `/api/v1${rawPath}`;
11324
+ const unauthenticatedEndpoints = ["/api/v1/auth/device/start", "/api/v1/auth/device/token"];
11325
+ const isUnauthenticatedEndpoint = unauthenticatedEndpoints.some(
11326
+ (endpoint) => apiPath === endpoint || apiPath.startsWith(`${endpoint}?`)
11327
+ );
11328
+ if (!isUnauthenticatedEndpoint && !apiKey && !jwt) {
11329
+ const message = config.allowHeaderAuth ? "Missing authentication: CONTEXTSTREAM_ALLOW_HEADER_AUTH is enabled, but this request did not include auth credentials." : "Missing authentication: set CONTEXTSTREAM_API_KEY or CONTEXTSTREAM_JWT.";
11330
+ throw new HttpError(401, message);
11331
+ }
11315
11332
  const url = `${apiUrl.replace(/\/$/, "")}${apiPath}`;
11316
11333
  const maxRetries = options.retries ?? MAX_RETRIES;
11317
11334
  const baseDelay = options.retryDelay ?? BASE_DELAY;
@@ -12294,6 +12311,12 @@ var ContextStreamClient = class {
12294
12311
  this.indexRefreshInProgress = false;
12295
12312
  this.hasTriggeredAutoIndex = false;
12296
12313
  }
12314
+ hasEffectiveAuth() {
12315
+ const authOverride = getAuthOverride();
12316
+ const apiKey = authOverride?.apiKey ?? this.config.apiKey;
12317
+ const jwt = authOverride?.jwt ?? this.config.jwt;
12318
+ return Boolean(apiKey || jwt);
12319
+ }
12297
12320
  /**
12298
12321
  * Update the client's default workspace/project IDs at runtime.
12299
12322
  *
@@ -14731,7 +14754,7 @@ ${context2}`;
14731
14754
  let candidateContext;
14732
14755
  const CS_HEADER = "\u2B21 ContextStream \u2014 Smart Context & Memory";
14733
14756
  const CS_HEADER_MINIFIED = "CS:active";
14734
- const CS_FIRST_RULE = "\u26A0\uFE0F STOP: Before Glob/Grep/Read \u2192 search(mode=hybrid) FIRST. Local tools ONLY if 0 results.";
14757
+ const CS_FIRST_RULE = "\u26A0\uFE0F STOP: Before Glob/Grep/Read \u2192 search(mode=auto) FIRST. Local tools ONLY if 0 results.";
14735
14758
  const CS_FIRST_RULE_MINIFIED = "R:CS-first|Idx:project.index_status->ingest|NoLocalScanUnlessCSempty";
14736
14759
  if (format === "minified") {
14737
14760
  const parts = [CS_HEADER_MINIFIED, CS_FIRST_RULE_MINIFIED];
@@ -16541,6 +16564,17 @@ var TASK_HINTS = {
16541
16564
  };
16542
16565
 
16543
16566
  // src/tools.ts
16567
+ function parseBoolEnvDefault(raw, fallback) {
16568
+ if (raw === void 0) return fallback;
16569
+ const normalized = raw.trim().toLowerCase();
16570
+ if (normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on") {
16571
+ return true;
16572
+ }
16573
+ if (normalized === "0" || normalized === "false" || normalized === "no" || normalized === "off") {
16574
+ return false;
16575
+ }
16576
+ return fallback;
16577
+ }
16544
16578
  var LOG_LEVEL = (process.env.CONTEXTSTREAM_LOG_LEVEL || "normal").toLowerCase();
16545
16579
  var LOG_QUIET = LOG_LEVEL === "quiet";
16546
16580
  var LOG_VERBOSE = LOG_LEVEL === "verbose";
@@ -16604,7 +16638,7 @@ var lastAutoSaveTime = 0;
16604
16638
  var lastAutoSavePressureLevel = "";
16605
16639
  var contextCallsSinceLastSave = 0;
16606
16640
  var SEARCH_RULES_REMINDER_ENABLED = process.env.CONTEXTSTREAM_SEARCH_REMINDER?.toLowerCase() !== "false";
16607
- var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="hybrid") before Glob/Grep/Read. Local tools only if 0 results.`;
16641
+ var SEARCH_RULES_REMINDER = `[SEARCH] Use search(mode="auto") before Glob/Grep/Read. Local tools only if 0 results.`;
16608
16642
  var CONTEXT_CALL_REMINDER = `[CONTEXT] Call context(user_message="...") at start of EVERY response. This is MANDATORY.`;
16609
16643
  var LESSONS_REMINDER_PREFIX = `
16610
16644
  \u{1F6A8} [LESSONS_WARNING] Past Mistakes Found - READ BEFORE PROCEEDING!
@@ -17834,7 +17868,7 @@ function getBundleInfo() {
17834
17868
  core: "Essential session tools (always enabled)",
17835
17869
  session: "Extended session management and utilities",
17836
17870
  memory: "Full memory CRUD operations",
17837
- search: "Semantic, hybrid, and keyword search",
17871
+ search: "Auto, semantic, hybrid (legacy alias), and keyword search",
17838
17872
  graph: "Code graph analysis and dependencies",
17839
17873
  workspace: "Workspace management",
17840
17874
  project: "Project management and indexing",
@@ -17937,11 +17971,22 @@ function parsePositiveInt(raw, fallback) {
17937
17971
  var OUTPUT_FORMAT = process.env.CONTEXTSTREAM_OUTPUT_FORMAT || "compact";
17938
17972
  var COMPACT_OUTPUT = OUTPUT_FORMAT === "compact";
17939
17973
  var SHOW_TIMING = process.env.CONTEXTSTREAM_SHOW_TIMING === "true" || process.env.CONTEXTSTREAM_SHOW_TIMING === "1";
17974
+ var INCLUDE_STRUCTURED_CONTENT = parseBoolEnvDefault(
17975
+ process.env.CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT,
17976
+ true
17977
+ );
17940
17978
  var DEFAULT_SEARCH_LIMIT = parsePositiveInt(process.env.CONTEXTSTREAM_SEARCH_LIMIT, 3);
17941
17979
  var DEFAULT_SEARCH_CONTENT_MAX_CHARS = parsePositiveInt(
17942
17980
  process.env.CONTEXTSTREAM_SEARCH_MAX_CHARS,
17943
17981
  400
17944
17982
  );
17983
+ function maybeStripStructuredContent(result) {
17984
+ if (INCLUDE_STRUCTURED_CONTENT || !Object.prototype.hasOwnProperty.call(result, "structuredContent")) {
17985
+ return result;
17986
+ }
17987
+ const { structuredContent: _structuredContent, ...rest } = result;
17988
+ return rest;
17989
+ }
17945
17990
  var CONSOLIDATED_MODE = process.env.CONTEXTSTREAM_CONSOLIDATED !== "false";
17946
17991
  var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
17947
17992
  "init",
@@ -17971,6 +18016,38 @@ var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
17971
18016
  "help"
17972
18017
  // Consolidates session_tools, auth_me, mcp_server_version, etc.
17973
18018
  ]);
18019
+ function mapToolToConsolidatedDomain(toolName) {
18020
+ if (CONSOLIDATED_TOOLS.has(toolName)) return toolName;
18021
+ if (toolName === "session_init") return "init";
18022
+ if (toolName === "context_smart") return "context";
18023
+ if (toolName === "generate_rules" || toolName === "generate_editor_rules") return "generate_rules";
18024
+ if (toolName.startsWith("search_")) return "search";
18025
+ if (toolName.startsWith("session_") || toolName === "context_feedback") return "session";
18026
+ if (toolName.startsWith("memory_") || toolName === "decision_trace") return "memory";
18027
+ if (toolName.startsWith("graph_")) return "graph";
18028
+ if (toolName.startsWith("projects_")) return "project";
18029
+ if (toolName.startsWith("workspaces_") || toolName.startsWith("workspace_")) return "workspace";
18030
+ if (toolName.startsWith("reminders_")) return "reminder";
18031
+ if (toolName.startsWith("slack_") || toolName.startsWith("github_") || toolName.startsWith("integrations_") || toolName.startsWith("notion_")) {
18032
+ return "integration";
18033
+ }
18034
+ if (toolName.startsWith("media_")) return "media";
18035
+ if (toolName === "session_tools" || toolName === "auth_me" || toolName === "mcp_server_version" || toolName === "tools_enable_bundle") {
18036
+ return "help";
18037
+ }
18038
+ return null;
18039
+ }
18040
+ function resolveConsolidatedAllowlist(allowlist) {
18041
+ if (!allowlist) return null;
18042
+ const mapped = /* @__PURE__ */ new Set();
18043
+ for (const toolName of allowlist) {
18044
+ const consolidated = mapToolToConsolidatedDomain(toolName);
18045
+ if (consolidated) {
18046
+ mapped.add(consolidated);
18047
+ }
18048
+ }
18049
+ return mapped;
18050
+ }
17974
18051
  var TOOLSET_ALIASES = {
17975
18052
  // Light mode - minimal, fastest
17976
18053
  light: LIGHT_TOOLSET,
@@ -18141,10 +18218,15 @@ function setupClientDetection(server) {
18141
18218
  function registerTools(server, client, sessionManager) {
18142
18219
  const upgradeUrl2 = process.env.CONTEXTSTREAM_UPGRADE_URL || "https://contextstream.io/pricing";
18143
18220
  const toolFilter = resolveToolFilter();
18144
- const toolAllowlist = toolFilter.allowlist;
18221
+ const rawToolAllowlist = toolFilter.allowlist;
18222
+ const toolAllowlist = CONSOLIDATED_MODE ? resolveConsolidatedAllowlist(rawToolAllowlist) : rawToolAllowlist;
18145
18223
  if (toolAllowlist) {
18146
18224
  const source = toolFilter.source;
18147
- logDebug(`Toolset: ${source} (${toolAllowlist.size} tools)`);
18225
+ if (CONSOLIDATED_MODE) {
18226
+ logDebug(`Toolset: ${source} (${toolAllowlist.size} consolidated domain tools)`);
18227
+ } else {
18228
+ logDebug(`Toolset: ${source} (${toolAllowlist.size} tools)`);
18229
+ }
18148
18230
  } else {
18149
18231
  logDebug("Toolset: complete (all tools)");
18150
18232
  }
@@ -18541,7 +18623,8 @@ function registerTools(server, client, sessionManager) {
18541
18623
  if (proGated) return proGated;
18542
18624
  const integrationGated = await gateIfIntegrationTool(name);
18543
18625
  if (integrationGated) return integrationGated;
18544
- return await handler(input, extra);
18626
+ const result = await handler(input, extra);
18627
+ return maybeStripStructuredContent(result);
18545
18628
  } catch (error) {
18546
18629
  const errorMessage = error?.message || String(error);
18547
18630
  const errorDetails = error?.body || error?.details || null;
@@ -18561,11 +18644,11 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
18561
18644
  }
18562
18645
  };
18563
18646
  const errorText = `[${errorCode}] ${errorMessage}${upgradeHint}${sessionHint}${errorDetails ? `: ${JSON.stringify(errorDetails)}` : ""}`;
18564
- return {
18647
+ return maybeStripStructuredContent({
18565
18648
  content: [{ type: "text", text: errorText }],
18566
18649
  structuredContent: errorPayload,
18567
18650
  isError: true
18568
- };
18651
+ });
18569
18652
  }
18570
18653
  };
18571
18654
  serverRef.registerTool(name, annotatedConfig, wrapWithAutoContext(name, safeHandler));
@@ -18622,7 +18705,7 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
18622
18705
  });
18623
18706
  return;
18624
18707
  }
18625
- if (!CONSOLIDATED_MODE && toolAllowlist && !toolAllowlist.has(name)) {
18708
+ if (toolAllowlist && !toolAllowlist.has(name)) {
18626
18709
  if (ROUTER_MODE && !ROUTER_DIRECT_TOOLS.has(name)) {
18627
18710
  operationsRegistry.set(name, {
18628
18711
  name,
@@ -18771,7 +18854,7 @@ Hint: Run session_init(folder_path="<your_project_path>") first to establish a s
18771
18854
  Available bundles:
18772
18855
  - session: Extended session management (~6 tools)
18773
18856
  - memory: Full memory CRUD operations (~16 tools)
18774
- - search: Semantic, hybrid, and keyword search (~3 tools)
18857
+ - search: Auto, semantic, hybrid (legacy alias), and keyword search (~3 tools)
18775
18858
  - graph: Code graph analysis and dependencies (~9 tools)
18776
18859
  - workspace: Workspace management (~4 tools)
18777
18860
  - project: Project management and indexing (~10 tools)
@@ -19226,10 +19309,18 @@ Access: Free`,
19226
19309
  output_format: input.output_format
19227
19310
  };
19228
19311
  }
19312
+ function getSearchAuthError() {
19313
+ if (client.hasEffectiveAuth()) return null;
19314
+ return errorResult(
19315
+ "Authentication required for search. Set CONTEXTSTREAM_API_KEY or CONTEXTSTREAM_JWT, or pass auth credentials from your MCP client when CONTEXTSTREAM_ALLOW_HEADER_AUTH=true."
19316
+ );
19317
+ }
19229
19318
  registerTool(
19230
19319
  "search_semantic",
19231
19320
  { title: "Semantic search", description: "Semantic vector search", inputSchema: searchSchema },
19232
19321
  async (input) => {
19322
+ const authError = getSearchAuthError();
19323
+ if (authError) return authError;
19233
19324
  const result = await client.searchSemantic(normalizeSearchParams(input));
19234
19325
  return {
19235
19326
  content: [{ type: "text", text: formatContent(result) }]
@@ -19244,6 +19335,8 @@ Access: Free`,
19244
19335
  inputSchema: searchSchema
19245
19336
  },
19246
19337
  async (input) => {
19338
+ const authError = getSearchAuthError();
19339
+ if (authError) return authError;
19247
19340
  const result = await client.searchHybrid(normalizeSearchParams(input));
19248
19341
  return {
19249
19342
  content: [{ type: "text", text: formatContent(result) }]
@@ -19254,6 +19347,8 @@ Access: Free`,
19254
19347
  "search_keyword",
19255
19348
  { title: "Keyword search", description: "Keyword search", inputSchema: searchSchema },
19256
19349
  async (input) => {
19350
+ const authError = getSearchAuthError();
19351
+ if (authError) return authError;
19257
19352
  const result = await client.searchKeyword(normalizeSearchParams(input));
19258
19353
  return {
19259
19354
  content: [{ type: "text", text: formatContent(result) }]
@@ -19264,6 +19359,8 @@ Access: Free`,
19264
19359
  "search_pattern",
19265
19360
  { title: "Pattern search", description: "Pattern/regex search", inputSchema: searchSchema },
19266
19361
  async (input) => {
19362
+ const authError = getSearchAuthError();
19363
+ if (authError) return authError;
19267
19364
  const result = await client.searchPattern(normalizeSearchParams(input));
19268
19365
  return {
19269
19366
  content: [{ type: "text", text: formatContent(result) }]
@@ -20557,7 +20654,7 @@ Format options:
20557
20654
 
20558
20655
  Example output (grouped):
20559
20656
  Session: init(start-conv) smart(each-msg) capture(save) recall(find) remember(quick)
20560
- Search: semantic(meaning) hybrid(combo) keyword(exact)
20657
+ Search: auto(default) semantic(meaning) hybrid(alias) keyword(exact)
20561
20658
  Memory: events(crud) nodes(knowledge) search(find) decisions(choices)`,
20562
20659
  inputSchema: external_exports.object({
20563
20660
  format: external_exports.enum(["grouped", "minimal", "full"]).optional().default("grouped").describe(
@@ -23160,11 +23257,11 @@ Use this to remove a reminder that is no longer relevant.`,
23160
23257
  "search",
23161
23258
  {
23162
23259
  title: "Search",
23163
- description: `Search workspace memory and knowledge. Modes: semantic (meaning-based), hybrid (semantic + keyword), keyword (exact match), pattern (regex), exhaustive (all matches like grep), refactor (word-boundary matching for symbol renaming), team (cross-project team search - team plans only).
23260
+ description: `Search workspace memory and knowledge. Modes: auto (recommended), semantic (meaning-based), hybrid (legacy alias for auto), keyword (exact match), pattern (regex), exhaustive (all matches like grep), refactor (word-boundary matching for symbol renaming), team (cross-project team search - team plans only).
23164
23261
 
23165
23262
  Output formats: full (default, includes content), paths (file paths only - 80% token savings), minimal (compact - 60% savings), count (match counts only - 90% savings).`,
23166
23263
  inputSchema: external_exports.object({
23167
- mode: external_exports.enum(["semantic", "hybrid", "keyword", "pattern", "exhaustive", "refactor", "team"]).describe("Search mode"),
23264
+ mode: external_exports.enum(["auto", "semantic", "hybrid", "keyword", "pattern", "exhaustive", "refactor", "team"]).optional().default("auto").describe("Search mode (auto recommended; hybrid is a backward-compatible alias)"),
23168
23265
  query: external_exports.string().describe("Search query"),
23169
23266
  workspace_id: external_exports.string().uuid().optional(),
23170
23267
  project_id: external_exports.string().uuid().optional(),
@@ -23179,21 +23276,25 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23179
23276
  })
23180
23277
  },
23181
23278
  async (input) => {
23279
+ const authError = getSearchAuthError();
23280
+ if (authError) return authError;
23182
23281
  client.checkAndIndexChangedFiles().catch(() => {
23183
23282
  });
23184
23283
  const params = normalizeSearchParams(input);
23185
23284
  const startTime = Date.now();
23285
+ const requestedMode = input.mode || "auto";
23186
23286
  let result;
23187
23287
  let toolType;
23188
- switch (input.mode) {
23189
- case "semantic":
23190
- result = await client.searchSemantic(params);
23191
- toolType = "search_semantic";
23192
- break;
23288
+ switch (requestedMode) {
23289
+ case "auto":
23193
23290
  case "hybrid":
23194
23291
  result = await client.searchHybrid(params);
23195
23292
  toolType = "search_hybrid";
23196
23293
  break;
23294
+ case "semantic":
23295
+ result = await client.searchSemantic(params);
23296
+ toolType = "search_semantic";
23297
+ break;
23197
23298
  case "keyword":
23198
23299
  result = await client.searchKeyword(params);
23199
23300
  toolType = "search_keyword";
@@ -23257,6 +23358,7 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
23257
23358
  break;
23258
23359
  }
23259
23360
  default:
23361
+ result = await client.searchHybrid(params);
23260
23362
  toolType = "search_hybrid";
23261
23363
  }
23262
23364
  const roundTripMs = Date.now() - startTime;
@@ -29098,21 +29200,49 @@ async function isEditorInstalled(editor) {
29098
29200
  }
29099
29201
  }
29100
29202
  var IS_WINDOWS = process.platform === "win32";
29101
- function buildContextStreamMcpServer(params) {
29102
- const env = {
29203
+ function escapeTomlString(value) {
29204
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
29205
+ }
29206
+ function formatTomlEnvLines(env) {
29207
+ return Object.entries(env).map(([key, value]) => `${key} = "${escapeTomlString(value)}"`).join("\n");
29208
+ }
29209
+ function buildSetupEnv(params) {
29210
+ const contextPack = params.contextPackEnabled === false ? "false" : "true";
29211
+ const progressive = params.toolset === "router" ? "true" : "false";
29212
+ const restoreContext = params.restoreContextEnabled === false ? "false" : "true";
29213
+ const showTiming = params.showTiming ? "true" : "false";
29214
+ return {
29103
29215
  CONTEXTSTREAM_API_URL: params.apiUrl,
29104
- CONTEXTSTREAM_API_KEY: params.apiKey
29216
+ CONTEXTSTREAM_API_KEY: params.apiKey,
29217
+ CONTEXTSTREAM_JWT: "",
29218
+ CONTEXTSTREAM_ALLOW_HEADER_AUTH: "false",
29219
+ CONTEXTSTREAM_WORKSPACE_ID: "",
29220
+ CONTEXTSTREAM_PROJECT_ID: "",
29221
+ CONTEXTSTREAM_USER_AGENT: `contextstream-mcp/${VERSION}`,
29222
+ CONTEXTSTREAM_TOOLSET: "standard",
29223
+ CONTEXTSTREAM_TOOL_ALLOWLIST: "",
29224
+ CONTEXTSTREAM_AUTO_TOOLSET: "false",
29225
+ CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS: "true",
29226
+ CONTEXTSTREAM_SCHEMA_MODE: "full",
29227
+ CONTEXTSTREAM_PROGRESSIVE_MODE: progressive,
29228
+ CONTEXTSTREAM_ROUTER_MODE: "false",
29229
+ CONTEXTSTREAM_OUTPUT_FORMAT: "compact",
29230
+ CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT: "true",
29231
+ CONTEXTSTREAM_SEARCH_LIMIT: "3",
29232
+ CONTEXTSTREAM_SEARCH_MAX_CHARS: "400",
29233
+ CONTEXTSTREAM_CONSOLIDATED: "true",
29234
+ CONTEXTSTREAM_CONTEXT_PACK: contextPack,
29235
+ CONTEXTSTREAM_CONTEXT_PACK_ENABLED: contextPack,
29236
+ CONTEXTSTREAM_RESTORE_CONTEXT: restoreContext,
29237
+ CONTEXTSTREAM_SHOW_TIMING: showTiming,
29238
+ CONTEXTSTREAM_PRO_TOOLS: "",
29239
+ CONTEXTSTREAM_UPGRADE_URL: "https://contextstream.io/pricing",
29240
+ CONTEXTSTREAM_ENABLE_PROMPTS: "true",
29241
+ CONTEXTSTREAM_LOG_LEVEL: "normal"
29105
29242
  };
29106
- if (params.toolset === "router") {
29107
- env.CONTEXTSTREAM_PROGRESSIVE_MODE = "true";
29108
- }
29109
- env.CONTEXTSTREAM_CONTEXT_PACK = params.contextPackEnabled === false ? "false" : "true";
29110
- if (params.restoreContextEnabled === false) {
29111
- env.CONTEXTSTREAM_RESTORE_CONTEXT = "false";
29112
- }
29113
- if (params.showTiming) {
29114
- env.CONTEXTSTREAM_SHOW_TIMING = "true";
29115
- }
29243
+ }
29244
+ function buildContextStreamMcpServer(params) {
29245
+ const env = buildSetupEnv(params);
29116
29246
  if (IS_WINDOWS) {
29117
29247
  return {
29118
29248
  command: "cmd",
@@ -29127,20 +29257,7 @@ function buildContextStreamMcpServer(params) {
29127
29257
  };
29128
29258
  }
29129
29259
  function buildContextStreamVsCodeServer(params) {
29130
- const env = {
29131
- CONTEXTSTREAM_API_URL: params.apiUrl,
29132
- CONTEXTSTREAM_API_KEY: params.apiKey
29133
- };
29134
- if (params.toolset === "router") {
29135
- env.CONTEXTSTREAM_PROGRESSIVE_MODE = "true";
29136
- }
29137
- env.CONTEXTSTREAM_CONTEXT_PACK = params.contextPackEnabled === false ? "false" : "true";
29138
- if (params.restoreContextEnabled === false) {
29139
- env.CONTEXTSTREAM_RESTORE_CONTEXT = "false";
29140
- }
29141
- if (params.showTiming) {
29142
- env.CONTEXTSTREAM_SHOW_TIMING = "true";
29143
- }
29260
+ const env = buildSetupEnv(params);
29144
29261
  if (IS_WINDOWS) {
29145
29262
  return {
29146
29263
  type: "stdio",
@@ -29238,16 +29355,10 @@ async function upsertCodexTomlConfig(filePath, params) {
29238
29355
  await fs7.mkdir(path8.dirname(filePath), { recursive: true });
29239
29356
  const exists = await fileExists(filePath);
29240
29357
  const existing = exists ? await fs7.readFile(filePath, "utf8").catch(() => "") : "";
29358
+ const env = buildSetupEnv(params);
29359
+ const envLines = formatTomlEnvLines(env);
29241
29360
  const marker = "[mcp_servers.contextstream]";
29242
29361
  const envMarker = "[mcp_servers.contextstream.env]";
29243
- const toolsetLine = params.toolset === "router" ? `CONTEXTSTREAM_PROGRESSIVE_MODE = "true"
29244
- ` : "";
29245
- const contextPackLine = `CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"
29246
- `;
29247
- const restoreContextLine = params.restoreContextEnabled === false ? `CONTEXTSTREAM_RESTORE_CONTEXT = "false"
29248
- ` : "";
29249
- const showTimingLine = params.showTiming ? `CONTEXTSTREAM_SHOW_TIMING = "true"
29250
- ` : "";
29251
29362
  const commandLine = IS_WINDOWS ? `command = "cmd"
29252
29363
  args = ["/c", "npx", "--prefer-online", "-y", "@contextstream/mcp-server@latest"]
29253
29364
  ` : `command = "npx"
@@ -29259,9 +29370,7 @@ args = ["--prefer-online", "-y", "@contextstream/mcp-server@latest"]
29259
29370
  [mcp_servers.contextstream]
29260
29371
  ` + commandLine + `
29261
29372
  [mcp_servers.contextstream.env]
29262
- CONTEXTSTREAM_API_URL = "${params.apiUrl}"
29263
- CONTEXTSTREAM_API_KEY = "${params.apiKey}"
29264
- ` + toolsetLine + contextPackLine + restoreContextLine + showTimingLine;
29373
+ ` + envLines + "\n";
29265
29374
  if (!exists) {
29266
29375
  await fs7.writeFile(filePath, block.trimStart(), "utf8");
29267
29376
  return "created";
@@ -29273,10 +29382,7 @@ CONTEXTSTREAM_API_KEY = "${params.apiKey}"
29273
29382
  if (!existing.includes(envMarker)) {
29274
29383
  await fs7.writeFile(
29275
29384
  filePath,
29276
- existing.trimEnd() + "\n\n" + envMarker + `
29277
- CONTEXTSTREAM_API_URL = "${params.apiUrl}"
29278
- CONTEXTSTREAM_API_KEY = "${params.apiKey}"
29279
- ` + toolsetLine + contextPackLine,
29385
+ existing.trimEnd() + "\n\n" + envMarker + "\n" + envLines + "\n",
29280
29386
  "utf8"
29281
29387
  );
29282
29388
  return "updated";
@@ -29284,51 +29390,43 @@ CONTEXTSTREAM_API_KEY = "${params.apiKey}"
29284
29390
  const lines = existing.split(/\r?\n/);
29285
29391
  const out = [];
29286
29392
  let inEnv = false;
29287
- let sawUrl = false;
29288
- let sawKey = false;
29289
- let sawContextPack = false;
29393
+ const seen = /* @__PURE__ */ new Set();
29394
+ const managedKeys = new Set(Object.keys(env));
29290
29395
  for (const line of lines) {
29291
29396
  const trimmed = line.trim();
29292
29397
  if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
29293
29398
  if (inEnv && trimmed !== envMarker) {
29294
- if (!sawUrl) out.push(`CONTEXTSTREAM_API_URL = "${params.apiUrl}"`);
29295
- if (!sawKey) out.push(`CONTEXTSTREAM_API_KEY = "${params.apiKey}"`);
29296
- if (!sawContextPack)
29297
- out.push(
29298
- `CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"`
29299
- );
29399
+ for (const [key, value] of Object.entries(env)) {
29400
+ if (!seen.has(key)) {
29401
+ out.push(`${key} = "${escapeTomlString(value)}"`);
29402
+ }
29403
+ }
29300
29404
  inEnv = false;
29301
29405
  }
29302
- if (trimmed === envMarker) inEnv = true;
29406
+ if (trimmed === envMarker) {
29407
+ inEnv = true;
29408
+ seen.clear();
29409
+ }
29303
29410
  out.push(line);
29304
29411
  continue;
29305
29412
  }
29306
- if (inEnv && /^\s*CONTEXTSTREAM_API_URL\s*=/.test(line)) {
29307
- out.push(`CONTEXTSTREAM_API_URL = "${params.apiUrl}"`);
29308
- sawUrl = true;
29309
- continue;
29310
- }
29311
- if (inEnv && /^\s*CONTEXTSTREAM_API_KEY\s*=/.test(line)) {
29312
- out.push(`CONTEXTSTREAM_API_KEY = "${params.apiKey}"`);
29313
- sawKey = true;
29314
- continue;
29315
- }
29316
- if (inEnv && /^\s*CONTEXTSTREAM_CONTEXT_PACK\s*=/.test(line)) {
29317
- out.push(
29318
- `CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"`
29319
- );
29320
- sawContextPack = true;
29321
- continue;
29413
+ if (inEnv) {
29414
+ const match = line.match(/^\s*([A-Za-z0-9_]+)\s*=/);
29415
+ if (match && managedKeys.has(match[1])) {
29416
+ const key = match[1];
29417
+ out.push(`${key} = "${escapeTomlString(env[key])}"`);
29418
+ seen.add(key);
29419
+ continue;
29420
+ }
29322
29421
  }
29323
29422
  out.push(line);
29324
29423
  }
29325
29424
  if (inEnv) {
29326
- if (!sawUrl) out.push(`CONTEXTSTREAM_API_URL = "${params.apiUrl}"`);
29327
- if (!sawKey) out.push(`CONTEXTSTREAM_API_KEY = "${params.apiKey}"`);
29328
- if (!sawContextPack)
29329
- out.push(
29330
- `CONTEXTSTREAM_CONTEXT_PACK = "${params.contextPackEnabled === false ? "false" : "true"}"`
29331
- );
29425
+ for (const [key, value] of Object.entries(env)) {
29426
+ if (!seen.has(key)) {
29427
+ out.push(`${key} = "${escapeTomlString(value)}"`);
29428
+ }
29429
+ }
29332
29430
  }
29333
29431
  const updated = out.join("\n");
29334
29432
  if (updated === existing) return "skipped";
@@ -30371,6 +30469,7 @@ Environment variables:
30371
30469
  CONTEXTSTREAM_PROGRESSIVE_MODE Progressive disclosure: true|false (default: false, starts with ~13 core tools)
30372
30470
  CONTEXTSTREAM_ROUTER_MODE Router pattern: true|false (default: false, exposes only 2 meta-tools)
30373
30471
  CONTEXTSTREAM_OUTPUT_FORMAT Output verbosity: compact|pretty (default: compact, ~30% fewer tokens)
30472
+ CONTEXTSTREAM_INCLUDE_STRUCTURED_CONTENT Include structured JSON payloads in tool results: true|false (default: true)
30374
30473
  CONTEXTSTREAM_SEARCH_LIMIT Default MCP search limit (default: 3)
30375
30474
  CONTEXTSTREAM_SEARCH_MAX_CHARS Max chars per search result content (default: 400)
30376
30475
  CONTEXTSTREAM_CONSOLIDATED Consolidated domain tools: true|false (default: true in v0.4.x, ~75% token reduction)