@contextstream/mcp-server 0.4.20 → 0.4.22
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 +6 -4
- package/dist/index.js +204 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -289,7 +289,7 @@ In consolidated mode, you call **domain tools** with `action` / `mode`:
|
|
|
289
289
|
### Examples
|
|
290
290
|
|
|
291
291
|
```
|
|
292
|
-
search(mode="semantic", query="auth middleware")
|
|
292
|
+
search(mode="semantic", query="auth middleware", limit=3)
|
|
293
293
|
memory(action="create_node", node_type="decision", title="Auth strategy", content="...")
|
|
294
294
|
graph(action="impact", target="UserService")
|
|
295
295
|
```
|
|
@@ -327,9 +327,11 @@ Set **one** of:
|
|
|
327
327
|
| `CONTEXTSTREAM_PROGRESSIVE_MODE` | Enables Router mode (~2 meta-tools) |
|
|
328
328
|
| `CONTEXTSTREAM_CONTEXT_PACK` | Enable Context Pack for `context_smart` (code + graph + distill). Defaults to `true` |
|
|
329
329
|
| `CONTEXTSTREAM_TOOLSET` | Legacy granular tool bundle: `light` / `standard` / `complete` (only when consolidated is off) |
|
|
330
|
-
| `CONTEXTSTREAM_TOOL_ALLOWLIST` | Comma-separated tool names to expose (legacy granular mode) |
|
|
331
|
-
| `CONTEXTSTREAM_SCHEMA_MODE` | Reduce schema verbosity; e.g., `compact` |
|
|
332
|
-
| `CONTEXTSTREAM_OUTPUT_FORMAT` | Output formatting; e.g., `compact` / `pretty` |
|
|
330
|
+
| `CONTEXTSTREAM_TOOL_ALLOWLIST` | Comma-separated tool names to expose (legacy granular mode) |
|
|
331
|
+
| `CONTEXTSTREAM_SCHEMA_MODE` | Reduce schema verbosity; e.g., `compact` |
|
|
332
|
+
| `CONTEXTSTREAM_OUTPUT_FORMAT` | Output formatting; e.g., `compact` / `pretty` |
|
|
333
|
+
| `CONTEXTSTREAM_SEARCH_LIMIT` | Default MCP search limit (default: 3) |
|
|
334
|
+
| `CONTEXTSTREAM_SEARCH_MAX_CHARS` | Max chars per search result content (default: 400) |
|
|
333
335
|
|
|
334
336
|
### Optional Defaults
|
|
335
337
|
|
package/dist/index.js
CHANGED
|
@@ -7482,6 +7482,7 @@ var CONTEXTSTREAM_TOOL_NAMES = [
|
|
|
7482
7482
|
"session_init",
|
|
7483
7483
|
"context_smart",
|
|
7484
7484
|
"context_feedback",
|
|
7485
|
+
"generate_rules",
|
|
7485
7486
|
// Consolidated domain tools (v0.4.x default)
|
|
7486
7487
|
"search",
|
|
7487
7488
|
// Modes: semantic, hybrid, keyword, pattern
|
|
@@ -7518,7 +7519,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
7518
7519
|
|
|
7519
7520
|
| Message | What to Call |
|
|
7520
7521
|
|---------|--------------|
|
|
7521
|
-
| **1st message** | \`session_init(folder_path="...", context_hint="<user's message>")\` |
|
|
7522
|
+
| **1st message** | \`session_init(folder_path="...", context_hint="<user's message>")\`, then \`context_smart(user_message="<user's message>", format="minified", max_tokens=400)\` |
|
|
7522
7523
|
| **2nd+ messages** | \`context_smart(user_message="<user's message>", format="minified", max_tokens=400)\` |
|
|
7523
7524
|
| **Before risky/non-trivial work** | \`session(action="get_lessons", query="<topic>")\` |
|
|
7524
7525
|
| **After completing task** | \`session(action="capture", event_type="decision", ...)\` - MUST capture |
|
|
@@ -7527,6 +7528,8 @@ Rules Version: ${RULES_VERSION}
|
|
|
7527
7528
|
|
|
7528
7529
|
**NO EXCEPTIONS.** Do not skip even if you think you have enough context.
|
|
7529
7530
|
|
|
7531
|
+
**First message rule:** After \`session_init\`, always call \`context_smart\` before any other tool or response.
|
|
7532
|
+
|
|
7530
7533
|
**Context Pack (Pro+):** If enabled, use \`context_smart(..., mode="pack", distill=true)\` for code/file queries. If unavailable or disabled, omit \`mode\` and proceed with standard \`context_smart\` (the API will fall back).
|
|
7531
7534
|
|
|
7532
7535
|
**Tool naming:** Use the exact tool names exposed by your MCP client. Claude Code typically uses \`mcp__<server>__<tool>\` where \`<server>\` matches your MCP config (often \`contextstream\`). If a tool call fails with "No such tool available", refresh rules and match the tool list.
|
|
@@ -7539,13 +7542,13 @@ v0.4.x consolidates ~58 individual tools into ~11 domain tools with action/mode
|
|
|
7539
7542
|
|
|
7540
7543
|
### Standalone Tools (Always Call)
|
|
7541
7544
|
- **\`session_init\`** - Initialize session with workspace detection + context
|
|
7542
|
-
- **\`context_smart\`** - Semantic search for relevant context (CALL EVERY MESSAGE)
|
|
7545
|
+
- **\`context_smart\`** - Semantic search for relevant context (CALL EVERY MESSAGE, including immediately after \`session_init\`)
|
|
7543
7546
|
|
|
7544
7547
|
### Domain Tools (Use action/mode parameter)
|
|
7545
7548
|
|
|
7546
7549
|
| Domain | Actions/Modes | Example |
|
|
7547
7550
|
|--------|---------------|---------|
|
|
7548
|
-
| **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="hybrid", query="auth implementation")\` |
|
|
7551
|
+
| **\`search\`** | mode: semantic, hybrid, keyword, pattern | \`search(mode="hybrid", query="auth implementation", limit=3)\` |
|
|
7549
7552
|
| **\`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="...")\` |
|
|
7550
7553
|
| **\`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)\` |
|
|
7551
7554
|
| **\`graph\`** | action: dependencies, impact, call_path, related, path, decisions, ingest, circular_dependencies, unused_code, contradictions | \`graph(action="impact", symbol_name="AuthService")\` |
|
|
@@ -7588,7 +7591,7 @@ If context still feels missing, use \`session(action="recall", query="...")\` fo
|
|
|
7588
7591
|
|
|
7589
7592
|
### Rules Update Notices
|
|
7590
7593
|
|
|
7591
|
-
- If you see **[RULES_NOTICE]**, update rules via \`
|
|
7594
|
+
- If you see **[RULES_NOTICE]**, update rules via \`generate_rules()\` (or rerun setup).
|
|
7592
7595
|
- If you see **[VERSION_NOTICE]**, tell the user to update MCP using the provided command.
|
|
7593
7596
|
|
|
7594
7597
|
---
|
|
@@ -7618,11 +7621,13 @@ Only after this preflight, proceed with search/analysis below.
|
|
|
7618
7621
|
|
|
7619
7622
|
**Search order:**
|
|
7620
7623
|
1. \`session(action="smart_search", query="...")\` - context-enriched
|
|
7621
|
-
2. \`search(mode="hybrid", query="...")\` or \`search(mode="keyword", query="<filename>")\`
|
|
7624
|
+
2. \`search(mode="hybrid", query="...", limit=3)\` or \`search(mode="keyword", query="<filename>", limit=3)\`
|
|
7622
7625
|
3. \`project(action="files")\` - file tree/list (only when needed)
|
|
7623
7626
|
4. \`graph(action="dependencies", ...)\` - code structure
|
|
7624
7627
|
5. Local repo scans (rg/ls/find) - only if ContextStream returns no results, errors, or the user explicitly asks
|
|
7625
7628
|
|
|
7629
|
+
**Search defaults:** \`search\` returns the top 3 results with compact snippets. Use \`limit\` + \`offset\` for pagination, and \`content_max_chars\` to expand snippets when needed.
|
|
7630
|
+
|
|
7626
7631
|
If ContextStream returns results, stop and use them. Do NOT use local Search/Explore/Read unless you need exact code edits.
|
|
7627
7632
|
|
|
7628
7633
|
**Code Analysis:**
|
|
@@ -7709,7 +7714,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
7709
7714
|
|
|
7710
7715
|
| Message | What to Call |
|
|
7711
7716
|
|---------|--------------|
|
|
7712
|
-
| **1st message** | \`session_init(folder_path="<cwd>", context_hint="<user_message>")\` |
|
|
7717
|
+
| **1st message** | \`session_init(folder_path="<cwd>", context_hint="<user_message>")\`, then \`context_smart(user_message="<user_message>", format="minified", max_tokens=400)\` |
|
|
7713
7718
|
| **2nd+ messages** | \`context_smart(user_message="<user_message>", format="minified", max_tokens=400)\` |
|
|
7714
7719
|
| **Capture decisions** | \`session(action="capture", event_type="decision", title="...", content="...")\` |
|
|
7715
7720
|
| **Before risky work** | \`session(action="get_lessons", query="<topic>")\` |
|
|
@@ -7723,7 +7728,7 @@ Rules Version: ${RULES_VERSION}
|
|
|
7723
7728
|
|
|
7724
7729
|
| Tool | Common Usage |
|
|
7725
7730
|
|------|--------------|
|
|
7726
|
-
| \`search\` | \`search(mode="semantic", query="...")\` \u2014 modes: semantic, hybrid, keyword, pattern |
|
|
7731
|
+
| \`search\` | \`search(mode="semantic", query="...", limit=3)\` \u2014 modes: semantic, hybrid, keyword, pattern |
|
|
7727
7732
|
| \`session\` | \`session(action="capture", ...)\` \u2014 actions: capture, capture_lesson, get_lessons, recall, remember, user_context, summary, compress, delta, smart_search |
|
|
7728
7733
|
| \`memory\` | \`memory(action="list_events", ...)\` \u2014 CRUD for events/nodes, search, decisions, timeline, summary |
|
|
7729
7734
|
| \`graph\` | \`graph(action="dependencies", ...)\` \u2014 dependencies, impact, call_path, related, ingest |
|
|
@@ -7734,14 +7739,14 @@ Rules Version: ${RULES_VERSION}
|
|
|
7734
7739
|
|
|
7735
7740
|
### Behavior Rules
|
|
7736
7741
|
|
|
7737
|
-
- **First message**:
|
|
7742
|
+
- **First message**: Call \`session_init\` with context_hint, then call \`context_smart\` before any other tool or response
|
|
7738
7743
|
- **Every message after**: Always call \`context_smart\` BEFORE responding (semantic search for relevant context)
|
|
7739
7744
|
- **Before searching files/code**: Check \`project(action="index_status")\`; if missing/stale run \`project(action="ingest_local", path="<cwd>")\` or \`project(action="index")\`, and use \`graph(action="ingest")\` if needed
|
|
7740
7745
|
- **For discovery**: Use \`session(action="smart_search")\` or \`search(mode="hybrid")\` before any local repo scans
|
|
7741
7746
|
- **For file/function/config lookups**: Use \`search\`/\`graph\` first; only fall back to rg/ls/find if ContextStream returns no results
|
|
7742
7747
|
- **If ContextStream returns results**: Do NOT use local Search/Explore/Read; only open specific files when needed for exact edits
|
|
7743
7748
|
- **For code analysis**: Use \`graph(action="dependencies")\` or \`graph(action="impact")\` for call/dependency analysis
|
|
7744
|
-
- **On [RULES_NOTICE]**: Use \`
|
|
7749
|
+
- **On [RULES_NOTICE]**: Use \`generate_rules()\` to update rules
|
|
7745
7750
|
- **After completing work**: Always capture decisions/insights with \`session(action="capture")\`
|
|
7746
7751
|
- **On mistakes/corrections**: Immediately capture lessons with \`session(action="capture_lesson")\`
|
|
7747
7752
|
|
|
@@ -8174,12 +8179,12 @@ function getRulesNotice(folderPath, clientName) {
|
|
|
8174
8179
|
const candidates = resolveRulesCandidatePaths(folderPath, editorKey);
|
|
8175
8180
|
const existing = candidates.filter((filePath) => fs3.existsSync(filePath));
|
|
8176
8181
|
if (existing.length === 0) {
|
|
8177
|
-
const updateCommand2 =
|
|
8182
|
+
const updateCommand2 = "generate_rules()";
|
|
8178
8183
|
const notice2 = {
|
|
8179
8184
|
status: "missing",
|
|
8180
8185
|
latest: RULES_VERSION,
|
|
8181
8186
|
files_checked: candidates,
|
|
8182
|
-
update_tool: "
|
|
8187
|
+
update_tool: "generate_rules",
|
|
8183
8188
|
update_args: {
|
|
8184
8189
|
...folderPath ? { folder_path: folderPath } : {},
|
|
8185
8190
|
editors: editorKey ? [editorKey] : ["all"]
|
|
@@ -8213,7 +8218,7 @@ function getRulesNotice(folderPath, clientName) {
|
|
|
8213
8218
|
return null;
|
|
8214
8219
|
}
|
|
8215
8220
|
const current = versions.sort(compareVersions2).at(-1);
|
|
8216
|
-
const updateCommand =
|
|
8221
|
+
const updateCommand = "generate_rules()";
|
|
8217
8222
|
const notice = {
|
|
8218
8223
|
status: filesOutdated.length > 0 ? "behind" : "unknown",
|
|
8219
8224
|
current,
|
|
@@ -8221,7 +8226,7 @@ function getRulesNotice(folderPath, clientName) {
|
|
|
8221
8226
|
files_checked: existing,
|
|
8222
8227
|
...filesOutdated.length > 0 ? { files_outdated: filesOutdated } : {},
|
|
8223
8228
|
...filesMissingVersion.length > 0 ? { files_missing_version: filesMissingVersion } : {},
|
|
8224
|
-
update_tool: "
|
|
8229
|
+
update_tool: "generate_rules",
|
|
8225
8230
|
update_args: {
|
|
8226
8231
|
...folderPath ? { folder_path: folderPath } : {},
|
|
8227
8232
|
editors: editorKey ? [editorKey] : ["all"]
|
|
@@ -8444,6 +8449,55 @@ async function writeEditorRules(options) {
|
|
|
8444
8449
|
}
|
|
8445
8450
|
return results;
|
|
8446
8451
|
}
|
|
8452
|
+
function listGlobalRuleTargets(editors) {
|
|
8453
|
+
const targets = [];
|
|
8454
|
+
for (const editor of editors) {
|
|
8455
|
+
const globalPaths = RULES_GLOBAL_FILES[editor];
|
|
8456
|
+
if (!globalPaths || globalPaths.length === 0) {
|
|
8457
|
+
continue;
|
|
8458
|
+
}
|
|
8459
|
+
for (const filePath of globalPaths) {
|
|
8460
|
+
targets.push({ editor, filePath });
|
|
8461
|
+
}
|
|
8462
|
+
}
|
|
8463
|
+
return targets;
|
|
8464
|
+
}
|
|
8465
|
+
async function writeGlobalRules(options) {
|
|
8466
|
+
const results = [];
|
|
8467
|
+
for (const editor of options.editors) {
|
|
8468
|
+
const rule = generateRuleContent(editor, {
|
|
8469
|
+
mode: options.mode
|
|
8470
|
+
});
|
|
8471
|
+
if (!rule) {
|
|
8472
|
+
results.push({ editor, filename: "", status: "unknown editor", scope: "global" });
|
|
8473
|
+
continue;
|
|
8474
|
+
}
|
|
8475
|
+
const globalPaths = RULES_GLOBAL_FILES[editor] ?? [];
|
|
8476
|
+
if (globalPaths.length === 0) {
|
|
8477
|
+
results.push({ editor, filename: rule.filename, status: "skipped (no global path)", scope: "global" });
|
|
8478
|
+
continue;
|
|
8479
|
+
}
|
|
8480
|
+
for (const filePath of globalPaths) {
|
|
8481
|
+
if (fs3.existsSync(filePath) && !options.overwriteExisting) {
|
|
8482
|
+
results.push({ editor, filename: filePath, status: "skipped (exists)", scope: "global" });
|
|
8483
|
+
continue;
|
|
8484
|
+
}
|
|
8485
|
+
try {
|
|
8486
|
+
const status = await upsertRuleFile(filePath, rule.content);
|
|
8487
|
+
results.push({ editor, filename: filePath, status, scope: "global" });
|
|
8488
|
+
} catch (err) {
|
|
8489
|
+
results.push({
|
|
8490
|
+
editor,
|
|
8491
|
+
filename: filePath,
|
|
8492
|
+
status: `error: ${err.message}`,
|
|
8493
|
+
scope: "global"
|
|
8494
|
+
});
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
rulesNoticeCache.clear();
|
|
8499
|
+
return results;
|
|
8500
|
+
}
|
|
8447
8501
|
var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
8448
8502
|
"create",
|
|
8449
8503
|
"update",
|
|
@@ -8645,6 +8699,7 @@ var LIGHT_TOOLSET = /* @__PURE__ */ new Set([
|
|
|
8645
8699
|
"session_delta",
|
|
8646
8700
|
// Setup and configuration (3)
|
|
8647
8701
|
"generate_editor_rules",
|
|
8702
|
+
"generate_rules",
|
|
8648
8703
|
"workspace_associate",
|
|
8649
8704
|
"workspace_bootstrap",
|
|
8650
8705
|
// Project management (5)
|
|
@@ -8690,6 +8745,7 @@ var STANDARD_TOOLSET = /* @__PURE__ */ new Set([
|
|
|
8690
8745
|
"session_delta",
|
|
8691
8746
|
// Setup and configuration (3)
|
|
8692
8747
|
"generate_editor_rules",
|
|
8748
|
+
"generate_rules",
|
|
8693
8749
|
"workspace_associate",
|
|
8694
8750
|
"workspace_bootstrap",
|
|
8695
8751
|
// Workspace management (2)
|
|
@@ -8898,7 +8954,8 @@ var TOOL_BUNDLES = {
|
|
|
8898
8954
|
"session_compress",
|
|
8899
8955
|
"session_delta",
|
|
8900
8956
|
"decision_trace",
|
|
8901
|
-
"generate_editor_rules"
|
|
8957
|
+
"generate_editor_rules",
|
|
8958
|
+
"generate_rules"
|
|
8902
8959
|
]),
|
|
8903
8960
|
// Memory bundle (~12 tools) - full memory CRUD operations
|
|
8904
8961
|
memory: /* @__PURE__ */ new Set([
|
|
@@ -9033,7 +9090,7 @@ function inferOperationCategory(name) {
|
|
|
9033
9090
|
if (name.startsWith("reminder")) return "Reminders";
|
|
9034
9091
|
if (name.startsWith("slack_") || name.startsWith("github_") || name.startsWith("integration")) return "Integrations";
|
|
9035
9092
|
if (name.startsWith("ai_")) return "AI";
|
|
9036
|
-
if (name === "auth_me" || name === "mcp_server_version" || name === "generate_editor_rules") return "Utility";
|
|
9093
|
+
if (name === "auth_me" || name === "mcp_server_version" || name === "generate_editor_rules" || name === "generate_rules") return "Utility";
|
|
9037
9094
|
if (name === "tools_enable_bundle" || name === "contextstream" || name === "contextstream_help") return "Meta";
|
|
9038
9095
|
return "Other";
|
|
9039
9096
|
}
|
|
@@ -9099,14 +9156,22 @@ function getOperationSchema(name) {
|
|
|
9099
9156
|
};
|
|
9100
9157
|
}
|
|
9101
9158
|
}
|
|
9159
|
+
function parsePositiveInt(raw, fallback) {
|
|
9160
|
+
const parsed = Number.parseInt(raw ?? "", 10);
|
|
9161
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
9162
|
+
}
|
|
9102
9163
|
var OUTPUT_FORMAT = process.env.CONTEXTSTREAM_OUTPUT_FORMAT || "compact";
|
|
9103
9164
|
var COMPACT_OUTPUT = OUTPUT_FORMAT === "compact";
|
|
9165
|
+
var DEFAULT_SEARCH_LIMIT = parsePositiveInt(process.env.CONTEXTSTREAM_SEARCH_LIMIT, 3);
|
|
9166
|
+
var DEFAULT_SEARCH_CONTENT_MAX_CHARS = parsePositiveInt(process.env.CONTEXTSTREAM_SEARCH_MAX_CHARS, 400);
|
|
9104
9167
|
var CONSOLIDATED_MODE = process.env.CONTEXTSTREAM_CONSOLIDATED !== "false";
|
|
9105
9168
|
var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
|
|
9106
9169
|
"session_init",
|
|
9107
9170
|
// Standalone - complex initialization
|
|
9108
9171
|
"context_smart",
|
|
9109
9172
|
// Standalone - called every message
|
|
9173
|
+
"generate_rules",
|
|
9174
|
+
// Standalone - rule generation helper
|
|
9110
9175
|
"search",
|
|
9111
9176
|
// Consolidates search_semantic, search_hybrid, search_keyword, search_pattern
|
|
9112
9177
|
"session",
|
|
@@ -10246,13 +10311,28 @@ Access: Free`,
|
|
|
10246
10311
|
query: external_exports.string(),
|
|
10247
10312
|
workspace_id: external_exports.string().uuid().optional(),
|
|
10248
10313
|
project_id: external_exports.string().uuid().optional(),
|
|
10249
|
-
limit: external_exports.number().optional()
|
|
10314
|
+
limit: external_exports.number().optional().describe("Max results to return (default: 3)"),
|
|
10315
|
+
offset: external_exports.number().optional().describe("Offset for pagination"),
|
|
10316
|
+
content_max_chars: external_exports.number().optional().describe("Max chars per result content (default: 400)")
|
|
10250
10317
|
});
|
|
10318
|
+
function normalizeSearchParams(input) {
|
|
10319
|
+
const limit = typeof input.limit === "number" && input.limit > 0 ? Math.min(Math.floor(input.limit), 100) : DEFAULT_SEARCH_LIMIT;
|
|
10320
|
+
const offset = typeof input.offset === "number" && input.offset > 0 ? Math.floor(input.offset) : void 0;
|
|
10321
|
+
const contentMax = typeof input.content_max_chars === "number" && input.content_max_chars > 0 ? Math.max(50, Math.min(Math.floor(input.content_max_chars), 1e4)) : DEFAULT_SEARCH_CONTENT_MAX_CHARS;
|
|
10322
|
+
return {
|
|
10323
|
+
query: input.query,
|
|
10324
|
+
workspace_id: resolveWorkspaceId(input.workspace_id),
|
|
10325
|
+
project_id: resolveProjectId(input.project_id),
|
|
10326
|
+
limit,
|
|
10327
|
+
offset,
|
|
10328
|
+
content_max_chars: contentMax
|
|
10329
|
+
};
|
|
10330
|
+
}
|
|
10251
10331
|
registerTool(
|
|
10252
10332
|
"search_semantic",
|
|
10253
10333
|
{ title: "Semantic search", description: "Semantic vector search", inputSchema: searchSchema },
|
|
10254
10334
|
async (input) => {
|
|
10255
|
-
const result = await client.searchSemantic(input);
|
|
10335
|
+
const result = await client.searchSemantic(normalizeSearchParams(input));
|
|
10256
10336
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
10257
10337
|
}
|
|
10258
10338
|
);
|
|
@@ -10260,7 +10340,7 @@ Access: Free`,
|
|
|
10260
10340
|
"search_hybrid",
|
|
10261
10341
|
{ title: "Hybrid search", description: "Hybrid search (semantic + keyword)", inputSchema: searchSchema },
|
|
10262
10342
|
async (input) => {
|
|
10263
|
-
const result = await client.searchHybrid(input);
|
|
10343
|
+
const result = await client.searchHybrid(normalizeSearchParams(input));
|
|
10264
10344
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
10265
10345
|
}
|
|
10266
10346
|
);
|
|
@@ -10268,7 +10348,7 @@ Access: Free`,
|
|
|
10268
10348
|
"search_keyword",
|
|
10269
10349
|
{ title: "Keyword search", description: "Keyword search", inputSchema: searchSchema },
|
|
10270
10350
|
async (input) => {
|
|
10271
|
-
const result = await client.searchKeyword(input);
|
|
10351
|
+
const result = await client.searchKeyword(normalizeSearchParams(input));
|
|
10272
10352
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
10273
10353
|
}
|
|
10274
10354
|
);
|
|
@@ -10276,7 +10356,7 @@ Access: Free`,
|
|
|
10276
10356
|
"search_pattern",
|
|
10277
10357
|
{ title: "Pattern search", description: "Pattern/regex search", inputSchema: searchSchema },
|
|
10278
10358
|
async (input) => {
|
|
10279
|
-
const result = await client.searchPattern(input);
|
|
10359
|
+
const result = await client.searchPattern(normalizeSearchParams(input));
|
|
10280
10360
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
10281
10361
|
}
|
|
10282
10362
|
);
|
|
@@ -11819,6 +11899,98 @@ Example: "What were the auth decisions?" or "What are my TypeScript preferences?
|
|
|
11819
11899
|
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
11820
11900
|
}
|
|
11821
11901
|
);
|
|
11902
|
+
registerTool(
|
|
11903
|
+
"generate_rules",
|
|
11904
|
+
{
|
|
11905
|
+
title: "Generate ContextStream rules",
|
|
11906
|
+
description: `Generate AI rule files for editors (Windsurf, Cursor, Cline, Kilo Code, Roo Code, Claude Code, Aider).
|
|
11907
|
+
Defaults to the current project folder; no folder_path required when run from a project.
|
|
11908
|
+
Supported editors: ${getAvailableEditors().join(", ")}`,
|
|
11909
|
+
inputSchema: external_exports.object({
|
|
11910
|
+
folder_path: external_exports.string().optional().describe("Absolute path to the project folder (defaults to IDE root/cwd)"),
|
|
11911
|
+
editors: external_exports.array(external_exports.enum(["codex", "windsurf", "cursor", "cline", "kilo", "roo", "claude", "aider", "all"])).optional().describe("Which editors to generate rules for. Defaults to all."),
|
|
11912
|
+
workspace_name: external_exports.string().optional().describe("Workspace name to include in rules"),
|
|
11913
|
+
workspace_id: external_exports.string().uuid().optional().describe("Workspace ID to include in rules"),
|
|
11914
|
+
project_name: external_exports.string().optional().describe("Project name to include in rules"),
|
|
11915
|
+
additional_rules: external_exports.string().optional().describe("Additional project-specific rules to append"),
|
|
11916
|
+
mode: external_exports.enum(["minimal", "full"]).optional().describe("Rule verbosity mode (default: minimal)"),
|
|
11917
|
+
overwrite_existing: external_exports.boolean().optional().describe("Allow overwriting existing rule files (ContextStream block only)"),
|
|
11918
|
+
apply_global: external_exports.boolean().optional().describe("Also write global rule files for supported editors"),
|
|
11919
|
+
dry_run: external_exports.boolean().optional().describe("If true, return content without writing files")
|
|
11920
|
+
})
|
|
11921
|
+
},
|
|
11922
|
+
async (input) => {
|
|
11923
|
+
const folderPath = resolveFolderPath(input.folder_path, sessionManager);
|
|
11924
|
+
if (!folderPath) {
|
|
11925
|
+
return errorResult("Error: folder_path is required. Provide folder_path or run from a project directory.");
|
|
11926
|
+
}
|
|
11927
|
+
const editors = input.editors?.includes("all") || !input.editors ? getAvailableEditors() : input.editors.filter((e) => e !== "all");
|
|
11928
|
+
const results = [];
|
|
11929
|
+
if (input.dry_run) {
|
|
11930
|
+
for (const editor of editors) {
|
|
11931
|
+
const rule = generateRuleContent(editor, {
|
|
11932
|
+
workspaceName: input.workspace_name,
|
|
11933
|
+
workspaceId: input.workspace_id,
|
|
11934
|
+
projectName: input.project_name,
|
|
11935
|
+
additionalRules: input.additional_rules,
|
|
11936
|
+
mode: input.mode
|
|
11937
|
+
});
|
|
11938
|
+
if (!rule) {
|
|
11939
|
+
results.push({ editor, filename: "", status: "unknown editor" });
|
|
11940
|
+
continue;
|
|
11941
|
+
}
|
|
11942
|
+
results.push({
|
|
11943
|
+
editor,
|
|
11944
|
+
filename: rule.filename,
|
|
11945
|
+
status: "dry run - would update"
|
|
11946
|
+
});
|
|
11947
|
+
}
|
|
11948
|
+
} else {
|
|
11949
|
+
const writeResults = await writeEditorRules({
|
|
11950
|
+
folderPath,
|
|
11951
|
+
editors,
|
|
11952
|
+
workspaceName: input.workspace_name,
|
|
11953
|
+
workspaceId: input.workspace_id,
|
|
11954
|
+
projectName: input.project_name,
|
|
11955
|
+
additionalRules: input.additional_rules,
|
|
11956
|
+
mode: input.mode,
|
|
11957
|
+
overwriteExisting: input.overwrite_existing
|
|
11958
|
+
});
|
|
11959
|
+
results.push(...writeResults);
|
|
11960
|
+
}
|
|
11961
|
+
const globalTargets = listGlobalRuleTargets(editors);
|
|
11962
|
+
let globalResults;
|
|
11963
|
+
if (input.apply_global) {
|
|
11964
|
+
if (input.dry_run) {
|
|
11965
|
+
globalResults = globalTargets.map((target) => ({
|
|
11966
|
+
editor: target.editor,
|
|
11967
|
+
filename: target.filePath,
|
|
11968
|
+
status: "dry run - would update",
|
|
11969
|
+
scope: "global"
|
|
11970
|
+
}));
|
|
11971
|
+
} else {
|
|
11972
|
+
globalResults = await writeGlobalRules({
|
|
11973
|
+
editors,
|
|
11974
|
+
mode: input.mode,
|
|
11975
|
+
overwriteExisting: input.overwrite_existing
|
|
11976
|
+
});
|
|
11977
|
+
}
|
|
11978
|
+
}
|
|
11979
|
+
const createdCount = results.filter((r) => r.status === "created" || r.status === "updated" || r.status === "appended").length;
|
|
11980
|
+
const skippedCount = results.filter((r) => r.status.startsWith("skipped")).length;
|
|
11981
|
+
const baseMessage = input.dry_run ? "Dry run complete. Use dry_run: false to write files." : skippedCount > 0 ? `Generated ${createdCount} rule files. ${skippedCount} skipped (existing files). Re-run with overwrite_existing: true to replace ContextStream blocks.` : `Generated ${createdCount} rule files.`;
|
|
11982
|
+
const globalPrompt = input.apply_global ? "Global rule update complete." : globalTargets.length > 0 ? "Apply rules globally too? Re-run with apply_global: true." : "No global rule locations are known for these editors.";
|
|
11983
|
+
const summary = {
|
|
11984
|
+
folder: folderPath,
|
|
11985
|
+
results,
|
|
11986
|
+
...globalResults ? { global_results: globalResults } : {},
|
|
11987
|
+
...globalTargets.length > 0 ? { global_targets: globalTargets } : {},
|
|
11988
|
+
message: baseMessage,
|
|
11989
|
+
global_prompt: globalPrompt
|
|
11990
|
+
};
|
|
11991
|
+
return { content: [{ type: "text", text: formatContent(summary) }], structuredContent: toStructured(summary) };
|
|
11992
|
+
}
|
|
11993
|
+
);
|
|
11822
11994
|
registerTool(
|
|
11823
11995
|
"generate_editor_rules",
|
|
11824
11996
|
{
|
|
@@ -12969,16 +13141,13 @@ Use this to remove a reminder that is no longer relevant.`,
|
|
|
12969
13141
|
query: external_exports.string().describe("Search query"),
|
|
12970
13142
|
workspace_id: external_exports.string().uuid().optional(),
|
|
12971
13143
|
project_id: external_exports.string().uuid().optional(),
|
|
12972
|
-
limit: external_exports.number().optional()
|
|
13144
|
+
limit: external_exports.number().optional().describe("Max results to return (default: 3)"),
|
|
13145
|
+
offset: external_exports.number().optional().describe("Offset for pagination"),
|
|
13146
|
+
content_max_chars: external_exports.number().optional().describe("Max chars per result content (default: 400)")
|
|
12973
13147
|
})
|
|
12974
13148
|
},
|
|
12975
13149
|
async (input) => {
|
|
12976
|
-
const params =
|
|
12977
|
-
query: input.query,
|
|
12978
|
-
workspace_id: resolveWorkspaceId(input.workspace_id),
|
|
12979
|
-
project_id: resolveProjectId(input.project_id),
|
|
12980
|
-
limit: input.limit
|
|
12981
|
-
};
|
|
13150
|
+
const params = normalizeSearchParams(input);
|
|
12982
13151
|
let result;
|
|
12983
13152
|
switch (input.mode) {
|
|
12984
13153
|
case "semantic":
|
|
@@ -15169,7 +15338,8 @@ function registerPrompts(server) {
|
|
|
15169
15338
|
"- Otherwise ask me for an absolute folder path.",
|
|
15170
15339
|
"- Ask which editor(s) (windsurf,cursor,cline,kilo,roo,claude,aider) or default to all.",
|
|
15171
15340
|
"",
|
|
15172
|
-
"Then call `
|
|
15341
|
+
"Then call `generate_rules` and confirm which files were created/updated.",
|
|
15342
|
+
"Ask if the user also wants to apply rules globally (pass apply_global: true)."
|
|
15173
15343
|
].join("\n")
|
|
15174
15344
|
}
|
|
15175
15345
|
}
|
|
@@ -16589,10 +16759,7 @@ Code: ${device.user_code}`);
|
|
|
16589
16759
|
}
|
|
16590
16760
|
apiKey = createdKey.secret_key.trim();
|
|
16591
16761
|
apiKeySource = "browser";
|
|
16592
|
-
|
|
16593
|
-
console.log(`
|
|
16594
|
-
Created API key: ${maskedNewKey}
|
|
16595
|
-
`);
|
|
16762
|
+
console.log("\nCreated API key\n");
|
|
16596
16763
|
}
|
|
16597
16764
|
}
|
|
16598
16765
|
const client = new ContextStreamClient(buildClientConfig({ apiUrl, apiKey }));
|
|
@@ -16604,14 +16771,11 @@ Created API key: ${maskedNewKey}
|
|
|
16604
16771
|
throw new Error(`Authentication failed. Check your API key. (${message})`);
|
|
16605
16772
|
}
|
|
16606
16773
|
const email = typeof me?.data?.email === "string" ? me.data.email : typeof me?.email === "string" ? me.email : void 0;
|
|
16607
|
-
|
|
16608
|
-
console.log(`Authenticated as: ${email || "unknown user"} (${maskedKey})
|
|
16609
|
-
`);
|
|
16774
|
+
console.log("Authenticated\n");
|
|
16610
16775
|
if (!dryRun && (apiKeySource === "browser" || apiKeySource === "paste")) {
|
|
16611
16776
|
try {
|
|
16612
|
-
|
|
16613
|
-
console.log(
|
|
16614
|
-
`);
|
|
16777
|
+
await writeSavedCredentials({ apiUrl, apiKey, email });
|
|
16778
|
+
console.log("Saved API key for future runs\n");
|
|
16615
16779
|
} catch (err) {
|
|
16616
16780
|
const msg = err instanceof Error ? err.message : String(err);
|
|
16617
16781
|
console.log(`Warning: failed to save API key for future runs (${credentialsFilePath()}): ${msg}
|
|
@@ -17098,6 +17262,8 @@ Environment variables:
|
|
|
17098
17262
|
CONTEXTSTREAM_PROGRESSIVE_MODE Progressive disclosure: true|false (default: false, starts with ~13 core tools)
|
|
17099
17263
|
CONTEXTSTREAM_ROUTER_MODE Router pattern: true|false (default: false, exposes only 2 meta-tools)
|
|
17100
17264
|
CONTEXTSTREAM_OUTPUT_FORMAT Output verbosity: compact|pretty (default: compact, ~30% fewer tokens)
|
|
17265
|
+
CONTEXTSTREAM_SEARCH_LIMIT Default MCP search limit (default: 3)
|
|
17266
|
+
CONTEXTSTREAM_SEARCH_MAX_CHARS Max chars per search result content (default: 400)
|
|
17101
17267
|
CONTEXTSTREAM_CONSOLIDATED Consolidated domain tools: true|false (default: true in v0.4.x, ~75% token reduction)
|
|
17102
17268
|
CONTEXTSTREAM_CONTEXT_PACK Enable Context Pack in context_smart: true|false (default: true)
|
|
17103
17269
|
CONTEXTSTREAM_PRO_TOOLS Optional comma-separated PRO tool names (default: AI tools)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contextstream/mcp-server",
|
|
3
3
|
"mcpName": "io.github.contextstreamio/mcp-server",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.22",
|
|
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",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"typecheck": "tsc --noEmit"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
26
26
|
"zod": "^3.23.8"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|