@contextstream/mcp-server 0.4.30 → 0.4.32

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 (3) hide show
  1. package/README.md +460 -460
  2. package/dist/index.js +730 -80
  3. package/package.json +70 -70
package/dist/index.js CHANGED
@@ -7436,7 +7436,7 @@ W:${wsHint}
7436
7436
  const query = new URLSearchParams();
7437
7437
  if (params?.days) query.set("days", String(params.days));
7438
7438
  const suffix = query.toString() ? `?${query.toString()}` : "";
7439
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/stats${suffix}`, {
7439
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/slack/stats${suffix}`, {
7440
7440
  method: "GET"
7441
7441
  });
7442
7442
  }
@@ -7452,7 +7452,7 @@ W:${wsHint}
7452
7452
  if (params?.page) query.set("page", String(params.page));
7453
7453
  if (params?.per_page) query.set("per_page", String(params.per_page));
7454
7454
  const suffix = query.toString() ? `?${query.toString()}` : "";
7455
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/users${suffix}`, {
7455
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/slack/users${suffix}`, {
7456
7456
  method: "GET"
7457
7457
  });
7458
7458
  }
@@ -7464,7 +7464,7 @@ W:${wsHint}
7464
7464
  if (!withDefaults.workspace_id) {
7465
7465
  throw new Error("workspace_id is required for Slack channels");
7466
7466
  }
7467
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/channels`, {
7467
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/slack/channels`, {
7468
7468
  method: "GET"
7469
7469
  });
7470
7470
  }
@@ -7483,7 +7483,7 @@ W:${wsHint}
7483
7483
  const suffix = query.toString() ? `?${query.toString()}` : "";
7484
7484
  return request(
7485
7485
  this.config,
7486
- `/workspaces/${withDefaults.workspace_id}/slack/activity${suffix}`,
7486
+ `/integrations/workspaces/${withDefaults.workspace_id}/slack/activity${suffix}`,
7487
7487
  { method: "GET" }
7488
7488
  );
7489
7489
  }
@@ -7500,7 +7500,7 @@ W:${wsHint}
7500
7500
  const suffix = query.toString() ? `?${query.toString()}` : "";
7501
7501
  return request(
7502
7502
  this.config,
7503
- `/workspaces/${withDefaults.workspace_id}/slack/discussions${suffix}`,
7503
+ `/integrations/workspaces/${withDefaults.workspace_id}/slack/discussions${suffix}`,
7504
7504
  { method: "GET" }
7505
7505
  );
7506
7506
  }
@@ -7517,7 +7517,7 @@ W:${wsHint}
7517
7517
  const suffix = query.toString() ? `?${query.toString()}` : "";
7518
7518
  return request(
7519
7519
  this.config,
7520
- `/workspaces/${withDefaults.workspace_id}/slack/contributors${suffix}`,
7520
+ `/integrations/workspaces/${withDefaults.workspace_id}/slack/contributors${suffix}`,
7521
7521
  { method: "GET" }
7522
7522
  );
7523
7523
  }
@@ -7529,7 +7529,7 @@ W:${wsHint}
7529
7529
  if (!withDefaults.workspace_id) {
7530
7530
  throw new Error("workspace_id is required for syncing Slack users");
7531
7531
  }
7532
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/sync-users`, {
7532
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/slack/sync-users`, {
7533
7533
  method: "POST"
7534
7534
  });
7535
7535
  }
@@ -7546,7 +7546,7 @@ W:${wsHint}
7546
7546
  if (params?.limit) query.set("limit", String(params.limit));
7547
7547
  return request(
7548
7548
  this.config,
7549
- `/workspaces/${withDefaults.workspace_id}/slack/search?${query.toString()}`,
7549
+ `/integrations/workspaces/${withDefaults.workspace_id}/slack/search?${query.toString()}`,
7550
7550
  { method: "GET" }
7551
7551
  );
7552
7552
  }
@@ -7561,7 +7561,7 @@ W:${wsHint}
7561
7561
  if (!withDefaults.workspace_id) {
7562
7562
  throw new Error("workspace_id is required for GitHub stats");
7563
7563
  }
7564
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/stats`, {
7564
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/github/stats`, {
7565
7565
  method: "GET"
7566
7566
  });
7567
7567
  }
@@ -7573,7 +7573,7 @@ W:${wsHint}
7573
7573
  if (!withDefaults.workspace_id) {
7574
7574
  throw new Error("workspace_id is required for GitHub repos");
7575
7575
  }
7576
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/repos`, {
7576
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/github/repos`, {
7577
7577
  method: "GET"
7578
7578
  });
7579
7579
  }
@@ -7593,7 +7593,7 @@ W:${wsHint}
7593
7593
  const suffix = query.toString() ? `?${query.toString()}` : "";
7594
7594
  return request(
7595
7595
  this.config,
7596
- `/workspaces/${withDefaults.workspace_id}/github/activity${suffix}`,
7596
+ `/integrations/workspaces/${withDefaults.workspace_id}/github/activity${suffix}`,
7597
7597
  { method: "GET" }
7598
7598
  );
7599
7599
  }
@@ -7611,7 +7611,7 @@ W:${wsHint}
7611
7611
  if (params?.state) query.set("state", params.state);
7612
7612
  if (params?.repo) query.set("repo", params.repo);
7613
7613
  const suffix = query.toString() ? `?${query.toString()}` : "";
7614
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/issues${suffix}`, {
7614
+ return request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/github/issues${suffix}`, {
7615
7615
  method: "GET"
7616
7616
  });
7617
7617
  }
@@ -7628,7 +7628,7 @@ W:${wsHint}
7628
7628
  const suffix = query.toString() ? `?${query.toString()}` : "";
7629
7629
  return request(
7630
7630
  this.config,
7631
- `/workspaces/${withDefaults.workspace_id}/github/contributors${suffix}`,
7631
+ `/integrations/workspaces/${withDefaults.workspace_id}/github/contributors${suffix}`,
7632
7632
  { method: "GET" }
7633
7633
  );
7634
7634
  }
@@ -7645,7 +7645,7 @@ W:${wsHint}
7645
7645
  if (params?.limit) query.set("limit", String(params.limit));
7646
7646
  return request(
7647
7647
  this.config,
7648
- `/workspaces/${withDefaults.workspace_id}/github/search?${query.toString()}`,
7648
+ `/integrations/workspaces/${withDefaults.workspace_id}/github/search?${query.toString()}`,
7649
7649
  { method: "GET" }
7650
7650
  );
7651
7651
  }
@@ -7663,7 +7663,7 @@ W:${wsHint}
7663
7663
  const suffix = query.toString() ? `?${query.toString()}` : "";
7664
7664
  return request(
7665
7665
  this.config,
7666
- `/workspaces/${withDefaults.workspace_id}/github/knowledge${suffix}`,
7666
+ `/integrations/workspaces/${withDefaults.workspace_id}/github/knowledge${suffix}`,
7667
7667
  { method: "GET" }
7668
7668
  );
7669
7669
  }
@@ -7681,7 +7681,7 @@ W:${wsHint}
7681
7681
  const suffix = query.toString() ? `?${query.toString()}` : "";
7682
7682
  return request(
7683
7683
  this.config,
7684
- `/workspaces/${withDefaults.workspace_id}/slack/knowledge${suffix}`,
7684
+ `/integrations/workspaces/${withDefaults.workspace_id}/slack/knowledge${suffix}`,
7685
7685
  { method: "GET" }
7686
7686
  );
7687
7687
  }
@@ -7693,9 +7693,10 @@ W:${wsHint}
7693
7693
  if (!withDefaults.workspace_id) {
7694
7694
  throw new Error("workspace_id is required for integrations status");
7695
7695
  }
