@morphllm/morphmcp 0.8.18 → 0.8.22

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/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.22",
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 };