diff --git a/README.md b/README.md index fd05660..50d1a4c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@

- CI + CI Coverage License: MIT VS Code @@ -80,8 +80,8 @@ Add to your Claude Desktop or VS Code settings: { "mcpServers": { "cortex": { - "command": "bun", - "args": ["run", "/path/to/Cortex/packages/mcp-server/dist/mcp-server.js"] + "command": "npx", + "args": ["-y", "@ecuabyte/cortex-mcp-server"] } } } diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md new file mode 100644 index 0000000..4a0386a --- /dev/null +++ b/packages/mcp-server/README.md @@ -0,0 +1,60 @@ +# Cortex MCP Server + +The **Model Context Protocol (MCP)** server for [Cortex](https://github.com/EcuaByte-lat/Cortex). + +Connects your AI tools (Claude Desktop, Cursor, Zed, etc.) to your project's persistent memory. + +## 🚀 Usage + +### Quick Start (npx) + +The easiest way to run the server is with `npx`: + +```bash +npx -y @ecuabyte/cortex-mcp-server +``` + +### via Claude Desktop + +Add this to your `claude_desktop_config.json`: + +```json +{ + "mcpServers": { + "cortex": { + "command": "npx", + "args": ["-y", "@ecuabyte/cortex-mcp-server"] + } + } +} +``` + +### via Cursor / Windsurf + +1. Go to **Settings** > **MCP** +2. Add new server: + - **Type**: `command` + - **Command**: `npx` + - **Args**: `-y @ecuabyte/cortex-mcp-server` + +## 🛠️ Tools + +This server provides the following MCP tools to your AI agent: + +- `cortex_search`: Search project memories (facts, decisions, code patterns) +- `cortex_add`: Add a new memory +- `cortex_list`: List recent memories +- `cortex_context`: Get intelligent context for a specific task +- `cortex_scan`: Scan the current project to extract context automatically + +## 📦 Installation + +```bash +npm install -g @ecuabyte/cortex-mcp-server +# or +bun add -g @ecuabyte/cortex-mcp-server +``` + +## 📄 License + +MIT diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 7b87a35..8e56681 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -12,7 +12,7 @@ "dev": "bun run --watch src/mcp-server.ts", "start": "bun run dist/mcp-server.js", "typecheck": "bunx tsc --noEmit", - "generate-config": "bun run scripts/generate-config.ts" + "generate-config": "bun run src/mcp-server.ts generate-config" }, "keywords": [ "mcp", diff --git a/packages/mcp-server/scripts/generate-config.ts b/packages/mcp-server/scripts/generate-config.ts deleted file mode 100644 index a8f17ef..0000000 --- a/packages/mcp-server/scripts/generate-config.ts +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bun -import { parseArgs } from 'node:util'; - -// Define supported targets -type Target = 'claude' | 'cursor' | 'windsurf' | 'goose'; - -const targets: Target[] = ['claude', 'cursor', 'windsurf', 'goose']; - -// Parse arguments -const { values } = parseArgs({ - args: Bun.argv, - options: { - target: { - type: 'string', - }, - }, - strict: true, - allowPositionals: true, -}); - -const target = values.target as Target | undefined; - -if (!target || !targets.includes(target)) { - console.error(`Usage: bun run generate-config --target <${targets.join('|')}>`); - process.exit(1); -} - -// Get the absolute path to the MCP server package (assuming installed globally or resolving relative) -// For this script, we'll assume the user wants to use "npx -y @ecuabyte/cortex-mcp-server" for portability -// But since we are "generating" it, we can also offer a local path version if running from source. -// For simplicity in the universal guide, we default to the npx version which is most robust. - -const command = 'npx'; -const args = ['-y', '@ecuabyte/cortex-mcp-server']; - -// Generate config based on target -let config: Record; - -switch (target) { - case 'claude': - config = { - mcpServers: { - cortex: { - command, - args, - }, - }, - }; - break; - - case 'cursor': - case 'windsurf': - // Cursor/Windsurf typically use a simpler structure or just the command in their settings UI - // But they also support an `mcp.json` in project root or settings. - // Here we output the object structure they expect in their config file. - config = { - cortex: { - type: 'command', - command: command, - args: args, - }, - }; - break; - - case 'goose': - console.log(`Run this command to configure Goose:`); - console.log(`goose configure mcp add cortex "${command} ${args.join(' ')}"`); - process.exit(0); -} - -console.log(JSON.stringify(config, null, 2)); diff --git a/packages/mcp-server/src/mcp-server.ts b/packages/mcp-server/src/mcp-server.ts index d8a16c8..a858b8b 100644 --- a/packages/mcp-server/src/mcp-server.ts +++ b/packages/mcp-server/src/mcp-server.ts @@ -14,6 +14,73 @@ const store = new MemoryStore(); const router = new ContextRouter(store); const guard = new ContextGuard(); +// CLI Argument Handling for `generate-config` +import { parseArgs } from 'node:util'; + +try { + const args = process.argv.slice(2); + if (args[0] === 'generate-config') { + const { values } = parseArgs({ + args: args, + options: { + target: { + type: 'string', + }, + }, + strict: false, // allow params we don't know yet (though we should know them) + }); + + const target = values.target as string | undefined; + const targets = ['claude', 'cursor', 'windsurf', 'goose']; + + if (!target || !targets.includes(target)) { + console.error(`Usage: cortex-mcp generate-config --target <${targets.join('|')}>`); + process.exit(1); + } + + const command = 'npx'; + const cmdArgs = ['-y', '@ecuabyte/cortex-mcp-server']; + let config: Record = {}; + + switch (target) { + case 'claude': + config = { + mcpServers: { + cortex: { + command, + args: cmdArgs, + }, + }, + }; + break; + case 'cursor': + case 'windsurf': + config = { + cortex: { + type: 'command', + command, + args: cmdArgs, + }, + }; + break; + case 'goose': + console.log(`Run this command to configure Goose:`); + console.log(`goose configure mcp add cortex "${command} ${cmdArgs.join(' ')}"`); + process.exit(0); + } + + console.log(JSON.stringify(config, null, 2)); + process.exit(0); + } +} catch (e) { + // If parsing fails or other issues, just ignore and proceed to server start + // or print error if it was clearly a CLI attempt + if (process.argv[2] === 'generate-config') { + console.error('Error generating config:', e); + process.exit(1); + } +} + // Initialize embedding provider if available (Ollama or OpenAI) let embeddingInitialized = false; (async () => { diff --git a/packages/vscode-extension/package.json b/packages/vscode-extension/package.json index bc096cd..ed023d0 100644 --- a/packages/vscode-extension/package.json +++ b/packages/vscode-extension/package.json @@ -56,7 +56,7 @@ ], "devDependencies": { "@types/sql.js": "^1.4.9", - "@types/vscode": "^1.85.0", + "@types/vscode": "^1.107.0", "@vscode/vsce": "^3.7.1", "@ecuabyte/cortex-shared": "workspace:*" }, diff --git a/packages/vscode-extension/src/extension.ts b/packages/vscode-extension/src/extension.ts index 87247d7..beea1cc 100644 --- a/packages/vscode-extension/src/extension.ts +++ b/packages/vscode-extension/src/extension.ts @@ -1,3 +1,4 @@ +import { setDefaultAutoSelectFamilyAttemptTimeout } from 'node:net'; import type { Memory } from '@ecuabyte/cortex-shared'; import * as vscode from 'vscode'; import { ContextObserver } from './contextObserver'; @@ -9,6 +10,14 @@ import { CortexTaskProvider } from './taskProvider'; import type { Tool } from './toolScanner'; import { ToolTreeProvider } from './toolTreeProvider'; +// Set default timeout for IPv4/IPv6 family auto-selection to avoid long delays +// This fixes: Set default auto-select family attempt timeout to 1000ms +try { + setDefaultAutoSelectFamilyAttemptTimeout(1000); +} catch (e) { + console.warn('[Cortex] Failed to set auto-select family timeout:', e); +} + export async function activate(context: vscode.ExtensionContext) { try { console.log('[Cortex] Extension activation started'); diff --git a/packages/vscode-extension/src/storage.ts b/packages/vscode-extension/src/storage.ts index b717834..3617d52 100644 --- a/packages/vscode-extension/src/storage.ts +++ b/packages/vscode-extension/src/storage.ts @@ -99,6 +99,19 @@ export class MemoryStore implements IMemoryStore { END `); + // Fix for "no such module: fts5" error + // If the DB was created by the CLI/Core (which uses Bun:sqlite with FTS5), + // the triggers will exist but sql.js (wasm) might not support FTS5. + // We safely drop them here to allow basic CRUD operations to work. + try { + const triggers = ['memories_ai', 'memories_ad', 'memories_bu', 'memories_au']; + for (const trigger of triggers) { + this.db.run(`DROP TRIGGER IF EXISTS ${trigger}`); + } + } catch (e) { + console.warn('[Cortex] Failed to clean up FTS5 triggers:', e); + } + this.saveToFile(); } catch (error) { console.error('[Cortex] Failed to initialize MemoryStore:', error);