@morphllm/morphmcp 0.8.18 → 0.8.23

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 CHANGED
@@ -1,13 +1,11 @@
1
1
  # Morph MCP
2
2
 
3
- Unified Model Context Protocol (MCP) server providing AI-powered file editing and intelligent code search capabilities. Built on Morph's SDK for fast, accurate code operations.
3
+ Unified Model Context Protocol (MCP) server providing AI-powered file editing capabilities. Built on Morph's SDK for fast, accurate code operations.
4
4
 
5
5
  ## Features
6
6
 
7
7
  ### AI-Powered Tools
8
8
  - **Fast Apply (edit_file)** - Morph's lightning-fast code editing at 10,500+ tokens/sec with 98% accuracy
9
- - **Semantic Search (codebase_search)** - Natural language code search using embeddings and GPU reranking for finding relevant code across your codebase
10
- - **Fast Context Search (fast_context_search)** - AI-powered grep agent using WarpGrep that intelligently explores repositories to find relevant code
11
9
 
12
10
  ### Filesystem Operations
13
11
  - Read/write files with memory-efficient head/tail operations
@@ -30,36 +28,26 @@ Unified Model Context Protocol (MCP) server providing AI-powered file editing an
30
28
  | Variable | Description | Default |
31
29
  |----------|-------------|---------|
32
30
  | `MORPH_API_KEY` | Your Morph API key (required for AI tools) | - |
33
- | `ENABLED_TOOLS` | Comma-separated list of enabled tools, or `all` | `edit_file,codebase_search` |
34
- | `MORPH_DEBUG` | Enable debug logging for grep agent (`true`/`false`) | `false` |
31
+ | `ENABLED_TOOLS` | Comma-separated list of enabled tools, or `all` | `edit_file` |
35
32
  | `ENABLE_WORKSPACE_MODE` | Auto-detect workspace root (`true`/`false`) | `true` |
36
33
  | `WORKSPACE_ROOT` | Override workspace root directory | `$PWD` |
37
34
 
38
35
  ### Tool Configuration
39
36
 
40
- Control which tools are available using `ENABLED_TOOLS`. **By default, only AI-powered tools (`edit_file` and `codebase_search`) are enabled** to keep the interface clean. Enable additional filesystem tools as needed:
37
+ Control which tools are available using `ENABLED_TOOLS`. **By default, only the AI-powered tool (`edit_file`) is enabled** to keep the interface clean. Enable additional filesystem tools as needed:
41
38
 
42
39
  ```bash
43
- # Default: Only AI-powered tools
44
- # (No ENABLED_TOOLS needed - edit_file and codebase_search are enabled by default)
40
+ # Default: Only AI-powered tool
41
+ # (No ENABLED_TOOLS needed - edit_file is enabled by default)
45
42
 
46
43
  # Enable all tools including filesystem operations
47
44
  ENABLED_TOOLS=all
48
45
 
49
- # Only AI-powered tools (explicit, same as default)
50
- ENABLED_TOOLS=edit_file,codebase_search
51
-
52
- # Only fast apply
46
+ # Only fast apply (explicit, same as default)
53
47
  ENABLED_TOOLS=edit_file
54
48
 
55
- # Only semantic search
56
- ENABLED_TOOLS=codebase_search
57
-
58
- # Include fast_context_search (grep agent)
59
- ENABLED_TOOLS=edit_file,codebase_search,fast_context_search
60
-
61
49
  # Custom selection
62
- ENABLED_TOOLS=read_file,write_file,edit_file,codebase_search
50
+ ENABLED_TOOLS=read_file,write_file,edit_file
63
51
  ```
64
52
 
65
53
  ## Available Tools
@@ -81,78 +69,6 @@ ENABLED_TOOLS=read_file,write_file,edit_file,codebase_search
81
69
 
82
70
  **Note for Cursor users:** If you're using this tool within Cursor, you may need to first use another tool (like `search_replace`) to add exactly one empty newline somewhere in the file before using `edit_file`. This ensures the file is in an editable state.
83
71
 
