@contextstream/mcp-server 0.3.70 → 0.3.72

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.
Files changed (2) hide show
  1. package/dist/index.js +654 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7556,7 +7556,7 @@ var CONTEXTSTREAM_RULES_MINIMAL = `
7556
7556
 
7557
7557
  ### Tool Catalog
7558
7558
 
7559
- By default, the MCP server exposes the **standard** toolset (~50 tools). To expose fewer tools, set \`CONTEXTSTREAM_TOOLSET=light\`. To expose everything (~86 tools), set \`CONTEXTSTREAM_TOOLSET=complete\` in your MCP config:
7559
+ By default, the MCP server exposes the **standard** toolset (~58 tools). To expose fewer tools, set \`CONTEXTSTREAM_TOOLSET=light\`. To expose everything (~86 tools), set \`CONTEXTSTREAM_TOOLSET=complete\` in your MCP config:
7560
7560
 
7561
7561
  \`\`\`json
7562
7562
  {
@@ -8083,10 +8083,11 @@ var LIGHT_TOOLSET = /* @__PURE__ */ new Set([
8083
8083
  "mcp_server_version"
8084
8084
  ]);
8085
8085
  var STANDARD_TOOLSET = /* @__PURE__ */ new Set([
8086
- // Core session tools (13)
8086
+ // Core session tools (14)
8087
8087
  "session_init",
8088
8088
  "session_tools",
8089
8089
  "context_smart",
8090
+ "context_feedback",
8090
8091
  "session_summary",
8091
8092
  "session_capture",
8092
8093
  "session_capture_lesson",
@@ -8127,9 +8128,15 @@ var STANDARD_TOOLSET = /* @__PURE__ */ new Set([
8127
8128
  "memory_delete_event",
8128
8129
  "memory_timeline",
8129
8130
  "memory_summary",
8130
- // Memory nodes (2)
8131
+ // Memory nodes (6) - full CRUD for memory hygiene
8131
8132
  "memory_create_node",
8132
8133
  "memory_list_nodes",
8134
+ "memory_get_node",
8135
+ "memory_update_node",
8136
+ "memory_delete_node",
8137
+ "memory_supersede_node",
8138
+ // Memory distillation (1)
8139
+ "memory_distill_event",
8133
8140
  // Knowledge graph analysis (8)
8134
8141
  "graph_related",
8135
8142
  "graph_decisions",
@@ -8155,6 +8162,275 @@ var STANDARD_TOOLSET = /* @__PURE__ */ new Set([
8155
8162
  "auth_me",
8156
8163
  "mcp_server_version"
8157
8164
  ]);
8165
+ var SLACK_TOOLS = /* @__PURE__ */ new Set([
8166
+ "slack_stats",
8167
+ "slack_channels",
8168
+ "slack_search",
8169
+ "slack_discussions",
8170
+ "slack_activity",
8171
+ "slack_contributors",
8172
+ "slack_knowledge",
8173
+ "slack_summary",
8174
+ "slack_sync_users"
8175
+ ]);
8176
+ var GITHUB_TOOLS = /* @__PURE__ */ new Set([
8177
+ "github_stats",
8178
+ "github_repos",
8179
+ "github_search",
8180
+ "github_issues",
8181
+ "github_activity",
8182
+ "github_contributors",
8183
+ "github_knowledge",
8184
+ "github_summary"
8185
+ ]);
8186
+ var CROSS_INTEGRATION_TOOLS = /* @__PURE__ */ new Set([
8187
+ "integrations_status",
8188
+ "integrations_search",
8189
+ "integrations_summary",
8190
+ "integrations_knowledge"
8191
+ ]);
8192
+ var ALL_INTEGRATION_TOOLS = /* @__PURE__ */ new Set([
8193
+ ...SLACK_TOOLS,
8194
+ ...GITHUB_TOOLS,
8195
+ ...CROSS_INTEGRATION_TOOLS
8196
+ ]);
8197
+ var AUTO_HIDE_INTEGRATIONS = process.env.CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS !== "false";
8198
+ var TOKEN_SENSITIVE_CLIENTS = /* @__PURE__ */ new Set([
8199
+ "claude",
8200
+ "claude-code",
8201
+ "claude code",
8202
+ "claude desktop",
8203
+ "anthropic"
8204
+ ]);
8205
+ var AUTO_TOOLSET_ENABLED = process.env.CONTEXTSTREAM_AUTO_TOOLSET === "true";
8206
+ var SCHEMA_MODE = process.env.CONTEXTSTREAM_SCHEMA_MODE || "full";
8207
+ var COMPACT_SCHEMA_ENABLED = SCHEMA_MODE === "compact";
8208
+ function compactifyDescription(description) {
8209
+ if (!description) return "";
8210
+ let compact = description.replace(/```[\s\S]*?```/g, "");
8211
+ compact = compact.replace(/\n*Example:[\s\S]*$/i, "");
8212
+ compact = compact.replace(/\n*Access:.*$/gm, "");
8213
+ const firstLine = compact.split("\n")[0].trim();
8214
+ const firstSentence = firstLine.split(/\.(?:\s|$)/)[0];
8215
+ let result = firstSentence.length >= 20 ? firstSentence : firstLine;
8216
+ if (result.length > 120) {
8217
+ result = result.substring(0, 117) + "...";
8218
+ }
8219
+ return result;
8220
+ }
8221
+ function compactifyParamDescription(description) {
8222
+ if (!description) return void 0;
8223
+ const firstClause = description.split(/[.,;]/)[0].trim();
8224
+ if (firstClause.length > 40) {
8225
+ return firstClause.substring(0, 37) + "...";
8226
+ }
8227
+ return firstClause;
8228
+ }
8229
+ function applyCompactParamDescriptions(schema) {
8230
+ if (!(schema instanceof external_exports.ZodObject)) {
8231
+ return schema;
8232
+ }
8233
+ const shape = schema.shape;
8234
+ let changed = false;
8235
+ const nextShape = {};
8236
+ for (const [key, field] of Object.entries(shape)) {
8237
+ let nextField = field;
8238
+ const existingDescription = getDescription(field);
8239
+ if (field instanceof external_exports.ZodObject) {
8240
+ const nested = applyCompactParamDescriptions(field);
8241
+ if (nested !== field) {
8242
+ nextField = nested;
8243
+ changed = true;
8244
+ }
8245
+ }
8246
+ if (existingDescription) {
8247
+ const compact = compactifyParamDescription(existingDescription);
8248
+ if (compact && compact !== existingDescription) {
8249
+ nextField = nextField.describe(compact);
8250
+ changed = true;
8251
+ }
8252
+ }
8253
+ nextShape[key] = nextField;
8254
+ }
8255
+ if (!changed) return schema;
8256
+ let nextSchema = external_exports.object(nextShape);
8257
+ const def = schema._def;
8258
+ if (def?.catchall) nextSchema = nextSchema.catchall(def.catchall);
8259
+ if (def?.unknownKeys === "passthrough") nextSchema = nextSchema.passthrough();
8260
+ if (def?.unknownKeys === "strict") nextSchema = nextSchema.strict();
8261
+ return nextSchema;
8262
+ }
8263
+ function detectClaudeCodeFromEnv() {
8264
+ return process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE_ENTRYPOINT !== void 0 || process.env.CLAUDE_CODE === "1";
8265
+ }
8266
+ function isTokenSensitiveClient(clientName) {
8267
+ if (!clientName) return false;
8268
+ const normalized = clientName.toLowerCase().trim();
8269
+ for (const pattern of TOKEN_SENSITIVE_CLIENTS) {
8270
+ if (normalized.includes(pattern)) {
8271
+ return true;
8272
+ }
8273
+ }
8274
+ return false;
8275
+ }
8276
+ function getRecommendedToolset(clientName, fromEnv) {
8277
+ if (!AUTO_TOOLSET_ENABLED) return null;
8278
+ if (isTokenSensitiveClient(clientName) || fromEnv) {
8279
+ return LIGHT_TOOLSET;
8280
+ }
8281
+ return null;
8282
+ }
8283
+ var detectedClientInfo = null;
8284
+ var clientDetectedFromEnv = false;
8285
+ var PROGRESSIVE_MODE = process.env.CONTEXTSTREAM_PROGRESSIVE_MODE === "true";
8286
+ var TOOL_BUNDLES = {
8287
+ // Core bundle (~12 tools) - always enabled, essential for any session
8288
+ core: /* @__PURE__ */ new Set([
8289
+ "session_init",
8290
+ "session_tools",
8291
+ "context_smart",
8292
+ "context_feedback",
8293
+ "session_capture",
8294
+ "session_capture_lesson",
8295
+ "session_get_lessons",
8296
+ "session_recall",
8297
+ "session_remember",
8298
+ "session_get_user_context",
8299
+ "tools_enable_bundle",
8300
+ // Meta-tool to enable other bundles
8301
+ "auth_me",
8302
+ "mcp_server_version"
8303
+ ]),
8304
+ // Session bundle (~6 tools) - extended session management
8305
+ session: /* @__PURE__ */ new Set([
8306
+ "session_summary",
8307
+ "session_smart_search",
8308
+ "session_compress",
8309
+ "session_delta",
8310
+ "decision_trace",
8311
+ "generate_editor_rules"
8312
+ ]),
8313
+ // Memory bundle (~12 tools) - full memory CRUD operations
8314
+ memory: /* @__PURE__ */ new Set([
8315
+ "memory_create_event",
8316
+ "memory_update_event",
8317
+ "memory_delete_event",
8318
+ "memory_list_events",
8319
+ "memory_get_event",
8320
+ "memory_search",
8321
+ "memory_decisions",
8322
+ "memory_timeline",
8323
+ "memory_summary",
8324
+ "memory_create_node",
8325
+ "memory_update_node",
8326
+ "memory_delete_node",
8327
+ "memory_list_nodes",
8328
+ "memory_get_node",
8329
+ "memory_supersede_node",
8330
+ "memory_distill_event"
8331
+ ]),
8332
+ // Search bundle (~4 tools) - search capabilities
8333
+ search: /* @__PURE__ */ new Set([
8334
+ "search_semantic",
8335
+ "search_hybrid",
8336
+ "search_keyword"
8337
+ ]),
8338
+ // Graph bundle (~9 tools) - code graph analysis
8339
+ graph: /* @__PURE__ */ new Set([
8340
+ "graph_related",
8341
+ "graph_decisions",
8342
+ "graph_path",
8343
+ "graph_dependencies",
8344
+ "graph_call_path",
8345
+ "graph_impact",
8346
+ "graph_circular_dependencies",
8347
+ "graph_unused_code",
8348
+ "graph_ingest"
8349
+ ]),
8350
+ // Workspace bundle (~4 tools) - workspace management
8351
+ workspace: /* @__PURE__ */ new Set([
8352
+ "workspaces_list",
8353
+ "workspaces_get",
8354
+ "workspace_associate",
8355
+ "workspace_bootstrap"
8356
+ ]),
8357
+ // Project bundle (~10 tools) - project management and indexing
8358
+ project: /* @__PURE__ */ new Set([
8359
+ "projects_create",
8360
+ "projects_update",
8361
+ "projects_list",
8362
+ "projects_get",
8363
+ "projects_overview",
8364
+ "projects_statistics",
8365
+ "projects_index",
8366
+ "projects_index_status",
8367
+ "projects_files",
8368
+ "projects_ingest_local"
8369
+ ]),
8370
+ // Reminders bundle (~6 tools) - reminder management
8371
+ reminders: /* @__PURE__ */ new Set([
8372
+ "reminders_list",
8373
+ "reminders_active",
8374
+ "reminders_create",
8375
+ "reminders_snooze",
8376
+ "reminders_complete",
8377
+ "reminders_dismiss"
8378
+ ]),
8379
+ // Integrations bundle - Slack/GitHub tools (auto-hidden when not connected)
8380
+ integrations: /* @__PURE__ */ new Set([
8381
+ "slack_stats",
8382
+ "slack_channels",
8383
+ "slack_search",
8384
+ "slack_discussions",
8385
+ "slack_activity",
8386
+ "slack_contributors",
8387
+ "slack_knowledge",
8388
+ "slack_summary",
8389
+ "slack_sync_users",
8390
+ "github_stats",
8391
+ "github_repos",
8392
+ "github_search",
8393
+ "github_issues",
8394
+ "github_activity",
8395
+ "github_contributors",
8396
+ "github_knowledge",
8397
+ "github_summary",
8398
+ "integrations_status",
8399
+ "integrations_search",
8400
+ "integrations_summary",
8401
+ "integrations_knowledge"
8402
+ ])
8403
+ };
8404
+ var enabledBundles = /* @__PURE__ */ new Set(["core"]);
8405
+ function isToolInEnabledBundles(toolName) {
8406
+ for (const bundleName of enabledBundles) {
8407
+ const bundle = TOOL_BUNDLES[bundleName];
8408
+ if (bundle?.has(toolName)) {
8409
+ return true;
8410
+ }
8411
+ }
8412
+ return false;
8413
+ }
8414
+ function getBundleInfo() {
8415
+ const descriptions = {
8416
+ core: "Essential session tools (always enabled)",
8417
+ session: "Extended session management and utilities",
8418
+ memory: "Full memory CRUD operations",
8419
+ search: "Semantic, hybrid, and keyword search",
8420
+ graph: "Code graph analysis and dependencies",
8421
+ workspace: "Workspace management",
8422
+ project: "Project management and indexing",
8423
+ reminders: "Reminder management",
8424
+ integrations: "Slack and GitHub integrations"
8425
+ };
8426
+ return Object.entries(TOOL_BUNDLES).map(([name, tools]) => ({
8427
+ name,
8428
+ size: tools.size,
8429
+ enabled: enabledBundles.has(name),
8430
+ description: descriptions[name] || `${name} tools`
8431
+ }));
8432
+ }
8433
+ var deferredTools = /* @__PURE__ */ new Map();
8158
8434
  var TOOLSET_ALIASES = {
8159
8435
  // Light mode - minimal, fastest
8160
8436
  light: LIGHT_TOOLSET,
@@ -8167,6 +8443,8 @@ var TOOLSET_ALIASES = {
8167
8443
  complete: null,
8168
8444
  full: null,
8169
8445
  all: null
8446
+ // Auto mode - handled separately in resolveToolFilter, but listed here for reference
8447
+ // auto: STANDARD_TOOLSET (will be adjusted based on client detection)
8170
8448
  };
8171
8449
  function parseToolList(raw) {
8172
8450
  return new Set(
@@ -8179,24 +8457,41 @@ function resolveToolFilter() {
8179
8457
  const allowlist = parseToolList(allowlistRaw);
8180
8458
  if (allowlist.size === 0) {
8181
8459
  console.error("[ContextStream] CONTEXTSTREAM_TOOL_ALLOWLIST is empty; using standard toolset.");
8182
- return { allowlist: STANDARD_TOOLSET, source: "standard" };
8460
+ return { allowlist: STANDARD_TOOLSET, source: "standard", autoDetected: false };
8183
8461
  }
8184
- return { allowlist, source: "allowlist" };
8462
+ return { allowlist, source: "allowlist", autoDetected: false };
8185
8463
  }
8186
8464
  const toolsetRaw = process.env.CONTEXTSTREAM_TOOLSET;
8187
8465
  if (!toolsetRaw) {
8188
- return { allowlist: STANDARD_TOOLSET, source: "standard" };
8466
+ clientDetectedFromEnv = detectClaudeCodeFromEnv();
8467
+ if (clientDetectedFromEnv && AUTO_TOOLSET_ENABLED) {
8468
+ const recommended = getRecommendedToolset(void 0, true);
8469
+ if (recommended) {
8470
+ console.error("[ContextStream] Detected Claude Code via environment. Using light toolset for optimal token usage.");
8471
+ return { allowlist: recommended, source: "auto-claude", autoDetected: true };
8472
+ }
8473
+ }
8474
+ return { allowlist: STANDARD_TOOLSET, source: "standard", autoDetected: false };
8475
+ }
8476
+ if (toolsetRaw.trim().toLowerCase() === "auto") {
8477
+ clientDetectedFromEnv = detectClaudeCodeFromEnv();
8478
+ if (clientDetectedFromEnv) {
8479
+ console.error("[ContextStream] TOOLSET=auto: Detected Claude Code, using light toolset.");
8480
+ return { allowlist: LIGHT_TOOLSET, source: "auto-claude", autoDetected: true };
8481
+ }
8482
+ console.error("[ContextStream] TOOLSET=auto: Will adjust toolset based on MCP client (currently standard).");
8483
+ return { allowlist: STANDARD_TOOLSET, source: "auto-pending", autoDetected: true };
8189
8484
  }
8190
8485
  const key = toolsetRaw.trim().toLowerCase();
8191
8486
  if (key in TOOLSET_ALIASES) {
8192
8487
  const resolved = TOOLSET_ALIASES[key];
8193
8488
  if (resolved === null) {
8194
- return { allowlist: null, source: "complete" };
8489
+ return { allowlist: null, source: "complete", autoDetected: false };
8195
8490
  }
8196
- return { allowlist: resolved, source: key };
8491
+ return { allowlist: resolved, source: key, autoDetected: false };
8197
8492
  }
8198
8493
  console.error(`[ContextStream] Unknown CONTEXTSTREAM_TOOLSET "${toolsetRaw}". Using standard toolset.`);
8199
- return { allowlist: STANDARD_TOOLSET, source: "standard" };
8494
+ return { allowlist: STANDARD_TOOLSET, source: "standard", autoDetected: false };
8200
8495
  }
8201
8496
  function formatContent(data) {
8202
8497
  return JSON.stringify(data, null, 2);
@@ -8265,17 +8560,79 @@ function isDuplicateLessonCapture(signature) {
8265
8560
  recentLessonCaptures.set(signature, now);
8266
8561
  return false;
8267
8562
  }
8563
+ function setupClientDetection(server) {
8564
+ if (!AUTO_TOOLSET_ENABLED) {
8565
+ console.error("[ContextStream] Auto-toolset: DISABLED (set CONTEXTSTREAM_AUTO_TOOLSET=true to enable)");
8566
+ return;
8567
+ }
8568
+ if (clientDetectedFromEnv) {
8569
+ console.error("[ContextStream] Client detection: Already detected Claude Code from environment");
8570
+ return;
8571
+ }
8572
+ const lowLevelServer = server.server;
8573
+ if (!lowLevelServer) {
8574
+ console.error("[ContextStream] Warning: Could not access low-level MCP server for client detection");
8575
+ return;
8576
+ }
8577
+ lowLevelServer.oninitialized = () => {
8578
+ try {
8579
+ const clientVersion = lowLevelServer.getClientVersion?.();
8580
+ if (clientVersion) {
8581
+ detectedClientInfo = clientVersion;
8582
+ const clientName = clientVersion.name || "unknown";
8583
+ const clientVer = clientVersion.version || "unknown";
8584
+ console.error(`[ContextStream] MCP Client detected: ${clientName} v${clientVer}`);
8585
+ if (isTokenSensitiveClient(clientName)) {
8586
+ console.error("[ContextStream] Token-sensitive client detected. Consider using CONTEXTSTREAM_TOOLSET=light for optimal performance.");
8587
+ try {
8588
+ lowLevelServer.sendToolsListChanged?.();
8589
+ console.error("[ContextStream] Emitted tools/list_changed notification");
8590
+ } catch (error) {
8591
+ }
8592
+ }
8593
+ }
8594
+ } catch (error) {
8595
+ console.error("[ContextStream] Error in client detection callback:", error);
8596
+ }
8597
+ };
8598
+ console.error("[ContextStream] Client detection: Callback registered for MCP initialize");
8599
+ }
8268
8600
  function registerTools(server, client, sessionManager) {
8269
8601
  const upgradeUrl2 = process.env.CONTEXTSTREAM_UPGRADE_URL || "https://contextstream.io/pricing";
8270
8602
  const toolFilter = resolveToolFilter();
8271
8603
  const toolAllowlist = toolFilter.allowlist;
8272
8604
  if (toolAllowlist) {
8273
8605
  const source = toolFilter.source;
8274
- const hint = source === "light" ? " Set CONTEXTSTREAM_TOOLSET=standard or complete for more tools." : source === "standard" ? " Set CONTEXTSTREAM_TOOLSET=complete for all tools." : "";
8275
- console.error(`[ContextStream] Toolset: ${source} (${toolAllowlist.size} tools).${hint}`);
8606
+ const autoNote = toolFilter.autoDetected ? " (auto-detected)" : "";
8607
+ const hint = source === "light" || source === "auto-claude" ? " Set CONTEXTSTREAM_TOOLSET=standard or complete for more tools." : source === "standard" ? " Set CONTEXTSTREAM_TOOLSET=complete for all tools." : source === "auto-pending" ? " Toolset may be adjusted when MCP client is detected." : "";
8608
+ console.error(`[ContextStream] Toolset: ${source} (${toolAllowlist.size} tools)${autoNote}.${hint}`);
8276
8609
  } else {
8277
8610
  console.error(`[ContextStream] Toolset: complete (all tools).`);
8278
8611
  }
8612
+ if (AUTO_TOOLSET_ENABLED) {
8613
+ if (clientDetectedFromEnv) {
8614
+ console.error("[ContextStream] Auto-toolset: ACTIVE (Claude Code detected from environment)");
8615
+ } else {
8616
+ console.error("[ContextStream] Auto-toolset: ENABLED (will detect MCP client on initialize)");
8617
+ }
8618
+ }
8619
+ if (AUTO_HIDE_INTEGRATIONS) {
8620
+ console.error(`[ContextStream] Integration auto-hide: ENABLED (${ALL_INTEGRATION_TOOLS.size} tools hidden until integrations connected)`);
8621
+ console.error("[ContextStream] Set CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS=false to disable.");
8622
+ } else {
8623
+ console.error("[ContextStream] Integration auto-hide: disabled");
8624
+ }
8625
+ if (COMPACT_SCHEMA_ENABLED) {
8626
+ console.error("[ContextStream] Schema mode: COMPACT (shorter descriptions, minimal params)");
8627
+ } else {
8628
+ console.error("[ContextStream] Schema mode: full (set CONTEXTSTREAM_SCHEMA_MODE=compact to reduce token overhead)");
8629
+ }
8630
+ if (PROGRESSIVE_MODE) {
8631
+ const coreBundle = TOOL_BUNDLES.core;
8632
+ console.error(`[ContextStream] Progressive mode: ENABLED (starting with ${coreBundle.size} core tools)`);
8633
+ console.error("[ContextStream] Use tools_enable_bundle to unlock additional tool bundles dynamically.");
8634
+ }
8635
+ let serverRef = server;
8279
8636
  const defaultProTools = /* @__PURE__ */ new Set([
8280
8637
  // AI endpoints (typically paid/credit-metered)
8281
8638
  "ai_context",
@@ -8354,6 +8711,128 @@ function registerTools(server, client, sessionManager) {
8354
8711
  ].join("\n")
8355
8712
  );
8356
8713
  }
8714
+ let integrationStatus = { checked: false, slack: false, github: false };
8715
+ let toolsListChangedNotified = false;
8716
+ async function checkIntegrationStatus(workspaceId) {
8717
+ if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId) {
8718
+ return { slack: integrationStatus.slack, github: integrationStatus.github };
8719
+ }
8720
+ if (!workspaceId) {
8721
+ return { slack: false, github: false };
8722
+ }
8723
+ try {
8724
+ const status = await client.integrationsStatus({ workspace_id: workspaceId });
8725
+ const slackConnected = status?.some(
8726
+ (s) => s.provider === "slack" && s.status === "connected"
8727
+ ) ?? false;
8728
+ const githubConnected = status?.some(
8729
+ (s) => s.provider === "github" && s.status === "connected"
8730
+ ) ?? false;
8731
+ integrationStatus = {
8732
+ checked: true,
8733
+ slack: slackConnected,
8734
+ github: githubConnected,
8735
+ workspaceId
8736
+ };
8737
+ console.error(`[ContextStream] Integration status: Slack=${slackConnected}, GitHub=${githubConnected}`);
8738
+ return { slack: slackConnected, github: githubConnected };
8739
+ } catch (error) {
8740
+ console.error("[ContextStream] Failed to check integration status:", error);
8741
+ return { slack: false, github: false };
8742
+ }
8743
+ }
8744
+ function updateIntegrationStatus(status, workspaceId) {
8745
+ const hadSlack = integrationStatus.slack;
8746
+ const hadGithub = integrationStatus.github;
8747
+ integrationStatus = {
8748
+ checked: true,
8749
+ slack: status.slack,
8750
+ github: status.github,
8751
+ workspaceId
8752
+ };
8753
+ if (AUTO_HIDE_INTEGRATIONS && !toolsListChangedNotified) {
8754
+ const newlyConnected = !hadSlack && status.slack || !hadGithub && status.github;
8755
+ if (newlyConnected) {
8756
+ try {
8757
+ server.server?.sendToolsListChanged?.();
8758
+ toolsListChangedNotified = true;
8759
+ console.error("[ContextStream] Emitted tools/list_changed notification (integrations detected)");
8760
+ } catch (error) {
8761
+ console.error("[ContextStream] Failed to emit tools/list_changed:", error);
8762
+ }
8763
+ }
8764
+ }
8765
+ }
8766
+ async function gateIfIntegrationTool(toolName) {
8767
+ if (!AUTO_HIDE_INTEGRATIONS) return null;
8768
+ const requiresSlack = SLACK_TOOLS.has(toolName);
8769
+ const requiresGithub = GITHUB_TOOLS.has(toolName);
8770
+ const requiresCrossIntegration = CROSS_INTEGRATION_TOOLS.has(toolName);
8771
+ if (!requiresSlack && !requiresGithub && !requiresCrossIntegration) {
8772
+ return null;
8773
+ }
8774
+ const workspaceId = sessionManager?.getContext()?.workspace_id;
8775
+ const status = await checkIntegrationStatus(workspaceId);
8776
+ if (requiresSlack && !status.slack) {
8777
+ return errorResult(
8778
+ [
8779
+ `Integration not connected: \`${toolName}\` requires Slack integration.`,
8780
+ "",
8781
+ "To use Slack tools:",
8782
+ "1. Go to https://contextstream.io/settings/integrations",
8783
+ "2. Connect your Slack workspace",
8784
+ "3. Try this command again",
8785
+ "",
8786
+ "Note: Even without explicit Slack tools, context_smart and session_smart_search",
8787
+ "will automatically include relevant Slack context when the integration is connected."
8788
+ ].join("\n")
8789
+ );
8790
+ }
8791
+ if (requiresGithub && !status.github) {
8792
+ return errorResult(
8793
+ [
8794
+ `Integration not connected: \`${toolName}\` requires GitHub integration.`,
8795
+ "",
8796
+ "To use GitHub tools:",
8797
+ "1. Go to https://contextstream.io/settings/integrations",
8798
+ "2. Connect your GitHub repositories",
8799
+ "3. Try this command again",
8800
+ "",
8801
+ "Note: Even without explicit GitHub tools, context_smart and session_smart_search",
8802
+ "will automatically include relevant GitHub context when the integration is connected."
8803
+ ].join("\n")
8804
+ );
8805
+ }
8806
+ if (requiresCrossIntegration && !status.slack && !status.github) {
8807
+ return errorResult(
8808
+ [
8809
+ `Integration not connected: \`${toolName}\` requires at least one integration (Slack or GitHub).`,
8810
+ "",
8811
+ "To use cross-integration tools:",
8812
+ "1. Go to https://contextstream.io/settings/integrations",
8813
+ "2. Connect Slack and/or GitHub",
8814
+ "3. Try this command again"
8815
+ ].join("\n")
8816
+ );
8817
+ }
8818
+ return null;
8819
+ }
8820
+ function shouldRegisterIntegrationTool(toolName) {
8821
+ if (!AUTO_HIDE_INTEGRATIONS) return true;
8822
+ if (!integrationStatus.checked) {
8823
+ return !ALL_INTEGRATION_TOOLS.has(toolName);
8824
+ }
8825
+ if (SLACK_TOOLS.has(toolName)) {
8826
+ return integrationStatus.slack;
8827
+ }
8828
+ if (GITHUB_TOOLS.has(toolName)) {
8829
+ return integrationStatus.github;
8830
+ }
8831
+ if (CROSS_INTEGRATION_TOOLS.has(toolName)) {
8832
+ return integrationStatus.slack || integrationStatus.github;
8833
+ }
8834
+ return true;
8835
+ }
8357
8836
  async function gateIfGraphTool(toolName, input) {
8358
8837
  const requiredTier = graphToolTiers.get(toolName);
8359
8838
  if (!requiredTier) return null;
@@ -8447,22 +8926,28 @@ function registerTools(server, client, sessionManager) {
8447
8926
  });
