@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.
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/hooks/media-aware.ts
4
- var ENABLED = process.env.CONTEXTSTREAM_MEDIA_HOOK_ENABLED !== "false";
5
- var PATTERNS = [
6
- /\b(video|videos|clip|clips|footage|keyframe)s?\b/i,
7
- /\b(remotion|timeline|video\s*edit)\b/i,
8
- /\b(image|images|photo|photos|picture|thumbnail)s?\b/i,
9
- /\b(audio|podcast|transcript|transcription|voice)\b/i,
10
- /\b(media|asset|assets|creative|b-roll)\b/i,
11
- /\b(find|search|show).*(clip|video|image|audio|footage|media)\b/i
12
- ];
13
- var MEDIA_CONTEXT = `[MEDIA TOOLS AVAILABLE]
14
- Your workspace may have indexed media. Use ContextStream media tools:
15
-
16
- - **Search**: \`mcp__contextstream__media(action="search", query="description")\`
17
- - **Get clip**: \`mcp__contextstream__media(action="get_clip", content_id="...", start="1:34", end="2:15", output_format="remotion|ffmpeg|raw")\`
18
- - **List assets**: \`mcp__contextstream__media(action="list")\`
19
- - **Index**: \`mcp__contextstream__media(action="index", file_path="...", content_type="video|audio|image|document")\`
20
-
21
- For Remotion: use \`output_format="remotion"\` to get frame-based props.
22
- [END MEDIA TOOLS]`;
23
- function matchesMediaPattern(text) {
24
- return PATTERNS.some((pattern) => pattern.test(text));
25
- }
26
- function extractPrompt(input) {
27
- if (input.prompt) {
28
- return input.prompt;
29
- }
30
- if (input.session?.messages) {
31
- for (let i = input.session.messages.length - 1; i >= 0; i--) {
32
- const msg = input.session.messages[i];
33
- if (msg.role === "user") {
34
- if (typeof msg.content === "string") {
35
- return msg.content;
36
- }
37
- if (Array.isArray(msg.content)) {
38
- for (const block of msg.content) {
39
- if (block.type === "text" && block.text) {
40
- return block.text;
41
- }
42
- }
43
- }
44
- break;
45
- }
46
- }
47
- }
48
- return "";
49
- }
50
- function detectEditorFormat(input) {
51
- if (input.hookName !== void 0) {
52
- return "cline";
53
- }
54
- return "claude";
55
- }
56
- async function runMediaAwareHook() {
57
- if (!ENABLED) {
58
- process.exit(0);
59
- }
60
- let inputData = "";
61
- for await (const chunk of process.stdin) {
62
- inputData += chunk;
63
- }
64
- if (!inputData.trim()) {
65
- process.exit(0);
66
- }
67
- let input;
68
- try {
69
- input = JSON.parse(inputData);
70
- } catch {
71
- process.exit(0);
72
- }
73
- const prompt = extractPrompt(input);
74
- if (!prompt || !matchesMediaPattern(prompt)) {
75
- process.exit(0);
76
- }
77
- const editorFormat = detectEditorFormat(input);
78
- if (editorFormat === "claude") {
79
- console.log(
80
- JSON.stringify({
81
- hookSpecificOutput: {
82
- hookEventName: "UserPromptSubmit",
83
- additionalContext: MEDIA_CONTEXT
84
- }
85
- })
86
- );
87
- } else {
88
- console.log(
89
- JSON.stringify({
90
- cancel: false,
91
- contextModification: MEDIA_CONTEXT
92
- })
93
- );
94
- }
95
- process.exit(0);
96
- }
97
- var isDirectRun = process.argv[1]?.includes("media-aware") || process.argv[2] === "media-aware";
98
- if (isDirectRun) {
99
- runMediaAwareHook().catch(() => process.exit(0));
100
- }
101
- export {
102
- runMediaAwareHook
103
- };
@@ -1,162 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/hooks/common.ts
4
- import * as fs from "node:fs";
5
- import * as path from "node:path";
6
- import { homedir } from "node:os";
7
- var DEFAULT_API_URL = "https://api.contextstream.io";
8
- var HOOK_SPECIFIC_OUTPUT_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "UserPromptSubmit", "PostToolUse"]);
9
- function readHookInput() {
10
- try {
11
- return JSON.parse(fs.readFileSync(0, "utf8"));
12
- } catch {
13
- return {};
14
- }
15
- }
16
- function writeHookOutput(output) {
17
- const eventName = output?.hookEventName || (typeof process.env.HOOK_EVENT_NAME === "string" ? process.env.HOOK_EVENT_NAME.trim() : "");
18
- const canUseHookSpecificOutput = HOOK_SPECIFIC_OUTPUT_EVENTS.has(eventName);
19
- const payload = output && (output.additionalContext || output.blocked || output.reason) ? {
20
- hookSpecificOutput: output.additionalContext && canUseHookSpecificOutput ? {
21
- hookEventName: eventName,
22
- additionalContext: output.additionalContext
23
- } : void 0,
24
- additionalContext: output.additionalContext,
25
- blocked: output.blocked,
26
- reason: output.reason
27
- } : {};
28
- console.log(JSON.stringify(payload));
29
- }
30
- function extractCwd(input) {
31
- const cwd = typeof input.cwd === "string" && input.cwd.trim() ? input.cwd.trim() : process.cwd();
32
- return cwd;
33
- }
34
- function loadHookConfig(cwd) {
35
- let apiUrl = process.env.CONTEXTSTREAM_API_URL || DEFAULT_API_URL;
36
- let apiKey = process.env.CONTEXTSTREAM_API_KEY || "";
37
- let jwt = process.env.CONTEXTSTREAM_JWT || "";
38
- let workspaceId = process.env.CONTEXTSTREAM_WORKSPACE_ID || null;
39
- let projectId = process.env.CONTEXTSTREAM_PROJECT_ID || null;
40
- let searchDir = path.resolve(cwd);
41
- for (let i = 0; i < 6; i++) {
42
- if (!apiKey && !jwt) {
43
- const mcpPath = path.join(searchDir, ".mcp.json");
44
- if (fs.existsSync(mcpPath)) {
45
- try {
46
- const config = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
47
- const env = config.mcpServers?.contextstream?.env;
48
- if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
49
- if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
50
- if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
51
- if (env?.CONTEXTSTREAM_WORKSPACE_ID && !workspaceId) workspaceId = env.CONTEXTSTREAM_WORKSPACE_ID;
52
- if (env?.CONTEXTSTREAM_PROJECT_ID && !projectId) projectId = env.CONTEXTSTREAM_PROJECT_ID;
53
- } catch {
54
- }
55
- }
56
- }
57
- if (!workspaceId || !projectId) {
58
- const localConfigPath = path.join(searchDir, ".contextstream", "config.json");
59
- if (fs.existsSync(localConfigPath)) {
60
- try {
61
- const localConfig = JSON.parse(fs.readFileSync(localConfigPath, "utf8"));
62
- if (localConfig.workspace_id && !workspaceId) workspaceId = localConfig.workspace_id;
63
- if (localConfig.project_id && !projectId) projectId = localConfig.project_id;
64
- } catch {
65
- }
66
- }
67
- }
68
- const parentDir = path.dirname(searchDir);
69
- if (parentDir === searchDir) break;
70
- searchDir = parentDir;
71
- }
72
- if (!apiKey && !jwt) {
73
- const homeMcpPath = path.join(homedir(), ".mcp.json");
74
- if (fs.existsSync(homeMcpPath)) {
75
- try {
76
- const config = JSON.parse(fs.readFileSync(homeMcpPath, "utf8"));
77
- const env = config.mcpServers?.contextstream?.env;
78
- if (env?.CONTEXTSTREAM_API_KEY) apiKey = env.CONTEXTSTREAM_API_KEY;
79
- if (env?.CONTEXTSTREAM_JWT) jwt = env.CONTEXTSTREAM_JWT;
80
- if (env?.CONTEXTSTREAM_API_URL) apiUrl = env.CONTEXTSTREAM_API_URL;
81
- } catch {
82
- }
83
- }
84
- }
85
- return { apiUrl, apiKey, jwt, workspaceId, projectId };
86
- }
87
- function isConfigured(config) {
88
- return Boolean(config.apiKey || config.jwt);
89
- }
90
- function authHeaders(config) {
91
- if (config.apiKey) {
92
- return { "X-API-Key": config.apiKey };
93
- }
94
- if (config.jwt) {
95
- return { Authorization: `Bearer ${config.jwt}` };
96
- }
97
- return {};
98
- }
99
- async function apiRequest(config, apiPath, init = {}) {
100
- const response = await fetch(`${config.apiUrl}${apiPath}`, {
101
- method: init.method || "GET",
102
- headers: {
103
- "Content-Type": "application/json",
104
- ...authHeaders(config)
105
- },
106
- body: init.body !== void 0 ? JSON.stringify(init.body) : void 0
107
- });
108
- if (!response.ok) {
109
- throw new Error(`${response.status} ${response.statusText}`);
110
- }
111
- const json = await response.json();
112
- if (json && typeof json === "object" && "data" in json) {
113
- return json.data;
114
- }
115
- return json;
116
- }
117
- async function postMemoryEvent(config, title, content, tags, eventType = "operation") {
118
- if (!isConfigured(config) || !config.workspaceId) return;
119
- await apiRequest(config, "/memory/events", {
120
- method: "POST",
121
- body: {
122
- workspace_id: config.workspaceId,
123
- project_id: config.projectId || void 0,
124
- event_type: eventType,
125
- title,
126
- content: typeof content === "string" ? content : JSON.stringify(content),
127
- metadata: {
128
- tags,
129
- source: "mcp_hook",
130
- captured_at: (/* @__PURE__ */ new Date()).toISOString()
131
- }
132
- }
133
- });
134
- }
135
-
136
- // src/hooks/notification.ts
137
- async function runNotificationHook() {
138
- const input = readHookInput();
139
- const cwd = extractCwd(input);
140
- const config = loadHookConfig(cwd);
141
- if (isConfigured(config)) {
142
- const message = typeof input.title === "string" && input.title || typeof input.message === "string" && input.message || "Notification event";
143
- await postMemoryEvent(
144
- config,
145
- `Notification: ${message}`,
146
- {
147
- notification: input,
148
- captured_at: (/* @__PURE__ */ new Date()).toISOString()
149
- },
150
- ["hook", "notification"]
151
- ).catch(() => {
152
- });
153
- }
154
- writeHookOutput();
155
- }
156
- var isDirectRun = process.argv[1]?.includes("notification") || process.argv[2] === "notification";
157
- if (isDirectRun) {
158
- runNotificationHook().catch(() => process.exit(0));
159
- }
160
- export {
161
- runNotificationHook
162
- };
@@ -1,190 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/hooks/on-bash.ts
4
- import * as fs from "node:fs";
5
- import * as path from "node:path";
6
- import { homedir } from "node:os";
7
- var ENABLED = process.env.CONTEXTSTREAM_BASH_HOOK_ENABLED !== "false";
8
- var API_URL = process.env.CONTEXTSTREAM_API_URL || "https://api.contextstream.io";
9
- var API_KEY = process.env.CONTEXTSTREAM_API_KEY || "";
10
- var WORKSPACE_ID = null;
11
- function loadConfigFromMcpJson(cwd) {
12
- let searchDir = path.resolve(cwd);
13
- for (let i = 0; i < 5; i++) {
14
- if (!API_KEY) {
15
- const mcpPath = path.join(searchDir, ".mcp.json");
16
- if (fs.existsSync(mcpPath)) {
17
- try {
18
- const content = fs.readFileSync(mcpPath, "utf-8");
19
- const config = JSON.parse(content);
20
- const csEnv = config.mcpServers?.contextstream?.env;
21
- if (csEnv?.CONTEXTSTREAM_API_KEY) {
22
- API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
23
- }
24
- if (csEnv?.CONTEXTSTREAM_API_URL) {
25
- API_URL = csEnv.CONTEXTSTREAM_API_URL;
26
- }
27
- } catch {
28
- }
29
- }
30
- }
31
- if (!WORKSPACE_ID) {
32
- const csConfigPath = path.join(searchDir, ".contextstream", "config.json");
33
- if (fs.existsSync(csConfigPath)) {
34
- try {
35
- const content = fs.readFileSync(csConfigPath, "utf-8");
36
- const csConfig = JSON.parse(content);
37
- if (csConfig.workspace_id) {
38
- WORKSPACE_ID = csConfig.workspace_id;
39
- }
40
- } catch {
41
- }
42
- }
43
- }
44
- const parentDir = path.dirname(searchDir);
45
- if (parentDir === searchDir) break;
46
- searchDir = parentDir;
47
- }
48
- if (!API_KEY) {
49
- const homeMcpPath = path.join(homedir(), ".mcp.json");
50
- if (fs.existsSync(homeMcpPath)) {
51
- try {
52
- const content = fs.readFileSync(homeMcpPath, "utf-8");
53
- const config = JSON.parse(content);
54
- const csEnv = config.mcpServers?.contextstream?.env;
55
- if (csEnv?.CONTEXTSTREAM_API_KEY) {
56
- API_KEY = csEnv.CONTEXTSTREAM_API_KEY;
57
- }
58
- if (csEnv?.CONTEXTSTREAM_API_URL) {
59
- API_URL = csEnv.CONTEXTSTREAM_API_URL;
60
- }
61
- } catch {
62
- }
63
- }
64
- }
65
- }
66
- async function captureCommand(command, output, exitCode, isError, sessionId) {
67
- if (!API_KEY) return;
68
- const payload = {
69
- event_type: isError ? "bash_error" : "bash_command",
70
- title: isError ? `Bash Error: ${command.slice(0, 50)}...` : `Command: ${command.slice(0, 50)}...`,
71
- content: JSON.stringify({
72
- command,
73
- output: output.slice(0, 2e3),
74
- exit_code: exitCode,
75
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
76
- }),
77
- importance: isError ? "high" : "low",
78
- tags: isError ? ["bash", "error", "command"] : ["bash", "command"],
79
- source_type: "hook",
80
- session_id: sessionId
81
- };
82
- if (WORKSPACE_ID) {
83
- payload.workspace_id = WORKSPACE_ID;
84
- }
85
- try {
86
- const controller = new AbortController();
87
- const timeoutId = setTimeout(() => controller.abort(), 3e3);
88
- await fetch(`${API_URL}/api/v1/memory/events`, {
89
- method: "POST",
90
- headers: {
91
- "Content-Type": "application/json",
92
- "X-API-Key": API_KEY
93
- },
94
- body: JSON.stringify(payload),
95
- signal: controller.signal
96
- });
97
- clearTimeout(timeoutId);
98
- } catch {
99
- }
100
- }
101
- async function suggestLesson(command, error) {
102
- const errorPatterns = [
103
- {
104
- pattern: /command not found/i,
105
- lesson: `The command "${command.split(" ")[0]}" is not installed. Check if the package needs to be installed first.`
106
- },
107
- {
108
- pattern: /permission denied/i,
109
- lesson: "Permission denied. May need sudo or to check file permissions."
110
- },
111
- {
112
- pattern: /no such file or directory/i,
113
- lesson: "Path does not exist. Verify the file/directory path before running commands."
114
- },
115
- {
116
- pattern: /EADDRINUSE|address already in use/i,
117
- lesson: "Port is already in use. Kill the existing process or use a different port."
118
- },
119
- {
120
- pattern: /npm ERR!|ERESOLVE/i,
121
- lesson: "npm dependency conflict. Try `npm install --legacy-peer-deps` or check package versions."
122
- },
123
- {
124
- pattern: /ENOENT.*package\.json/i,
125
- lesson: "No package.json found. Make sure you're in the right directory or run `npm init`."
126
- },
127
- {
128
- pattern: /git.*not a git repository/i,
129
- lesson: "Not in a git repository. Run `git init` or navigate to a git repo."
130
- }
131
- ];
132
- for (const { pattern, lesson } of errorPatterns) {
133
- if (pattern.test(error)) {
134
- return lesson;
135
- }
136
- }
137
- return null;
138
- }
139
- async function runOnBashHook() {
140
- if (!ENABLED) {
141
- process.exit(0);
142
- }
143
- let inputData = "";
144
- for await (const chunk of process.stdin) {
145
- inputData += chunk;
146
- }
147
- if (!inputData.trim()) {
148
- process.exit(0);
149
- }
150
- let input;
151
- try {
152
- input = JSON.parse(inputData);
153
- } catch {
154
- process.exit(0);
155
- }
156
- if (input.tool_name !== "Bash") {
157
- process.exit(0);
158
- }
159
- const cwd = input.cwd || process.cwd();
160
- loadConfigFromMcpJson(cwd);
161
- const command = input.tool_input?.command || "";
162
- const output = input.tool_result?.output || input.tool_result?.error || "";
163
- const exitCode = input.tool_result?.exit_code ?? 0;
164
- const sessionId = input.session_id || "unknown";
165
- const isError = exitCode !== 0 || !!input.tool_result?.error;
166
- captureCommand(command, output, exitCode, isError, sessionId).catch(() => {
167
- });
168
- if (isError) {
169
- const lesson = await suggestLesson(command, output);
170
- if (lesson) {
171
- console.log(
172
- JSON.stringify({
173
- hookSpecificOutput: {
174
- hookEventName: "PostToolUse",
175
- additionalContext: `[ContextStream Insight] ${lesson}`
176
- }
177
- })
178
- );
179
- process.exit(0);
180
- }
181
- }
182
- process.exit(0);
183
- }
184
- var isDirectRun = process.argv[1]?.includes("on-bash") || process.argv[2] === "on-bash";
185
- if (isDirectRun) {
186
- runOnBashHook().catch(() => process.exit(0));
187
- }
188
- export {
189
- runOnBashHook
190
- };