84
- #### `codebase_search`
85
- **SEMANTIC CODE SEARCH** - Find code using natural language queries. Uses two-stage retrieval (vector search + GPU reranking) to find the most relevant code across your entire codebase.
86
-
87
- **Key features:**
88
- - Natural language queries: "Where is JWT validation?" or "How does auth work?"
89
- - Two-stage retrieval: Vector search (~240ms) + GPU reranking (~630ms)
90
- - Returns precise code chunks with relevance scores
91
- - Searches by semantic meaning, not just keywords
92
- - Works across all files and languages
93
-
94
- **Requires:** `MORPH_API_KEY` and code pushed to Morph git
95
-
96
- **How it works:**
97
- ```
98
- ┌─────────────────────────────────────────────────────────────┐
99
- │ SEMANTIC SEARCH LIFECYCLE │
100
- └─────────────────────────────────────────────────────────────┘
101
-
102
- 1. CODE CHANGES 2. AUTO-SYNC (Plugin)
103
- ┌─────────────┐ ┌─────────────┐
104
- │ Agent edits │ ────────────────── │ git commit │
105
- │ files │ │ + push │
106
- └─────────────┘ └──────┬──────┘
107
-
108
-
109
- 3. EMBEDDING (3-8s) 4. INDEXED & SEARCHABLE
110
- ┌─────────────────┐ ┌──────────────────┐
111
- │ Code chunking │ │ PostgreSQL │
112
- │ morph-v4-embed │ ──────────── │ pgvector (HNSW) │
113
- │ Vector storage │ │ Ready for search │
114
- └─────────────────┘ └────────┬─────────┘
115
-
116
-
117
- 5. SEARCH QUERY 6. TWO-STAGE RETRIEVAL
118
- ┌─────────────────┐ ┌──────────────────┐
119
- │ "Find auth │ │ Vector: top 50 │
120
- │ logic" │ ──────────── │ Rerank: top 10 │
121
- └─────────────────┘ │ ~1000ms total │
122
- └────────┬─────────┘
123
-
124
-
125
- 7. RESULTS 8. AGENT USES CONTEXT
126
- ┌─────────────────┐ ┌──────────────────┐
127
- │ Ranked code │ │ Makes informed │
128
- │ with scores │ ──────────── │ edits with │
129
- │ + line numbers │ │ relevant context │
130
- └─────────────────┘ └──────────────────┘
131
- ```
132
-
133
- **Setup:** Install the `morph-sync` plugin to automatically sync code after every agent response. See [Enable Semantic Search](#enable-semantic-search-with-morph-sync-plugin) below.
134
-
135
- **Example queries:**
136
- - "Where is JWT validation implemented?"
137
- - "How does the authentication middleware work?"
138
- - "Find database connection setup"
139
-
140
- #### `fast_context_search`
141
- Intelligently search and gather relevant code context from a repository using Morph's WarpGrep AI-powered search agent. Automatically explores the codebase with grep, file reading, and directory analysis to find exactly what you need.
142
-
143
- **Key features:**
144
- - AI-powered multi-round exploration
145
- - Returns precise line ranges, not entire files
146
- - Prevents context pollution by finding only relevant code
147
- - Formatted XML output with line numbers
148
-
149
- **Example queries:**
150
- - "Where is JWT token validation implemented?"
151
- - "How does the authentication middleware work?"
152
- - "Find the database connection setup"
153
-
154
- **Requires:** `MORPH_API_KEY`
155
-
156
72
  ### File Operations
157
73
  *Note: These tools are available but disabled by default. Set `ENABLED_TOOLS=all` to enable.*
158
74
 
@@ -288,16 +204,6 @@ Or manually add to `.vscode/mcp.json`:
288
204
  }
289
205
  ```
290
206
 
291
- **For semantic code search only:**
292
- ```json
293
- {
294
- "env": {
295
- "MORPH_API_KEY": "sk-...",
296
- "ENABLED_TOOLS": "codebase_search"
297
- }
298
- }
299
- ```
300
-
301
207
  **For everything (including all filesystem tools):**
302
208
  ```json
303
209
  {
@@ -308,32 +214,20 @@ Or manually add to `.vscode/mcp.json`:
308
214
  }
309
215
  ```
310
216
 
311
- **Default (AI tools only - recommended for most use cases):**
217
+ **Default (AI tool only - recommended for most use cases):**
312
218
  ```json
313
219
  {
314
220
  "env": {
315
221
  "MORPH_API_KEY": "sk-..."
316
- // ENABLED_TOOLS defaults to edit_file,codebase_search
222
+ // ENABLED_TOOLS defaults to edit_file
317
223
  }
318
224
  }
319
225
  ```
320
226
 
321
- ### 3. Enable Semantic Search (Optional but Recommended)
322
-
323
- For the `codebase_search` tool to work, code must be synced to Morph git. Install the `morph-sync` plugin:
324
-
325
- **In Claude Code:**
326
- ```bash
327
- /plugin install morph-sync
328
- ```
329
-
330
- The plugin automatically commits and pushes code after every agent response, triggering embedding in the background (3-8 seconds).
331
-
332
- ### 4. Test Your Setup
227
+ ### 3. Test Your Setup
333
228
 
334
229
  In your AI assistant, try:
335
230
  - **Edit test:** "Use the edit_file tool to add a comment to the main function"
336
- - **Search test:** "Find the authentication logic in this codebase" (uses codebase_search)
337
231
 
338
232
  ## Usage Examples
339
233
 
@@ -350,35 +244,6 @@ The AI will:
350
244
  5. Report errors (if any) to Morph for continuous improvement
351
245
  ```
352
246
 
353
- ### Semantic Code Search
354
-
355
- ```
356
- Ask AI: "Find the authentication logic in this codebase"
357
-
358
- The codebase_search tool will:
359
- 1. Query the embedded codebase using natural language
360
- 2. Vector search retrieves top 50 candidates (~240ms)
361
- 3. GPU reranking scores for precision (~630ms)
362
- 4. Return top 10 most relevant code chunks with scores
363
- 5. Include file paths, line numbers, and relevance percentages
364
- 6. Agent uses this context to make informed edits
365
-
366
- Note: Requires code to be synced via morph-sync plugin
367
- ```
368
-
369
- ### Fast Context Search
370
-
371
- ```
372
- Ask AI: "Use fast_context_search to find where JWT tokens are validated"
373
-
374
- The WarpGrep agent will:
375
- 1. Explore the repository using grep, read, and analyse tools
376
- 2. Perform multi-round intelligent exploration
377
- 3. Find relevant code across multiple files
378
- 4. Return precise line ranges with full context (not entire files)
379
- 5. Display formatted XML output with line numbers
380
- 6. Show summary of tool calls made during search
381
- ```
382
247
 