7696
- return request(this.config, `/workspaces/${withDefaults.workspace_id}/integrations/status`, {
7696
+ const result = await request(this.config, `/integrations/workspaces/${withDefaults.workspace_id}/integrations/status`, {
7697
7697
  method: "GET"
7698
7698
  });
7699
+ return unwrapApiResponse(result);
7699
7700
  }
7700
7701
  /**
7701
7702
  * Get GitHub summary for a workspace
@@ -7774,6 +7775,187 @@ W:${wsHint}
7774
7775
  });
7775
7776
  }
7776
7777
  // ============================================
7778
+ // Notion Integration Methods
7779
+ // ============================================
7780
+ /**
7781
+ * Create a page in Notion
7782
+ */
7783
+ async createNotionPage(params) {
7784
+ const withDefaults = this.withDefaults(params || {});
7785
+ if (!withDefaults.workspace_id) {
7786
+ throw new Error("workspace_id is required for creating Notion pages");
7787
+ }
7788
+ return request(this.config, `/integrations/notion/pages?workspace_id=${withDefaults.workspace_id}`, {
7789
+ method: "POST",
7790
+ body: {
7791
+ title: params.title,
7792
+ content: params.content,
7793
+ parent_database_id: params.parent_database_id,
7794
+ parent_page_id: params.parent_page_id
7795
+ }
7796
+ });
7797
+ }
7798
+ /**
7799
+ * Get Notion integration statistics and overview
7800
+ */
7801
+ async notionStats(params) {
7802
+ const withDefaults = this.withDefaults(params || {});
7803
+ if (!withDefaults.workspace_id) {
7804
+ throw new Error("workspace_id is required for Notion stats");
7805
+ }
7806
+ const query = new URLSearchParams();
7807
+ if (params?.days) query.set("days", String(params.days));
7808
+ const suffix = query.toString() ? `?${query.toString()}` : "";
7809
+ return request(
7810
+ this.config,
7811
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/stats${suffix}`,
7812
+ { method: "GET" }
7813
+ );
7814
+ }
7815
+ /**
7816
+ * Get recent Notion activity feed
7817
+ */
7818
+ async notionActivity(params) {
7819
+ const withDefaults = this.withDefaults(params || {});
7820
+ if (!withDefaults.workspace_id) {
7821
+ throw new Error("workspace_id is required for Notion activity");
7822
+ }
7823
+ const query = new URLSearchParams();
7824
+ if (params?.limit) query.set("limit", String(params.limit));
7825
+ if (params?.database_id) query.set("database_id", params.database_id);
7826
+ const suffix = query.toString() ? `?${query.toString()}` : "";
7827
+ return request(
7828
+ this.config,
7829
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/activity${suffix}`,
7830
+ { method: "GET" }
7831
+ );
7832
+ }
7833
+ /**
7834
+ * Get knowledge extracted from Notion (decisions, lessons, insights)
7835
+ */
7836
+ async notionKnowledge(params) {
7837
+ const withDefaults = this.withDefaults(params || {});
7838
+ if (!withDefaults.workspace_id) {
7839
+ throw new Error("workspace_id is required for Notion knowledge");
7840
+ }
7841
+ const query = new URLSearchParams();
7842
+ if (params?.limit) query.set("limit", String(params.limit));
7843
+ if (params?.node_type) query.set("node_type", params.node_type);
7844
+ const suffix = query.toString() ? `?${query.toString()}` : "";
7845
+ return request(
7846
+ this.config,
7847
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/knowledge${suffix}`,
7848
+ { method: "GET" }
7849
+ );
7850
+ }
7851
+ /**
7852
+ * Get Notion summary for a workspace
7853
+ */
7854
+ async notionSummary(params) {
7855
+ const withDefaults = this.withDefaults(params || {});
7856
+ if (!withDefaults.workspace_id) {
7857
+ throw new Error("workspace_id is required for Notion summary");
7858
+ }
7859
+ const query = new URLSearchParams();
7860
+ if (params?.days) query.set("days", String(params.days));
7861
+ if (params?.database_id) query.set("database_id", params.database_id);
7862
+ const suffix = query.toString() ? `?${query.toString()}` : "";
7863
+ return request(
7864
+ this.config,
7865
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/summary${suffix}`,
7866
+ { method: "GET" }
7867
+ );
7868
+ }
7869
+ /**
7870
+ * List Notion databases available in the workspace
7871
+ */
7872
+ async notionListDatabases(params) {
7873
+ const withDefaults = this.withDefaults(params || {});
7874
+ if (!withDefaults.workspace_id) {
7875
+ throw new Error("workspace_id is required for listing Notion databases");
7876
+ }
7877
+ return request(
7878
+ this.config,
7879
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/databases`,
7880
+ { method: "GET" }
7881
+ );
7882
+ }
7883
+ /**
7884
+ * Search/list pages in Notion
7885
+ */
7886
+ async notionSearchPages(params) {
7887
+ const withDefaults = this.withDefaults(params || {});
7888
+ if (!withDefaults.workspace_id) {
7889
+ throw new Error("workspace_id is required for searching Notion pages");
7890
+ }
7891
+ const query = new URLSearchParams();
7892
+ if (params?.query) query.set("query", params.query);
7893
+ if (params?.database_id) query.set("database_id", params.database_id);
7894
+ if (params?.limit) query.set("limit", String(params.limit));
7895
+ const suffix = query.toString() ? `?${query.toString()}` : "";
7896
+ return request(
7897
+ this.config,
7898
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/pages${suffix}`,
7899
+ { method: "GET" }
7900
+ );
7901
+ }
7902
+ /**
7903
+ * Get a specific Notion page with its content
7904
+ */
7905
+ async notionGetPage(params) {
7906
+ const withDefaults = this.withDefaults(params || {});
7907
+ if (!withDefaults.workspace_id) {
7908
+ throw new Error("workspace_id is required for getting Notion page");
7909
+ }
7910
+ return request(
7911
+ this.config,
7912
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/pages/${params.page_id}`,
7913
+ { method: "GET" }
7914
+ );
7915
+ }
7916
+ /**
7917
+ * Query a Notion database (get rows with optional filters)
7918
+ */
7919
+ async notionQueryDatabase(params) {
7920
+ const withDefaults = this.withDefaults(params || {});
7921
+ if (!withDefaults.workspace_id) {
7922
+ throw new Error("workspace_id is required for querying Notion database");
7923
+ }
7924
+ return request(
7925
+ this.config,
7926
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/databases/${params.database_id}/query`,
7927
+ {
7928
+ method: "POST",
7929
+ body: {
7930
+ filter: params.filter,
7931
+ sorts: params.sorts,
7932
+ page_size: params.limit
7933
+ }
7934
+ }
7935
+ );
7936
+ }
7937
+ /**
7938
+ * Update a Notion page
7939
+ */
7940
+ async notionUpdatePage(params) {
7941
+ const withDefaults = this.withDefaults(params || {});
7942
+ if (!withDefaults.workspace_id) {
7943
+ throw new Error("workspace_id is required for updating Notion page");
7944
+ }
7945
+ return request(
7946
+ this.config,
7947
+ `/integrations/workspaces/${withDefaults.workspace_id}/notion/pages/${params.page_id}`,
7948
+ {
7949
+ method: "PATCH",
7950
+ body: {
7951
+ title: params.title,
7952
+ content: params.content,
7953
+ properties: params.properties
7954
+ }
7955
+ }
7956
+ );
7957
+ }
7958
+ // ============================================
7777
7959
  // Reminder Methods
7778
7960
  // ============================================
7779
7961
  /**
@@ -8020,13 +8202,76 @@ function applyMcpToolPrefix(markdown, toolPrefix) {
8020
8202
  return markdown.replace(toolRegex, `${toolPrefix}$1`);
8021
8203
  }
8022
8204
  var CONTEXTSTREAM_RULES_FULL = `
8023
- ## \u{1F6A8} CRITICAL RULE - READ EVERY TURN \u{1F6A8}
8205
+ ## \u{1F6A8} CRITICAL RULE #1 - CONTEXTSTREAM SEARCH FIRST \u{1F6A8}
8024
8206
 
8025
- **BEFORE using Glob, Grep, Search, Read (for discovery), or Explore:**
8207
+ **BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local file scanning:**
8026
8208
  \`\`\`
8027
8209
  STOP \u2192 Call search(mode="hybrid", query="...") FIRST
8028
8210
  \`\`\`
8029
- Only use local tools if ContextStream returns 0 results. This applies to EVERY search throughout the ENTIRE conversation, not just the first message.
8211
+
8212
+ **Claude Code users:** Your tool names are \`mcp__contextstream__search\`, \`mcp__contextstream__session_init\`, etc.
8213
+
8214
+ \u274C **NEVER DO THIS:**
8215
+ - \`Glob("**/*.ts")\` \u2192 Use \`search(mode="pattern", query="*.ts")\` instead
8216
+ - \`Grep("functionName")\` \u2192 Use \`search(mode="keyword", query="functionName")\` instead
8217
+ - \`Read(file)\` for discovery \u2192 Use \`search(mode="hybrid", query="...")\` instead
8218
+ - \`Task(subagent_type="Explore")\` \u2192 Use \`search(mode="hybrid")\` instead
8219
+
8220
+ \u2705 **ALWAYS DO THIS:**
8221
+ 1. \`search(mode="hybrid", query="what you're looking for")\`
8222
+ 2. Only use local tools (Glob/Grep/Read) if ContextStream returns **0 results**
8223
+ 3. Use Read ONLY for exact file edits after you know the file path
8224
+
8225
+ This applies to **EVERY search** throughout the **ENTIRE conversation**, not just the first message.
8226
+
8227
+ ---
8228
+
8229
+ ## \u{1F6A8} CRITICAL RULE #2 - AUTO-INDEXING \u{1F6A8}
8230
+
8231
+ **ContextStream auto-indexes your project on \`session_init\`.** You do NOT need to:
8232
+ - Ask the user to index
8233
+ - Manually trigger ingestion
8234
+ - Check index_status before every search
8235
+
8236
+ **When \`session_init\` returns \`indexing_status: "started"\` or \`"refreshing"\`:**
8237
+ - Background indexing is running automatically
8238
+ - Search results will be available within seconds to minutes
8239
+ - **DO NOT fall back to local tools** - wait for ContextStream search to work
8240
+ - If search returns 0 results initially, try again after a moment
8241
+
8242
+ **Only manually trigger indexing if:**
8243
+ - \`session_init\` returned \`ingest_recommendation.recommended: true\` (rare edge case)
8244
+ - User explicitly asks to re-index
8245
+
8246
+ ---
8247
+
8248
+ ## \u{1F6A8} CRITICAL RULE #3 - LESSONS (PAST MISTAKES) \u{1F6A8}
8249
+
8250
+ **Lessons are past mistakes that MUST inform your work.** Ignoring lessons leads to repeated failures.
8251
+
8252
+ ### On \`session_init\`:
8253
+ - Check for \`lessons\` and \`lessons_warning\` in the response
8254
+ - If present, **READ THEM IMMEDIATELY** before doing any work
8255
+ - These are high-priority lessons (critical/high severity) relevant to your context
8256
+ - **Apply the prevention steps** from each lesson to avoid repeating mistakes
8257
+
8258
+ ### On \`context_smart\`:
8259
+ - Check for any lessons in the returned context
8260
+ - Lessons may be included based on semantic relevance to the user's message
8261
+
8262
+ ### Before ANY Non-Trivial Work:
8263
+ **ALWAYS call \`session(action="get_lessons", query="<topic>")\`** where \`<topic>\` matches what you're about to do:
8264
+ - Before refactoring \u2192 \`session(action="get_lessons", query="refactoring")\`
8265
+ - Before API changes \u2192 \`session(action="get_lessons", query="API changes")\`
8266
+ - Before database work \u2192 \`session(action="get_lessons", query="database migrations")\`
8267
+ - Before deployments \u2192 \`session(action="get_lessons", query="deployment")\`
8268
+
8269
+ ### When Lessons Are Found:
8270
+ 1. **Summarize the lessons** to the user before proceeding
8271
+ 2. **Explicitly state how you will avoid the past mistakes**
8272
+ 3. If a lesson conflicts with the current approach, **warn the user**
8273
+
8274
+ **Failing to check lessons before risky work is a critical error.**
8030
8275
 
8031
8276
  ---
8032
8277
 
@@ -8040,17 +8285,20 @@ Rules Version: ${RULES_VERSION}
8040
8285
 
8041
8286
  | Message | What to Call |
8042
8287
  |---------|--------------|
8043
- | **1st message** | \`session_init(folder_path="...", context_hint="<user's message>")\`, then \`context_smart(user_message="<user's message>", format="minified", max_tokens=400)\` |
8288
+ | **1st message** | \`session_init(folder_path="...", context_hint="<user's message>")\`, then \`context_smart(...)\` |
8289
+ | **\u26A0\uFE0F After session_init** | **CHECK \`lessons\` field** - if present, read and apply them BEFORE any work |
8044
8290
  | **2nd+ messages** | \`context_smart(user_message="<user's message>", format="minified", max_tokens=400)\` |
8045
8291
  | **\u{1F50D} ANY code search** | \`search(mode="hybrid", query="...")\` \u2014 ALWAYS before Glob/Grep/Search/Read |
8046
- | **Before risky/non-trivial work** | \`session(action="get_lessons", query="<topic>")\` |
8292
+ | **\u26A0\uFE0F Before ANY risky work** | \`session(action="get_lessons", query="<topic>")\` \u2014 **MANDATORY, not optional** |
8047
8293
  | **After completing task** | \`session(action="capture", event_type="decision", ...)\` - MUST capture |
8048
8294
  | **User frustration/correction** | \`session(action="capture_lesson", ...)\` - MUST capture lessons |
8049
8295
  | **Command/tool error + fix** | \`session(action="capture_lesson", ...)\` - MUST capture lessons |
8050
8296
 
8051
8297
  **NO EXCEPTIONS.** Do not skip even if you think you have enough context.
8052
8298
 
8053
- **First message rule:** After \`session_init\`, always call \`context_smart\` before any other tool or response.
8299
+ **First message rule:** After \`session_init\`:
8300
+ 1. Check for \`lessons\` in response - if present, READ and SUMMARIZE them to user
8301
+ 2. Then call \`context_smart\` before any other tool or response
8054
8302
 
8055
8303
  **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).
8056
8304
 
@@ -8126,18 +8374,20 @@ If context still feels missing, use \`session(action="recall", query="...")\` fo
8126
8374
 
8127
8375
  ---
8128
8376
 
8129
- ### Index & Graph Preflight (REQUIRED for code/file search)
8377
+ ### Index Status (Auto-Managed)
8130
8378
 
8131
- Before searching files or code, confirm the project is indexed and the graph is available:
8379
+ **Indexing is automatic.** After \`session_init\`, the project is auto-indexed in the background.
8132
8380
 
8133
- 1. \`project(action="index_status")\` for the current project
8134
- 2. If missing/stale:
8135
- - Local repo: \`project(action="ingest_local", path="<cwd>")\`
8136
- - Otherwise: \`project(action="index")\`
8137
- 3. If graph queries are empty/unavailable: \`graph(action="ingest")\`
8138
- 4. If indexing is in progress, tell the user and wait; do not fall back to local scans.
8381
+ **You do NOT need to manually check index_status before every search.** Just use \`search()\`.
8139
8382
 
8140
- Only after this preflight, proceed with search/analysis below.
8383
+ **If search returns 0 results and you expected matches:**
8384
+ 1. Check if \`session_init\` returned \`indexing_status: "started"\` - indexing may still be in progress
8385
+ 2. Wait a moment and retry \`search()\`
8386
+ 3. Only as a last resort: \`project(action="index_status")\` to check
8387
+
8388
+ **Graph data:** If graph queries (\`dependencies\`, \`impact\`) return empty, run \`graph(action="ingest")\` once.
8389
+
8390
+ **NEVER fall back to local tools (Glob/Grep/Read) just because search returned 0 results on first try.** Retry first.
8141
8391
 
8142
8392
  ### Search & Code Intelligence (ContextStream-first)
8143
8393
 
@@ -8223,13 +8473,44 @@ If ContextStream returns results, stop and use them. NEVER use local Search/Expl
8223
8473
 
8224
8474
  ---
8225
8475
 
8226
- ### Plans & Tasks
8476
+ ### \u{1F6A8} Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS \u{1F6A8}
8477
+
8478
+ **CRITICAL: When the user requests planning, implementation plans, roadmaps, task breakdowns, or step-by-step approaches:**
8479
+
8480
+ \u274C **DO NOT** use built-in plan mode (EnterPlanMode tool)
8481
+ \u274C **DO NOT** write plans to markdown files or plan documents
8482
+ \u274C **DO NOT** ask "should I create a plan file?"
8483
+
8484
+ \u2705 **ALWAYS** use ContextStream's plan/task system instead
8227
8485
 
8228
- When user asks to create a plan or implementation roadmap:
8229
- 1. Create plan: \`session(action="capture_plan", title="Plan Title", description="...", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1}, ...])\`
8230
- 2. Get plan_id from response, then create tasks: \`memory(action="create_task", title="Task Title", plan_id="<plan_id>", priority="high|medium|low", description="...")\`
8486
+ **Trigger phrases to detect (use ContextStream immediately):**
8487
+ - "create a plan", "make a plan", "plan this", "plan for"
8488
+ - "implementation plan", "roadmap", "milestones"
8489
+ - "break down", "breakdown", "break this into steps"
8490
+ - "what are the steps", "step by step", "outline the approach"
8491
+ - "task list", "todo list", "action items"
8492
+ - "how should we approach", "implementation strategy"
8231
8493
 
8232
- To manage existing plans/tasks:
8494
+ **When detected, immediately:**
8495
+
8496
+ 1. **Create the plan in ContextStream:**
8497
+ \`\`\`
8498
+ session(action="capture_plan", title="<descriptive title>", description="<what this plan accomplishes>", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1, description: "..."}, ...])
8499
+ \`\`\`
8500
+
8501
+ 2. **Create tasks for each step:**
8502
+ \`\`\`
8503
+ memory(action="create_task", title="<task title>", plan_id="<plan_id from step 1>", priority="high|medium|low", description="<detailed task description>")
8504
+ \`\`\`
8505
+
8506
+ **Why ContextStream plans are better:**
8507
+ - Plans persist across sessions and are searchable
8508
+ - Tasks track status (pending/in_progress/completed/blocked)
8509
+ - Context is preserved with workspace/project association
8510
+ - Can be retrieved with \`session(action="get_plan", plan_id="...", include_tasks=true)\`
8511
+ - Future sessions can continue from where you left off
8512
+
8513
+ **Managing plans/tasks:**
8233
8514
  - List plans: \`session(action="list_plans")\`
8234
8515
  - Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
8235
8516
  - List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
@@ -8268,13 +8549,35 @@ To manage existing plans/tasks:
8268
8549
  See full documentation: https://contextstream.io/docs/mcp/tools
8269
8550
  `.trim();
8270
8551
  var CONTEXTSTREAM_RULES_MINIMAL = `
8271
- ## \u{1F6A8} CRITICAL RULE - READ EVERY TURN \u{1F6A8}
8552
+ ## \u{1F6A8} CRITICAL: CONTEXTSTREAM SEARCH FIRST \u{1F6A8}
8272
8553
 
8273
- **BEFORE using Glob, Grep, Search, Read (for discovery), or Explore:**
8554
+ **BEFORE using Glob, Grep, Search, Read (for discovery), Explore, or ANY local scanning:**
8274
8555
  \`\`\`
8275
8556
  STOP \u2192 Call search(mode="hybrid", query="...") FIRST
8276
8557
  \`\`\`
8277
- Only use local tools if ContextStream returns 0 results. This applies to EVERY search throughout the ENTIRE conversation, not just the first message.
8558
+
8559
+ **Claude Code:** Tools are \`mcp__contextstream__search\`, \`mcp__contextstream__session_init\`, etc.
8560
+
8561
+ \u274C **NEVER:** \`Glob\`, \`Grep\`, \`Read\` for discovery, \`Task(Explore)\`
8562
+ \u2705 **ALWAYS:** \`search(mode="hybrid", query="...")\` first, local tools ONLY if 0 results
8563
+
8564
+ ---
8565
+
8566
+ ## \u{1F6A8} AUTO-INDEXING \u{1F6A8}
8567
+
8568
+ **\`session_init\` auto-indexes your project.** No manual ingestion needed.
8569
+
8570
+ If \`indexing_status: "started"\`: Search will work shortly. **DO NOT fall back to local tools.**
8571
+
8572
+ ---
8573
+
8574
+ ## \u{1F6A8} LESSONS (PAST MISTAKES) - CRITICAL \u{1F6A8}
8575
+
8576
+ **After \`session_init\`:** Check for \`lessons\` field. If present, **READ and APPLY** before any work.
8577
+
8578
+ **Before ANY risky work:** \`session(action="get_lessons", query="<topic>")\` \u2014 **MANDATORY**
8579
+
8580
+ **When lessons found:** Summarize to user, state how you'll avoid past mistakes.
8278
8581
 
8279
8582
  ---
8280
8583
 
@@ -8287,11 +8590,12 @@ Rules Version: ${RULES_VERSION}
8287
8590
 
8288
8591
  | Message | What to Call |
8289
8592
  |---------|--------------|
8290
- | **1st message** | \`session_init(folder_path="<cwd>", context_hint="<user_message>")\`, then \`context_smart(user_message="<user_message>", format="minified", max_tokens=400)\` |
8593
+ | **1st message** | \`session_init(folder_path="<cwd>", context_hint="<user_message>")\`, then \`context_smart(...)\` |
8594
+ | **\u26A0\uFE0F After session_init** | **CHECK \`lessons\` field** \u2014 read and apply BEFORE any work |
8291
8595
  | **2nd+ messages** | \`context_smart(user_message="<user_message>", format="minified", max_tokens=400)\` |
8292
8596
  | **\u{1F50D} ANY code search** | \`search(mode="hybrid", query="...")\` \u2014 ALWAYS before Glob/Grep/Search/Read |
8597
+ | **\u26A0\uFE0F Before risky work** | \`session(action="get_lessons", query="<topic>")\` \u2014 **MANDATORY** |
8293
8598
  | **Capture decisions** | \`session(action="capture", event_type="decision", title="...", content="...")\` |
8294
- | **Before risky work** | \`session(action="get_lessons", query="<topic>")\` |
8295
8599
  | **On user frustration** | \`session(action="capture_lesson", title="...", trigger="...", impact="...", prevention="...")\` |
8296
8600
 
8297
8601
  **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).
@@ -8327,17 +8631,16 @@ search(mode="hybrid", query="function implementation") \u2192 done (results incl
8327
8631
 
8328
8632
  **Why?** ContextStream search returns semantic matches + context + file locations in ONE call. Local tools require multiple round-trips.
8329
8633
 
8330
- - **First message**: Call \`session_init\` with context_hint, then call \`context_smart\` before any other tool or response
8331
- - **On [INGEST_RECOMMENDED]**: Ask the user if they want to enable semantic code search. Explain: "Indexing your codebase enables AI-powered code search, dependency analysis, and better context. This takes a few minutes." If user agrees, run the provided \`project(action="ingest_local")\` command.
8332
- - **Every message after**: Always call \`context_smart\` BEFORE responding (semantic search for relevant context)
8333
- - **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
8334
- - **For discovery**: Use \`session(action="smart_search")\` or \`search(mode="hybrid")\` \u2014 NEVER use local Glob/Grep/Read first
8335
- - **For file/function/config lookups**: Use \`search\`/\`graph\` first; only fall back to rg/ls/find if ContextStream returns no results
8336
- - **If ContextStream returns results**: Do NOT use local Search/Explore/Read; only open specific files when needed for exact edits
8337
- - **For code analysis**: Use \`graph(action="dependencies")\` or \`graph(action="impact")\` for call/dependency analysis
8634
+ - **First message**: Call \`session_init\` with context_hint, then \`context_smart\` before any other tool
8635
+ - **Every message**: Call \`context_smart\` BEFORE responding
8636
+ - **For discovery**: Use \`search(mode="hybrid")\` \u2014 **NEVER use local Glob/Grep/Read first**
8637
+ - **If search returns 0 results**: Retry once (indexing may be in progress), THEN try local tools
8638
+ - **For file lookups**: Use \`search\`/\`graph\` first; fall back to local ONLY if ContextStream returns nothing
8639
+ - **If ContextStream returns results**: Do NOT use local tools; Read ONLY for exact edits
8640
+ - **For code analysis**: \`graph(action="dependencies")\` or \`graph(action="impact")\`
8338
8641
  - **On [RULES_NOTICE]**: Use \`generate_rules()\` to update rules
8339
- - **After completing work**: Always capture decisions/insights with \`session(action="capture")\`
8340
- - **On mistakes/corrections**: Immediately capture lessons with \`session(action="capture_lesson")\`
8642
+ - **After completing work**: Capture with \`session(action="capture")\`
8643
+ - **On mistakes**: Capture with \`session(action="capture_lesson")\`
8341
8644
 
8342
8645
  ### Search Mode Selection
8343
8646
 
@@ -8371,20 +8674,26 @@ Use \`output_format\` to reduce response size:
8371
8674
  **Example:** User asks "how many TODO comments?" \u2192
8372
8675
  \`search(mode="exhaustive", query="TODO", output_format="count")\` returns \`{total: 47}\` (not 47 full results)
8373
8676
 
8374
- ### Plans & Tasks
8677
+ ### \u{1F6A8} Plans & Tasks - USE CONTEXTSTREAM, NOT FILE-BASED PLANS \u{1F6A8}
8375
8678
 
8376
- When user asks to create a plan or implementation roadmap:
8377
- 1. Create plan: \`session(action="capture_plan", title="Plan Title", description="...", goals=["goal1", "goal2"], steps=[{id: "1", title: "Step 1", order: 1}, ...])\`
8378
- 2. Get plan_id from response, then create tasks: \`memory(action="create_task", title="Task Title", plan_id="<plan_id>", priority="high|medium|low", description="...")\`
8679
+ **CRITICAL: When user requests planning, implementation plans, roadmaps, or task breakdowns:**
8379
8680
 
8380
- To manage existing plans/tasks:
8681
+ \u274C **DO NOT** use built-in plan mode (EnterPlanMode) or write plan files
8682
+ \u2705 **ALWAYS** use ContextStream's plan/task system
8683
+
8684
+ **Trigger phrases (use ContextStream immediately):**
8685
+ - "plan", "roadmap", "milestones", "break down", "steps", "task list", "implementation strategy"
8686
+
8687
+ **Create plans in ContextStream:**
8688
+ 1. \`session(action="capture_plan", title="...", description="...", goals=[...], steps=[{id: "1", title: "Step 1", order: 1}, ...])\`
8689
+ 2. \`memory(action="create_task", title="...", plan_id="<plan_id>", priority="high|medium|low", description="...")\`
8690
+
8691
+ **Manage plans/tasks:**
8381
8692
  - List plans: \`session(action="list_plans")\`
8382
8693
  - Get plan with tasks: \`session(action="get_plan", plan_id="<uuid>", include_tasks=true)\`
8383
8694
  - List tasks: \`memory(action="list_tasks", plan_id="<uuid>")\` or \`memory(action="list_tasks")\` for all
8384
8695
  - Update task status: \`memory(action="update_task", task_id="<uuid>", task_status="pending|in_progress|completed|blocked")\`
8385
- - Link task to plan: \`memory(action="update_task", task_id="<uuid>", plan_id="<plan_uuid>")\`
8386
- - Unlink task from plan: \`memory(action="update_task", task_id="<uuid>", plan_id=null)\`
8387
- - Delete: \`memory(action="delete_task", task_id="<uuid>")\` or \`memory(action="delete_event", event_id="<plan_uuid>")\`
8696
+ - Delete: \`memory(action="delete_task", task_id="<uuid>")\`
8388
8697
 
8389
8698
  Full docs: https://contextstream.io/docs/mcp/tools
8390
8699
  `.trim();
@@ -8603,6 +8912,12 @@ var TOOL_CATALOG = [
8603
8912
  { name: "tasks", hint: "breakdown" },
8604
8913
  { name: "embeddings", hint: "vectors" }
8605
8914
  ]
8915
+ },
8916
+ {
8917
+ name: "Notion",
8918
+ tools: [
8919
+ { name: "create_page", hint: "new-page" }
8920
+ ]
8606
8921
  }
