@contextstream/mcp-server 0.3.26 → 0.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -10
- package/dist/index.js +502 -722
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6282,6 +6282,191 @@ W:${wsHint}
|
|
|
6282
6282
|
}
|
|
6283
6283
|
return matches / keywords.length;
|
|
6284
6284
|
}
|
|
6285
|
+
// ============================================
|
|
6286
|
+
// Slack Integration Methods
|
|
6287
|
+
// ============================================
|
|
6288
|
+
/**
|
|
6289
|
+
* Get Slack integration statistics and overview
|
|
6290
|
+
*/
|
|
6291
|
+
async slackStats(params) {
|
|
6292
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6293
|
+
if (!withDefaults.workspace_id) {
|
|
6294
|
+
throw new Error("workspace_id is required for Slack stats");
|
|
6295
|
+
}
|
|
6296
|
+
const query = new URLSearchParams();
|
|
6297
|
+
if (params?.days) query.set("days", String(params.days));
|
|
6298
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6299
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/stats${suffix}`, { method: "GET" });
|
|
6300
|
+
}
|
|
6301
|
+
/**
|
|
6302
|
+
* Get Slack users for a workspace
|
|
6303
|
+
*/
|
|
6304
|
+
async slackUsers(params) {
|
|
6305
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6306
|
+
if (!withDefaults.workspace_id) {
|
|
6307
|
+
throw new Error("workspace_id is required for Slack users");
|
|
6308
|
+
}
|
|
6309
|
+
const query = new URLSearchParams();
|
|
6310
|
+
if (params?.page) query.set("page", String(params.page));
|
|
6311
|
+
if (params?.per_page) query.set("per_page", String(params.per_page));
|
|
6312
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6313
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/users${suffix}`, { method: "GET" });
|
|
6314
|
+
}
|
|
6315
|
+
/**
|
|
6316
|
+
* Get Slack channels with stats
|
|
6317
|
+
*/
|
|
6318
|
+
async slackChannels(params) {
|
|
6319
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6320
|
+
if (!withDefaults.workspace_id) {
|
|
6321
|
+
throw new Error("workspace_id is required for Slack channels");
|
|
6322
|
+
}
|
|
6323
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/channels`, { method: "GET" });
|
|
6324
|
+
}
|
|
6325
|
+
/**
|
|
6326
|
+
* Get recent Slack activity feed
|
|
6327
|
+
*/
|
|
6328
|
+
async slackActivity(params) {
|
|
6329
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6330
|
+
if (!withDefaults.workspace_id) {
|
|
6331
|
+
throw new Error("workspace_id is required for Slack activity");
|
|
6332
|
+
}
|
|
6333
|
+
const query = new URLSearchParams();
|
|
6334
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6335
|
+
if (params?.offset) query.set("offset", String(params.offset));
|
|
6336
|
+
if (params?.channel_id) query.set("channel_id", params.channel_id);
|
|
6337
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6338
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/activity${suffix}`, { method: "GET" });
|
|
6339
|
+
}
|
|
6340
|
+
/**
|
|
6341
|
+
* Get high-engagement Slack discussions
|
|
6342
|
+
*/
|
|
6343
|
+
async slackDiscussions(params) {
|
|
6344
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6345
|
+
if (!withDefaults.workspace_id) {
|
|
6346
|
+
throw new Error("workspace_id is required for Slack discussions");
|
|
6347
|
+
}
|
|
6348
|
+
const query = new URLSearchParams();
|
|
6349
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6350
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6351
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/discussions${suffix}`, { method: "GET" });
|
|
6352
|
+
}
|
|
6353
|
+
/**
|
|
6354
|
+
* Get top Slack contributors
|
|
6355
|
+
*/
|
|
6356
|
+
async slackContributors(params) {
|
|
6357
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6358
|
+
if (!withDefaults.workspace_id) {
|
|
6359
|
+
throw new Error("workspace_id is required for Slack contributors");
|
|
6360
|
+
}
|
|
6361
|
+
const query = new URLSearchParams();
|
|
6362
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6363
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6364
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/contributors${suffix}`, { method: "GET" });
|
|
6365
|
+
}
|
|
6366
|
+
/**
|
|
6367
|
+
* Trigger a sync of Slack user profiles
|
|
6368
|
+
*/
|
|
6369
|
+
async slackSyncUsers(params) {
|
|
6370
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6371
|
+
if (!withDefaults.workspace_id) {
|
|
6372
|
+
throw new Error("workspace_id is required for syncing Slack users");
|
|
6373
|
+
}
|
|
6374
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/sync-users`, { method: "POST" });
|
|
6375
|
+
}
|
|
6376
|
+
/**
|
|
6377
|
+
* Search Slack messages
|
|
6378
|
+
*/
|
|
6379
|
+
async slackSearch(params) {
|
|
6380
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6381
|
+
if (!withDefaults.workspace_id) {
|
|
6382
|
+
throw new Error("workspace_id is required for Slack search");
|
|
6383
|
+
}
|
|
6384
|
+
const query = new URLSearchParams();
|
|
6385
|
+
query.set("q", params.q);
|
|
6386
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6387
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/slack/search?${query.toString()}`, { method: "GET" });
|
|
6388
|
+
}
|
|
6389
|
+
// ============================================
|
|
6390
|
+
// GitHub Integration Methods
|
|
6391
|
+
// ============================================
|
|
6392
|
+
/**
|
|
6393
|
+
* Get GitHub integration statistics and overview
|
|
6394
|
+
*/
|
|
6395
|
+
async githubStats(params) {
|
|
6396
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6397
|
+
if (!withDefaults.workspace_id) {
|
|
6398
|
+
throw new Error("workspace_id is required for GitHub stats");
|
|
6399
|
+
}
|
|
6400
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/stats`, { method: "GET" });
|
|
6401
|
+
}
|
|
6402
|
+
/**
|
|
6403
|
+
* Get GitHub repository stats
|
|
6404
|
+
*/
|
|
6405
|
+
async githubRepos(params) {
|
|
6406
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6407
|
+
if (!withDefaults.workspace_id) {
|
|
6408
|
+
throw new Error("workspace_id is required for GitHub repos");
|
|
6409
|
+
}
|
|
6410
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/repos`, { method: "GET" });
|
|
6411
|
+
}
|
|
6412
|
+
/**
|
|
6413
|
+
* Get recent GitHub activity feed
|
|
6414
|
+
*/
|
|
6415
|
+
async githubActivity(params) {
|
|
6416
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6417
|
+
if (!withDefaults.workspace_id) {
|
|
6418
|
+
throw new Error("workspace_id is required for GitHub activity");
|
|
6419
|
+
}
|
|
6420
|
+
const query = new URLSearchParams();
|
|
6421
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6422
|
+
if (params?.offset) query.set("offset", String(params.offset));
|
|
6423
|
+
if (params?.repo) query.set("repo", params.repo);
|
|
6424
|
+
if (params?.type) query.set("type", params.type);
|
|
6425
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6426
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/activity${suffix}`, { method: "GET" });
|
|
6427
|
+
}
|
|
6428
|
+
/**
|
|
6429
|
+
* Get GitHub issues and PRs
|
|
6430
|
+
*/
|
|
6431
|
+
async githubIssues(params) {
|
|
6432
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6433
|
+
if (!withDefaults.workspace_id) {
|
|
6434
|
+
throw new Error("workspace_id is required for GitHub issues");
|
|
6435
|
+
}
|
|
6436
|
+
const query = new URLSearchParams();
|
|
6437
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6438
|
+
if (params?.offset) query.set("offset", String(params.offset));
|
|
6439
|
+
if (params?.state) query.set("state", params.state);
|
|
6440
|
+
if (params?.repo) query.set("repo", params.repo);
|
|
6441
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6442
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/issues${suffix}`, { method: "GET" });
|
|
6443
|
+
}
|
|
6444
|
+
/**
|
|
6445
|
+
* Get top GitHub contributors
|
|
6446
|
+
*/
|
|
6447
|
+
async githubContributors(params) {
|
|
6448
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6449
|
+
if (!withDefaults.workspace_id) {
|
|
6450
|
+
throw new Error("workspace_id is required for GitHub contributors");
|
|
6451
|
+
}
|
|
6452
|
+
const query = new URLSearchParams();
|
|
6453
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6454
|
+
const suffix = query.toString() ? `?${query.toString()}` : "";
|
|
6455
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/contributors${suffix}`, { method: "GET" });
|
|
6456
|
+
}
|
|
6457
|
+
/**
|
|
6458
|
+
* Search GitHub content
|
|
6459
|
+
*/
|
|
6460
|
+
async githubSearch(params) {
|
|
6461
|
+
const withDefaults = this.withDefaults(params || {});
|
|
6462
|
+
if (!withDefaults.workspace_id) {
|
|
6463
|
+
throw new Error("workspace_id is required for GitHub search");
|
|
6464
|
+
}
|
|
6465
|
+
const query = new URLSearchParams();
|
|
6466
|
+
query.set("q", params.q);
|
|
6467
|
+
if (params?.limit) query.set("limit", String(params.limit));
|
|
6468
|
+
return request(this.config, `/workspaces/${withDefaults.workspace_id}/github/search?${query.toString()}`, { method: "GET" });
|
|
6469
|
+
}
|
|
6285
6470
|
};
|
|
6286
6471
|
|
|
6287
6472
|
// src/rules-templates.ts
|
|
@@ -6529,7 +6714,7 @@ function toStructured(data) {
|
|
|
6529
6714
|
return void 0;
|
|
6530
6715
|
}
|
|
6531
6716
|
function registerTools(server, client, sessionManager) {
|
|
6532
|
-
const
|
|
6717
|
+
const upgradeUrl = process.env.CONTEXTSTREAM_UPGRADE_URL || "https://contextstream.io/pricing";
|
|
6533
6718
|
const defaultProTools = /* @__PURE__ */ new Set([
|
|
6534
6719
|
// AI endpoints (typically paid/credit-metered)
|
|
6535
6720
|
"ai_context",
|
|
@@ -6537,7 +6722,22 @@ function registerTools(server, client, sessionManager) {
|
|
|
6537
6722
|
"ai_context_budget",
|
|
6538
6723
|
"ai_embeddings",
|
|
6539
6724
|
"ai_plan",
|
|
6540
|
-
"ai_tasks"
|
|
6725
|
+
"ai_tasks",
|
|
6726
|
+
// Slack integration tools
|
|
6727
|
+
"slack_stats",
|
|
6728
|
+
"slack_channels",
|
|
6729
|
+
"slack_contributors",
|
|
6730
|
+
"slack_activity",
|
|
6731
|
+
"slack_discussions",
|
|
6732
|
+
"slack_search",
|
|
6733
|
+
"slack_sync_users",
|
|
6734
|
+
// GitHub integration tools
|
|
6735
|
+
"github_stats",
|
|
6736
|
+
"github_repos",
|
|
6737
|
+
"github_contributors",
|
|
6738
|
+
"github_activity",
|
|
6739
|
+
"github_issues",
|
|
6740
|
+
"github_search"
|
|
6541
6741
|
]);
|
|
6542
6742
|
const proTools = (() => {
|
|
6543
6743
|
const raw = process.env.CONTEXTSTREAM_PRO_TOOLS;
|
|
@@ -6558,7 +6758,7 @@ function registerTools(server, client, sessionManager) {
|
|
|
6558
6758
|
return errorResult(
|
|
6559
6759
|
[
|
|
6560
6760
|
`Access denied: \`${toolName}\` requires ContextStream PRO.`,
|
|
6561
|
-
`Upgrade: ${
|
|
6761
|
+
`Upgrade: ${upgradeUrl}`
|
|
6562
6762
|
].join("\n")
|
|
6563
6763
|
);
|
|
6564
6764
|
}
|
|
@@ -6596,7 +6796,7 @@ function registerTools(server, client, sessionManager) {
|
|
|
6596
6796
|
title: `${config.title} (${accessLabel})`,
|
|
6597
6797
|
description: `${config.description}
|
|
6598
6798
|
|
|
6599
|
-
Access: ${accessLabel}${accessLabel === "PRO" ? ` (upgrade: ${
|
|
6799
|
+
Access: ${accessLabel}${accessLabel === "PRO" ? ` (upgrade: ${upgradeUrl})` : ""}`
|
|
6600
6800
|
};
|
|
6601
6801
|
const safeHandler = async (input) => {
|
|
6602
6802
|
try {
|
|
@@ -6609,7 +6809,7 @@ Access: ${accessLabel}${accessLabel === "PRO" ? ` (upgrade: ${upgradeUrl2})` : "
|
|
|
6609
6809
|
const errorCode = error?.code || error?.status || "UNKNOWN_ERROR";
|
|
6610
6810
|
const isPlanLimit = String(errorCode).toUpperCase() === "FORBIDDEN" && String(errorMessage).toLowerCase().includes("plan limit reached");
|
|
6611
6811
|
const upgradeHint = isPlanLimit ? `
|
|
6612
|
-
Upgrade: ${
|
|
6812
|
+
Upgrade: ${upgradeUrl}` : "";
|
|
6613
6813
|
const serializedError = new Error(
|
|
6614
6814
|
`[${errorCode}] ${errorMessage}${upgradeHint}${errorDetails ? `: ${JSON.stringify(errorDetails)}` : ""}`
|
|
6615
6815
|
);
|
|
@@ -8510,764 +8710,345 @@ This saves ~80% tokens compared to including full chat history.`,
|
|
|
8510
8710
|
};
|
|
8511
8711
|
}
|
|
8512
8712
|
);
|
|
8513
|
-
|
|
8514
|
-
|
|
8515
|
-
// src/resources.ts
|
|
8516
|
-
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8517
|
-
function wrapText(uri, text) {
|
|
8518
|
-
return { contents: [{ uri, text }] };
|
|
8519
|
-
}
|
|
8520
|
-
function registerResources(server, client, apiUrl) {
|
|
8521
|
-
server.registerResource(
|
|
8522
|
-
"contextstream-openapi",
|
|
8523
|
-
new ResourceTemplate("contextstream:openapi", { list: void 0 }),
|
|
8713
|
+
registerTool(
|
|
8714
|
+
"slack_stats",
|
|
8524
8715
|
{
|
|
8525
|
-
title: "
|
|
8526
|
-
description:
|
|
8527
|
-
|
|
8716
|
+
title: "Slack overview stats",
|
|
8717
|
+
description: `Get Slack integration statistics and overview for a workspace.
|
|
8718
|
+
Returns: total messages, threads, active users, channel stats, activity trends, and sync status.
|
|
8719
|
+
Use this to understand Slack activity and engagement patterns.`,
|
|
8720
|
+
inputSchema: external_exports.object({
|
|
8721
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8722
|
+
days: external_exports.number().optional().describe("Number of days to include in stats (default: 30)")
|
|
8723
|
+
})
|
|
8528
8724
|
},
|
|
8529
|
-
async () => {
|
|
8530
|
-
const
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8725
|
+
async (input) => {
|
|
8726
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8727
|
+
if (!workspaceId) {
|
|
8728
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8729
|
+
}
|
|
8730
|
+
const result = await client.slackStats({ workspace_id: workspaceId, days: input.days });
|
|
8731
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8534
8732
|
}
|
|
8535
8733
|
);
|
|
8536
|
-
|
|
8537
|
-
"
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8542
|
-
|
|
8734
|
+
registerTool(
|
|
8735
|
+
"slack_channels",
|
|
8736
|
+
{
|
|
8737
|
+
title: "List Slack channels",
|
|
8738
|
+
description: `Get synced Slack channels with statistics for a workspace.
|
|
8739
|
+
Returns: channel names, message counts, thread counts, and last activity timestamps.`,
|
|
8740
|
+
inputSchema: external_exports.object({
|
|
8741
|
+
workspace_id: external_exports.string().uuid().optional()
|
|
8742
|
+
})
|
|
8743
|
+
},
|
|
8744
|
+
async (input) => {
|
|
8745
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8746
|
+
if (!workspaceId) {
|
|
8747
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8748
|
+
}
|
|
8749
|
+
const result = await client.slackChannels({ workspace_id: workspaceId });
|
|
8750
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8543
8751
|
}
|
|
8544
8752
|
);
|
|
8545
|
-
|
|
8546
|
-
"
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
|
|
8753
|
+
registerTool(
|
|
8754
|
+
"slack_contributors",
|
|
8755
|
+
{
|
|
8756
|
+
title: "Slack top contributors",
|
|
8757
|
+
description: `Get top Slack contributors for a workspace.
|
|
8758
|
+
Returns: user profiles with message counts, sorted by activity level.`,
|
|
8759
|
+
inputSchema: external_exports.object({
|
|
8760
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8761
|
+
limit: external_exports.number().optional().describe("Maximum contributors to return (default: 20)")
|
|
8762
|
+
})
|
|
8763
|
+
},
|
|
8764
|
+
async (input) => {
|
|
8765
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8766
|
+
if (!workspaceId) {
|
|
8767
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8768
|
+
}
|
|
8769
|
+
const result = await client.slackContributors({ workspace_id: workspaceId, limit: input.limit });
|
|
8770
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8553
8771
|
}
|
|
8554
8772
|
);
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
// src/prompts.ts
|
|
8558
|
-
var ID_NOTES = [
|
|
8559
|
-
"Notes:",
|
|
8560
|
-
"- If ContextStream is not initialized in this conversation, call `session_init` first (omit ids).",
|
|
8561
|
-
"- Do not ask me for `workspace_id`/`project_id` \u2014 use session defaults or IDs returned by `session_init`.",
|
|
8562
|
-
"- Prefer omitting IDs in tool calls when the tool supports defaults."
|
|
8563
|
-
];
|
|
8564
|
-
var upgradeUrl = process.env.CONTEXTSTREAM_UPGRADE_URL || "https://contextstream.io/pricing";
|
|
8565
|
-
var proPrompts = /* @__PURE__ */ new Set([
|
|
8566
|
-
"build-context",
|
|
8567
|
-
"generate-plan",
|
|
8568
|
-
"generate-tasks",
|
|
8569
|
-
"token-budget-context"
|
|
8570
|
-
]);
|
|
8571
|
-
function promptAccessLabel(promptName) {
|
|
8572
|
-
return proPrompts.has(promptName) ? "PRO" : "Free";
|
|
8573
|
-
}
|
|
8574
|
-
function registerPrompts(server) {
|
|
8575
|
-
server.registerPrompt(
|
|
8576
|
-
"explore-codebase",
|
|
8773
|
+
registerTool(
|
|
8774
|
+
"slack_activity",
|
|
8577
8775
|
{
|
|
8578
|
-
title:
|
|
8579
|
-
description:
|
|
8776
|
+
title: "Slack activity feed",
|
|
8777
|
+
description: `Get recent Slack activity feed for a workspace.
|
|
8778
|
+
Returns: messages with user info, reactions, replies, and timestamps.
|
|
8779
|
+
Can filter by channel.`,
|
|
8780
|
+
inputSchema: external_exports.object({
|
|
8781
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8782
|
+
limit: external_exports.number().optional().describe("Maximum messages to return (default: 50)"),
|
|
8783
|
+
offset: external_exports.number().optional().describe("Pagination offset"),
|
|
8784
|
+
channel_id: external_exports.string().optional().describe("Filter by specific channel ID")
|
|
8785
|
+
})
|
|
8580
8786
|
},
|
|
8581
|
-
async () =>
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
"2. Use `projects_files` to identify key entry points.",
|
|
8595
|
-
"3. If a focus area is clear from our conversation, prioritize it; otherwise ask me what to focus on.",
|
|
8596
|
-
"4. Use `search_semantic` (and optionally `search_hybrid`) to find the most relevant files.",
|
|
8597
|
-
"5. Summarize the architecture, major modules, and where to start editing."
|
|
8598
|
-
].join("\n")
|
|
8599
|
-
}
|
|
8600
|
-
}
|
|
8601
|
-
]
|
|
8602
|
-
})
|
|
8787
|
+
async (input) => {
|
|
8788
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8789
|
+
if (!workspaceId) {
|
|
8790
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8791
|
+
}
|
|
8792
|
+
const result = await client.slackActivity({
|
|
8793
|
+
workspace_id: workspaceId,
|
|
8794
|
+
limit: input.limit,
|
|
8795
|
+
offset: input.offset,
|
|
8796
|
+
channel_id: input.channel_id
|
|
8797
|
+
});
|
|
8798
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8799
|
+
}
|
|
8603
8800
|
);
|
|
8604
|
-
|
|
8605
|
-
"
|
|
8801
|
+
registerTool(
|
|
8802
|
+
"slack_discussions",
|
|
8606
8803
|
{
|
|
8607
|
-
title:
|
|
8608
|
-
description:
|
|
8804
|
+
title: "Slack key discussions",
|
|
8805
|
+
description: `Get high-engagement Slack discussions/threads for a workspace.
|
|
8806
|
+
Returns: threads with high reply/reaction counts, sorted by engagement.
|
|
8807
|
+
Useful for finding important conversations and decisions.`,
|
|
8808
|
+
inputSchema: external_exports.object({
|
|
8809
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8810
|
+
limit: external_exports.number().optional().describe("Maximum discussions to return (default: 20)")
|
|
8811
|
+
})
|
|
8609
8812
|
},
|
|
8610
|
-
async () =>
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8616
|
-
|
|
8617
|
-
|
|
8618
|
-
"",
|
|
8619
|
-
...ID_NOTES,
|
|
8620
|
-
"",
|
|
8621
|
-
"Instructions:",
|
|
8622
|
-
"- If the decision is already described in this conversation, extract: title, context, decision, consequences/tradeoffs.",
|
|
8623
|
-
"- If anything is missing, ask me 1\u20133 quick questions to fill the gaps.",
|
|
8624
|
-
"- Then call `session_capture` with:",
|
|
8625
|
-
' - event_type: "decision"',
|
|
8626
|
-
" - title: (short ADR title)",
|
|
8627
|
-
" - content: a well-formatted ADR (Context, Decision, Consequences)",
|
|
8628
|
-
' - tags: include relevant tags (e.g., "adr", "architecture")',
|
|
8629
|
-
' - importance: "high"',
|
|
8630
|
-
"",
|
|
8631
|
-
"After capturing, confirm what was saved."
|
|
8632
|
-
].join("\n")
|
|
8633
|
-
}
|
|
8634
|
-
}
|
|
8635
|
-
]
|
|
8636
|
-
})
|
|
8813
|
+
async (input) => {
|
|
8814
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8815
|
+
if (!workspaceId) {
|
|
8816
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8817
|
+
}
|
|
8818
|
+
const result = await client.slackDiscussions({ workspace_id: workspaceId, limit: input.limit });
|
|
8819
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8820
|
+
}
|
|
8637
8821
|
);
|
|
8638
|
-
|
|
8639
|
-
"
|
|
8822
|
+
registerTool(
|
|
8823
|
+
"slack_search",
|
|
8640
8824
|
{
|
|
8641
|
-
title:
|
|
8642
|
-
description:
|
|
8825
|
+
title: "Search Slack messages",
|
|
8826
|
+
description: `Search Slack messages for a workspace.
|
|
8827
|
+
Returns: matching messages with channel, user, and engagement info.
|
|
8828
|
+
Use this to find specific conversations or topics.`,
|
|
8829
|
+
inputSchema: external_exports.object({
|
|
8830
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8831
|
+
q: external_exports.string().describe("Search query"),
|
|
8832
|
+
limit: external_exports.number().optional().describe("Maximum results (default: 50)")
|
|
8833
|
+
})
|
|
8643
8834
|
},
|
|
8644
|
-
async () =>
|
|
8645
|
-
|
|
8646
|
-
|
|
8647
|
-
|
|
8648
|
-
|
|
8649
|
-
|
|
8650
|
-
|
|
8651
|
-
|
|
8652
|
-
"",
|
|
8653
|
-
...ID_NOTES,
|
|
8654
|
-
"",
|
|
8655
|
-
"First:",
|
|
8656
|
-
"- If file paths and a short change description are not already in this conversation, ask me for them.",
|
|
8657
|
-
"",
|
|
8658
|
-
"Then build review context by:",
|
|
8659
|
-
"1. Using `graph_dependencies` to find what depends on the changed areas.",
|
|
8660
|
-
"2. Using `graph_impact` to assess potential blast radius.",
|
|
8661
|
-
"3. Using `memory_search` to find related decisions/notes.",
|
|
8662
|
-
"4. Using `search_semantic` to find related code patterns.",
|
|
8663
|
-
"",
|
|
8664
|
-
"Provide:",
|
|
8665
|
-
"- What the files/components do",
|
|
8666
|
-
"- What might be affected",
|
|
8667
|
-
"- Relevant prior decisions/lessons",
|
|
8668
|
-
"- Review checklist + risks to focus on"
|
|
8669
|
-
].join("\n")
|
|
8670
|
-
}
|
|
8671
|
-
}
|
|
8672
|
-
]
|
|
8673
|
-
})
|
|
8835
|
+
async (input) => {
|
|
8836
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8837
|
+
if (!workspaceId) {
|
|
8838
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8839
|
+
}
|
|
8840
|
+
const result = await client.slackSearch({ workspace_id: workspaceId, q: input.q, limit: input.limit });
|
|
8841
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8842
|
+
}
|
|
8674
8843
|
);
|
|
8675
|
-
|
|
8676
|
-
"
|
|
8844
|
+
registerTool(
|
|
8845
|
+
"slack_sync_users",
|
|
8677
8846
|
{
|
|
8678
|
-
title:
|
|
8679
|
-
description:
|
|
8847
|
+
title: "Sync Slack users",
|
|
8848
|
+
description: `Trigger a sync of Slack user profiles for a workspace.
|
|
8849
|
+
This fetches the latest user info from Slack and updates local profiles.
|
|
8850
|
+
Also auto-maps Slack users to ContextStream users by email.`,
|
|
8851
|
+
inputSchema: external_exports.object({
|
|
8852
|
+
workspace_id: external_exports.string().uuid().optional()
|
|
8853
|
+
})
|
|
8680
8854
|
},
|
|
8681
|
-
async () =>
|
|
8682
|
-
|
|
8683
|
-
|
|
8684
|
-
|
|
8685
|
-
|
|
8686
|
-
|
|
8687
|
-
|
|
8688
|
-
|
|
8689
|
-
|
|
8690
|
-
|
|
8691
|
-
|
|
8692
|
-
|
|
8693
|
-
|
|
8694
|
-
|
|
8695
|
-
"",
|
|
8696
|
-
"Then:",
|
|
8697
|
-
"1. Use `search_semantic` to find code related to the error/symptom.",
|
|
8698
|
-
"2. Use `search_pattern` to locate where similar errors are thrown or logged.",
|
|
8699
|
-
"3. If you identify key functions, use `graph_call_path` to trace call flows.",
|
|
8700
|
-
"4. Use `memory_search` to check if we have prior notes/bugs about this area.",
|
|
8701
|
-
"",
|
|
8702
|
-
"Return:",
|
|
8703
|
-
"- Likely origin locations",
|
|
8704
|
-
"- Call flow (if found)",
|
|
8705
|
-
"- Related past context",
|
|
8706
|
-
"- Suggested debugging steps"
|
|
8707
|
-
].join("\n")
|
|
8708
|
-
}
|
|
8709
|
-
}
|
|
8710
|
-
]
|
|
8711
|
-
})
|
|
8712
|
-
);
|
|
8713
|
-
server.registerPrompt(
|
|
8714
|
-
"explore-knowledge",
|
|
8715
|
-
{
|
|
8716
|
-
title: `Explore Knowledge Graph (${promptAccessLabel("explore-knowledge")})`,
|
|
8717
|
-
description: "Navigate and understand the knowledge graph for a workspace"
|
|
8718
|
-
},
|
|
8719
|
-
async () => ({
|
|
8720
|
-
messages: [
|
|
8721
|
-
{
|
|
8722
|
-
role: "user",
|
|
8723
|
-
content: {
|
|
8724
|
-
type: "text",
|
|
8725
|
-
text: [
|
|
8726
|
-
"Help me explore the knowledge captured in this workspace.",
|
|
8727
|
-
"",
|
|
8728
|
-
...ID_NOTES,
|
|
8729
|
-
"",
|
|
8730
|
-
"Approach:",
|
|
8731
|
-
"1. Use `memory_summary` for a high-level overview.",
|
|
8732
|
-
"2. Use `memory_decisions` to see decision history (titles + a few key details).",
|
|
8733
|
-
"3. Use `memory_list_nodes` to see available knowledge nodes.",
|
|
8734
|
-
"4. If a starting topic is clear from the conversation, use `memory_search` for it.",
|
|
8735
|
-
"5. Use `graph_related` on the most relevant nodes to expand connections.",
|
|
8736
|
-
"",
|
|
8737
|
-
"Provide:",
|
|
8738
|
-
"- Key themes and topics",
|
|
8739
|
-
"- Important decisions + rationale",
|
|
8740
|
-
"- Suggested \u201Cnext nodes\u201D to explore"
|
|
8741
|
-
].join("\n")
|
|
8742
|
-
}
|
|
8743
|
-
}
|
|
8744
|
-
]
|
|
8745
|
-
})
|
|
8746
|
-
);
|
|
8747
|
-
server.registerPrompt(
|
|
8748
|
-
"onboard-to-project",
|
|
8749
|
-
{
|
|
8750
|
-
title: `Project Onboarding (${promptAccessLabel("onboard-to-project")})`,
|
|
8751
|
-
description: "Generate onboarding context for a new team member"
|
|
8752
|
-
},
|
|
8753
|
-
async () => ({
|
|
8754
|
-
messages: [
|
|
8755
|
-
{
|
|
8756
|
-
role: "user",
|
|
8757
|
-
content: {
|
|
8758
|
-
type: "text",
|
|
8759
|
-
text: [
|
|
8760
|
-
"Create an onboarding guide for a new team member joining this project.",
|
|
8761
|
-
"",
|
|
8762
|
-
...ID_NOTES,
|
|
8763
|
-
"",
|
|
8764
|
-
"First:",
|
|
8765
|
-
"- If the role is not specified, ask me what role they are onboarding into (backend, frontend, fullstack, etc.).",
|
|
8766
|
-
"",
|
|
8767
|
-
"Gather context:",
|
|
8768
|
-
"1. Use `projects_overview` and `projects_statistics` for project summary.",
|
|
8769
|
-
"2. Use `projects_files` to identify key entry points.",
|
|
8770
|
-
"3. Use `memory_timeline` and `memory_decisions` to understand recent changes and architectural choices.",
|
|
8771
|
-
"4. Use `search_semantic` to find READMEs/docs/setup instructions.",
|
|
8772
|
-
"",
|
|
8773
|
-
"Output:",
|
|
8774
|
-
"- Project overview and purpose",
|
|
8775
|
-
"- Tech stack + architecture map",
|
|
8776
|
-
"- Key files/entry points relevant to the role",
|
|
8777
|
-
"- Important decisions + rationale",
|
|
8778
|
-
"- Recent changes/current focus",
|
|
8779
|
-
"- Step-by-step getting started"
|
|
8780
|
-
].join("\n")
|
|
8781
|
-
}
|
|
8782
|
-
}
|
|
8783
|
-
]
|
|
8784
|
-
})
|
|
8785
|
-
);
|
|
8786
|
-
server.registerPrompt(
|
|
8787
|
-
"analyze-refactoring",
|
|
8788
|
-
{
|
|
8789
|
-
title: `Refactoring Analysis (${promptAccessLabel("analyze-refactoring")})`,
|
|
8790
|
-
description: "Analyze a codebase for refactoring opportunities"
|
|
8791
|
-
},
|
|
8792
|
-
async () => ({
|
|
8793
|
-
messages: [
|
|
8794
|
-
{
|
|
8795
|
-
role: "user",
|
|
8796
|
-
content: {
|
|
8797
|
-
type: "text",
|
|
8798
|
-
text: [
|
|
8799
|
-
"Analyze the codebase for refactoring opportunities.",
|
|
8800
|
-
"",
|
|
8801
|
-
...ID_NOTES,
|
|
8802
|
-
"",
|
|
8803
|
-
"If a target area is obvious from our conversation, focus there; otherwise ask me what area to analyze.",
|
|
8804
|
-
"",
|
|
8805
|
-
"Please investigate:",
|
|
8806
|
-
"1. `graph_circular_dependencies` (circular deps to break)",
|
|
8807
|
-
"2. `graph_unused_code` (dead code to remove)",
|
|
8808
|
-
"3. `search_pattern` (duplication patterns)",
|
|
8809
|
-
"4. `projects_statistics` (high complexity hotspots)",
|
|
8810
|
-
"",
|
|
8811
|
-
"Provide a prioritized list with quick wins vs deeper refactors."
|
|
8812
|
-
].join("\n")
|
|
8813
|
-
}
|
|
8814
|
-
}
|
|
8815
|
-
]
|
|
8816
|
-
})
|
|
8817
|
-
);
|
|
8818
|
-
server.registerPrompt(
|
|
8819
|
-
"build-context",
|
|
8820
|
-
{
|
|
8821
|
-
title: `Build LLM Context (${promptAccessLabel("build-context")})`,
|
|
8822
|
-
description: "Build comprehensive context for an LLM task"
|
|
8823
|
-
},
|
|
8824
|
-
async () => ({
|
|
8825
|
-
messages: [
|
|
8826
|
-
{
|
|
8827
|
-
role: "user",
|
|
8828
|
-
content: {
|
|
8829
|
-
type: "text",
|
|
8830
|
-
text: [
|
|
8831
|
-
"Build comprehensive context for the task we are working on.",
|
|
8832
|
-
"",
|
|
8833
|
-
`Access: ${promptAccessLabel("build-context")}${promptAccessLabel("build-context") === "PRO" ? ` (upgrade: ${upgradeUrl})` : ""}`,
|
|
8834
|
-
"",
|
|
8835
|
-
...ID_NOTES,
|
|
8836
|
-
"",
|
|
8837
|
-
"First:",
|
|
8838
|
-
"- If the \u201Cquery\u201D is clear from the latest user request, use that.",
|
|
8839
|
-
"- Otherwise ask me: \u201CWhat do you need context for?\u201D",
|
|
8840
|
-
"",
|
|
8841
|
-
"Then:",
|
|
8842
|
-
"- Call `ai_enhanced_context` with include_code=true, include_docs=true, include_memory=true (omit IDs unless required).",
|
|
8843
|
-
"- Synthesize the returned context into a short briefing with links/file paths and key decisions/risks."
|
|
8844
|
-
].join("\n")
|
|
8845
|
-
}
|
|
8846
|
-
}
|
|
8847
|
-
]
|
|
8848
|
-
})
|
|
8849
|
-
);
|
|
8850
|
-
server.registerPrompt(
|
|
8851
|
-
"smart-search",
|
|
8852
|
-
{
|
|
8853
|
-
title: `Smart Search (${promptAccessLabel("smart-search")})`,
|
|
8854
|
-
description: "Search across memory, decisions, and code for a query"
|
|
8855
|
-
},
|
|
8856
|
-
async () => ({
|
|
8857
|
-
messages: [
|
|
8858
|
-
{
|
|
8859
|
-
role: "user",
|
|
8860
|
-
content: {
|
|
8861
|
-
type: "text",
|
|
8862
|
-
text: [
|
|
8863
|
-
"Find the most relevant context for what I am asking about.",
|
|
8864
|
-
"",
|
|
8865
|
-
...ID_NOTES,
|
|
8866
|
-
"",
|
|
8867
|
-
"First:",
|
|
8868
|
-
"- If a query is clear from the conversation, use it.",
|
|
8869
|
-
"- Otherwise ask me what I want to find.",
|
|
8870
|
-
"",
|
|
8871
|
-
"Then:",
|
|
8872
|
-
"1. Use `session_smart_search` for the query.",
|
|
8873
|
-
"2. If results are thin, follow up with `search_hybrid` and `memory_search`.",
|
|
8874
|
-
"3. Return the top results with file paths/links and a short synthesis."
|
|
8875
|
-
].join("\n")
|
|
8876
|
-
}
|
|
8877
|
-
}
|
|
8878
|
-
]
|
|
8879
|
-
})
|
|
8880
|
-
);
|
|
8881
|
-
server.registerPrompt(
|
|
8882
|
-
"recall-context",
|
|
8883
|
-
{
|
|
8884
|
-
title: `Recall Context (${promptAccessLabel("recall-context")})`,
|
|
8885
|
-
description: "Retrieve relevant past decisions and memory for a query"
|
|
8886
|
-
},
|
|
8887
|
-
async () => ({
|
|
8888
|
-
messages: [
|
|
8889
|
-
{
|
|
8890
|
-
role: "user",
|
|
8891
|
-
content: {
|
|
8892
|
-
type: "text",
|
|
8893
|
-
text: [
|
|
8894
|
-
"Recall relevant past context (decisions, notes, lessons) for what I am asking about.",
|
|
8895
|
-
"",
|
|
8896
|
-
...ID_NOTES,
|
|
8897
|
-
"",
|
|
8898
|
-
"First:",
|
|
8899
|
-
"- If a recall query is clear from the conversation, use it.",
|
|
8900
|
-
"- Otherwise ask me what topic I want to recall.",
|
|
8901
|
-
"",
|
|
8902
|
-
"Then:",
|
|
8903
|
-
"- Use `session_recall` with the query (omit IDs unless required).",
|
|
8904
|
-
"- Summarize the key points and any relevant decisions/lessons."
|
|
8905
|
-
].join("\n")
|
|
8906
|
-
}
|
|
8907
|
-
}
|
|
8908
|
-
]
|
|
8909
|
-
})
|
|
8910
|
-
);
|
|
8911
|
-
server.registerPrompt(
|
|
8912
|
-
"session-summary",
|
|
8913
|
-
{
|
|
8914
|
-
title: `Session Summary (${promptAccessLabel("session-summary")})`,
|
|
8915
|
-
description: "Get a compact summary of workspace/project context"
|
|
8916
|
-
},
|
|
8917
|
-
async () => ({
|
|
8918
|
-
messages: [
|
|
8919
|
-
{
|
|
8920
|
-
role: "user",
|
|
8921
|
-
content: {
|
|
8922
|
-
type: "text",
|
|
8923
|
-
text: [
|
|
8924
|
-
"Generate a compact, token-efficient summary of the current workspace/project context.",
|
|
8925
|
-
"",
|
|
8926
|
-
...ID_NOTES,
|
|
8927
|
-
"",
|
|
8928
|
-
"Use `session_summary` (default max_tokens=500 unless I specify otherwise).",
|
|
8929
|
-
"Then list:",
|
|
8930
|
-
"- Top decisions (titles only)",
|
|
8931
|
-
"- Any high-priority lessons to watch for"
|
|
8932
|
-
].join("\n")
|
|
8933
|
-
}
|
|
8934
|
-
}
|
|
8935
|
-
]
|
|
8936
|
-
})
|
|
8937
|
-
);
|
|
8938
|
-
server.registerPrompt(
|
|
8939
|
-
"capture-lesson",
|
|
8940
|
-
{
|
|
8941
|
-
title: `Capture Lesson (${promptAccessLabel("capture-lesson")})`,
|
|
8942
|
-
description: "Record a lesson learned from an error or correction"
|
|
8943
|
-
},
|
|
8944
|
-
async () => ({
|
|
8945
|
-
messages: [
|
|
8946
|
-
{
|
|
8947
|
-
role: "user",
|
|
8948
|
-
content: {
|
|
8949
|
-
type: "text",
|
|
8950
|
-
text: [
|
|
8951
|
-
"Capture a lesson learned so it is surfaced in future sessions.",
|
|
8952
|
-
"",
|
|
8953
|
-
...ID_NOTES,
|
|
8954
|
-
"",
|
|
8955
|
-
"If the lesson details are not fully present in the conversation, ask me for:",
|
|
8956
|
-
"- title (what to remember)",
|
|
8957
|
-
"- severity (low|medium|high|critical, default medium)",
|
|
8958
|
-
"- category (workflow|code_quality|verification|communication|project_specific)",
|
|
8959
|
-
"- trigger (what caused it)",
|
|
8960
|
-
"- impact (what went wrong)",
|
|
8961
|
-
"- prevention (how to prevent it)",
|
|
8962
|
-
"- keywords (optional)",
|
|
8963
|
-
"",
|
|
8964
|
-
"Then call `session_capture_lesson` with those fields and confirm it was saved."
|
|
8965
|
-
].join("\n")
|
|
8966
|
-
}
|
|
8967
|
-
}
|
|
8968
|
-
]
|
|
8969
|
-
})
|
|
8970
|
-
);
|
|
8971
|
-
server.registerPrompt(
|
|
8972
|
-
"capture-preference",
|
|
8973
|
-
{
|
|
8974
|
-
title: `Capture Preference (${promptAccessLabel("capture-preference")})`,
|
|
8975
|
-
description: "Save a user preference to memory"
|
|
8976
|
-
},
|
|
8977
|
-
async () => ({
|
|
8978
|
-
messages: [
|
|
8979
|
-
{
|
|
8980
|
-
role: "user",
|
|
8981
|
-
content: {
|
|
8982
|
-
type: "text",
|
|
8983
|
-
text: [
|
|
8984
|
-
"Save a user preference to ContextStream memory.",
|
|
8985
|
-
"",
|
|
8986
|
-
...ID_NOTES,
|
|
8987
|
-
"",
|
|
8988
|
-
"If the preference is not explicit in the conversation, ask me what to remember.",
|
|
8989
|
-
"",
|
|
8990
|
-
"Then call `session_capture` with:",
|
|
8991
|
-
'- event_type: "preference"',
|
|
8992
|
-
"- title: (short title)",
|
|
8993
|
-
"- content: (preference text)",
|
|
8994
|
-
'- importance: "medium"'
|
|
8995
|
-
].join("\n")
|
|
8996
|
-
}
|
|
8997
|
-
}
|
|
8998
|
-
]
|
|
8999
|
-
})
|
|
8855
|
+
async (input) => {
|
|
8856
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8857
|
+
if (!workspaceId) {
|
|
8858
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8859
|
+
}
|
|
8860
|
+
const result = await client.slackSyncUsers({ workspace_id: workspaceId });
|
|
8861
|
+
return {
|
|
8862
|
+
content: [{
|
|
8863
|
+
type: "text",
|
|
8864
|
+
text: `\u2705 Synced ${result.synced_users} Slack users, auto-mapped ${result.auto_mapped} by email.`
|
|
8865
|
+
}],
|
|
8866
|
+
structuredContent: toStructured(result)
|
|
8867
|
+
};
|
|
8868
|
+
}
|
|
9000
8869
|
);
|
|
9001
|
-
|
|
9002
|
-
"
|
|
8870
|
+
registerTool(
|
|
8871
|
+
"github_stats",
|
|
9003
8872
|
{
|
|
9004
|
-
title:
|
|
9005
|
-
description:
|
|
8873
|
+
title: "GitHub overview stats",
|
|
8874
|
+
description: `Get GitHub integration statistics and overview for a workspace.
|
|
8875
|
+
Returns: total issues, PRs, releases, comments, repository stats, activity trends, and sync status.
|
|
8876
|
+
Use this to understand GitHub activity and engagement patterns across synced repositories.`,
|
|
8877
|
+
inputSchema: external_exports.object({
|
|
8878
|
+
workspace_id: external_exports.string().uuid().optional()
|
|
8879
|
+
})
|
|
9006
8880
|
},
|
|
9007
|
-
async () =>
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
|
|
9012
|
-
|
|
9013
|
-
|
|
9014
|
-
|
|
9015
|
-
"",
|
|
9016
|
-
...ID_NOTES,
|
|
9017
|
-
"",
|
|
9018
|
-
"If the task is not explicit in the conversation, ask me what to capture.",
|
|
9019
|
-
"",
|
|
9020
|
-
"Then call `session_capture` with:",
|
|
9021
|
-
'- event_type: "task"',
|
|
9022
|
-
"- title: (short title)",
|
|
9023
|
-
"- content: (task details)",
|
|
9024
|
-
'- importance: "medium"'
|
|
9025
|
-
].join("\n")
|
|
9026
|
-
}
|
|
9027
|
-
}
|
|
9028
|
-
]
|
|
9029
|
-
})
|
|
8881
|
+
async (input) => {
|
|
8882
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8883
|
+
if (!workspaceId) {
|
|
8884
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8885
|
+
}
|
|
8886
|
+
const result = await client.githubStats({ workspace_id: workspaceId });
|
|
8887
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8888
|
+
}
|
|
9030
8889
|
);
|
|
9031
|
-
|
|
9032
|
-
"
|
|
8890
|
+
registerTool(
|
|
8891
|
+
"github_repos",
|
|
9033
8892
|
{
|
|
9034
|
-
title:
|
|
9035
|
-
description:
|
|
8893
|
+
title: "List GitHub repositories",
|
|
8894
|
+
description: `Get synced GitHub repositories with statistics for a workspace.
|
|
8895
|
+
Returns: repository names with issue, PR, release, and comment counts, plus last activity timestamps.`,
|
|
8896
|
+
inputSchema: external_exports.object({
|
|
8897
|
+
workspace_id: external_exports.string().uuid().optional()
|
|
8898
|
+
})
|
|
9036
8899
|
},
|
|
9037
|
-
async () =>
|
|
9038
|
-
|
|
9039
|
-
|
|
9040
|
-
|
|
9041
|
-
|
|
9042
|
-
|
|
9043
|
-
|
|
9044
|
-
|
|
9045
|
-
"",
|
|
9046
|
-
...ID_NOTES,
|
|
9047
|
-
"",
|
|
9048
|
-
"If details are missing, ask me for:",
|
|
9049
|
-
"- title",
|
|
9050
|
-
"- description",
|
|
9051
|
-
"- steps to reproduce (optional)",
|
|
9052
|
-
"- expected behavior (optional)",
|
|
9053
|
-
"- actual behavior (optional)",
|
|
9054
|
-
"",
|
|
9055
|
-
"Then call `session_capture` with:",
|
|
9056
|
-
'- event_type: "bug"',
|
|
9057
|
-
"- title: (bug title)",
|
|
9058
|
-
"- content: a well-formatted bug report (include all provided details)",
|
|
9059
|
-
"- tags: component/area tags",
|
|
9060
|
-
'- importance: "high"'
|
|
9061
|
-
].join("\n")
|
|
9062
|
-
}
|
|
9063
|
-
}
|
|
9064
|
-
]
|
|
9065
|
-
})
|
|
8900
|
+
async (input) => {
|
|
8901
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8902
|
+
if (!workspaceId) {
|
|
8903
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8904
|
+
}
|
|
8905
|
+
const result = await client.githubRepos({ workspace_id: workspaceId });
|
|
8906
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8907
|
+
}
|
|
9066
8908
|
);
|
|
9067
|
-
|
|
9068
|
-
"
|
|
8909
|
+
registerTool(
|
|
8910
|
+
"github_contributors",
|
|
9069
8911
|
{
|
|
9070
|
-
title:
|
|
9071
|
-
description:
|
|
8912
|
+
title: "GitHub top contributors",
|
|
8913
|
+
description: `Get top GitHub contributors for a workspace.
|
|
8914
|
+
Returns: usernames with contribution counts, sorted by activity level.`,
|
|
8915
|
+
inputSchema: external_exports.object({
|
|
8916
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8917
|
+
limit: external_exports.number().optional().describe("Maximum contributors to return (default: 20)")
|
|
8918
|
+
})
|
|
9072
8919
|
},
|
|
9073
|
-
async () =>
|
|
9074
|
-
|
|
9075
|
-
|
|
9076
|
-
|
|
9077
|
-
|
|
9078
|
-
|
|
9079
|
-
|
|
9080
|
-
|
|
9081
|
-
"",
|
|
9082
|
-
...ID_NOTES,
|
|
9083
|
-
"",
|
|
9084
|
-
"If details are missing, ask me for:",
|
|
9085
|
-
"- title",
|
|
9086
|
-
"- description",
|
|
9087
|
-
"- rationale (optional)",
|
|
9088
|
-
"- acceptance criteria (optional)",
|
|
9089
|
-
"",
|
|
9090
|
-
"Then call `session_capture` with:",
|
|
9091
|
-
'- event_type: "feature"',
|
|
9092
|
-
"- title: (feature title)",
|
|
9093
|
-
"- content: a well-formatted feature request",
|
|
9094
|
-
"- tags: component/area tags",
|
|
9095
|
-
'- importance: "medium"'
|
|
9096
|
-
].join("\n")
|
|
9097
|
-
}
|
|
9098
|
-
}
|
|
9099
|
-
]
|
|
9100
|
-
})
|
|
8920
|
+
async (input) => {
|
|
8921
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8922
|
+
if (!workspaceId) {
|
|
8923
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8924
|
+
}
|
|
8925
|
+
const result = await client.githubContributors({ workspace_id: workspaceId, limit: input.limit });
|
|
8926
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8927
|
+
}
|
|
9101
8928
|
);
|
|
9102
|
-
|
|
9103
|
-
"
|
|
8929
|
+
registerTool(
|
|
8930
|
+
"github_activity",
|
|
9104
8931
|
{
|
|
9105
|
-
title:
|
|
9106
|
-
description:
|
|
8932
|
+
title: "GitHub activity feed",
|
|
8933
|
+
description: `Get recent GitHub activity feed for a workspace.
|
|
8934
|
+
Returns: issues, PRs, releases, and comments with details like state, author, labels.
|
|
8935
|
+
Can filter by repository or type (issue, pull_request, release, comment).`,
|
|
8936
|
+
inputSchema: external_exports.object({
|
|
8937
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8938
|
+
limit: external_exports.number().optional().describe("Maximum items to return (default: 50)"),
|
|
8939
|
+
offset: external_exports.number().optional().describe("Pagination offset"),
|
|
8940
|
+
repo: external_exports.string().optional().describe("Filter by repository name"),
|
|
8941
|
+
type: external_exports.enum(["issue", "pull_request", "release", "comment"]).optional().describe("Filter by item type")
|
|
8942
|
+
})
|
|
9107
8943
|
},
|
|
9108
|
-
async () =>
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
"",
|
|
9123
|
-
"Then call `ai_plan` and present the plan as an ordered list with milestones and risks."
|
|
9124
|
-
].join("\n")
|
|
9125
|
-
}
|
|
9126
|
-
}
|
|
9127
|
-
]
|
|
9128
|
-
})
|
|
8944
|
+
async (input) => {
|
|
8945
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8946
|
+
if (!workspaceId) {
|
|
8947
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8948
|
+
}
|
|
8949
|
+
const result = await client.githubActivity({
|
|
8950
|
+
workspace_id: workspaceId,
|
|
8951
|
+
limit: input.limit,
|
|
8952
|
+
offset: input.offset,
|
|
8953
|
+
repo: input.repo,
|
|
8954
|
+
type: input.type
|
|
8955
|
+
});
|
|
8956
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8957
|
+
}
|
|
9129
8958
|
);
|
|
9130
|
-
|
|
9131
|
-
"
|
|
8959
|
+
registerTool(
|
|
8960
|
+
"github_issues",
|
|
9132
8961
|
{
|
|
9133
|
-
title:
|
|
9134
|
-
description:
|
|
8962
|
+
title: "GitHub issues and PRs",
|
|
8963
|
+
description: `Get GitHub issues and pull requests for a workspace.
|
|
8964
|
+
Returns: issues/PRs with title, state, author, labels, comment count.
|
|
8965
|
+
Can filter by state (open/closed) or repository.`,
|
|
8966
|
+
inputSchema: external_exports.object({
|
|
8967
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8968
|
+
limit: external_exports.number().optional().describe("Maximum items to return (default: 50)"),
|
|
8969
|
+
offset: external_exports.number().optional().describe("Pagination offset"),
|
|
8970
|
+
state: external_exports.enum(["open", "closed"]).optional().describe("Filter by state"),
|
|
8971
|
+
repo: external_exports.string().optional().describe("Filter by repository name")
|
|
8972
|
+
})
|
|
9135
8973
|
},
|
|
9136
|
-
async () =>
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
|
|
9143
|
-
|
|
9144
|
-
|
|
9145
|
-
|
|
9146
|
-
|
|
9147
|
-
|
|
9148
|
-
|
|
9149
|
-
|
|
9150
|
-
"If granularity is not specified, default to medium.",
|
|
9151
|
-
"",
|
|
9152
|
-
"Call `ai_tasks` and return a checklist of tasks with acceptance criteria for each."
|
|
9153
|
-
].join("\n")
|
|
9154
|
-
}
|
|
9155
|
-
}
|
|
9156
|
-
]
|
|
9157
|
-
})
|
|
8974
|
+
async (input) => {
|
|
8975
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
8976
|
+
if (!workspaceId) {
|
|
8977
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
8978
|
+
}
|
|
8979
|
+
const result = await client.githubIssues({
|
|
8980
|
+
workspace_id: workspaceId,
|
|
8981
|
+
limit: input.limit,
|
|
8982
|
+
offset: input.offset,
|
|
8983
|
+
state: input.state,
|
|
8984
|
+
repo: input.repo
|
|
8985
|
+
});
|
|
8986
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
8987
|
+
}
|
|
9158
8988
|
);
|
|
9159
|
-
|
|
9160
|
-
"
|
|
8989
|
+
registerTool(
|
|
8990
|
+
"github_search",
|
|
9161
8991
|
{
|
|
9162
|
-
title:
|
|
9163
|
-
description:
|
|
8992
|
+
title: "Search GitHub content",
|
|
8993
|
+
description: `Search GitHub issues, PRs, and comments for a workspace.
|
|
8994
|
+
Returns: matching items with repository, title, state, and content preview.
|
|
8995
|
+
Use this to find specific issues, PRs, or discussions.`,
|
|
8996
|
+
inputSchema: external_exports.object({
|
|
8997
|
+
workspace_id: external_exports.string().uuid().optional(),
|
|
8998
|
+
q: external_exports.string().describe("Search query"),
|
|
8999
|
+
limit: external_exports.number().optional().describe("Maximum results (default: 50)")
|
|
9000
|
+
})
|
|
9164
9001
|
},
|
|
9165
|
-
async () =>
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
"",
|
|
9174
|
-
`Access: ${promptAccessLabel("token-budget-context")}${promptAccessLabel("token-budget-context") === "PRO" ? ` (upgrade: ${upgradeUrl})` : ""}`,
|
|
9175
|
-
"",
|
|
9176
|
-
...ID_NOTES,
|
|
9177
|
-
"",
|
|
9178
|
-
"First:",
|
|
9179
|
-
"- If a query is clear from the conversation, use it; otherwise ask me for a query.",
|
|
9180
|
-
"- If max_tokens is not specified, ask me for a token budget (e.g., 500/1000/2000).",
|
|
9181
|
-
"",
|
|
9182
|
-
"Then call `ai_context_budget` and return the packed context plus a short note about what was included/excluded."
|
|
9183
|
-
].join("\n")
|
|
9184
|
-
}
|
|
9185
|
-
}
|
|
9186
|
-
]
|
|
9187
|
-
})
|
|
9002
|
+
async (input) => {
|
|
9003
|
+
const workspaceId = resolveWorkspaceId(input.workspace_id);
|
|
9004
|
+
if (!workspaceId) {
|
|
9005
|
+
return errorResult("Error: workspace_id is required. Please call session_init first or provide workspace_id explicitly.");
|
|
9006
|
+
}
|
|
9007
|
+
const result = await client.githubSearch({ workspace_id: workspaceId, q: input.q, limit: input.limit });
|
|
9008
|
+
return { content: [{ type: "text", text: formatContent(result) }], structuredContent: toStructured(result) };
|
|
9009
|
+
}
|
|
9188
9010
|
);
|
|
9189
|
-
|
|
9190
|
-
|
|
9011
|
+
}
|
|
9012
|
+
|
|
9013
|
+
// src/resources.ts
|
|
9014
|
+
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9015
|
+
function wrapText(uri, text) {
|
|
9016
|
+
return { contents: [{ uri, text }] };
|
|
9017
|
+
}
|
|
9018
|
+
function registerResources(server, client, apiUrl) {
|
|
9019
|
+
server.registerResource(
|
|
9020
|
+
"contextstream-openapi",
|
|
9021
|
+
new ResourceTemplate("contextstream:openapi", { list: void 0 }),
|
|
9191
9022
|
{
|
|
9192
|
-
title:
|
|
9193
|
-
description: "
|
|
9023
|
+
title: "ContextStream OpenAPI spec",
|
|
9024
|
+
description: "Machine-readable OpenAPI from the configured API endpoint",
|
|
9025
|
+
mimeType: "application/json"
|
|
9194
9026
|
},
|
|
9195
|
-
async () =>
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
text: [
|
|
9202
|
-
"Scan the codebase for TODO/FIXME/HACK notes and summarize them.",
|
|
9203
|
-
"",
|
|
9204
|
-
...ID_NOTES,
|
|
9205
|
-
"",
|
|
9206
|
-
"Use `search_pattern` with query `TODO|FIXME|HACK` (or a pattern inferred from the conversation).",
|
|
9207
|
-
"Group results by file path, summarize themes, and propose a small prioritized cleanup list."
|
|
9208
|
-
].join("\n")
|
|
9209
|
-
}
|
|
9210
|
-
}
|
|
9211
|
-
]
|
|
9212
|
-
})
|
|
9027
|
+
async () => {
|
|
9028
|
+
const uri = `${apiUrl.replace(/\/$/, "")}/api-docs/openapi.json`;
|
|
9029
|
+
const res = await fetch(uri);
|
|
9030
|
+
const text = await res.text();
|
|
9031
|
+
return wrapText("contextstream:openapi", text);
|
|
9032
|
+
}
|
|
9213
9033
|
);
|
|
9214
|
-
server.
|
|
9215
|
-
"
|
|
9216
|
-
{
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
{
|
|
9223
|
-
role: "user",
|
|
9224
|
-
content: {
|
|
9225
|
-
type: "text",
|
|
9226
|
-
text: [
|
|
9227
|
-
"Generate ContextStream AI rule files for my editor.",
|
|
9228
|
-
"",
|
|
9229
|
-
...ID_NOTES,
|
|
9230
|
-
"",
|
|
9231
|
-
"First:",
|
|
9232
|
-
"- If you can infer the project folder path from the environment/IDE roots, use it.",
|
|
9233
|
-
"- Otherwise ask me for an absolute folder path.",
|
|
9234
|
-
"- Ask which editor(s) (windsurf,cursor,cline,kilo,roo,claude,aider) or default to all.",
|
|
9235
|
-
"",
|
|
9236
|
-
"Then call `generate_editor_rules` and confirm which files were created/updated."
|
|
9237
|
-
].join("\n")
|
|
9238
|
-
}
|
|
9239
|
-
}
|
|
9240
|
-
]
|
|
9241
|
-
})
|
|
9034
|
+
server.registerResource(
|
|
9035
|
+
"contextstream-workspaces",
|
|
9036
|
+
new ResourceTemplate("contextstream:workspaces", { list: void 0 }),
|
|
9037
|
+
{ title: "Workspaces", description: "List of accessible workspaces" },
|
|
9038
|
+
async () => {
|
|
9039
|
+
const data = await client.listWorkspaces();
|
|
9040
|
+
return wrapText("contextstream:workspaces", JSON.stringify(data, null, 2));
|
|
9041
|
+
}
|
|
9242
9042
|
);
|
|
9243
|
-
server.
|
|
9244
|
-
"
|
|
9245
|
-
{
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
role: "user",
|
|
9253
|
-
content: {
|
|
9254
|
-
type: "text",
|
|
9255
|
-
text: [
|
|
9256
|
-
"Ingest local files into ContextStream for indexing/search.",
|
|
9257
|
-
"",
|
|
9258
|
-
...ID_NOTES,
|
|
9259
|
-
"",
|
|
9260
|
-
"First:",
|
|
9261
|
-
"- Ask me for the local directory path to ingest if it is not already specified.",
|
|
9262
|
-
"",
|
|
9263
|
-
"Then:",
|
|
9264
|
-
"- Call `projects_ingest_local` with the path (use session defaults for project, or the `project_id` returned by `session_init`).",
|
|
9265
|
-
"- Explain how to monitor progress via `projects_index_status`."
|
|
9266
|
-
].join("\n")
|
|
9267
|
-
}
|
|
9268
|
-
}
|
|
9269
|
-
]
|
|
9270
|
-
})
|
|
9043
|
+
server.registerResource(
|
|
9044
|
+
"contextstream-projects",
|
|
9045
|
+
new ResourceTemplate("contextstream:projects/{workspaceId}", { list: void 0 }),
|
|
9046
|
+
{ title: "Projects for workspace", description: "Projects in the specified workspace" },
|
|
9047
|
+
async (uri, { workspaceId }) => {
|
|
9048
|
+
const wsId = Array.isArray(workspaceId) ? workspaceId[0] : workspaceId;
|
|
9049
|
+
const data = await client.listProjects({ workspace_id: wsId });
|
|
9050
|
+
return wrapText(uri.href, JSON.stringify(data, null, 2));
|
|
9051
|
+
}
|
|
9271
9052
|
);
|
|
9272
9053
|
}
|
|
9273
9054
|
|
|
@@ -9633,7 +9414,6 @@ async function main() {
|
|
|
9633
9414
|
const sessionManager = new SessionManager(server, client);
|
|
9634
9415
|
registerTools(server, client, sessionManager);
|
|
9635
9416
|
registerResources(server, client, config.apiUrl);
|
|
9636
|
-
registerPrompts(server);
|
|
9637
9417
|
console.error(`ContextStream MCP server v${VERSION} starting...`);
|
|
9638
9418
|
console.error(`API URL: ${config.apiUrl}`);
|
|
9639
9419
|
console.error(`Auth: ${config.apiKey ? "API Key" : config.jwt ? "JWT" : "None"}`);
|