8448
8927
  };
8449
8928
  }
8450
- function registerTool(name, config, handler) {
8451
- if (toolAllowlist && !toolAllowlist.has(name)) {
8452
- return;
8453
- }
8929
+ function actuallyRegisterTool(name, config, handler) {
8454
8930
  const accessLabel = getToolAccessLabel(name);
8455
8931
  const showUpgrade = accessLabel !== "Free";
8932
+ let finalDescription;
8933
+ let finalSchema;
8934
+ if (COMPACT_SCHEMA_ENABLED) {
8935
+ finalDescription = compactifyDescription(config.description);
8936
+ finalSchema = config.inputSchema ? applyCompactParamDescriptions(config.inputSchema) : void 0;
8937
+ } else {
8938
+ finalDescription = `${config.description}
8939
+
8940
+ Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl2})` : ""}`;
8941
+ finalSchema = config.inputSchema ? applyParamDescriptions(config.inputSchema) : void 0;
8942
+ }
8456
8943
  const labeledConfig = {
8457
8944
  ...config,
8458
- title: `${config.title} (${accessLabel})`,
8459
- description: `${config.description}
8460
-
8461
- Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl2})` : ""}`
8945
+ title: COMPACT_SCHEMA_ENABLED ? config.title : `${config.title} (${accessLabel})`,
8946
+ description: finalDescription
8462
8947
  };