8607
8922
  ];
8608
8923
  function generateToolCatalog(format = "grouped", category) {
@@ -9479,6 +9794,18 @@ var GITHUB_TOOLS = /* @__PURE__ */ new Set([
9479
9794
  "github_knowledge",
9480
9795
  "github_summary"
9481
9796
  ]);
9797
+ var NOTION_TOOLS = /* @__PURE__ */ new Set([
9798
+ "notion_create_page",
9799
+ "notion_list_databases",
9800
+ "notion_search_pages",
9801
+ "notion_get_page",
9802
+ "notion_query_database",
9803
+ "notion_update_page",
9804
+ "notion_stats",
9805
+ "notion_activity",
9806
+ "notion_knowledge",
9807
+ "notion_summary"
9808
+ ]);
9482
9809
  var CROSS_INTEGRATION_TOOLS = /* @__PURE__ */ new Set([
9483
9810
  "integrations_status",
9484
9811
  "integrations_search",
@@ -9488,6 +9815,7 @@ var CROSS_INTEGRATION_TOOLS = /* @__PURE__ */ new Set([
9488
9815
  var ALL_INTEGRATION_TOOLS = /* @__PURE__ */ new Set([
9489
9816
  ...SLACK_TOOLS,
9490
9817
  ...GITHUB_TOOLS,
9818
+ ...NOTION_TOOLS,
9491
9819
  ...CROSS_INTEGRATION_TOOLS
9492
9820
  ]);
9493
9821
  var AUTO_HIDE_INTEGRATIONS = process.env.CONTEXTSTREAM_AUTO_HIDE_INTEGRATIONS !== "false";
@@ -9670,7 +9998,7 @@ var TOOL_BUNDLES = {
9670
9998
  "reminders_complete",
9671
9999
  "reminders_dismiss"
9672
10000
  ]),
9673
- // Integrations bundle - Slack/GitHub tools (auto-hidden when not connected)
10001
+ // Integrations bundle - Slack/GitHub/Notion tools (auto-hidden when not connected)
9674
10002
  integrations: /* @__PURE__ */ new Set([
9675
10003
  "slack_stats",
9676
10004
  "slack_channels",
@@ -9689,6 +10017,16 @@ var TOOL_BUNDLES = {
9689
10017
  "github_contributors",
9690
10018
  "github_knowledge",
9691
10019
  "github_summary",
10020
+ "notion_create_page",
10021
+ "notion_list_databases",
10022
+ "notion_search_pages",
10023
+ "notion_get_page",
10024
+ "notion_query_database",
10025
+ "notion_update_page",
10026
+ "notion_stats",
10027
+ "notion_activity",
10028
+ "notion_knowledge",
10029
+ "notion_summary",
9692
10030
  "integrations_status",
9693
10031
  "integrations_search",
9694
10032
  "integrations_summary",
@@ -9840,7 +10178,7 @@ var CONSOLIDATED_TOOLS = /* @__PURE__ */ new Set([
9840
10178
  "reminder",
9841
10179
  // Consolidates reminders_list, reminders_create, etc.
9842
10180
  "integration",
9843
- // Consolidates slack_*, github_*, integrations_*
10181
+ // Consolidates slack_*, github_*, notion_*, integrations_*
9844
10182
  "help"
9845
10183
  // Consolidates session_tools, auth_me, mcp_server_version, etc.
9846
10184
  ]);
@@ -10166,7 +10504,18 @@ function registerTools(server, client, sessionManager) {
10166
10504
  "github_contributors",
10167
10505
  "github_activity",
10168
10506
  "github_issues",
10169
- "github_search"
10507
+ "github_search",
10508
+ // Notion integration tools
10509
+ "notion_create_page",
10510
+ "notion_list_databases",
10511
+ "notion_search_pages",
10512
+ "notion_get_page",
10513
+ "notion_query_database",
10514
+ "notion_update_page",
10515
+ "notion_stats",
10516
+ "notion_activity",
10517
+ "notion_knowledge",
10518
+ "notion_summary"
10170
10519
  ]);
10171
10520
  const proTools = (() => {
10172
10521
  const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
@@ -10221,14 +10570,14 @@ function registerTools(server, client, sessionManager) {
10221
10570
  ].join("\n")
10222
10571
  );
10223
10572
  }
10224
- let integrationStatus = { checked: false, slack: false, github: false };
10573
+ let integrationStatus = { checked: false, slack: false, github: false, notion: false };
10225
10574
  let toolsListChangedNotified = false;
10226
10575
  async function checkIntegrationStatus(workspaceId) {
10227
10576
  if (integrationStatus.checked && integrationStatus.workspaceId === workspaceId) {
10228
- return { slack: integrationStatus.slack, github: integrationStatus.github };
10577
+ return { slack: integrationStatus.slack, github: integrationStatus.github, notion: integrationStatus.notion };
10229
10578
  }
10230
10579
  if (!workspaceId) {
10231
- return { slack: false, github: false };
10580
+ return { slack: false, github: false, notion: false };
10232
10581
  }
10233
10582
  try {
10234
10583
  const status = await client.integrationsStatus({ workspace_id: workspaceId });
@@ -10238,32 +10587,38 @@ function registerTools(server, client, sessionManager) {
10238
10587
  const githubConnected = status?.some(
10239
10588
  (s) => s.provider === "github" && s.status === "connected"
10240
10589
  ) ?? false;
10590
+ const notionConnected = status?.some(
10591
+ (s) => s.provider === "notion" && s.status === "connected"
10592
+ ) ?? false;
10241
10593
  integrationStatus = {
10242
10594
  checked: true,
10243
10595
  slack: slackConnected,
10244
10596
  github: githubConnected,
10597
+ notion: notionConnected,
10245
10598
  workspaceId
10246
10599
  };
10247
10600
  console.error(
10248
- `[ContextStream] Integration status: Slack=${slackConnected}, GitHub=${githubConnected}`
10601
+ `[ContextStream] Integration status: Slack=${slackConnected}, GitHub=${githubConnected}, Notion=${notionConnected}`
10249
10602
  );
10250
- return { slack: slackConnected, github: githubConnected };
10603
+ return { slack: slackConnected, github: githubConnected, notion: notionConnected };
10251
10604
  } catch (error) {
10252
10605
  console.error("[ContextStream] Failed to check integration status:", error);
10253
- return { slack: false, github: false };
10606
+ return { slack: false, github: false, notion: false };
10254
10607
  }
10255
10608
  }
10256
10609
  function updateIntegrationStatus(status, workspaceId) {
10257
10610
  const hadSlack = integrationStatus.slack;
10258
10611
  const hadGithub = integrationStatus.github;
10612
+ const hadNotion = integrationStatus.notion;
10259
10613
  integrationStatus = {
10260
10614
  checked: true,
10261
10615
  slack: status.slack,
10262
10616
  github: status.github,
10617
+ notion: status.notion,
10263
10618
  workspaceId
10264
10619
  };
10265
10620
  if (AUTO_HIDE_INTEGRATIONS && !toolsListChangedNotified) {
10266
- const newlyConnected = !hadSlack && status.slack || !hadGithub && status.github;
10621
+ const newlyConnected = !hadSlack && status.slack || !hadGithub && status.github || !hadNotion && status.notion;
10267
10622
  if (newlyConnected) {
10268
10623
  try {
10269
10624
  server.server?.sendToolsListChanged?.();
@@ -10281,8 +10636,9 @@ function registerTools(server, client, sessionManager) {
10281
10636
  if (!AUTO_HIDE_INTEGRATIONS) return null;
10282
10637
  const requiresSlack = SLACK_TOOLS.has(toolName);
10283
10638
  const requiresGithub = GITHUB_TOOLS.has(toolName);
10639
+ const requiresNotion = NOTION_TOOLS.has(toolName);
10284
10640
  const requiresCrossIntegration = CROSS_INTEGRATION_TOOLS.has(toolName);
10285
- if (!requiresSlack && !requiresGithub && !requiresCrossIntegration) {
10641
+ if (!requiresSlack && !requiresGithub && !requiresNotion && !requiresCrossIntegration) {
10286
10642
  return null;
10287
10643
  }
10288
10644
  const workspaceId = sessionManager?.getContext()?.workspace_id;
@@ -10317,6 +10673,18 @@ function registerTools(server, client, sessionManager) {
10317
10673
  ].join("\n")
10318
10674
  );
10319
10675
  }
10676
+ if (requiresNotion && !status.notion) {
10677
+ return errorResult(
10678
+ [
10679
+ `Integration not connected: \`${toolName}\` requires Notion integration.`,
10680
+ "",
10681
+ "To use Notion tools:",
10682
+ "1. Go to https://contextstream.io/settings/integrations",
10683
+ "2. Connect your Notion workspace",
10684
+ "3. Try this command again"
10685
+ ].join("\n")
10686
+ );
10687
+ }
10320
10688
  if (requiresCrossIntegration && !status.slack && !status.github) {
10321
10689
  return errorResult(
10322
10690
  [
@@ -10342,6 +10710,9 @@ function registerTools(server, client, sessionManager) {
10342
10710
  if (GITHUB_TOOLS.has(toolName)) {
10343
10711
  return integrationStatus.github;
10344
10712
  }
10713
+ if (NOTION_TOOLS.has(toolName)) {
10714
+ return integrationStatus.notion;
10715
+ }
10345
10716
  if (CROSS_INTEGRATION_TOOLS.has(toolName)) {
10346
10717
  return integrationStatus.slack || integrationStatus.github;
10347
10718
  }
@@ -14149,6 +14520,57 @@ Example prompts:
14149
14520
  };
14150
14521
  }
14151
14522
  );
14523
+ registerTool(
14524
+ "notion_create_page",
14525
+ {
14526
+ title: "Create Notion page",
14527
+ description: `Create a new page in a connected Notion workspace.
14528
+ Returns: the created page ID, URL, title, and timestamps.
14529
+ Use this to save notes, documentation, or any content to Notion.
14530
+ Supports Markdown content which is automatically converted to Notion blocks.
14531
+
14532
+ Example prompts:
14533
+ - "Create a Notion page with today's meeting notes"
14534
+ - "Save this documentation to Notion"
14535
+ - "Create a new page in my Notion workspace"`,
14536
+ inputSchema: external_exports.object({
14537
+ workspace_id: external_exports.string().uuid().optional().describe("Workspace ID (uses session default if not provided)"),
14538
+ title: external_exports.string().describe("Page title"),
14539
+ content: external_exports.string().optional().describe("Page content in Markdown format"),
14540
+ parent_database_id: external_exports.string().optional().describe("Parent database ID to create page in"),
14541
+ parent_page_id: external_exports.string().optional().describe("Parent page ID to create page under")
14542
+ })
14543
+ },
14544
+ async (input) => {
14545
+ const workspaceId = resolveWorkspaceId(input.workspace_id);
14546
+ if (!workspaceId) {
14547
+ return errorResult(
14548
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
14549
+ );
14550
+ }
14551
+ const result = await client.createNotionPage({
14552
+ workspace_id: workspaceId,
14553
+ title: input.title,
14554
+ content: input.content,
14555
+ parent_database_id: input.parent_database_id,
14556
+ parent_page_id: input.parent_page_id
14557
+ });
14558
+ return {
14559
+ content: [
14560
+ {
14561
+ type: "text",
14562
+ text: `Page created successfully!
14563
+
14564
+ Title: ${result.title}
14565
+ URL: ${result.url}
14566
+ ID: ${result.id}
14567
+ Created: ${result.created_time}`
14568
+ }
14569
+ ],
14570
+ structuredContent: toStructured(result)
14571
+ };
14572
+ }
14573
+ );
14152
14574
  registerTool(
14153
14575
  "integrations_search",
14154
14576
  {
@@ -14291,7 +14713,10 @@ Use this to verify integrations are healthy and syncing properly.`,
14291
14713
  const githubConnected = result?.some(
14292
14714
  (s) => s.provider === "github" && s.status === "connected"
14293
14715
  ) ?? false;
14294
- updateIntegrationStatus({ slack: slackConnected, github: githubConnected }, workspaceId);
14716
+ const notionConnected = result?.some(
14717
+ (s) => s.provider === "notion" && s.status === "connected"
14718
+ ) ?? false;
14719
+ updateIntegrationStatus({ slack: slackConnected, github: githubConnected, notion: notionConnected }, workspaceId);
14295
14720
  }
14296
14721
  if (result.length === 0) {
14297
14722
  return {
@@ -15974,9 +16399,9 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
15974
16399
  "integration",
15975
16400
  {
15976
16401
  title: "Integration",
15977
- description: `Integration operations for Slack and GitHub. Provider: slack, github, all. Actions: status, search, stats, activity, contributors, knowledge, summary, channels (slack), discussions (slack), repos (github), issues (github).`,
16402
+ description: `Integration operations for Slack, GitHub, and Notion. Provider: slack, github, notion, all. Actions: status, search, stats, activity, contributors, knowledge, summary, channels (slack), discussions (slack), repos (github), issues (github), create_page (notion), list_databases (notion), search_pages (notion), get_page (notion), query_database (notion), update_page (notion).`,
15978
16403
  inputSchema: external_exports.object({
15979
- provider: external_exports.enum(["slack", "github", "all"]).describe("Integration provider"),
16404
+ provider: external_exports.enum(["slack", "github", "notion", "all"]).describe("Integration provider"),
15980
16405
  action: external_exports.enum([
15981
16406
  "status",
15982
16407
  "search",
@@ -15989,21 +16414,43 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
15989
16414
  "discussions",
15990
16415
  "sync_users",
15991
16416
  "repos",
15992
- "issues"
16417
+ "issues",
16418
+ // Notion-specific actions
16419
+ "create_page",
16420
+ "list_databases",
16421
+ "search_pages",
16422
+ "get_page",
16423
+ "query_database",
16424
+ "update_page"
15993
16425
  ]).describe("Action to perform"),
15994
16426
  workspace_id: external_exports.string().uuid().optional(),
15995
16427
  project_id: external_exports.string().uuid().optional(),
15996
16428
  query: external_exports.string().optional(),
15997
16429
  limit: external_exports.number().optional(),
15998
16430
  since: external_exports.string().optional(),
15999
- until: external_exports.string().optional()
16431
+ until: external_exports.string().optional(),
16432
+ // Notion-specific parameters
16433
+ title: external_exports.string().optional().describe("Page title (for Notion create_page/update_page)"),
16434
+ content: external_exports.string().optional().describe("Page content in Markdown (for Notion create_page/update_page)"),
16435
+ parent_database_id: external_exports.string().optional().describe("Parent database ID (for Notion create_page)"),
16436
+ parent_page_id: external_exports.string().optional().describe("Parent page ID (for Notion create_page)"),
16437
+ page_id: external_exports.string().optional().describe("Page ID (for Notion get_page/update_page)"),
16438
+ database_id: external_exports.string().optional().describe("Database ID (for Notion query_database/search_pages/activity)"),
16439
+ days: external_exports.number().optional().describe("Number of days for stats/summary (default: 7)"),
16440
+ node_type: external_exports.string().optional().describe("Filter knowledge by type (for Notion knowledge)"),
16441
+ filter: external_exports.record(external_exports.unknown()).optional().describe("Query filter (for Notion query_database)"),
16442
+ sorts: external_exports.array(external_exports.object({
16443
+ property: external_exports.string(),
16444
+ direction: external_exports.enum(["ascending", "descending"])
16445
+ })).optional().describe("Sort order (for Notion query_database)"),
16446
+ properties: external_exports.record(external_exports.unknown()).optional().describe("Page properties (for Notion update_page)")
16000
16447
  })
16001
16448
  },
16002
16449
  async (input) => {
16003
16450
  const workspaceId = resolveWorkspaceId(input.workspace_id);
16004
16451
  const projectId = resolveProjectId(input.project_id);
16005
16452
  const integrationGated = await gateIfIntegrationTool(
16006
- input.provider === "slack" ? "slack_search" : input.provider === "github" ? "github_search" : "integrations_status"
16453
+ input.provider === "slack" ? "slack_search" : input.provider === "github" ? "github_search" : input.provider === "notion" ? "notion_create_page" : "integrations_status"
16007
16454
  );
16008
16455
  if (integrationGated) return integrationGated;
16009
16456
  const params = {
@@ -16071,8 +16518,17 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
16071
16518
  content: [{ type: "text", text: formatContent(result) }],
16072
16519
  structuredContent: toStructured(result)
16073
16520
  };
16521
+ } else if (input.provider === "notion") {
16522
+ const result = await client.notionStats({
16523
+ workspace_id: workspaceId,
16524
+ days: input.days
16525
+ });
16526
+ return {
16527
+ content: [{ type: "text", text: formatContent(result) }],
16528
+ structuredContent: toStructured(result)
16529
+ };
16074
16530
  }
16075
- return errorResult("stats requires provider: slack or github");
16531
+ return errorResult("stats requires provider: slack, github, or notion");
16076
16532
  }
16077
16533
  case "activity": {
16078
16534
  if (input.provider === "slack") {
@@ -16087,8 +16543,18 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
16087
16543
  content: [{ type: "text", text: formatContent(result) }],
16088
16544
  structuredContent: toStructured(result)
16089
16545
  };
16546
+ } else if (input.provider === "notion") {
16547
+ const result = await client.notionActivity({
16548
+ workspace_id: workspaceId,
16549
+ limit: input.limit,
16550
+ database_id: input.database_id
16551
+ });
16552
+ return {
16553
+ content: [{ type: "text", text: formatContent(result) }],
16554
+ structuredContent: toStructured(result)
16555
+ };
16090
16556
  }
16091
- return errorResult("activity requires provider: slack or github");
16557
+ return errorResult("activity requires provider: slack, github, or notion");
16092
16558
  }
16093
16559
  case "contributors": {
16094
16560
  if (input.provider === "slack") {
@@ -16119,6 +16585,16 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
16119
16585
  content: [{ type: "text", text: formatContent(result) }],
16120
16586
  structuredContent: toStructured(result)
16121
16587
  };
16588
+ } else if (input.provider === "notion") {
16589
+ const result = await client.notionKnowledge({
16590
+ workspace_id: workspaceId,
16591
+ limit: input.limit,
16592
+ node_type: input.node_type
16593
+ });
16594
+ return {
16595
+ content: [{ type: "text", text: formatContent(result) }],
16596
+ structuredContent: toStructured(result)
16597
+ };
16122
16598
  } else {
16123
16599
  const result = await client.integrationsKnowledge(params);
16124
16600
  return {
@@ -16140,6 +16616,16 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
16140
16616
  content: [{ type: "text", text: formatContent(result) }],
16141
16617
  structuredContent: toStructured(result)
16142
16618
  };
16619
+ } else if (input.provider === "notion") {
16620
+ const result = await client.notionSummary({
16621
+ workspace_id: workspaceId,
16622
+ days: input.days,
16623
+ database_id: input.database_id
16624
+ });
16625
+ return {
16626
+ content: [{ type: "text", text: formatContent(result) }],
16627
+ structuredContent: toStructured(result)
16628
+ };
16143
16629
  } else {
16144
16630
  const result = await client.integrationsSummary(params);
16145
16631
  return {
@@ -16198,6 +16684,170 @@ Output formats: full (default, includes content), paths (file paths only - 80% t
16198
16684
  structuredContent: toStructured(result)
16199
16685
  };
16200
16686
  }
16687
+ case "create_page": {
16688
+ if (input.provider !== "notion") {
16689
+ return errorResult("create_page is only available for notion provider");
16690
+ }
16691
+ if (!input.title) {
16692
+ return errorResult("title is required for create_page action");
16693
+ }
16694
+ if (!workspaceId) {
16695
+ return errorResult(
16696
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16697
+ );
16698
+ }
16699
+ const result = await client.createNotionPage({
16700
+ workspace_id: workspaceId,
16701
+ title: input.title,
16702
+ content: input.content,
16703
+ parent_database_id: input.parent_database_id,
16704
+ parent_page_id: input.parent_page_id
16705
+ });
16706
+ return {
16707
+ content: [
16708
+ {
16709
+ type: "text",
16710
+ text: `Page created successfully!
16711
+
16712
+ Title: ${result.title}
16713
+ URL: ${result.url}
16714
+ ID: ${result.id}
16715
+ Created: ${result.created_time}`
16716
+ }
16717
+ ],
16718
+ structuredContent: toStructured(result)
16719
+ };
16720
+ }
16721
+ case "list_databases": {
16722
+ if (input.provider !== "notion") {
16723
+ return errorResult("list_databases is only available for notion provider");
16724
+ }
16725
+ if (!workspaceId) {
16726
+ return errorResult(
16727
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16728
+ );
16729
+ }
16730
+ const databases = await client.notionListDatabases({
16731
+ workspace_id: workspaceId
16732
+ });
16733
+ return {
16734
+ content: [{ type: "text", text: formatContent(databases) }],
16735
+ structuredContent: toStructured(databases)
16736
+ };
16737
+ }
16738
+ case "search_pages": {
16739
+ if (input.provider !== "notion") {
16740
+ return errorResult("search_pages is only available for notion provider");
16741
+ }
16742
+ if (!workspaceId) {
16743
+ return errorResult(
16744
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16745
+ );
16746
+ }
16747
+ const pages = await client.notionSearchPages({
16748
+ workspace_id: workspaceId,
16749
+ query: input.query,
16750
+ database_id: input.database_id,
16751
+ limit: input.limit
16752
+ });
16753
+ return {
16754
+ content: [{ type: "text", text: formatContent(pages) }],
16755
+ structuredContent: toStructured(pages)
16756
+ };
16757
+ }
16758
+ case "get_page": {
16759
+ if (input.provider !== "notion") {
16760
+ return errorResult("get_page is only available for notion provider");
16761
+ }
16762
+ if (!input.page_id) {
16763
+ return errorResult("page_id is required for get_page action");
16764
+ }
16765
+ if (!workspaceId) {
16766
+ return errorResult(
16767
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16768
+ );
16769
+ }
16770
+ const page = await client.notionGetPage({
16771
+ workspace_id: workspaceId,
16772
+ page_id: input.page_id
16773
+ });
16774
+ return {
16775
+ content: [
16776
+ {
16777
+ type: "text",
16778
+ text: `# ${page.title}
16779
+
16780
+ ID: ${page.id}
16781
+ URL: ${page.url}
16782
+ Created: ${page.created_time}
16783
+ Last edited: ${page.last_edited_time}
16784
+
16785
+ ---
16786
+
16787
+ ${page.content}`
16788
+ }
16789
+ ],
16790
+ structuredContent: toStructured(page)
16791
+ };
16792
+ }
16793
+ case "query_database": {
16794
+ if (input.provider !== "notion") {
16795
+ return errorResult("query_database is only available for notion provider");
16796
+ }
16797
+ if (!input.database_id) {
16798
+ return errorResult("database_id is required for query_database action");
16799
+ }
16800
+ if (!workspaceId) {
16801
+ return errorResult(
16802
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16803
+ );
16804
+ }
16805
+ const queryResult = await client.notionQueryDatabase({
16806
+ workspace_id: workspaceId,
16807
+ database_id: input.database_id,
16808
+ filter: input.filter,
16809
+ sorts: input.sorts,
16810
+ limit: input.limit
16811
+ });
16812
+ return {
16813
+ content: [{ type: "text", text: formatContent(queryResult) }],
16814
+ structuredContent: toStructured(queryResult)
16815
+ };
16816
+ }
16817
+ case "update_page": {
16818
+ if (input.provider !== "notion") {
16819
+ return errorResult("update_page is only available for notion provider");
16820
+ }
16821
+ if (!input.page_id) {
16822
+ return errorResult("page_id is required for update_page action");
16823
+ }
16824
+ if (!workspaceId) {
16825
+ return errorResult(
16826
+ "Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly."
16827
+ );
16828
+ }
16829
+ const updatedPage = await client.notionUpdatePage({
16830
+ workspace_id: workspaceId,
16831
+ page_id: input.page_id,
16832
+ title: input.title,
16833
+ content: input.content,
16834
+ properties: input.properties
16835
+ });
16836
+ return {
16837
+ content: [
16838
+ {
16839
+ type: "text",
16840
+ text: `Page updated successfully!
16841
+
16842
+ Title: ${updatedPage.title}
16843
+ URL: ${updatedPage.url}
16844
+ ID: ${updatedPage.id}
16845
+ Last edited: ${updatedPage.last_edited_time}`
16846
+ }
16847
+ ],
16848
+ structuredContent: toStructured(updatedPage)
16849
+ };
16850
+ }
16201
16851
  default:
16202
16852
  return errorResult(`Unknown action: ${input.action}`);
16203
16853
  }
@@ -17150,7 +17800,7 @@ function registerPrompts(server) {
17150
17800
  "First:",
17151
17801
  "- If you can infer the project folder path from the environment/IDE roots, use it.",
17152
17802
  "- Otherwise ask me for an absolute folder path.",
17153
- "- Ask which editor(s) (windsurf,cursor,cline,kilo,roo,claude,aider) or default to all.",
17803
+ "- Ask which editor(s) (codex,windsurf,cursor,cline,kilo,roo,claude,aider,antigravity) or default to all.",
17154
17804
  "",
17155
17805
  "Then call `generate_rules` and confirm which files were created/updated.",
17156
17806
  "Ask if the user also wants to apply rules globally (pass apply_global: true)."