@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 +13 -181
- package/dist/index.js +165 -173
- package/package.json +1 -1
- package/dist/morph-client.d.ts +0 -56
- package/dist/morph-client.js +0 -160
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
|
|
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
|
|
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
|
|
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
|
|
44
|
-
# (No ENABLED_TOOLS needed - edit_file
|
|
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
|
|
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
|
|
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
|
|
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
|
|
222
|
+
// ENABLED_TOOLS defaults to edit_file
|
|
317
223
|
}
|
|
318
224
|
}
|
|
319
225
|
```
|
|
320
226
|
|
|
321
|
-
### 3.
|
|
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
|
|
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
|
|
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`
|
|
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
|
-
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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.
|
|
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)",
|
package/dist/morph-client.d.ts
DELETED
|
@@ -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 };
|
package/dist/morph-client.js
DELETED
|
@@ -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 };
|