8463
8948
  const annotatedConfig = {
8464
8949
  ...labeledConfig,
8465
- inputSchema: labeledConfig.inputSchema ? applyParamDescriptions(labeledConfig.inputSchema) : void 0,
8950
+ inputSchema: finalSchema,
8466
8951
  annotations: {
8467
8952
  ...inferToolAnnotations(name),
8468
8953
  ...labeledConfig.annotations
@@ -8470,8 +8955,10 @@ Access: ${accessLabel}${showUpgrade ? ` (upgrade: ${upgradeUrl2})` : ""}`
8470
8955
  };
8471
8956
  const safeHandler = async (input, extra) => {
8472
8957
  try {
8473
- const gated = await gateIfProTool(name);
8474
- if (gated) return gated;
8958
+ const proGated = await gateIfProTool(name);
8959
+ if (proGated) return proGated;
8960
+ const integrationGated = await gateIfIntegrationTool(name);
8961
+ if (integrationGated) return integrationGated;
8475
8962
  return await handler(input, extra);
8476
8963
  } catch (error) {
8477
8964
  const errorMessage = error?.message || String(error);
@@ -8496,12 +8983,51 @@ Upgrade: ${upgradeUrl2}` : "";
8496
8983
  };
8497
8984
  }
8498
8985
  };
