@contextstream/mcp-server 0.4.81 → 1.0.0

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.
@@ -0,0 +1,390 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { spawn } from "node:child_process";
3
+ import { createReadStream } from "node:fs";
4
+ import {
5
+ chmod,
6
+ lstat,
7
+ mkdir,
8
+ open,
9
+ readFile,
10
+ rename,
11
+ stat,
12
+ unlink,
13
+ writeFile,
14
+ } from "node:fs/promises";
15
+ import { homedir } from "node:os";
16
+ import path from "node:path";
17
+ import { fileURLToPath } from "node:url";
18
+
19
+ const packagePath = fileURLToPath(new URL("../package.json", import.meta.url));
20
+ const packageMetadata = JSON.parse(await readFile(packagePath, "utf8"));
21
+
22
+ export const PACKAGE_VERSION = packageMetadata.version;
23
+ export const RELEASE_ROOT =
24
+ "https://pub-68429b9f7857416c9484b75bf1887b96.r2.dev/";
25
+
26
+ const MAX_DOWNLOAD_BYTES = 200 * 1024 * 1024;
27
+ const FETCH_TIMEOUT_MS = 30_000;
28
+ const LOCK_TIMEOUT_MS = 30_000;
29
+ const STALE_LOCK_MS = 120_000;
30
+
31
+ const PLATFORM_ARTIFACTS = Object.freeze({
32
+ "linux-x64": "contextstream-mcp-linux-x64",
33
+ "linux-arm64": "contextstream-mcp-linux-arm64",
34
+ "darwin-x64": "contextstream-mcp-darwin-x64",
35
+ "darwin-arm64": "contextstream-mcp-darwin-arm64",
36
+ "win32-x64": "contextstream-mcp-win-x64.exe",
37
+ });
38
+
39
+ class FetchFailure extends Error {
40
+ constructor(message, options) {
41
+ super(message, options);
42
+ this.name = "FetchFailure";
43
+ }
44
+ }
45
+
46
+ export function artifactFor(platform = process.platform, arch = process.arch) {
47
+ const key = `${platform}-${arch}`;
48
+ const artifact = PLATFORM_ARTIFACTS[key];
49
+ if (!artifact) {
50
+ throw new Error(
51
+ `@contextstream/mcp-server ${PACKAGE_VERSION} does not publish a binary for ${key}`,
52
+ );
53
+ }
54
+ const manifestKey = platform === "win32" ? "win-x64" : key;
55
+ return { artifact, key, manifestKey };
56
+ }
57
+
58
+ function truthy(value) {
59
+ return /^(1|true|yes|on|enabled)$/i.test(value ?? "");
60
+ }
61
+
62
+ function releasePrefix(version, env) {
63
+ const override = env.CONTEXTSTREAM_MCP_RELEASE_BASE_URL;
64
+ const testOverrideEnabled = truthy(env.CONTEXTSTREAM_MCP_TEST_ALLOW_HTTP);
65
+ if (override && !testOverrideEnabled) {
66
+ throw new Error(
67
+ "release base URL overrides are disabled outside the explicit test harness",
68
+ );
69
+ }
70
+ const prefix = override
71
+ ? new URL(override.endsWith("/") ? override : `${override}/`)
72
+ : new URL(`mcp/v${version}/`, RELEASE_ROOT);
73
+ if (
74
+ prefix.protocol !== "https:" &&
75
+ !(testOverrideEnabled && prefix.protocol === "http:")
76
+ ) {
77
+ throw new Error("release base URL must use HTTPS");
78
+ }
79
+ if (prefix.username || prefix.password || prefix.search || prefix.hash) {
80
+ throw new Error("release base URL must not contain credentials, a query, or a fragment");
81
+ }
82
+ return prefix;
83
+ }
84
+
85
+ function defaultCacheRoot(env) {
86
+ if (env.CONTEXTSTREAM_MCP_CACHE_DIR) {
87
+ return path.resolve(env.CONTEXTSTREAM_MCP_CACHE_DIR);
88
+ }
89
+ if (process.platform === "win32" && env.LOCALAPPDATA) {
90
+ return path.join(env.LOCALAPPDATA, "ContextStream", "mcp");
91
+ }
92
+ if (env.XDG_CACHE_HOME) {
93
+ return path.join(env.XDG_CACHE_HOME, "contextstream", "mcp");
94
+ }
95
+ return path.join(homedir(), ".cache", "contextstream", "mcp");
96
+ }
97
+
98
+ async function fetchBuffer(url, maxBytes = MAX_DOWNLOAD_BYTES) {
99
+ const controller = new AbortController();
100
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
101
+ try {
102
+ let response;
103
+ try {
104
+ response = await fetch(url, {
105
+ redirect: "error",
106
+ signal: controller.signal,
107
+ headers: { "user-agent": `contextstream-npm-launcher/${PACKAGE_VERSION}` },
108
+ });
109
+ } catch (error) {
110
+ throw new FetchFailure(`could not download ${url}`, { cause: error });
111
+ }
112
+ if (!response.ok) {
113
+ throw new FetchFailure(`download returned HTTP ${response.status} for ${url}`);
114
+ }
115
+ const declaredLength = Number(response.headers.get("content-length"));
116
+ if (Number.isFinite(declaredLength) && declaredLength > maxBytes) {
117
+ throw new Error(`release object exceeds the ${maxBytes}-byte safety limit`);
118
+ }
119
+ if (!response.body) {
120
+ throw new FetchFailure(`download returned no body for ${url}`);
121
+ }
122
+
123
+ const chunks = [];
124
+ let total = 0;
125
+ for await (const chunk of response.body) {
126
+ const buffer = Buffer.from(chunk);
127
+ total += buffer.length;
128
+ if (total > maxBytes) {
129
+ throw new Error(`release object exceeds the ${maxBytes}-byte safety limit`);
130
+ }
131
+ chunks.push(buffer);
132
+ }
133
+ return Buffer.concat(chunks, total);
134
+ } finally {
135
+ clearTimeout(timeout);
136
+ }
137
+ }
138
+
139
+ function parseChecksums(contents) {
140
+ const checksums = new Map();
141
+ for (const line of contents.trim().split(/\r?\n/u)) {
142
+ const match = /^([0-9a-f]{64}) ([A-Za-z0-9._-]+)$/u.exec(line);
143
+ if (!match || checksums.has(match[2])) {
144
+ throw new Error("release checksums.txt has an invalid or duplicate entry");
145
+ }
146
+ checksums.set(match[2], match[1]);
147
+ }
148
+ return checksums;
149
+ }
150
+
151
+ async function releaseMetadata({ artifact, manifestKey, prefix, version }) {
152
+ const manifestUrl = new URL("version.json", prefix);
153
+ const checksumsUrl = new URL("checksums.txt", prefix);
154
+ const [manifestBytes, checksumBytes] = await Promise.all([
155
+ fetchBuffer(manifestUrl, 128 * 1024),
156
+ fetchBuffer(checksumsUrl, 128 * 1024),
157
+ ]);
158
+
159
+ let manifest;
160
+ try {
161
+ manifest = JSON.parse(manifestBytes.toString("utf8"));
162
+ } catch (error) {
163
+ throw new Error("release version.json is not valid JSON", { cause: error });
164
+ }
165
+ if (
166
+ !manifest ||
167
+ typeof manifest !== "object" ||
168
+ manifest.version !== version ||
169
+ !manifest.files ||
170
+ manifest.files[manifestKey] !== artifact
171
+ ) {
172
+ throw new Error("release manifest does not match this exact npm package/platform");
173
+ }
174
+
175
+ const checksums = parseChecksums(checksumBytes.toString("utf8"));
176
+ const sha256 = checksums.get(artifact);
177
+ if (!sha256) {
178
+ throw new Error(`release checksums do not declare ${artifact}`);
179
+ }
180
+ return {
181
+ artifact,
182
+ manifestKey,
183
+ prefix: prefix.href,
184
+ sha256,
185
+ version,
186
+ };
187
+ }
188
+
189
+ async function sha256File(filename) {
190
+ const hash = createHash("sha256");
191
+ await new Promise((resolve, reject) => {
192
+ const stream = createReadStream(filename);
193
+ stream.on("data", (chunk) => hash.update(chunk));
194
+ stream.on("error", reject);
195
+ stream.on("end", resolve);
196
+ });
197
+ return hash.digest("hex");
198
+ }
199
+
200
+ async function regularFile(filename) {
201
+ try {
202
+ const details = await lstat(filename);
203
+ return details.isFile() && !details.isSymbolicLink();
204
+ } catch {
205
+ return false;
206
+ }
207
+ }
208
+
209
+ async function validCachedBinary(binaryPath, metadataPath, expected = undefined) {
210
+ if (!(await regularFile(binaryPath)) || !(await regularFile(metadataPath))) {
211
+ return false;
212
+ }
213
+ let metadata;
214
+ try {
215
+ metadata = JSON.parse(await readFile(metadataPath, "utf8"));
216
+ } catch {
217
+ return false;
218
+ }
219
+ if (
220
+ metadata.version !== PACKAGE_VERSION ||
221
+ !/^[0-9a-f]{64}$/u.test(metadata.sha256 ?? "") ||
222
+ (expected &&
223
+ (metadata.sha256 !== expected.sha256 ||
224
+ metadata.artifact !== expected.artifact ||
225
+ metadata.manifestKey !== expected.manifestKey))
226
+ ) {
227
+ return false;
228
+ }
229
+ return (await sha256File(binaryPath)) === metadata.sha256;
230
+ }
231
+
232
+ async function acquireLock(lockPath, ready) {
233
+ const deadline = Date.now() + LOCK_TIMEOUT_MS;
234
+ while (Date.now() < deadline) {
235
+ try {
236
+ const handle = await open(lockPath, "wx", 0o600);
237
+ await handle.writeFile(`${process.pid} ${Date.now()}\n`);
238
+ return async () => {
239
+ await handle.close().catch(() => undefined);
240
+ await unlink(lockPath).catch(() => undefined);
241
+ };
242
+ } catch (error) {
243
+ if (error?.code !== "EEXIST") throw error;
244
+ if (await ready()) return null;
245
+ try {
246
+ const details = await stat(lockPath);
247
+ if (Date.now() - details.mtimeMs > STALE_LOCK_MS) {
248
+ await unlink(lockPath);
249
+ continue;
250
+ }
251
+ } catch (statError) {
252
+ if (statError?.code !== "ENOENT") throw statError;
253
+ }
254
+ await new Promise((resolve) => setTimeout(resolve, 100));
255
+ }
256
+ }
257
+ throw new Error("timed out waiting for another launcher to populate the cache");
258
+ }
259
+
260
+ async function installCachedBinary(binaryPath, metadataPath, metadata) {
261
+ const unique = `${process.pid}-${randomUUID()}`;
262
+ const temporaryBinary = `${binaryPath}.${unique}.tmp`;
263
+ const temporaryMetadata = `${metadataPath}.${unique}.tmp`;
264
+ try {
265
+ const bytes = await fetchBuffer(new URL(metadata.artifact, metadata.prefix));
266
+ const digest = createHash("sha256").update(bytes).digest("hex");
267
+ if (digest !== metadata.sha256) {
268
+ throw new Error(
269
+ `downloaded ${metadata.artifact} failed SHA-256 verification`,
270
+ );
271
+ }
272
+ await writeFile(temporaryBinary, bytes, { flag: "wx", mode: 0o700 });
273
+ if (process.platform !== "win32") await chmod(temporaryBinary, 0o700);
274
+ await writeFile(
275
+ temporaryMetadata,
276
+ `${JSON.stringify({ ...metadata, installedAt: new Date().toISOString() }, null, 2)}\n`,
277
+ { flag: "wx", mode: 0o600 },
278
+ );
279
+ await rename(temporaryBinary, binaryPath);
280
+ await rename(temporaryMetadata, metadataPath);
281
+ } finally {
282
+ await unlink(temporaryBinary).catch(() => undefined);
283
+ await unlink(temporaryMetadata).catch(() => undefined);
284
+ }
285
+ }
286
+
287
+ export async function ensureBinary({
288
+ env = process.env,
289
+ platform = process.platform,
290
+ arch = process.arch,
291
+ } = {}) {
292
+ const selected = artifactFor(platform, arch);
293
+ const cacheDirectory = path.join(
294
+ defaultCacheRoot(env),
295
+ `v${PACKAGE_VERSION}`,
296
+ selected.key,
297
+ );
298
+ const binaryPath = path.join(
299
+ cacheDirectory,
300
+ platform === "win32" ? "contextstream-mcp.exe" : "contextstream-mcp",
301
+ );
302
+ const metadataPath = path.join(cacheDirectory, "metadata.json");
303
+ const lockPath = path.join(cacheDirectory, ".install.lock");
304
+ await mkdir(cacheDirectory, { recursive: true, mode: 0o700 });
305
+
306
+ if (truthy(env.CONTEXTSTREAM_MCP_OFFLINE)) {
307
+ if (await validCachedBinary(binaryPath, metadataPath)) return binaryPath;
308
+ throw new Error(
309
+ `offline mode requested, but no verified ${PACKAGE_VERSION} binary is cached`,
310
+ );
311
+ }
312
+
313
+ let metadata;
314
+ try {
315
+ metadata = await releaseMetadata({
316
+ ...selected,
317
+ prefix: releasePrefix(PACKAGE_VERSION, env),
318
+ version: PACKAGE_VERSION,
319
+ });
320
+ } catch (error) {
321
+ if (error instanceof FetchFailure && (await validCachedBinary(binaryPath, metadataPath))) {
322
+ process.stderr.write(
323
+ `ContextStream launcher: release service unavailable; using verified cached v${PACKAGE_VERSION}.\n`,
324
+ );
325
+ return binaryPath;
326
+ }
327
+ throw error;
328
+ }
329
+
330
+ if (await validCachedBinary(binaryPath, metadataPath, metadata)) return binaryPath;
331
+ const release = await acquireLock(lockPath, () =>
332
+ validCachedBinary(binaryPath, metadataPath, metadata),
333
+ );
334
+ if (release === null) return binaryPath;
335
+ try {
336
+ if (!(await validCachedBinary(binaryPath, metadataPath, metadata))) {
337
+ await installCachedBinary(binaryPath, metadataPath, metadata);
338
+ }
339
+ } finally {
340
+ await release();
341
+ }
342
+ if (!(await validCachedBinary(binaryPath, metadataPath, metadata))) {
343
+ throw new Error("cached ContextStream binary failed post-install verification");
344
+ }
345
+ return binaryPath;
346
+ }
347
+
348
+ function forwardSignals(child) {
349
+ const signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
350
+ const handlers = new Map();
351
+ for (const signal of signals) {
352
+ const handler = () => child.kill(signal);
353
+ handlers.set(signal, handler);
354
+ process.on(signal, handler);
355
+ }
356
+ return () => {
357
+ for (const [signal, handler] of handlers) process.off(signal, handler);
358
+ };
359
+ }
360
+
361
+ export async function run({ mode = "server", args = process.argv.slice(2), env = process.env } = {}) {
362
+ if (!matchesMode(mode)) throw new Error(`unsupported launcher mode: ${mode}`);
363
+ const binaryPath = await ensureBinary({ env });
364
+ const childArgs = mode === "hook" ? ["hook", ...args] : args;
365
+ const child = spawn(binaryPath, childArgs, {
366
+ env: {
367
+ ...env,
368
+ CONTEXTSTREAM_DISABLE_SELF_UPDATE: "1",
369
+ CONTEXTSTREAM_MANAGED_BY_NPM: "1",
370
+ CONTEXTSTREAM_NPM_PACKAGE_VERSION: PACKAGE_VERSION,
371
+ },
372
+ stdio: "inherit",
373
+ windowsHide: true,
374
+ });
375
+ const stopForwarding = forwardSignals(child);
376
+ const result = await new Promise((resolve, reject) => {
377
+ child.once("error", reject);
378
+ child.once("exit", (code, signal) => resolve({ code, signal }));
379
+ }).finally(stopForwarding);
380
+
381
+ if (result.signal) {
382
+ process.kill(process.pid, result.signal);
383
+ return;
384
+ }
385
+ process.exitCode = result.code ?? 1;
386
+ }
387
+
388
+ function matchesMode(mode) {
389
+ return mode === "server" || mode === "hook";
390
+ }
package/package.json CHANGED
@@ -1,84 +1,35 @@
1
1
  {
2
2
  "name": "@contextstream/mcp-server",
3
- "mcpName": "io.github.contextstreamio/mcp-server",
4
- "version": "0.4.81",
5
- "description": "MCP server that gives AI coding assistants persistent memory, semantic code search, a dependency graph, grounded Q&A, and team context (GitHub/Slack/Notion) — works with Claude Code, Cursor, VS Code Copilot, Windsurf, Cline, and any Model Context Protocol client.",
3
+ "mcpName": "io.github.contextstream/mcp-server",
4
+ "version": "1.0.0",
5
+ "description": "Verified npm launcher for the open-source ContextStream Rust MCP server",
6
6
  "type": "module",
7
7
  "license": "MIT",
8
- "main": "dist/index.js",
9
8
  "files": [
10
- "dist",
11
- "dist/hooks",
12
- "scripts",
13
- "README.md"
9
+ "npm/bin",
10
+ "npm/launcher.mjs",
11
+ "README.md",
12
+ "LICENSE",
13
+ "NOTICE",
14
+ "server.json"
14
15
  ],
15
16
  "bin": {
16
- "mcp-server": "dist/index.js",
17
- "contextstream-mcp": "dist/index.js",
18
- "contextstream-hook": "dist/hooks/runner.js"
17
+ "mcp-server": "npm/bin/mcp-server.mjs",
18
+ "contextstream-mcp": "npm/bin/contextstream-mcp.mjs",
19
+ "contextstream-hook": "npm/bin/contextstream-hook.mjs"
19
20
  },
20
21
  "scripts": {
21
- "dev": "tsx src/index.ts",
22
- "build": "esbuild src/index.ts --bundle --platform=node --target=node18 --outfile=dist/index.js --format=esm --external:@modelcontextprotocol/sdk --banner:js=\"#!/usr/bin/env node\" && esbuild src/test-server.ts --bundle --platform=node --target=node18 --outfile=dist/test-server.js --format=esm --external:@modelcontextprotocol/sdk && esbuild src/hooks/runner.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/runner.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/post-write.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/post-write.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/pre-tool-use.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/pre-tool-use.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/post-tool-use-failure.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/post-tool-use-failure.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/user-prompt-submit.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/user-prompt-submit.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/media-aware.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/media-aware.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/pre-compact.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/pre-compact.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/post-compact.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/post-compact.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/notification.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/notification.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/permission-request.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/permission-request.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/subagent-start.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/subagent-start.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/subagent-stop.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/subagent-stop.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/task-completed.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/task-completed.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/teammate-idle.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/teammate-idle.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/stop.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/stop.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/auto-rules.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/auto-rules.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/on-bash.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/on-bash.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/on-task.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/on-task.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/on-read.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/on-read.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/on-web.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/on-web.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/session-init.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/session-init.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/session-end.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/session-end.js --format=esm --banner:js=\"#!/usr/bin/env node\" && esbuild src/hooks/on-save-intent.ts --bundle --platform=node --target=node18 --outfile=dist/hooks/on-save-intent.js --format=esm --banner:js=\"#!/usr/bin/env node\"",
23
- "start": "node dist/index.js",
24
- "test-server": "tsx src/test-server.ts",
25
- "test-server:start": "node dist/test-server.js",
26
- "typecheck": "tsc --noEmit",
27
- "test": "vitest run --config vitest.config.cjs",
28
- "test:watch": "vitest --config vitest.config.cjs",
29
- "lint": "eslint src/",
30
- "lint:fix": "eslint src/ --fix",
31
- "format": "prettier --write src/",
32
- "format:check": "prettier --check src/",
33
- "prepublishOnly": "npm run build",
34
- "postinstall": "node scripts/postinstall.js"
35
- },
36
- "dependencies": {
37
- "@modelcontextprotocol/sdk": ">=1.25.1 <1.28.0",
38
- "ignore": "^7.0.5",
39
- "zod": "^3.23.8"
40
- },
41
- "devDependencies": {
42
- "@eslint/js": "^9.39.2",
43
- "@types/node": "^20.10.0",
44
- "@typescript-eslint/eslint-plugin": "^8.52.0",
45
- "@typescript-eslint/parser": "^8.52.0",
46
- "esbuild": "^0.27.0",
47
- "eslint": "^9.39.2",
48
- "prettier": "^3.7.4",
49
- "tsx": "^4.15.4",
50
- "typescript": "^5.6.3",
51
- "typescript-eslint": "^8.52.0",
52
- "vitest": "^4.1.9"
22
+ "test": "node --test npm/test/*.test.mjs",
23
+ "pack:check": "npm pack --dry-run",
24
+ "prepublishOnly": "npm test"
53
25
  },
54
26
  "engines": {
55
- "node": ">=18"
27
+ "node": ">=20"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public",
31
+ "provenance": true
56
32
  },
57
- "keywords": [
58
- "mcp",
59
- "mcp-server",
60
- "model-context-protocol",
61
- "contextstream",
62
- "ai",
63
- "ai-memory",
64
- "ai-agent",
65
- "ai-coding-assistant",
66
- "claude",
67
- "claude-code",
68
- "cursor",
69
- "copilot",
70
- "windsurf",
71
- "semantic-search",
72
- "code-search",
73
- "vector-search",
74
- "code-context",
75
- "context-engineering",
76
- "memory",
77
- "knowledge-graph",
78
- "rag",
79
- "llm",
80
- "developer-tools"
81
- ],
82
33
  "repository": {
83
34
  "type": "git",
84
35
  "url": "git+https://github.com/contextstream/mcp-server.git"
@@ -87,7 +38,12 @@
87
38
  "bugs": {
88
39
  "url": "https://github.com/contextstream/mcp-server/issues"
89
40
  },
90
- "overrides": {
91
- "qs": "6.14.2"
92
- }
41
+ "keywords": [
42
+ "mcp",
43
+ "model-context-protocol",
44
+ "contextstream",
45
+ "semantic-search",
46
+ "memory",
47
+ "developer-tools"
48
+ ]
93
49
  }
package/server.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.contextstream/mcp-server",
4
+ "title": "ContextStream MCP Server",
5
+ "description": "Project memory, semantic code search, and grounded agent context.",
6
+ "version": "1.0.0",
7
+ "repository": {
8
+ "url": "https://github.com/contextstream/mcp-server",
9
+ "source": "github"
10
+ },
11
+ "websiteUrl": "https://contextstream.io/docs/mcp",
12
+ "remotes": [
13
+ {
14
+ "type": "streamable-http",
15
+ "url": "https://mcp.contextstream.io/mcp"
16
+ }
17
+ ],
18
+ "packages": [
19
+ {
20
+ "registryType": "npm",
21
+ "registryBaseUrl": "https://registry.npmjs.org",
22
+ "identifier": "@contextstream/mcp-server",
23
+ "version": "1.0.0",
24
+ "transport": {
25
+ "type": "stdio"
26
+ },
27
+ "environmentVariables": [
28
+ {
29
+ "name": "CONTEXTSTREAM_API_KEY",
30
+ "description": "ContextStream API key",
31
+ "isRequired": true,
32
+ "isSecret": true
33
+ },
34
+ {
35
+ "name": "CONTEXTSTREAM_API_URL",
36
+ "description": "ContextStream API base URL",
37
+ "isRequired": false,
38
+ "default": "https://api.contextstream.io"
39
+ }
40
+ ]
41
+ }
42
+ ]
43
+ }