@contextos/mcp 0.1.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.
- package/.turbo/turbo-build.log +12 -0
- package/README.md +121 -0
- package/dist/index.js +741 -0
- package/package.json +34 -0
- package/src/definitions.ts +166 -0
- package/src/index.ts +253 -0
- package/src/provider.ts +458 -0
- package/tsconfig.json +20 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import {
|
|
7
|
+
CallToolRequestSchema,
|
|
8
|
+
ListToolsRequestSchema,
|
|
9
|
+
ListResourcesRequestSchema,
|
|
10
|
+
ReadResourceRequestSchema,
|
|
11
|
+
ListPromptsRequestSchema,
|
|
12
|
+
GetPromptRequestSchema
|
|
13
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
14
|
+
|
|
15
|
+
// src/provider.ts
|
|
16
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "fs";
|
|
17
|
+
import { join, relative } from "path";
|
|
18
|
+
var core = null;
|
|
19
|
+
async function loadCore() {
|
|
20
|
+
if (core) return core;
|
|
21
|
+
try {
|
|
22
|
+
core = await import("@contextos/core");
|
|
23
|
+
return core;
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
var ContextOSProvider = class {
|
|
29
|
+
projectDir;
|
|
30
|
+
contextDir;
|
|
31
|
+
lastContext = "";
|
|
32
|
+
constructor(projectDir) {
|
|
33
|
+
this.projectDir = projectDir || process.cwd();
|
|
34
|
+
this.contextDir = join(this.projectDir, ".contextos");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if ContextOS is initialized in the current directory
|
|
38
|
+
*/
|
|
39
|
+
isInitialized() {
|
|
40
|
+
return existsSync(this.contextDir);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Build optimized context for a goal
|
|
44
|
+
*/
|
|
45
|
+
async buildContext(goal) {
|
|
46
|
+
const core2 = await loadCore();
|
|
47
|
+
if (core2) {
|
|
48
|
+
try {
|
|
49
|
+
const builder = await core2.getContextBuilder();
|
|
50
|
+
const result = await builder.build({ goal, maxTokens: 32e3 });
|
|
51
|
+
this.lastContext = result.context;
|
|
52
|
+
return this.formatContext(goal, result);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return this.buildSimpleContext(goal);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Analyze codebase with RLM
|
|
60
|
+
*/
|
|
61
|
+
async analyze(query) {
|
|
62
|
+
const core2 = await loadCore();
|
|
63
|
+
if (core2) {
|
|
64
|
+
try {
|
|
65
|
+
const engine = new core2.RLMEngine({
|
|
66
|
+
maxDepth: 3,
|
|
67
|
+
maxTokenBudget: 5e4
|
|
68
|
+
});
|
|
69
|
+
const result = await engine.execute(query, await this.getCurrentContext());
|
|
70
|
+
return result.answer;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
return `Analysis requires AI API key. Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return "Full analysis requires @contextos/core with AI API key configured.";
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Find files matching pattern
|
|
79
|
+
*/
|
|
80
|
+
async findFiles(pattern) {
|
|
81
|
+
const files = this.walkDirectory(this.projectDir, pattern);
|
|
82
|
+
if (files.length === 0) {
|
|
83
|
+
return `No files found matching pattern: ${pattern}`;
|
|
84
|
+
}
|
|
85
|
+
return `# Files matching "${pattern}"
|
|
86
|
+
|
|
87
|
+
${files.map((f) => `- ${f}`).join("\n")}`;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get dependencies of a file
|
|
91
|
+
*/
|
|
92
|
+
async getDependencies(file, depth = 2) {
|
|
93
|
+
const core2 = await loadCore();
|
|
94
|
+
if (core2) {
|
|
95
|
+
try {
|
|
96
|
+
const fullPath = join(this.projectDir, file);
|
|
97
|
+
if (!existsSync(fullPath)) {
|
|
98
|
+
return `File not found: ${file}`;
|
|
99
|
+
}
|
|
100
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
101
|
+
const result = core2.parseWithRegex(content, this.detectLanguage(file));
|
|
102
|
+
const deps = result.imports.map((i) => i.source);
|
|
103
|
+
return `# Dependencies of ${file}
|
|
104
|
+
|
|
105
|
+
${deps.map((d) => `- ${d}`).join("\n") || "No imports found"}`;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return this.extractImportsSimple(file);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Explain a file
|
|
113
|
+
*/
|
|
114
|
+
async explainFile(file) {
|
|
115
|
+
const fullPath = join(this.projectDir, file);
|
|
116
|
+
if (!existsSync(fullPath)) {
|
|
117
|
+
return `File not found: ${file}`;
|
|
118
|
+
}
|
|
119
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
120
|
+
const lines = content.split("\n").length;
|
|
121
|
+
const core2 = await loadCore();
|
|
122
|
+
let analysis = "";
|
|
123
|
+
if (core2) {
|
|
124
|
+
try {
|
|
125
|
+
const result = core2.parseWithRegex(content, this.detectLanguage(file));
|
|
126
|
+
analysis = `
|
|
127
|
+
## Structure
|
|
128
|
+
|
|
129
|
+
- **Functions**: ${result.functions.join(", ") || "None"}
|
|
130
|
+
- **Classes**: ${result.classes.join(", ") || "None"}
|
|
131
|
+
- **Imports**: ${result.imports.length} imports
|
|
132
|
+
`;
|
|
133
|
+
} catch {
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return `# ${file}
|
|
137
|
+
|
|
138
|
+
## Overview
|
|
139
|
+
|
|
140
|
+
- **Lines**: ${lines}
|
|
141
|
+
- **Language**: ${this.detectLanguage(file)}
|
|
142
|
+
${analysis}
|
|
143
|
+
|
|
144
|
+
## Content Preview
|
|
145
|
+
|
|
146
|
+
\`\`\`${this.detectLanguage(file)}
|
|
147
|
+
${content.slice(0, 2e3)}${content.length > 2e3 ? "\n... (truncated)" : ""}
|
|
148
|
+
\`\`\`
|
|
149
|
+
`;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Get current status
|
|
153
|
+
*/
|
|
154
|
+
async getStatus() {
|
|
155
|
+
const initialized = this.isInitialized();
|
|
156
|
+
let status = `# ContextOS Status
|
|
157
|
+
|
|
158
|
+
`;
|
|
159
|
+
status += `- **Project Directory**: ${this.projectDir}
|
|
160
|
+
`;
|
|
161
|
+
status += `- **Initialized**: ${initialized ? "\u2705 Yes" : "\u274C No"}
|
|
162
|
+
`;
|
|
163
|
+
if (initialized) {
|
|
164
|
+
const configPath = join(this.contextDir, "context.yaml");
|
|
165
|
+
if (existsSync(configPath)) {
|
|
166
|
+
status += `- **Config**: context.yaml found
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
status += `
|
|
171
|
+
> Run \`ctx init\` to initialize ContextOS in this project.`;
|
|
172
|
+
}
|
|
173
|
+
return status;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get current context
|
|
177
|
+
*/
|
|
178
|
+
async getCurrentContext() {
|
|
179
|
+
if (this.lastContext) {
|
|
180
|
+
return this.lastContext;
|
|
181
|
+
}
|
|
182
|
+
const cachePath = join(this.contextDir, "cache", "last-context.md");
|
|
183
|
+
if (existsSync(cachePath)) {
|
|
184
|
+
return readFileSync(cachePath, "utf-8");
|
|
185
|
+
}
|
|
186
|
+
return "No context built yet. Use contextos_build tool first.";
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get project info
|
|
190
|
+
*/
|
|
191
|
+
async getProjectInfo() {
|
|
192
|
+
const configPath = join(this.contextDir, "context.yaml");
|
|
193
|
+
if (!existsSync(configPath)) {
|
|
194
|
+
return JSON.stringify({
|
|
195
|
+
error: "ContextOS not initialized",
|
|
196
|
+
suggestion: "Run ctx init"
|
|
197
|
+
}, null, 2);
|
|
198
|
+
}
|
|
199
|
+
const content = readFileSync(configPath, "utf-8");
|
|
200
|
+
const info = {};
|
|
201
|
+
const lines = content.split("\n");
|
|
202
|
+
for (const line of lines) {
|
|
203
|
+
const match = line.match(/^\s*(name|language|framework|description):\s*["']?([^"'\n]+)["']?/);
|
|
204
|
+
if (match) {
|
|
205
|
+
info[match[1]] = match[2].trim();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return JSON.stringify(info, null, 2);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Get constraints
|
|
212
|
+
*/
|
|
213
|
+
async getConstraints() {
|
|
214
|
+
const configPath = join(this.contextDir, "context.yaml");
|
|
215
|
+
if (!existsSync(configPath)) {
|
|
216
|
+
return "No constraints defined (ContextOS not initialized)";
|
|
217
|
+
}
|
|
218
|
+
const content = readFileSync(configPath, "utf-8");
|
|
219
|
+
const constraintsMatch = content.match(/constraints:\s*\n((?:\s+-[^\n]+\n?)+)/);
|
|
220
|
+
if (!constraintsMatch) {
|
|
221
|
+
return "No constraints defined in context.yaml";
|
|
222
|
+
}
|
|
223
|
+
return `# Project Constraints
|
|
224
|
+
|
|
225
|
+
${constraintsMatch[1]}`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get project structure
|
|
229
|
+
*/
|
|
230
|
+
async getProjectStructure() {
|
|
231
|
+
const tree = this.buildTree(this.projectDir, "", 0, 3);
|
|
232
|
+
return `# Project Structure
|
|
233
|
+
|
|
234
|
+
\`\`\`
|
|
235
|
+
${tree}\`\`\``;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get file with dependencies
|
|
239
|
+
*/
|
|
240
|
+
async getFileWithDeps(file) {
|
|
241
|
+
const content = await this.explainFile(file);
|
|
242
|
+
const deps = await this.getDependencies(file);
|
|
243
|
+
return `${content}
|
|
244
|
+
|
|
245
|
+
${deps}`;
|
|
246
|
+
}
|
|
247
|
+
// ═══════════════════════════════════════════════════════════
|
|
248
|
+
// PRIVATE HELPERS
|
|
249
|
+
// ═══════════════════════════════════════════════════════════
|
|
250
|
+
formatContext(goal, result) {
|
|
251
|
+
return `# Context for: ${goal}
|
|
252
|
+
|
|
253
|
+
## Statistics
|
|
254
|
+
- Files included: ${result.files.length}
|
|
255
|
+
- Total tokens: ~${result.tokens}
|
|
256
|
+
|
|
257
|
+
## Files
|
|
258
|
+
${result.files.map((f) => `- ${f}`).join("\n")}
|
|
259
|
+
|
|
260
|
+
## Content
|
|
261
|
+
|
|
262
|
+
${result.context}
|
|
263
|
+
`;
|
|
264
|
+
}
|
|
265
|
+
async buildSimpleContext(goal) {
|
|
266
|
+
const keywords = goal.toLowerCase().split(/\s+/).filter((w) => w.length > 3);
|
|
267
|
+
const files = this.walkDirectory(this.projectDir);
|
|
268
|
+
const relevant = files.filter((file) => {
|
|
269
|
+
const lower = file.toLowerCase();
|
|
270
|
+
return keywords.some((kw) => lower.includes(kw));
|
|
271
|
+
}).slice(0, 10);
|
|
272
|
+
if (relevant.length === 0) {
|
|
273
|
+
return `No files found related to: ${goal}
|
|
274
|
+
|
|
275
|
+
Try running \`ctx index\` first.`;
|
|
276
|
+
}
|
|
277
|
+
let context = `# Context for: ${goal}
|
|
278
|
+
|
|
279
|
+
`;
|
|
280
|
+
for (const file of relevant) {
|
|
281
|
+
const fullPath = join(this.projectDir, file);
|
|
282
|
+
try {
|
|
283
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
284
|
+
context += `## ${file}
|
|
285
|
+
|
|
286
|
+
\`\`\`
|
|
287
|
+
${content.slice(0, 3e3)}
|
|
288
|
+
\`\`\`
|
|
289
|
+
|
|
290
|
+
`;
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
this.lastContext = context;
|
|
295
|
+
return context;
|
|
296
|
+
}
|
|
297
|
+
walkDirectory(dir, pattern, maxFiles = 100) {
|
|
298
|
+
const results = [];
|
|
299
|
+
const ignored = ["node_modules", ".git", "dist", "build", ".contextos", "coverage"];
|
|
300
|
+
const walk = (currentDir) => {
|
|
301
|
+
if (results.length >= maxFiles) return;
|
|
302
|
+
try {
|
|
303
|
+
const entries = readdirSync(currentDir);
|
|
304
|
+
for (const entry of entries) {
|
|
305
|
+
if (results.length >= maxFiles) break;
|
|
306
|
+
if (ignored.includes(entry)) continue;
|
|
307
|
+
const fullPath = join(currentDir, entry);
|
|
308
|
+
const relativePath = relative(this.projectDir, fullPath);
|
|
309
|
+
try {
|
|
310
|
+
const stat = statSync(fullPath);
|
|
311
|
+
if (stat.isDirectory()) {
|
|
312
|
+
walk(fullPath);
|
|
313
|
+
} else if (stat.isFile()) {
|
|
314
|
+
if (!pattern || this.matchPattern(relativePath, pattern)) {
|
|
315
|
+
results.push(relativePath);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} catch {
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch {
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
walk(dir);
|
|
325
|
+
return results;
|
|
326
|
+
}
|
|
327
|
+
matchPattern(path, pattern) {
|
|
328
|
+
const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
329
|
+
return new RegExp(regex).test(path);
|
|
330
|
+
}
|
|
331
|
+
detectLanguage(file) {
|
|
332
|
+
const ext = file.split(".").pop()?.toLowerCase() || "";
|
|
333
|
+
const langMap = {
|
|
334
|
+
ts: "typescript",
|
|
335
|
+
tsx: "typescript",
|
|
336
|
+
js: "javascript",
|
|
337
|
+
jsx: "javascript",
|
|
338
|
+
py: "python",
|
|
339
|
+
go: "go",
|
|
340
|
+
rs: "rust",
|
|
341
|
+
java: "java"
|
|
342
|
+
};
|
|
343
|
+
return langMap[ext] || ext;
|
|
344
|
+
}
|
|
345
|
+
extractImportsSimple(file) {
|
|
346
|
+
const fullPath = join(this.projectDir, file);
|
|
347
|
+
if (!existsSync(fullPath)) {
|
|
348
|
+
return `File not found: ${file}`;
|
|
349
|
+
}
|
|
350
|
+
const content = readFileSync(fullPath, "utf-8");
|
|
351
|
+
const imports = [];
|
|
352
|
+
const patterns = [
|
|
353
|
+
/import\s+.*from\s+['"]([^'"]+)['"]/g,
|
|
354
|
+
/require\s*\(\s*['"]([^'"]+)['"]\s*\)/g,
|
|
355
|
+
/^import\s+([\w.]+)/gm,
|
|
356
|
+
/^from\s+([\w.]+)\s+import/gm
|
|
357
|
+
];
|
|
358
|
+
for (const pattern of patterns) {
|
|
359
|
+
let match;
|
|
360
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
361
|
+
if (match[1] && !imports.includes(match[1])) {
|
|
362
|
+
imports.push(match[1]);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
return `# Dependencies of ${file}
|
|
367
|
+
|
|
368
|
+
${imports.map((i) => `- ${i}`).join("\n") || "No imports found"}`;
|
|
369
|
+
}
|
|
370
|
+
buildTree(dir, prefix, depth, maxDepth) {
|
|
371
|
+
if (depth >= maxDepth) return "";
|
|
372
|
+
const ignored = ["node_modules", ".git", "dist", "build", ".contextos", "coverage", "__pycache__"];
|
|
373
|
+
let result = "";
|
|
374
|
+
try {
|
|
375
|
+
const entries = readdirSync(dir).filter((e) => !ignored.includes(e)).sort();
|
|
376
|
+
for (let i = 0; i < entries.length && i < 20; i++) {
|
|
377
|
+
const entry = entries[i];
|
|
378
|
+
const isLast = i === entries.length - 1 || i === 19;
|
|
379
|
+
const fullPath = join(dir, entry);
|
|
380
|
+
try {
|
|
381
|
+
const stat = statSync(fullPath);
|
|
382
|
+
const connector = isLast ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
383
|
+
result += `${prefix}${connector}${entry}${stat.isDirectory() ? "/" : ""}
|
|
384
|
+
`;
|
|
385
|
+
if (stat.isDirectory()) {
|
|
386
|
+
const newPrefix = prefix + (isLast ? " " : "\u2502 ");
|
|
387
|
+
result += this.buildTree(fullPath, newPrefix, depth + 1, maxDepth);
|
|
388
|
+
}
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (entries.length > 20) {
|
|
393
|
+
result += `${prefix}\u2514\u2500\u2500 ... (${entries.length - 20} more)
|
|
394
|
+
`;
|
|
395
|
+
}
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
return result;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
// src/definitions.ts
|
|
403
|
+
var TOOLS = [
|
|
404
|
+
{
|
|
405
|
+
name: "contextos_build",
|
|
406
|
+
description: "Build optimized context for a specific goal. Returns the most relevant files from the codebase based on semantic similarity, dependency graph, and custom rules.",
|
|
407
|
+
inputSchema: {
|
|
408
|
+
type: "object",
|
|
409
|
+
properties: {
|
|
410
|
+
goal: {
|
|
411
|
+
type: "string",
|
|
412
|
+
description: 'The coding task or goal (e.g., "Add authentication to UserController")'
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
required: ["goal"]
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
name: "contextos_analyze",
|
|
420
|
+
description: "Perform deep analysis of the codebase using RLM engine. Can find patterns, security issues, or answer complex questions about the code.",
|
|
421
|
+
inputSchema: {
|
|
422
|
+
type: "object",
|
|
423
|
+
properties: {
|
|
424
|
+
query: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: 'Analysis query (e.g., "Find potential security vulnerabilities")'
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
required: ["query"]
|
|
430
|
+
}
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: "contextos_find",
|
|
434
|
+
description: "Find files matching a pattern in the indexed codebase.",
|
|
435
|
+
inputSchema: {
|
|
436
|
+
type: "object",
|
|
437
|
+
properties: {
|
|
438
|
+
pattern: {
|
|
439
|
+
type: "string",
|
|
440
|
+
description: 'Glob pattern to match (e.g., "**/auth/**/*.ts")'
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
required: ["pattern"]
|
|
444
|
+
}
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
name: "contextos_deps",
|
|
448
|
+
description: "Get dependencies of a file up to a specified depth.",
|
|
449
|
+
inputSchema: {
|
|
450
|
+
type: "object",
|
|
451
|
+
properties: {
|
|
452
|
+
file: {
|
|
453
|
+
type: "string",
|
|
454
|
+
description: "File path to analyze dependencies for"
|
|
455
|
+
},
|
|
456
|
+
depth: {
|
|
457
|
+
type: "number",
|
|
458
|
+
description: "Maximum depth to traverse (default: 2)"
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
required: ["file"]
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "contextos_explain",
|
|
466
|
+
description: "Get an AI-powered explanation of a file, including its purpose, key functions, and how it relates to other parts of the codebase.",
|
|
467
|
+
inputSchema: {
|
|
468
|
+
type: "object",
|
|
469
|
+
properties: {
|
|
470
|
+
file: {
|
|
471
|
+
type: "string",
|
|
472
|
+
description: "File path to explain"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
required: ["file"]
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
name: "contextos_status",
|
|
480
|
+
description: "Get the current status of ContextOS: project info, index status, and configuration.",
|
|
481
|
+
inputSchema: {
|
|
482
|
+
type: "object",
|
|
483
|
+
properties: {}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
];
|
|
487
|
+
var RESOURCES = [
|
|
488
|
+
{
|
|
489
|
+
uri: "contextos://context/current",
|
|
490
|
+
name: "Current Context",
|
|
491
|
+
description: "The most recently built context (output of ctx build/goal)",
|
|
492
|
+
mimeType: "text/markdown"
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
uri: "contextos://project/info",
|
|
496
|
+
name: "Project Info",
|
|
497
|
+
description: "Project configuration from context.yaml (name, language, framework, stack)",
|
|
498
|
+
mimeType: "application/json"
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
uri: "contextos://project/constraints",
|
|
502
|
+
name: "Coding Constraints",
|
|
503
|
+
description: "Project coding rules and constraints that should be followed",
|
|
504
|
+
mimeType: "text/markdown"
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
uri: "contextos://project/structure",
|
|
508
|
+
name: "Project Structure",
|
|
509
|
+
description: "Directory tree of the project (excluding node_modules, etc.)",
|
|
510
|
+
mimeType: "text/plain"
|
|
511
|
+
}
|
|
512
|
+
];
|
|
513
|
+
var PROMPTS = [
|
|
514
|
+
{
|
|
515
|
+
name: "code_with_context",
|
|
516
|
+
description: "Start a coding task with optimized context from ContextOS",
|
|
517
|
+
arguments: [
|
|
518
|
+
{
|
|
519
|
+
name: "goal",
|
|
520
|
+
description: "The coding task or goal",
|
|
521
|
+
required: true
|
|
522
|
+
}
|
|
523
|
+
]
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "review_code",
|
|
527
|
+
description: "Review a file and its dependencies",
|
|
528
|
+
arguments: [
|
|
529
|
+
{
|
|
530
|
+
name: "file",
|
|
531
|
+
description: "File path to review",
|
|
532
|
+
required: true
|
|
533
|
+
}
|
|
534
|
+
]
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: "debug_issue",
|
|
538
|
+
description: "Debug an issue with relevant context",
|
|
539
|
+
arguments: [
|
|
540
|
+
{
|
|
541
|
+
name: "issue",
|
|
542
|
+
description: "Description of the issue",
|
|
543
|
+
required: true
|
|
544
|
+
}
|
|
545
|
+
]
|
|
546
|
+
}
|
|
547
|
+
];
|
|
548
|
+
|
|
549
|
+
// src/index.ts
|
|
550
|
+
var server = new Server(
|
|
551
|
+
{
|
|
552
|
+
name: "contextos",
|
|
553
|
+
version: "0.1.0"
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
capabilities: {
|
|
557
|
+
tools: {},
|
|
558
|
+
resources: {},
|
|
559
|
+
prompts: {}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
);
|
|
563
|
+
var provider = new ContextOSProvider();
|
|
564
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
565
|
+
tools: TOOLS
|
|
566
|
+
}));
|
|
567
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
568
|
+
const { name, arguments: args } = request.params;
|
|
569
|
+
try {
|
|
570
|
+
switch (name) {
|
|
571
|
+
case "contextos_build": {
|
|
572
|
+
const goal = args.goal;
|
|
573
|
+
const result = await provider.buildContext(goal);
|
|
574
|
+
return {
|
|
575
|
+
content: [{ type: "text", text: result }]
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
case "contextos_analyze": {
|
|
579
|
+
const query = args.query;
|
|
580
|
+
const result = await provider.analyze(query);
|
|
581
|
+
return {
|
|
582
|
+
content: [{ type: "text", text: result }]
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
case "contextos_find": {
|
|
586
|
+
const pattern = args.pattern;
|
|
587
|
+
const result = await provider.findFiles(pattern);
|
|
588
|
+
return {
|
|
589
|
+
content: [{ type: "text", text: result }]
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
case "contextos_deps": {
|
|
593
|
+
const file = args.file;
|
|
594
|
+
const depth = args.depth || 2;
|
|
595
|
+
const result = await provider.getDependencies(file, depth);
|
|
596
|
+
return {
|
|
597
|
+
content: [{ type: "text", text: result }]
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
case "contextos_explain": {
|
|
601
|
+
const file = args.file;
|
|
602
|
+
const result = await provider.explainFile(file);
|
|
603
|
+
return {
|
|
604
|
+
content: [{ type: "text", text: result }]
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
case "contextos_status": {
|
|
608
|
+
const result = await provider.getStatus();
|
|
609
|
+
return {
|
|
610
|
+
content: [{ type: "text", text: result }]
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
default:
|
|
614
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
615
|
+
}
|
|
616
|
+
} catch (error) {
|
|
617
|
+
return {
|
|
618
|
+
content: [{
|
|
619
|
+
type: "text",
|
|
620
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
621
|
+
}],
|
|
622
|
+
isError: true
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
627
|
+
resources: RESOURCES
|
|
628
|
+
}));
|
|
629
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
630
|
+
const { uri } = request.params;
|
|
631
|
+
try {
|
|
632
|
+
if (uri === "contextos://context/current") {
|
|
633
|
+
const content = await provider.getCurrentContext();
|
|
634
|
+
return {
|
|
635
|
+
contents: [{ uri, mimeType: "text/markdown", text: content }]
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
if (uri === "contextos://project/info") {
|
|
639
|
+
const content = await provider.getProjectInfo();
|
|
640
|
+
return {
|
|
641
|
+
contents: [{ uri, mimeType: "application/json", text: content }]
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
if (uri === "contextos://project/constraints") {
|
|
645
|
+
const content = await provider.getConstraints();
|
|
646
|
+
return {
|
|
647
|
+
contents: [{ uri, mimeType: "text/markdown", text: content }]
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
if (uri === "contextos://project/structure") {
|
|
651
|
+
const content = await provider.getProjectStructure();
|
|
652
|
+
return {
|
|
653
|
+
contents: [{ uri, mimeType: "text/plain", text: content }]
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
657
|
+
} catch (error) {
|
|
658
|
+
throw new Error(`Failed to read resource: ${error instanceof Error ? error.message : String(error)}`);
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
662
|
+
prompts: PROMPTS
|
|
663
|
+
}));
|
|
664
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
665
|
+
const { name, arguments: args } = request.params;
|
|
666
|
+
switch (name) {
|
|
667
|
+
case "code_with_context": {
|
|
668
|
+
const goal = args?.goal || "general coding task";
|
|
669
|
+
const context = await provider.buildContext(goal);
|
|
670
|
+
return {
|
|
671
|
+
messages: [
|
|
672
|
+
{
|
|
673
|
+
role: "user",
|
|
674
|
+
content: {
|
|
675
|
+
type: "text",
|
|
676
|
+
text: `# Project Context
|
|
677
|
+
|
|
678
|
+
${context}
|
|
679
|
+
|
|
680
|
+
# Task
|
|
681
|
+
|
|
682
|
+
${goal}`
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
]
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
case "review_code": {
|
|
689
|
+
const file = args?.file || "";
|
|
690
|
+
const content = await provider.getFileWithDeps(file);
|
|
691
|
+
return {
|
|
692
|
+
messages: [
|
|
693
|
+
{
|
|
694
|
+
role: "user",
|
|
695
|
+
content: {
|
|
696
|
+
type: "text",
|
|
697
|
+
text: `# Code Review Request
|
|
698
|
+
|
|
699
|
+
Please review the following code and its dependencies:
|
|
700
|
+
|
|
701
|
+
${content}`
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
]
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
case "debug_issue": {
|
|
708
|
+
const issue = args?.issue || "unknown issue";
|
|
709
|
+
const context = await provider.buildContext(`debug: ${issue}`);
|
|
710
|
+
return {
|
|
711
|
+
messages: [
|
|
712
|
+
{
|
|
713
|
+
role: "user",
|
|
714
|
+
content: {
|
|
715
|
+
type: "text",
|
|
716
|
+
text: `# Debug Request
|
|
717
|
+
|
|
718
|
+
## Issue
|
|
719
|
+
${issue}
|
|
720
|
+
|
|
721
|
+
## Relevant Context
|
|
722
|
+
|
|
723
|
+
${context}`
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
default:
|
|
730
|
+
throw new Error(`Unknown prompt: ${name}`);
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
async function main() {
|
|
734
|
+
const transport = new StdioServerTransport();
|
|
735
|
+
await server.connect(transport);
|
|
736
|
+
console.error("ContextOS MCP Server started");
|
|
737
|
+
}
|
|
738
|
+
main().catch((error) => {
|
|
739
|
+
console.error("Failed to start server:", error);
|
|
740
|
+
process.exit(1);
|
|
741
|
+
});
|