8499
- server.registerTool(
8986
+ serverRef.registerTool(
8500
8987
  name,
8501
8988
  annotatedConfig,
8502
8989
  wrapWithAutoContext(name, safeHandler)
8503
8990
  );
8504
8991
  }
8992
+ function enableBundle(bundleName) {
8993
+ if (enabledBundles.has(bundleName)) {
8994
+ return { success: true, message: `Bundle '${bundleName}' is already enabled.`, toolsEnabled: 0 };
8995
+ }
8996
+ const bundle = TOOL_BUNDLES[bundleName];
8997
+ if (!bundle) {
8998
+ return { success: false, message: `Unknown bundle '${bundleName}'. Available: ${Object.keys(TOOL_BUNDLES).join(", ")}`, toolsEnabled: 0 };
8999
+ }
9000
+ enabledBundles.add(bundleName);
9001
+ let toolsEnabled = 0;
9002
+ for (const toolName of bundle) {
9003
+ const deferred = deferredTools.get(toolName);
9004
+ if (deferred) {
9005
+ actuallyRegisterTool(deferred.name, deferred.config, deferred.handler);
9006
+ deferredTools.delete(toolName);
9007
+ toolsEnabled++;
9008
+ }
9009
+ }
9010
+ try {
9011
+ const lowLevelServer = serverRef;
9012
+ lowLevelServer.sendToolsListChanged?.();
9013
+ } catch {
9014
+ }
9015
+ console.error(`[ContextStream] Bundle '${bundleName}' enabled with ${toolsEnabled} tools.`);
9016
+ return { success: true, message: `Enabled bundle '${bundleName}' with ${toolsEnabled} tools.`, toolsEnabled };
9017
+ }
9018
+ function registerTool(name, config, handler) {
9019
+ if (toolAllowlist && !toolAllowlist.has(name)) {
9020
+ return;
9021
+ }
9022
+ if (!shouldRegisterIntegrationTool(name)) {
9023
+ return;
9024
+ }
9025
+ if (PROGRESSIVE_MODE && !isToolInEnabledBundles(name)) {
9026
+ deferredTools.set(name, { name, config, handler });
9027
+ return;
9028
+ }
9029
+ actuallyRegisterTool(name, config, handler);
9030
+ }
8505
9031
  function errorResult(text) {
8506
9032
  return {
8507
9033
  content: [{ type: "text", text }],
@@ -8571,6 +9097,56 @@ Upgrade: ${upgradeUrl2}` : "";
8571
9097
  return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
8572
9098
  }
8573
9099
  );
9100
+ registerTool(
9101
+ "tools_enable_bundle",
9102
+ {
9103
+ title: "Enable tool bundle",
9104
+ description: `Enable a bundle of related tools dynamically. Only available when CONTEXTSTREAM_PROGRESSIVE_MODE=true.
9105
+
9106
+ Available bundles:
9107
+ - session: Extended session management (~6 tools)
9108
+ - memory: Full memory CRUD operations (~16 tools)
9109
+ - search: Semantic, hybrid, and keyword search (~3 tools)
9110
+ - graph: Code graph analysis and dependencies (~9 tools)
9111
+ - workspace: Workspace management (~4 tools)
9112
+ - project: Project management and indexing (~10 tools)
9113
+ - reminders: Reminder management (~6 tools)
9114
+ - integrations: Slack and GitHub integrations (~21 tools)
9115
+
9116
+ Example: Enable memory tools before using memory_create_event.`,
9117
+ inputSchema: external_exports.object({
9118
+ bundle: external_exports.enum(["session", "memory", "search", "graph", "workspace", "project", "reminders", "integrations"]).describe("Name of the bundle to enable"),
9119
+ list_bundles: external_exports.boolean().optional().describe("If true, list all available bundles and their status")
9120
+ })
9121
+ },
9122
+ async (input) => {
9123
+ if (input.list_bundles) {
9124
+ const bundles = getBundleInfo();
9125
+ const result2 = {
9126
+ progressive_mode: PROGRESSIVE_MODE,
9127
+ bundles,
9128
+ hint: PROGRESSIVE_MODE ? "Call tools_enable_bundle with a bundle name to enable additional tools." : "Progressive mode is disabled. All tools from your toolset are already available."
9129
+ };
9130
+ return { content: [{ type: "text", text: formatContent(result2) }], structuredContent: toStructured(result2) };
9131
+ }
9132
+ if (!PROGRESSIVE_MODE) {
9133
+ const result2 = {
9134
+ success: true,
9135
+ message: `Progressive mode is disabled. All tools from your toolset are already available. Bundle '${input.bundle}' tools are accessible.`,
9136
+ progressive_mode: false
9137
+ };
9138
+ return { content: [{ type: "text", text: formatContent(result2) }], structuredContent: toStructured(result2) };
9139
+ }
9140
+ const result = enableBundle(input.bundle);
9141
+ const response = {
9142
+ ...result,
9143
+ progressive_mode: true,
9144
+ enabled_bundles: Array.from(enabledBundles),
9145
+ hint: result.success && result.toolsEnabled > 0 ? "New tools are now available. The client should refresh its tool list." : void 0
9146
+ };
9147
+ return { content: [{ type: "text", text: formatContent(response) }], structuredContent: toStructured(response) };
9148
+ }
9149
+ );
8574
9150
  registerTool(
8575
9151
  "workspaces_list",
8576
9152
  {
@@ -9679,6 +10255,21 @@ This does semantic search on the first message. You only need context_smart on s
9679
10255
  if (sessionManager) {
9680
10256
  sessionManager.markInitialized(result);
9681
10257
  }
10258
+ const workspaceId = typeof result.workspace_id === "string" ? result.workspace_id : void 0;
10259
+ if (workspaceId && AUTO_HIDE_INTEGRATIONS) {
10260
+ try {
10261
+ const intStatus = await checkIntegrationStatus(workspaceId);
10262
+ updateIntegrationStatus(intStatus, workspaceId);
10263
+ result.integrations = {
10264
+ slack_connected: intStatus.slack,
10265
+ github_connected: intStatus.github,
10266
+ auto_hide_enabled: true,
10267
+ hint: intStatus.slack || intStatus.github ? "Integration tools are now available in the tool list." : "Connect integrations at https://contextstream.io/settings/integrations to enable Slack/GitHub tools."
10268
+ };
10269
+ } catch (error) {
10270
+ console.error("[ContextStream] Failed to check integration status in session_init:", error);
10271
+ }
10272
+ }
9682
10273
  const status = typeof result.status === "string" ? result.status : "";
9683
10274
  const workspaceWarning = typeof result.workspace_warning === "string" ? result.workspace_warning : "";
9684
10275
  let text = formatContent(result);
@@ -9757,9 +10348,26 @@ Memory: events(crud) nodes(knowledge) search(find) decisions(choices)`,
9757
10348
  async (input) => {
9758
10349
  const format = input.format || "grouped";
9759
10350
  const catalog = generateToolCatalog(format, input.category);
10351
+ let bundleInfo = "";
10352
+ if (PROGRESSIVE_MODE) {
10353
+ const bundles = getBundleInfo();
10354
+ const enabledList = bundles.filter((b) => b.enabled).map((b) => b.name).join(", ");
10355
+ const availableList = bundles.filter((b) => !b.enabled).map((b) => `${b.name}(${b.size})`).join(", ");
10356
+ bundleInfo = `
10357
+
10358
+ [Progressive Mode]
10359
+ Enabled: ${enabledList}
10360
+ Available: ${availableList}
10361
+ Use tools_enable_bundle to unlock more tools.`;
10362
+ }
9760
10363
  return {
9761
- content: [{ type: "text", text: catalog }],
9762
- structuredContent: { format, catalog }
10364
+ content: [{ type: "text", text: catalog + bundleInfo }],
10365
+ structuredContent: {
10366
+ format,
10367
+ catalog,
10368
+ progressive_mode: PROGRESSIVE_MODE,
10369
+ bundles: PROGRESSIVE_MODE ? getBundleInfo() : void 0
10370
+ }
9763
10371
  };
9764
10372
  }
9765
10373
  );
@@ -11242,6 +11850,15 @@ Use this to verify integrations are healthy and syncing properly.`,
11242
11850
  return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
11243
11851
  }
11244
11852
  const result = await client.integrationsStatus({ workspace_id: workspaceId });
11853
+ if (AUTO_HIDE_INTEGRATIONS) {
11854
+ const slackConnected = result?.some(
11855
+ (s) => s.provider === "slack" && s.status === "connected"
11856
+ ) ?? false;
11857
+ const githubConnected = result?.some(
11858
+ (s) => s.provider === "github" && s.status === "connected"
11859
+ ) ?? false;
11860
+ updateIntegrationStatus({ slack: slackConnected, github: githubConnected }, workspaceId);
11861
+ }
11245
11862
  if (result.length === 0) {
11246
11863
  return { content: [{ type: "text", text: "No integrations configured for this workspace." }] };
11247
11864
  }
@@ -13524,14 +14141,15 @@ Created API key: ${maskedNewKey}
13524
14141
  console.log(`
13525
14142
  Detected plan: ${planLabel} (graph: ${graphTierLabel})`);
13526
14143
  console.log("\nMCP toolset (which tools to expose to the AI):");
13527
- console.log(" 1) Light \u2014 core session, project, and basic memory/graph tools (~30 tools)");
13528
- console.log(" Best for: faster responses, simpler workflows, resource-constrained environments");
13529
- console.log(" 2) Standard (recommended) \u2014 adds workspace, memory CRUD, graph analysis, search (~50 tools)");
14144
+ console.log(" 1) Light \u2014 core session, project, and basic memory/graph tools (~31 tools)");
14145
+ console.log(" Best for: Claude Code, faster responses, token-constrained environments");
14146
+ console.log(" 2) Standard (recommended) \u2014 adds workspace, memory CRUD, graph analysis, search (~58 tools)");
13530
14147
  console.log(" Best for: most users, full development workflow with memory + code analysis");
13531
14148
  console.log(" 3) Complete \u2014 all tools including full graph + AI, GitHub, Slack integrations (~86 tools)");
13532
14149
  console.log(" Best for: Elite users or power users needing full graph + integrations");
13533
14150
  console.log("");
13534
- console.log(" Tip: Elite users should choose Complete to unlock full graph tools (call path, path, circular, unused).");
14151
+ console.log(" Note: Slack/GitHub tools are auto-hidden until you connect those integrations.");
14152
+ console.log(" Tip: Elite users should choose Complete to unlock full graph tools.");
13535
14153
  console.log(" Tip: Change later by setting CONTEXTSTREAM_TOOLSET=light|standard|complete");
13536
14154
  const toolsetDefault = detectedGraphTier === "full" ? "3" : "2";
13537
14155
  const toolsetChoice = normalizeInput(await rl.question(`Choose [1/2/3] (default ${toolsetDefault}): `)) || toolsetDefault;
@@ -13846,8 +14464,9 @@ Applying to ${projects.length} project(s)...`);
13846
14464
  const skipped = writeActions.filter((a) => a.status === "skipped").length;
