magector 2.9.1 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +5 -5
  2. package/src/mcp-server.js +79 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.9.1",
3
+ "version": "2.10.0",
4
4
  "description": "Semantic code search for Magento 2 — index, search, MCP server",
5
5
  "type": "module",
6
6
  "main": "src/mcp-server.js",
@@ -33,10 +33,10 @@
33
33
  "ruvector": "^0.1.96"
34
34
  },
35
35
  "optionalDependencies": {
36
- "@magector/cli-darwin-arm64": "2.9.1",
37
- "@magector/cli-linux-x64": "2.9.1",
38
- "@magector/cli-linux-arm64": "2.9.1",
39
- "@magector/cli-win32-x64": "2.9.1"
36
+ "@magector/cli-darwin-arm64": "2.10.0",
37
+ "@magector/cli-linux-x64": "2.10.0",
38
+ "@magector/cli-linux-arm64": "2.10.0",
39
+ "@magector/cli-win32-x64": "2.10.0"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -4143,13 +4143,35 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
4143
4143
  },
4144
4144
  context: {
4145
4145
  type: 'number',
4146
- description: 'Lines of context around each match (default: 0). Like grep -C.',
4147
- default: 0
4146
+ description: 'Lines of context around each match (default: 2). Like grep -C.',
4147
+ default: 2
4148
4148
  }
4149
4149
  },
4150
4150
  required: ['pattern']
4151
4151
  }
4152
4152
  },
4153
+ {
4154
+ name: 'magento_read',
4155
+ description: 'Read a file from the Magento codebase. Use in magento_batch to read multiple files in a single MCP call (e.g., grep finds 5 files → read all 5 in one batch). Supports line ranges for large files.',
4156
+ inputSchema: {
4157
+ type: 'object',
4158
+ properties: {
4159
+ path: {
4160
+ type: 'string',
4161
+ description: 'File path relative to MAGENTO_ROOT. Example: "vendor/acme/module-sales/Model/OrderService.php"'
4162
+ },
4163
+ startLine: {
4164
+ type: 'number',
4165
+ description: 'Start reading from this line number (1-based). Default: 1 (beginning of file).'
4166
+ },
4167
+ endLine: {
4168
+ type: 'number',
4169
+ description: 'Stop reading at this line number (inclusive). Default: end of file. Use with startLine for large files.'
4170
+ }
4171
+ },
4172
+ required: ['path']
4173
+ }
4174
+ },
4153
4175
  ]
4154
4176
  }));
4155
4177
 
@@ -4168,7 +4190,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4168
4190
  // These tools have filesystem/di.xml fallbacks — work without serve process
4169
4191
  'magento_find_class', 'magento_find_method', 'magento_find_plugin',
4170
4192
  'magento_find_observer', 'magento_find_di_wiring', 'magento_module_structure',
4171
- 'magento_batch', 'magento_find_config', 'magento_find_callers', 'magento_grep'];
4193
+ 'magento_batch', 'magento_find_config', 'magento_find_callers', 'magento_grep', 'magento_read'];
4172
4194
  if (warmupInProgress && !indexFreeTools.includes(name)) {
4173
4195
  logToFile('REQ', `${name} → blocked (warmup: loading index)`);
4174
4196
  return {
@@ -4969,7 +4991,22 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
4969
4991
  structure: structureOutput.categories
4970
4992
  });
4971
4993
 
4972
- return { content: [{ type: 'text', text: jsonOutput }] };
4994
+ // Include README.md if it exists in the module directory
4995
+ let readmeText = '';
4996
+ if (results.length > 0) {
4997
+ // Find module root from first result path
4998
+ const firstPath = results[0].path || '';
4999
+ const moduleRoot = firstPath.split('/').slice(0, 3).join('/');
5000
+ if (moduleRoot) {
5001
+ const readmePath = path.join(config.magentoRoot, moduleRoot, 'README.md');
5002
+ try {
5003
+ const readme = readFileSync(readmePath, 'utf-8');
5004
+ readmeText = '\n\n## README.md\n\n' + readme.slice(0, 2000) + (readme.length > 2000 ? '\n...(truncated)' : '');
5005
+ } catch { /* no README */ }
5006
+ }
5007
+ }
5008
+
5009
+ return { content: [{ type: 'text', text: jsonOutput + readmeText }] };
4973
5010
  }