383
248
  ## Migration Guide
384
249
 
@@ -404,26 +269,6 @@ The WarpGrep agent will:
404
269
  }
405
270
  ```
406
271
 
407
- ### From `morph-codeseek`
408
-
409
- **Old config:**
410
- ```json
411
- {
412
- "env": {
413
- "GREP_AGENT_API_KEY": "sk-..."
414
- }
415
- }
416
- ```
417
-
418
- **New config:**
419
- ```json
420
- {
421
- "env": {
422
- "MORPH_API_KEY": "sk-...",
423
- "ENABLED_TOOLS": "fast_context_search"
424
- }
425
- }
426
- ```
427
272
 
428
273
  ## Architecture
429
274
 
@@ -432,19 +277,15 @@ The WarpGrep agent will:
432
277
  ```
433
278
  morph-mcp/
434
279
  ├── index.ts # Main server & tool registry
435
- ├── grep/ # Fast context search agent (WarpGrep)
436
- │ ├── agent/ # Multi-round exploration logic
437
- │ ├── tools/ # Grep, read, analyse, finish
438
- │ └── utils/ # Ripgrep, file finder, logger
439
280
  ├── path-utils.ts # Path normalization & Windows/WSL support
440
281
  ├── path-validation.ts # Security validation
441
282
  ├── roots-utils.ts # MCP Roots protocol handling