13847
14465
  const dry = writeActions.filter((a) => a.status === "dry-run").length;
13848
14466
  console.log(`Summary: ${created} created, ${updated} updated, ${appended} appended, ${skipped} skipped, ${dry} dry-run.`);
13849
- const toolsetDesc = toolset === "light" ? "~30 tools" : toolset === "complete" ? "~86 tools" : "~50 tools";
14467
+ const toolsetDesc = toolset === "light" ? "~31 tools" : toolset === "complete" ? "~86 tools" : "~58 tools";
13850
14468
  console.log(`Toolset: ${toolset} (${toolsetDesc})`);
14469
+ console.log(`Auto-hide: Slack/GitHub tools hidden until integrations connected.`);
13851
14470
  }
13852
14471
  console.log("\nNext steps:");
13853
14472
  console.log("- Restart your editor/CLI after changing MCP config or rules.");
@@ -13911,6 +14530,10 @@ Environment variables:
13911
14530
  CONTEXTSTREAM_PROJECT_ID Optional default project ID
13912
14531
  CONTEXTSTREAM_TOOLSET Tool mode: light|standard|complete (default: standard)
13913
14532
  CONTEXTSTREAM_TOOL_ALLOWLIST Optional comma-separated tool names to expose (overrides toolset)
14533
+ CONTEXTSTREAM_AUTO_TOOLSET Auto-detect client and adjust toolset (default: false)
14534
+ CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS Auto-hide Slack/GitHub tools when not connected (default: true)
14535
+ CONTEXTSTREAM_SCHEMA_MODE Schema verbosity: compact|full (default: full, compact reduces tokens)
14536
+ CONTEXTSTREAM_PROGRESSIVE_MODE Progressive disclosure: true|false (default: false, starts with ~13 core tools)
13914
14537
  CONTEXTSTREAM_PRO_TOOLS Optional comma-separated PRO tool names (default: AI tools)
13915
14538
  CONTEXTSTREAM_UPGRADE_URL Optional upgrade URL shown for PRO tools on Free plan
13916
14539
  CONTEXTSTREAM_ENABLE_PROMPTS Enable MCP prompts list (default: true)
@@ -13960,6 +14583,7 @@ async function main() {
13960
14583
  name: "contextstream-mcp",
13961
14584
  version: VERSION
13962
14585
  });
14586
+ setupClientDetection(server);
13963
14587
  const sessionManager = new SessionManager(server, client);
13964
14588
  registerTools(server, client, sessionManager);
13965
14589
  registerResources(server, client, config.apiUrl);
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.3.70",
4
+ "version": "0.3.72",
5
5
  "description": "MCP server exposing ContextStream public API - code context, memory, search, and AI tools for developers",
6
6
  "type": "module",
7
7
  "license": "MIT",