4974
5011
 
4975
5012
  case 'magento_analyze_diff': {
@@ -5983,13 +6020,28 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
5983
6020
  }
5984
6021
  break;
5985
6022
  }
6023
+ case 'magento_read': {
6024
+ const filePath = path.join(config.magentoRoot, a.path);
6025
+ let fileContent;
6026
+ try { fileContent = readFileSync(filePath, 'utf-8'); } catch {
6027
+ text = `File not found: ${a.path}`;
6028
+ break;
6029
+ }
6030
+ const allLines = fileContent.split('\n');
6031
+ const s = Math.max((a.startLine || 1) - 1, 0);
6032
+ const e = a.endLine ? Math.min(a.endLine, allLines.length) : allLines.length;
6033
+ const sl = allLines.slice(s, e);
6034
+ text = sl.map((line, i) => `${s + i + 1}\t${line}`).join('\n');
6035
+ break;
6036
+ }
5986
6037
  case 'magento_grep': {
5987
6038
  const searchPath = a.path || '.';
5988
6039
  const include = a.include || '*.php';
5989
6040
  const maxRes = Math.min(a.maxResults || 30, 100);
6041
+ const batchCtx = a.context !== undefined ? a.context : 2;
5990
6042
  const gArgs = ['-rn'];
5991
6043
  if (a.ignoreCase) gArgs.push('-i');
5992
- if (a.context) gArgs.push('-C', String(a.context));
6044
+ if (batchCtx > 0) gArgs.push('-C', String(batchCtx));
5993
6045
  for (const pat of include.split(',').map(p => p.trim())) gArgs.push('--include=' + pat);
5994
6046
  gArgs.push('--', a.pattern, searchPath);
5995
6047
  let out;
@@ -6023,9 +6075,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6023
6075
  const searchPath = args.path || '.';
6024
6076
  const include = args.include || '*.php';
6025
6077
  const maxResults = Math.min(args.maxResults || 50, 200);
6078
+ const ctxLines = args.context !== undefined ? args.context : 2;
6026
6079
  const grepArgs = ['-rn'];
6027
6080
  if (args.ignoreCase) grepArgs.push('-i');
6028
- if (args.context) grepArgs.push('-C', String(args.context));
6081
+ if (ctxLines > 0) grepArgs.push('-C', String(ctxLines));
6029
6082
  // Support multiple include patterns (e.g., "*.{php,xml}")
6030
6083
  for (const pat of include.split(',').map(p => p.trim())) {
6031
6084
  grepArgs.push('--include=' + pat);
@@ -6053,6 +6106,26 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
6053
6106
  return { content: [{ type: 'text', text }] };
6054
6107
  }
6055
6108
 
6109
+ case 'magento_read': {
6110
+ const root = config.magentoRoot;
6111
+ if (!root) return { content: [{ type: 'text', text: 'MAGENTO_ROOT not set.' }], isError: true };
6112
+ const filePath = path.join(root, args.path);
6113
+ let content;
6114
+ try { content = readFileSync(filePath, 'utf-8'); } catch (err) {
6115
+ return { content: [{ type: 'text', text: `File not found: ${args.path}` }], isError: true };
6116
+ }
6117
+ const allLines = content.split('\n');
6118
+ const start = Math.max((args.startLine || 1) - 1, 0);
6119
+ const end = args.endLine ? Math.min(args.endLine, allLines.length) : allLines.length;
6120
+ const sliced = allLines.slice(start, end);
6121
+ // Format with line numbers
6122
+ const numbered = sliced.map((line, i) => `${start + i + 1}\t${line}`).join('\n');
6123
+ let text = `## ${args.path}`;
6124
+ if (args.startLine || args.endLine) text += ` (lines ${start + 1}-${end})`;
6125
+ text += `\n\n${numbered}`;
6126
+ return { content: [{ type: 'text', text }] };
6127
+ }
6128
+
6056
6129
  default:
6057
6130
  return {
6058
6131
  content: [{