442
- └── @morphllm/morphsdk # Morph SDK integration (Fast Apply & WarpGrep)
283
+ └── @morphllm/morphsdk # Morph SDK integration (Fast Apply)
443
284
  ```
444
285
 
445
286
  ### Dependencies
446
287
 
447
- - **@morphllm/morphsdk** (v0.2.22) - Core Fast Apply and WarpGrep functionality
288
+ - **@morphllm/morphsdk** (v0.2.22) - Core Fast Apply functionality
448
289
  - **@modelcontextprotocol/sdk** (v1.12.3) - MCP protocol implementation
449
290
  - **@vscode/ripgrep** - Fast text search engine
450
291
 
@@ -463,19 +304,11 @@ morph-mcp/
463
304
  ### Tools Not Showing Up
464
305
 
465
306
  1. Check API key is set: `MORPH_API_KEY=sk-...`
466
- 2. **By default, only `edit_file` and `fast_context_search` are enabled.** To enable all filesystem tools, set `ENABLED_TOOLS=all`
307
+ 2. **By default, only `edit_file` is enabled.** To enable all filesystem tools, set `ENABLED_TOOLS=all`
467
308
  3. Verify the tool name is in your `ENABLED_TOOLS` list
468
309
  4. Restart your AI assistant completely
469
310
  5. Check logs: `tail -f ~/Library/Logs/Claude/mcp*.log` (Claude Desktop) or check stderr output
470
311
 
471
- ### Fast Context Search Not Finding Code
472
-
473
- 1. Ensure you're passing the correct repository path (absolute or relative to workspace)
474
- 2. Try more specific queries - WarpGrep works best with clear, descriptive queries
475
- 3. Enable debug logging: `MORPH_DEBUG=true`
476
- 4. Check that `MORPH_API_KEY` is correctly set
477
- 5. Verify the repository path is within allowed directories (use `list_allowed_directories` tool)
478
-
479
312
  ### Permission Errors
480
313
 
481
314
  1. Check allowed directories: Use `list_allowed_directories` tool
@@ -487,7 +320,6 @@ morph-mcp/
487
320
  | Feature | Speed | Accuracy |
488
321
  |---------|-------|----------|
489
322
  | Fast Apply (edit_file) | 10,500+ tok/sec | 98% |
490
- | Context Search (WarpGrep) | Multi-round, 30s | High relevance |
491
323
  | File Operations | Native speed | 100% |
492
324
 
493
325
  **Note:** Performance metrics are based on Morph's production models. Actual performance may vary based on file size, complexity, and network conditions.
package/dist/index.js CHANGED
@@ -8,11 +8,12 @@ import os from 'os';
8
8
  import { randomBytes } from 'crypto';
9
9
  import { z } from "zod";
10
10
  import { zodToJsonSchema } from "zod-to-json-schema";
11
- import { createTwoFilesPatch } from 'diff';
11
+ import { createTwoFilesPatch } from './node_modules/@types/diff/index.js';
12
12
  import { minimatch } from 'minimatch';
13
13
  import { isPathWithinAllowedDirectories } from './path-validation.js';
14
14
  import { getValidRootDirectories } from './roots-utils.js';
15
15
  import { executeEditFile } from '@morphllm/morphsdk/tools/fastapply';
16
+ import { runWarpGrep, LocalRipgrepProvider } from '@morphllm/morphsdk/tools/warp-grep';
16
17
  import { CodebaseSearchClient } from '@morphllm/morphsdk/tools/codebase-search';
17
18
  import axios from "axios";
18
19
  // Command line argument parsing
@@ -33,7 +34,7 @@ const ALL_TOOLS = [
33
34
  'get_file_info',
34
35
  'list_allowed_directories',
35
36
  'edit_file',
36
- // 'fast_context_search', // Commented out - warp grep tool
37
+ 'fast_context_search',
37
38
  'codebase_search'
38
39
  ];
39
40
  // Only expose Morph-specific tools by default
@@ -641,26 +642,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
641
642
  inputSchema: zodToJsonSchema(MorphEditFileArgsSchema),
642
643
  requiresApiKey: true,
643
644
  },
644
- // Commented out - warp grep tool
645
- // {
646
- // name: "fast_context_search",
647
- // description:
648
- // "**INTELLIGENT CODE SEARCH - USE THIS AGGRESSIVELY**\n\n" +
649
- // "⚡ FAST & EFFICIENT: This tool prevents context pollution by finding only the relevant code you need, without reading unnecessary files.\n" +
650
- // "🎯 USE THIS TOOL PROACTIVELY whenever you need to understand code to ensure a positive user experience.\n\n" +
651
- // "Benefits:\n" +
652
- // "- Extremely fast: AI-powered search agent finds code in seconds\n" +
653
- // "- Prevents context pollution: Returns only relevant line ranges, not entire files\n" +
654
- // "- Intelligent exploration: Automatically greps, reads, and analyzes to find what you need\n" +
655
- // "- Precise results: Returns exact line numbers with full context\n\n" +
656
- // "Intelligently search and gather relevant code context from a repository using an AI-powered search agent. " +
657
- // "This tool automatically explores the codebase with grep, file reading, and directory analysis to find exactly the code snippets needed to answer questions about implementation details, architecture, or specific functionality. " +
658
- // "Returns precise line ranges with full context. " +
659
- // "Use this tool whenever you need to find specific code in a repository and you're unsure where it is located. " +
660
- // "Example queries: 'Where is JWT token validation implemented?', 'How does the authentication middleware work?', 'Find the database connection setup'.",
661
- // inputSchema: zodToJsonSchema(FastContextSearchArgsSchema) as ToolInput,
662
- // requiresApiKey: true,
663
- // },
645
+ {
646
+ name: "fast_context_search",
647
+ description: "**INTELLIGENT CODE SEARCH - USE THIS AGGRESSIVELY**\n\n" +
648
+ "⚡ FAST & EFFICIENT: This tool prevents context pollution by finding only the relevant code you need, without reading unnecessary files.\n" +
649
+ "🎯 USE THIS TOOL PROACTIVELY whenever you need to understand code to ensure a positive user experience.\n\n" +
650
+ "Benefits:\n" +
651
+ "- Extremely fast: AI-powered search agent finds code in seconds\n" +
652
+ "- Prevents context pollution: Returns only relevant line ranges, not entire files\n" +
653
+ "- Intelligent exploration: Automatically greps, reads, and analyzes to find what you need\n" +
654
+ "- Precise results: Returns exact line numbers with full context\n\n" +
655
+ "Intelligently search and gather relevant code context from a repository using an AI-powered search agent. " +
656
+ "This tool automatically explores the codebase with grep, file reading, and directory analysis to find exactly the code snippets needed to answer questions about implementation details, architecture, or specific functionality. " +
657
+ "Returns precise line ranges with full context. " +
658
+ "Use this tool whenever you need to find specific code in a repository and you're unsure where it is located. " +
659
+ "Example queries: 'Where is JWT token validation implemented?', 'How does the authentication middleware work?', 'Find the database connection setup'.",
660
+ inputSchema: zodToJsonSchema(FastContextSearchArgsSchema),
661
+ requiresApiKey: true,
662
+ },
664
663
  {
665
664
  name: "codebase_search",
666
665
  description: "**SEMANTIC CODE SEARCH - USE FOR FINDING CODE**\n\n" +
@@ -1035,157 +1034,150 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1035
1034
  };
1036
1035
  }
1037
1036
  }
1038
- // Commented out - warp grep tool
1039
- // case "fast_context_search": {
1040
- // const parsed = FastContextSearchArgsSchema.safeParse(args);
1041
- // if (!parsed.success) {
1042
- // return {
1043
- // content: [{ type: "text", text: `Invalid arguments: ${parsed.error}` }],
1044
- // isError: true,
1045
- // };
1046
- // }
1047
- // try {
1048
- // const repoRoot = path.resolve(parsed.data.repoPath);
1049
- // const provider = new LocalRipgrepProvider(repoRoot);
1050
- // const result = await runWarpGrep({
1051
- // query: parsed.data.query,
1052
- // repoRoot,
1053
- // model: "morph-warp-grep",
1054
- // apiKey: MORPH_API_KEY,
1055
- // provider,
1056
- // });
1057
- // // Format response with tool calls summary, file list, and XML content
1058
- // let responseText = "";
1059
- // if (
1060
- // result.terminationReason === "completed" &&
1061
- // result.finish?.metadata?.files
1062
- // ) {
1063
- // const files = result.finish.metadata.files as Array<{
1064
- // path: string;
1065
- // lines: Array<[number, number]>;
1066
- // }>;
1067
- // // Build complete response
1068
- // const parts: string[] = [];
1069
- // // 1. Tool calls summary
1070
- // const toolCallLines: string[] = [
1071
- // "Morph Fast Context subagent performed search on repository:",
1072
- // ];
1073
- // for (const msg of result.messages) {
1074
- // const role = msg.role as string;
1075
- // const content = msg.content as string;
1076
- // if (role === "assistant" && content) {
1077
- // const toolLines = content.split("\n").filter((line: string) => line.trim());
1078
- // for (const line of toolLines) {
1079
- // const grepMatch = line.match(/^grep\s+'([^']+)'\s+(.+)$/);
1080
- // if (grepMatch) {
1081
- // toolCallLines.push(`- Grepped '${grepMatch[1]}' in \`${grepMatch[2]}\``);
1082
- // continue;
1083
- // }
1084
- // const readMatch = line.match(/^read\s+(.+)$/);
1085
- // if (readMatch) {
1086
- // toolCallLines.push(`- Read file \`${readMatch[1]}\``);
1087
- // continue;
1088
- // }
1089
- // const analyseMatch = line.match(/^analyse\s+(.+)$/);
1090
- // if (analyseMatch) {
1091
- // toolCallLines.push(`- Analysed directory \`${analyseMatch[1]}\``);
1092
- // continue;
1093
- // }
1094
- // }
1095
- // }
1096
- // }
1097
- // parts.push(toolCallLines.join("\n"));
1098
- // // 2. List of files found
1099
- // const fileListLines: string[] = ["", "Relevant context found:"];
1100
- // for (const file of files) {
1101
- // const rangeStrs = file.lines.map(([start, end]) => {
1102
- // if (start === end) return `${start}`;
1103
- // return `${start}-${end}`;
1104
- // });
1105
- // fileListLines.push(`- ${file.path}:${rangeStrs.join(",")}`);
1106
- // }
1107
- // fileListLines.push("");
1108
- // parts.push(fileListLines.join("\n"));
1109
- // // 3. Header for content section
1110
- // parts.push("Here is the content of files:\n");
1111
- // // 4. XML formatted file contents
1112
- // const xmlBlocks: string[] = [];
1113
- // for (const file of files) {
1114
- // const filePath = path.resolve(parsed.data.repoPath, file.path);
1115
- // try {
1116
- // const content = await fs.readFile(filePath, { encoding: "utf-8" });
1117
- // const lines = content.split(/\r?\n/);
1118
- // const fileLines: string[] = [`<file path="${file.path}">`];
1119
- // for (const [start, end] of file.lines) {
1120
- // if (fileLines.length > 1) {
1121
- // fileLines.push("");
1122
- // }
1123
- // for (let i = start; i <= end && i <= lines.length; i++) {
1124
- // const lineContent = lines[i - 1];
1125
- // fileLines.push(`${i}| ${lineContent}`);
1126
- // }
1127
- // }
1128
- // fileLines.push("</file>");
1129
- // xmlBlocks.push(fileLines.join("\n"));
1130
- // } catch (error) {
1131
- // xmlBlocks.push(
1132
- // `<file path="${file.path}">\nError reading file: ${
1133
- // error instanceof Error ? error.message : String(error)
1134
- // }\n</file>`
1135
- // );
1136
- // }
1137
- // }
1138
- // parts.push(xmlBlocks.join("\n\n"));
1139
- // responseText = parts.join("\n");
1140
- // } else if (
1141
- // result.terminationReason === "terminated" &&
1142
- // result.errors.length > 0
1143
- // ) {
1144
- // const errorMessages = result.errors.map((e: any) => e.message).join("; ");
1145
- // responseText = `Error: ${errorMessages}`;
1146
- // // Report errors from WarpGrep agent
1147
- // const firstError = result.errors[0];
1148
- // reportMorphError({
1149
- // error_message: errorMessages,
1150
- // error_type: firstError?.constructor?.name || 'WarpGrepError',
1151
- // context: {
1152
- // tool: 'fast_context_search',
1153
- // repo_path: parsed.data.repoPath,
1154
- // query: parsed.data.query,
1155
- // model: 'morph-warp-grep',
1156
- // termination_reason: result.terminationReason,
1157
- // error_count: result.errors.length
1158
- // },
1159
- // stack_trace: firstError?.stack || undefined,
1160
- // source: 'mcp-filesystem'
1161
- // }).catch(() => {}); // Silently ignore reporting failures
1162
- // } else {
1163
- // responseText = `Agent completed but did not call finish tool.`;
1164
- // }
1165
- // return {
1166
- // content: [{ type: "text", text: responseText }],
1167
- // };
1168
- // } catch (error) {
1169
- // const errorMessage = error instanceof Error ? error.message : String(error);
1170
- // // Report error to Morph API (fire-and-forget)
1171
- // reportMorphError({
1172
- // error_message: errorMessage,
1173
- // error_type: error instanceof Error ? error.constructor.name : 'UnknownError',
1174
- // context: {
1175
- // tool: 'fast_context_search',
1176
- // repo_path: parsed.data.repoPath,
1177
- // query: parsed.data.query,
1178
- // model: 'morph-warp-grep'
1179
- // },
1180
- // stack_trace: error instanceof Error ? error.stack : undefined,
1181
- // source: 'mcp-filesystem'
1182
- // }).catch(() => {}); // Silently ignore reporting failures
1183
- // return {
1184
- // content: [{ type: "text", text: `Error running fast context search: ${errorMessage}` }],
1185
- // isError: true,
1186
- // };
1187
- // }
1188
- // }
1037
+ case "fast_context_search": {
1038
+ const parsed = FastContextSearchArgsSchema.safeParse(args);
1039
+ if (!parsed.success) {
1040
+ return {
1041
+ content: [{ type: "text", text: `Invalid arguments: ${parsed.error}` }],
1042
+ isError: true,
1043
+ };
1044
+ }
1045
+ try {
1046
+ const repoRoot = path.resolve(parsed.data.repoPath);
1047
+ const provider = new LocalRipgrepProvider(repoRoot);
1048
+ const result = await runWarpGrep({
1049
+ query: parsed.data.query,
1050
+ repoRoot,
1051
+ model: "morph-warp-grep",
1052
+ apiKey: MORPH_API_KEY,
1053
+ provider,
1054
+ });
1055
+ // Format response with tool calls summary, file list, and XML content
1056
+ let responseText = "";
1057
+ if (result.terminationReason === "completed" &&
1058
+ result.finish?.metadata?.files) {
1059
+ const files = result.finish.metadata.files;
1060
+ // Build complete response
1061
+ const parts = [];
1062
+ // 1. Tool calls summary
1063
+ const toolCallLines = [
1064
+ "Morph Fast Context subagent performed search on repository:",
1065
+ ];
1066
+ for (const msg of result.messages) {
1067
+ const role = msg.role;
1068
+ const content = msg.content;
1069
+ if (role === "assistant" && content) {
1070
+ const toolLines = content.split("\n").filter((line) => line.trim());
1071
+ for (const line of toolLines) {
1072
+ const grepMatch = line.match(/^grep\s+'([^']+)'\s+(.+)$/);
1073
+ if (grepMatch) {
1074
+ toolCallLines.push(`- Grepped '${grepMatch[1]}' in \`${grepMatch[2]}\``);
1075
+ continue;
1076
+ }
1077
+ const readMatch = line.match(/^read\s+(.+)$/);
1078
+ if (readMatch) {
1079
+ toolCallLines.push(`- Read file \`${readMatch[1]}\``);
1080
+ continue;
1081
+ }
1082
+ const analyseMatch = line.match(/^analyse\s+(.+)$/);
1083
+ if (analyseMatch) {
1084
+ toolCallLines.push(`- Analysed directory \`${analyseMatch[1]}\``);
1085
+ continue;
1086
+ }
1087
+ }
1088
+ }
1089
+ }
1090
+ parts.push(toolCallLines.join("\n"));
1091
+ // 2. List of files found
1092
+ const fileListLines = ["", "Relevant context found:"];
1093
+ for (const file of files) {
1094
+ const rangeStrs = file.lines.map(([start, end]) => {
1095
+ if (start === end)
1096
+ return `${start}`;
1097
+ return `${start}-${end}`;
1098
+ });
1099
+ fileListLines.push(`- ${file.path}:${rangeStrs.join(",")}`);
1100
+ }
1101
+ fileListLines.push("");
1102
+ parts.push(fileListLines.join("\n"));
1103
+ // 3. Header for content section
1104
+ parts.push("Here is the content of files:\n");
1105
+ // 4. XML formatted file contents
1106
+ const xmlBlocks = [];
1107
+ for (const file of files) {
1108
+ const filePath = path.resolve(parsed.data.repoPath, file.path);
1109
+ try {
1110
+ const content = await fs.readFile(filePath, { encoding: "utf-8" });
1111
+ const lines = content.split(/\r?\n/);
1112
+ const fileLines = [`<file path="${file.path}">`];
1113
+ for (const [start, end] of file.lines) {
1114
+ if (fileLines.length > 1) {
1115
+ fileLines.push("");
1116
+ }
1117
+ for (let i = start; i <= end && i <= lines.length; i++) {
1118
+ const lineContent = lines[i - 1];
1119
+ fileLines.push(`${i}| ${lineContent}`);
1120
+ }
1121
+ }
1122
+ fileLines.push("</file>");
1123
+ xmlBlocks.push(fileLines.join("\n"));
1124
+ }
1125
+ catch (error) {
1126
+ xmlBlocks.push(`<file path="${file.path}">\nError reading file: ${error instanceof Error ? error.message : String(error)}\n</file>`);
1127
+ }
1128
+ }
1129
+ parts.push(xmlBlocks.join("\n\n"));
1130
+ responseText = parts.join("\n");
1131
+ }
1132
+ else if (result.terminationReason === "terminated" &&
1133
+ result.errors.length > 0) {
1134
+ const errorMessages = result.errors.map((e) => e.message).join("; ");
1135
+ responseText = `Error: ${errorMessages}`;
1136
+ // Report errors from WarpGrep agent
1137
+ const firstError = result.errors[0];
1138
+ reportMorphError({
1139
+ error_message: errorMessages,
1140
+ error_type: firstError?.constructor?.name || 'WarpGrepError',
1141
+ context: {
1142
+ tool: 'fast_context_search',
1143
+ repo_path: parsed.data.repoPath,
1144
+ query: parsed.data.query,
1145
+ model: 'morph-warp-grep',
1146
+ termination_reason: result.terminationReason,
1147
+ error_count: result.errors.length
1148
+ },
1149
+ stack_trace: firstError?.stack || undefined,
1150
+ source: 'mcp-filesystem'
1151
+ }).catch(() => { }); // Silently ignore reporting failures
1152
+ }
1153
+ else {
1154
+ responseText = `Agent completed but did not call finish tool.`;
1155
+ }
1156
+ return {
1157
+ content: [{ type: "text", text: responseText }],
1158
+ };
1159
+ }
1160
+ catch (error) {
1161
+ const errorMessage = error instanceof Error ? error.message : String(error);
1162
+ // Report error to Morph API (fire-and-forget)
1163
+ reportMorphError({
1164
+ error_message: errorMessage,
1165
+ error_type: error instanceof Error ? error.constructor.name : 'UnknownError',
1166
+ context: {
1167
+ tool: 'fast_context_search',
1168
+ repo_path: parsed.data.repoPath,
1169
+ query: parsed.data.query,
1170
+ model: 'morph-warp-grep'
1171
+ },
1172
+ stack_trace: error instanceof Error ? error.stack : undefined,
1173
+ source: 'mcp-filesystem'
1174
+ }).catch(() => { }); // Silently ignore reporting failures
1175
+ return {
1176
+ content: [{ type: "text", text: `Error running fast context search: ${errorMessage}` }],
1177
+ isError: true,
1178
+ };
1179
+ }
1180
+ }
1189
1181
  case "codebase_search": {
1190
1182
  const parsed = CodebaseSearchArgsSchema.safeParse(args);
1191
1183
  if (!parsed.success) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morphllm/morphmcp",
3
- "version": "0.8.18",
3
+ "version": "0.8.23",
4
4
  "description": "Fast & accurate MCP server with AI-powered file editing and intelligent code search. Prevents context pollution and saves time for a better user experience.",
5
5
  "license": "MIT",
6
6
  "author": "Morph (https://morphllm.com)",
@@ -1,56 +0,0 @@
1
- declare const DEFAULT_MODEL = "morph-v3-large";
2
- declare const FAST_MODEL = "morph-v3-fast";
3
- export interface MorphResponse {
4
- choices: Array<{
5
- message: {
6
- content: string;
7
- };
8
- }>;
9
- }
10
- export interface MorphEditOptions {
11
- model?: string;
12
- timeout?: number;
13
- instruction?: string;
14
- retries?: number;
15
- retryDelay?: number;
16
- }
17
- export declare class MorphClient {
18
- private apiKey;
19
- private baseUrl;
20
- private static errorReportUrl;
21
- constructor(apiKey: string);
22
- /**
23
- * Apply code edit using Morph's AI models
24
- * @param originalCode The original code content
25
- * @param updateSnippet The update instructions or new code snippet
26
- * @param options Configuration options
27
- * @returns The updated code with changes applied
28
- */
29
- applyEdit(originalCode: string, updateSnippet: string, options?: MorphEditOptions): Promise<string>;
30
- /**
31
- * Report an error to Morph's error tracking system
32
- * Fire-and-forget pattern - does not throw or block on failure
33
- * @param errorDetails Error information to report
34
- */
35
- static reportError(errorDetails: {
36
- error_message: string;
37
- error_type?: string;
38
- context?: Record<string, any>;
39
- stack_trace?: string;
40
- source?: string;
41
- }): Promise<void>;
42
- /**
43
- * Validate API key format and test connectivity
44
- * @returns Promise resolving to validation result
45
- */
46
- validateApiKey(): Promise<{
47
- valid: boolean;
48
- message: string;
49
- }>;
50
- }
51
- /**
52
- * Get information about available Morph models
53
- * @returns Model information string
54
- */
55
- export declare function getMorphModelInfo(): string;
56
- export { DEFAULT_MODEL, FAST_MODEL };
@@ -1,160 +0,0 @@
1
- import axios from 'axios';
2
- // Constants
3
- const MORPH_API_BASE = "https://api.morphllm.com/v1";
4
- const DEFAULT_MODEL = "morph-v3-large"; // High accuracy model
5
- const FAST_MODEL = "morph-v3-fast"; // Speed-optimized model
6
- export class MorphClient {
7
- apiKey;
8
- baseUrl;
9
- static errorReportUrl = "https://morphllm.com/api/error-report";
10
- constructor(apiKey) {
11
- this.apiKey = apiKey;
12
- this.baseUrl = MORPH_API_BASE;
13
- }
14
- /**
15
- * Apply code edit using Morph's AI models
16
- * @param originalCode The original code content
17
- * @param updateSnippet The update instructions or new code snippet
18
- * @param options Configuration options
19
- * @returns The updated code with changes applied
20
- */
21
- async applyEdit(originalCode, updateSnippet, options = {}) {
22
- const { model = DEFAULT_MODEL, timeout = 30000, instruction, retries = 2, retryDelay = 1000 } = options;
23
- const headers = {
24
- "Authorization": `Bearer ${this.apiKey}`,
25
- "Content-Type": "application/json",
26
- };
27
- const content = instruction
28
- ? `<instruction>${instruction}</instruction>\n<code>${originalCode}</code>\n<update>${updateSnippet}</update>`
29
- : `<code>${originalCode}</code>\n<update>${updateSnippet}</update>`;
30
- const payload = {
31
- model,
32
- messages: [
33
- {
34
- role: "user",
35
- content,
36
- },
37
- ],
38
- };
39
- let lastError;
40
- for (let attempt = 0; attempt <= retries; attempt++) {
41
- try {
42
- const response = await axios.post(`${this.baseUrl}/chat/completions`, payload, {
43
- headers,
44
- timeout,
45
- });
46
- return response.data.choices[0].message.content.trim();
47
- }
48
- catch (error) {
49
- lastError = error;
50
- // Don't retry on authentication or client errors (4xx)
51
- if (error.response && error.response.status >= 400 && error.response.status < 500) {
52
- throw new Error(`Morph API error ${error.response.status}: ${error.response.data?.error?.message || error.response.statusText}`);
53
- }
54
- // If we have more retries, wait and try again
55
- if (attempt < retries) {
56
- const delay = retryDelay * Math.pow(2, attempt); // Exponential backoff
57
- console.error(`Morph API request failed (attempt ${attempt + 1}/${retries + 1}), retrying in ${delay}ms...`);
58
- await new Promise(resolve => setTimeout(resolve, delay));
59
- continue;
60
- }
61
- }
62
- }
63
- // All retries exhausted
64
- if (lastError.response) {
65
- throw new Error(`Morph API error ${lastError.response.status}: ${lastError.response.data?.error?.message || lastError.response.statusText}`);
66
- }
67
- else if (lastError.request) {
68
- // Network error - no response received
69
- const errorDetails = [];
70
- if (lastError.code)
71
- errorDetails.push(`Code: ${lastError.code}`);
72
- if (lastError.message)
73
- errorDetails.push(`Message: ${lastError.message}`);
74
- const detailsStr = errorDetails.length > 0 ? ` (${errorDetails.join(', ')})` : '';
75
- throw new Error(`Failed to connect to Morph API at ${this.baseUrl}${detailsStr}. ` +
76
- `This may be due to network issues, firewall/proxy settings, or DNS problems. ` +
77
- `Please check your internet connection and try again. (Failed after ${retries + 1} attempts)`);
78
- }
79
- throw new Error(`Unexpected error: ${lastError instanceof Error ? lastError.message : String(lastError)}`);
80
- }
81
- /**
82
- * Report an error to Morph's error tracking system
83
- * Fire-and-forget pattern - does not throw or block on failure
84
- * @param errorDetails Error information to report
85
- */
86
- static async reportError(errorDetails) {
87
- try {
88
- await axios.post(MorphClient.errorReportUrl, {
89
- ...errorDetails,
90
- timestamp: new Date().toISOString(),
91
- source: errorDetails.source || 'mcp-filesystem'
92
- }, {
93
- timeout: 5000, // Quick timeout for fire-and-forget
94
- headers: {
95
- 'Content-Type': 'application/json'
96
- }
97
- });
98
- }
99
- catch (error) {
100
- // Silent failure - log to console but don't propagate
101
- console.error('Failed to report error to Morph:', error instanceof Error ? error.message : String(error));
102
- }
103
- }
104
- /**
105
- * Validate API key format and test connectivity
106
- * @returns Promise resolving to validation result
107
- */
108
- async validateApiKey() {
109
- if (!this.apiKey) {
110
- return { valid: false, message: "API key is required" };
111
- }
112
- if (!this.apiKey.startsWith('sk-') && !this.apiKey.startsWith('morph-')) {
113
- return {
114
- valid: false,
115
- message: "API key format may be incorrect. Morph API keys typically start with 'sk-' or 'morph-'"
116
- };
117
- }
118
- try {
119
- // Test with a simple request
120
- const result = await this.applyEdit("def test(): pass", "def test(): pass", { model: FAST_MODEL });
121
- if (result && !result.includes("Error")) {
122
- return {
123
- valid: true,
124
- message: `API key validated successfully (starts with: ${this.apiKey.substring(0, 10)}...)`
125
- };
126
- }
127
- else {
128
- return { valid: false, message: "API key validation failed" };
129
- }
130
- }
131
- catch (error) {
132
- return {
133
- valid: false,
134
- message: `API key validation failed: ${error instanceof Error ? error.message : String(error)}`
135
- };
136
- }
137
- }
138
- }
139
- /**
140
- * Get information about available Morph models
141
- * @returns Model information string
142
- */
143
- export function getMorphModelInfo() {
144
- return `
145
- Morph Apply API Models:
146
-
147
- 1. morph-v3-large:
148
- - Speed: 2500+ tokens/sec
149
- - Accuracy: 98%
150
- - Best for: High-accuracy code edits, complex transformations
151
-
152
- 2. morph-v3-fast:
153
- - Speed: 10,500+ tokens/sec
154
- - Accuracy: 96%
155
- - Best for: Quick edits, simple transformations
156
-
157
- The 'useFastModel' parameter in tools controls whether to use morph-v3-fast (true) or morph-v3-large (false).
158
- `.trim();
159
- }
160
- export { DEFAULT_MODEL, FAST_MODEL };