magector 2.13.3 → 2.14.1
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 +61 -0
- package/package.json +5 -5
- package/src/mcp-server.js +100 -8
package/README.md
CHANGED
|
@@ -1204,6 +1204,67 @@ gantt
|
|
|
1204
1204
|
|
|
1205
1205
|
---
|
|
1206
1206
|
|
|
1207
|
+
## Troubleshooting
|
|
1208
|
+
|
|
1209
|
+
All MCP server activity is logged to `.magector/magector.log` in the Magento project root. The log persists across MCP restarts and uses the format:
|
|
1210
|
+
|
|
1211
|
+
```
|
|
1212
|
+
[2026-04-12T18:30:00.000Z] [LEVEL] message
|
|
1213
|
+
```
|
|
1214
|
+
|
|
1215
|
+
### Log Levels
|
|
1216
|
+
|
|
1217
|
+
| Level | Meaning |
|
|
1218
|
+
|-------|---------|
|
|
1219
|
+
| `INFO` | Normal operations: startup config, tool completion, search fallbacks, enrichment progress |
|
|
1220
|
+
| `WARN` | Recoverable issues: slow grep queries (>5s), missing enrichment.db, file read errors, serve process disconnects |
|
|
1221
|
+
| `ERR` | Failures: semgrep crashes, transaction rollbacks, serve process errors, tool execution errors |
|
|
1222
|
+
| `REQ` | Every tool call with full input parameters (JSON) |
|
|
1223
|
+
| `RES` | Tool completion with elapsed time in milliseconds |
|
|
1224
|
+
| `QUERY` | Rust serve process queries (search, feedback) |
|
|
1225
|
+
| `CACHE` | Search cache hits |
|
|
1226
|
+
| `INDEX` | Background reindex progress |
|
|
1227
|
+
| `SERVE` | Rust serve process stderr (watcher events, model loading) |
|
|
1228
|
+
| `FATAL` | Server startup failures |
|
|
1229
|
+
|
|
1230
|
+
### Common Diagnostic Commands
|
|
1231
|
+
|
|
1232
|
+
```bash
|
|
1233
|
+
# Recent errors
|
|
1234
|
+
grep '\[ERR\]\|\[FATAL\]' .magector/magector.log | tail -20
|
|
1235
|
+
|
|
1236
|
+
# Tool timing (find slow tools)
|
|
1237
|
+
grep '\[RES\]' .magector/magector.log | tail -20
|
|
1238
|
+
|
|
1239
|
+
# Enrichment/null-risk analysis
|
|
1240
|
+
grep 'enrich:\|null_risks:' .magector/magector.log | tail -20
|
|
1241
|
+
|
|
1242
|
+
# AST search (semgrep) issues
|
|
1243
|
+
grep 'ast_search:' .magector/magector.log | tail -20
|
|
1244
|
+
|
|
1245
|
+
# Batch query breakdown (per-tool timing)
|
|
1246
|
+
grep 'batch\[' .magector/magector.log | tail -20
|
|
1247
|
+
|
|
1248
|
+
# Slow grep queries
|
|
1249
|
+
grep 'grep: slow\|grep: timed' .magector/magento.log | tail -20
|
|
1250
|
+
|
|
1251
|
+
# Full startup sequence
|
|
1252
|
+
grep 'server starting\|Config:\|primary\|Serve process' .magector/magector.log | tail -30
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
### What Gets Logged (v2.14+)
|
|
1256
|
+
|
|
1257
|
+
Every tool call logs `[REQ]` with input parameters and `[RES]` with elapsed time. Additionally:
|
|
1258
|
+
|
|
1259
|
+
- **`magento_ast_search`** — semgrep pattern, target path, execution time, result count, semgrep errors
|
|
1260
|
+
- **`magento_enrich`** — file count, progress every 10k files, read errors, transaction failures, final summary
|
|
1261
|
+
- **`magento_find_null_risks`** — query parameters, result count, query timing, missing DB warnings
|
|
1262
|
+
- **`magento_batch`** — query list on entry, per-sub-tool timing and errors
|
|
1263
|
+
- **`magento_grep`** — slow query warnings (>5s), timeout detection
|
|
1264
|
+
- **`magento_read`** — file-not-found with error codes, failed method extractions
|
|
1265
|
+
|
|
1266
|
+
---
|
|
1267
|
+
|
|
1207
1268
|
## License
|
|
1208
1269
|
|
|
1209
1270
|
MIT License. See [LICENSE](LICENSE) for details.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "magector",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.1",
|
|
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.
|
|
37
|
-
"@magector/cli-linux-x64": "2.
|
|
38
|
-
"@magector/cli-linux-arm64": "2.
|
|
39
|
-
"@magector/cli-win32-x64": "2.
|
|
36
|
+
"@magector/cli-darwin-arm64": "2.14.1",
|
|
37
|
+
"@magector/cli-linux-x64": "2.14.1",
|
|
38
|
+
"@magector/cli-linux-arm64": "2.14.1",
|
|
39
|
+
"@magector/cli-win32-x64": "2.14.1"
|
|
40
40
|
},
|
|
41
41
|
"keywords": [
|
|
42
42
|
"magento",
|
package/src/mcp-server.js
CHANGED
|
@@ -149,6 +149,44 @@ const SOCK_PATH = path.join(config.magentoRoot, '.magector', 'serve.sock');
|
|
|
149
149
|
const FORMAT_CACHE_PATH = path.join(config.magentoRoot, '.magector', 'format-ok.json');
|
|
150
150
|
const PRIMARY_LOCK_PATH = path.join(config.magentoRoot, '.magector', 'primary.lock');
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Expand brace patterns in include globs for GNU grep compatibility.
|
|
154
|
+
* GNU grep --include does NOT support brace expansion (that's a shell feature).
|
|
155
|
+
* Transforms "*.{php,xml,graphqls}" → ["*.php", "*.xml", "*.graphqls"]
|
|
156
|
+
* Also handles comma-separated patterns: "*.php, *.xml" → ["*.php", "*.xml"]
|
|
157
|
+
* And mixed: "*.{php,xml}, *.phtml" → ["*.php", "*.xml", "*.phtml"]
|
|
158
|
+
*/
|
|
159
|
+
function expandIncludePattern(include) {
|
|
160
|
+
// Split on commas NOT inside braces
|
|
161
|
+
const parts = [];
|
|
162
|
+
let depth = 0, current = '';
|
|
163
|
+
for (const ch of include) {
|
|
164
|
+
if (ch === '{') depth++;
|
|
165
|
+
if (ch === '}') depth--;
|
|
166
|
+
if (ch === ',' && depth === 0) {
|
|
167
|
+
parts.push(current.trim());
|
|
168
|
+
current = '';
|
|
169
|
+
} else {
|
|
170
|
+
current += ch;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (current.trim()) parts.push(current.trim());
|
|
174
|
+
// Expand brace patterns in each part
|
|
175
|
+
const patterns = [];
|
|
176
|
+
const braceRegex = /^(.*?)\{([^}]+)\}(.*)$/;
|
|
177
|
+
for (const part of parts) {
|
|
178
|
+
const m = part.match(braceRegex);
|
|
179
|
+
if (m) {
|
|
180
|
+
for (const alt of m[2].split(',').map(a => a.trim())) {
|
|
181
|
+
patterns.push(m[1] + alt + m[3]);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
patterns.push(part);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return patterns;
|
|
188
|
+
}
|
|
189
|
+
|
|
152
190
|
/**
|
|
153
191
|
* Try to acquire the primary lock (O_EXCL = atomic create-or-fail).
|
|
154
192
|
* Returns true if we are the primary instance, false if another instance holds the lock.
|
|
@@ -3459,12 +3497,15 @@ function hasNullGuard(lines, matchLineIdx, receiverExpr, guardRadius = 6) {
|
|
|
3459
3497
|
*/
|
|
3460
3498
|
async function enrichMethodChains(root) {
|
|
3461
3499
|
const dbPath = ENRICHMENT_DB_PATH(root);
|
|
3500
|
+
logToFile('INFO', `enrich: starting method-chain scan, db=${dbPath}`);
|
|
3501
|
+
const enrichStart = Date.now();
|
|
3462
3502
|
|
|
3463
3503
|
// Use node:sqlite (built-in, no deps)
|
|
3464
3504
|
let DatabaseSync;
|
|
3465
3505
|
try {
|
|
3466
3506
|
({ DatabaseSync } = await import('node:sqlite'));
|
|
3467
3507
|
} catch {
|
|
3508
|
+
logToFile('ERR', 'enrich: node:sqlite not available — requires Node.js 22.5+');
|
|
3468
3509
|
throw new Error('node:sqlite not available — requires Node.js 22.5+');
|
|
3469
3510
|
}
|
|
3470
3511
|
|
|
@@ -3491,8 +3532,10 @@ async function enrichMethodChains(root) {
|
|
|
3491
3532
|
const now = Date.now();
|
|
3492
3533
|
|
|
3493
3534
|
const phpFiles = await glob('vendor/**/*.php', { cwd: root, absolute: true, nodir: true });
|
|
3535
|
+
logToFile('INFO', `enrich: found ${phpFiles.length} PHP files in vendor/`);
|
|
3494
3536
|
let scanned = 0;
|
|
3495
3537
|
let chains = 0;
|
|
3538
|
+
let readErrors = 0;
|
|
3496
3539
|
|
|
3497
3540
|
const insertStmt = db.prepare(
|
|
3498
3541
|
'INSERT INTO method_chains (file, line, chain, first_method, second_method, has_null_guard, updated_at) VALUES (?,?,?,?,?,?,?)'
|
|
@@ -3519,11 +3562,18 @@ async function enrichMethodChains(root) {
|
|
|
3519
3562
|
return lo + 1; // 1-based
|
|
3520
3563
|
}
|
|
3521
3564
|
|
|
3565
|
+
// Progress logging every 10k files
|
|
3566
|
+
const progressInterval = 10000;
|
|
3567
|
+
|
|
3522
3568
|
db.exec('BEGIN');
|
|
3523
3569
|
try {
|
|
3524
3570
|
for (const phpFile of phpFiles) {
|
|
3525
3571
|
let content;
|
|
3526
|
-
try { content = readFileSync(phpFile, 'utf-8'); } catch {
|
|
3572
|
+
try { content = readFileSync(phpFile, 'utf-8'); } catch (err) {
|
|
3573
|
+
readErrors++;
|
|
3574
|
+
if (readErrors <= 5) logToFile('WARN', `enrich: cannot read ${phpFile}: ${err.code || err.message}`);
|
|
3575
|
+
continue;
|
|
3576
|
+
}
|
|
3527
3577
|
if (!content.includes('->')) continue;
|
|
3528
3578
|
|
|
3529
3579
|
const relPath = phpFile.replace(root + '/', '');
|
|
@@ -3551,14 +3601,20 @@ async function enrichMethodChains(root) {
|
|
|
3551
3601
|
}
|
|
3552
3602
|
}
|
|
3553
3603
|
scanned++;
|
|
3604
|
+
if (scanned % progressInterval === 0) {
|
|
3605
|
+
logToFile('INFO', `enrich: progress ${scanned}/${phpFiles.length} files, ${chains} chains so far (${Date.now() - enrichStart}ms)`);
|
|
3606
|
+
}
|
|
3554
3607
|
}
|
|
3555
3608
|
db.exec('COMMIT');
|
|
3556
3609
|
} catch (err) {
|
|
3610
|
+
logToFile('ERR', `enrich: transaction failed at file ${scanned}/${phpFiles.length}: ${err.message}`);
|
|
3557
3611
|
db.exec('ROLLBACK');
|
|
3558
3612
|
throw err;
|
|
3559
3613
|
}
|
|
3560
3614
|
|
|
3561
3615
|
db.close();
|
|
3616
|
+
const enrichElapsed = Date.now() - enrichStart;
|
|
3617
|
+
logToFile('INFO', `enrich: complete — ${scanned} files scanned, ${chains} chains indexed, ${readErrors} read errors, ${enrichElapsed}ms`);
|
|
3562
3618
|
return { scanned, chains };
|
|
3563
3619
|
}
|
|
3564
3620
|
|
|
@@ -3567,15 +3623,21 @@ async function enrichMethodChains(root) {
|
|
|
3567
3623
|
*/
|
|
3568
3624
|
async function queryNullRisks(root, firstMethod, limit = 100) {
|
|
3569
3625
|
const dbPath = ENRICHMENT_DB_PATH(root);
|
|
3570
|
-
if (!existsSync(dbPath))
|
|
3626
|
+
if (!existsSync(dbPath)) {
|
|
3627
|
+
logToFile('WARN', `null_risks: enrichment.db not found at ${dbPath} — run magento_enrich first`);
|
|
3628
|
+
return null;
|
|
3629
|
+
}
|
|
3571
3630
|
|
|
3572
3631
|
let DatabaseSync;
|
|
3573
3632
|
try {
|
|
3574
3633
|
({ DatabaseSync } = await import('node:sqlite'));
|
|
3575
|
-
} catch {
|
|
3634
|
+
} catch (err) {
|
|
3635
|
+
logToFile('ERR', `null_risks: node:sqlite not available: ${err.message}`);
|
|
3576
3636
|
return null;
|
|
3577
3637
|
}
|
|
3578
3638
|
|
|
3639
|
+
const queryStart = Date.now();
|
|
3640
|
+
logToFile('INFO', `null_risks: querying firstMethod=${firstMethod || '(all)'} limit=${limit}`);
|
|
3579
3641
|
const db = new DatabaseSync(dbPath, { open: true });
|
|
3580
3642
|
let rows;
|
|
3581
3643
|
try {
|
|
@@ -3591,6 +3653,7 @@ async function queryNullRisks(root, firstMethod, limit = 100) {
|
|
|
3591
3653
|
} finally {
|
|
3592
3654
|
db.close();
|
|
3593
3655
|
}
|
|
3656
|
+
logToFile('INFO', `null_risks: ${rows.length} unsafe chain(s) found in ${Date.now() - queryStart}ms`);
|
|
3594
3657
|
return rows;
|
|
3595
3658
|
}
|
|
3596
3659
|
|
|
@@ -3604,13 +3667,22 @@ async function astSearch(pattern, searchPath, lang, maxResults) {
|
|
|
3604
3667
|
const semgrepLang = lang || 'php';
|
|
3605
3668
|
const limit = Math.min(maxResults || 50, 200);
|
|
3606
3669
|
|
|
3670
|
+
logToFile('INFO', `ast_search: pattern="${pattern}" path="${searchPath || '.'}" lang=${semgrepLang} limit=${limit}`);
|
|
3671
|
+
const astStart = Date.now();
|
|
3672
|
+
|
|
3607
3673
|
// Create a temporary empty .semgrepignore in the target directory if none exists.
|
|
3608
3674
|
// Semgrep's default ignore list includes "vendor/" which is exactly what we need to scan.
|
|
3609
3675
|
// An empty .semgrepignore overrides the defaults: https://semgrep.dev/docs/ignoring-files-folders-code/
|
|
3610
3676
|
const semgrepIgnorePath = path.join(targetPath, '.semgrepignore');
|
|
3611
3677
|
let createdSemgrepIgnore = false;
|
|
3612
3678
|
if (!existsSync(semgrepIgnorePath)) {
|
|
3613
|
-
try {
|
|
3679
|
+
try {
|
|
3680
|
+
writeFileSync(semgrepIgnorePath, '# Magector: scan vendor/ and all project files\n');
|
|
3681
|
+
createdSemgrepIgnore = true;
|
|
3682
|
+
logToFile('INFO', `ast_search: created temporary .semgrepignore at ${targetPath}`);
|
|
3683
|
+
} catch (err) {
|
|
3684
|
+
logToFile('WARN', `ast_search: failed to create .semgrepignore: ${err.message}`);
|
|
3685
|
+
}
|
|
3614
3686
|
}
|
|
3615
3687
|
|
|
3616
3688
|
const semgrepArgs = [
|
|
@@ -3633,7 +3705,11 @@ async function astSearch(pattern, searchPath, lang, maxResults) {
|
|
|
3633
3705
|
} catch (err) {
|
|
3634
3706
|
// semgrep exits non-zero when it has findings — stdout still contains valid JSON
|
|
3635
3707
|
rawOutput = err.stdout || '';
|
|
3636
|
-
if (!rawOutput)
|
|
3708
|
+
if (!rawOutput) {
|
|
3709
|
+
const errMsg = (err.stderr || err.message || '').slice(0, 500);
|
|
3710
|
+
logToFile('ERR', `ast_search: semgrep failed after ${Date.now() - astStart}ms: ${errMsg}`);
|
|
3711
|
+
throw new Error(`semgrep failed: ${errMsg}`);
|
|
3712
|
+
}
|
|
3637
3713
|
} finally {
|
|
3638
3714
|
if (createdSemgrepIgnore) { try { unlinkSync(semgrepIgnorePath); } catch { /* best effort */ } }
|
|
3639
3715
|
}
|
|
@@ -3642,10 +3718,16 @@ async function astSearch(pattern, searchPath, lang, maxResults) {
|
|
|
3642
3718
|
try {
|
|
3643
3719
|
parsed = JSON.parse(rawOutput);
|
|
3644
3720
|
} catch {
|
|
3721
|
+
logToFile('ERR', `ast_search: failed to parse semgrep JSON output (${rawOutput.length} bytes)`);
|
|
3645
3722
|
throw new Error(`Failed to parse semgrep output. First 300 chars: ${rawOutput.slice(0, 300)}`);
|
|
3646
3723
|
}
|
|
3647
3724
|
|
|
3648
3725
|
const findings = (parsed.results || []).slice(0, limit);
|
|
3726
|
+
const astElapsed = Date.now() - astStart;
|
|
3727
|
+
logToFile('INFO', `ast_search: ${findings.length} match(es) in ${astElapsed}ms (semgrep returned ${(parsed.results || []).length} total)`);
|
|
3728
|
+
if (parsed.errors && parsed.errors.length > 0) {
|
|
3729
|
+
logToFile('WARN', `ast_search: semgrep reported ${parsed.errors.length} error(s): ${parsed.errors.slice(0, 3).map(e => e.message || e.type || JSON.stringify(e)).join('; ')}`);
|
|
3730
|
+
}
|
|
3649
3731
|
return findings.map(r => ({
|
|
3650
3732
|
file: r.path.replace(root + '/', ''),
|
|
3651
3733
|
line: r.start.line,
|
|
@@ -4765,6 +4847,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4765
4847
|
const root = args.path || config.magentoRoot;
|
|
4766
4848
|
const output = rustIndex(root);
|
|
4767
4849
|
// Auto-enrich after indexing: runs in background, doesn't block response
|
|
4850
|
+
logToFile('INFO', 'Auto-enrich: starting in background after index');
|
|
4768
4851
|
enrichMethodChains(root).then(({ scanned, chains }) => {
|
|
4769
4852
|
logToFile('INFO', `Auto-enrich complete: ${scanned} files, ${chains} chains`);
|
|
4770
4853
|
}).catch(err => {
|
|
@@ -6112,8 +6195,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6112
6195
|
if (queries.length > 10) {
|
|
6113
6196
|
return { content: [{ type: 'text', text: 'Maximum 10 queries per batch.' }], isError: true };
|
|
6114
6197
|
}
|
|
6198
|
+
logToFile('INFO', `batch: ${queries.length} queries: ${queries.map(q => q.tool).join(', ')}`);
|
|
6115
6199
|
// Run batch queries in parallel using existing standalone functions
|
|
6116
6200
|
const batchResults = await Promise.all(queries.map(async (q, idx) => {
|
|
6201
|
+
const batchItemStart = Date.now();
|
|
6117
6202
|
try {
|
|
6118
6203
|
const a = q.args || {};
|
|
6119
6204
|
let text = '';
|
|
@@ -6374,7 +6459,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6374
6459
|
const gArgs = batchFilesOnly ? ['-rl', '-E'] : ['-rn', '-E'];
|
|
6375
6460
|
if (a.ignoreCase) gArgs.push('-i');
|
|
6376
6461
|
if (!batchFilesOnly && batchCtx > 0) gArgs.push('-C', String(batchCtx));
|
|
6377
|
-
for (const pat of include
|
|
6462
|
+
for (const pat of expandIncludePattern(include)) gArgs.push('--include=' + pat);
|
|
6378
6463
|
gArgs.push('--', a.pattern, searchPath);
|
|
6379
6464
|
let out;
|
|
6380
6465
|
try {
|
|
@@ -6416,8 +6501,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6416
6501
|
default:
|
|
6417
6502
|
text = `Unsupported batch tool: ${q.tool}`;
|
|
6418
6503
|
}
|
|
6504
|
+
logToFile('INFO', `batch[${idx}]: ${q.tool} completed (${Date.now() - batchItemStart}ms)`);
|
|
6419
6505
|
return { idx, tool: q.tool, text };
|
|
6420
6506
|
} catch (err) {
|
|
6507
|
+
logToFile('ERR', `batch[${idx}]: ${q.tool} failed (${Date.now() - batchItemStart}ms): ${err.message}`);
|
|
6421
6508
|
return { idx, tool: q.tool, text: `Error: ${err.message}` };
|
|
6422
6509
|
}
|
|
6423
6510
|
}));
|
|
@@ -6440,12 +6527,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6440
6527
|
const grepArgs = filesOnly ? ['-rl', '-E'] : ['-rn', '-E'];
|
|
6441
6528
|
if (args.ignoreCase) grepArgs.push('-i');
|
|
6442
6529
|
if (!filesOnly && ctxLines > 0) grepArgs.push('-C', String(ctxLines));
|
|
6443
|
-
|
|
6444
|
-
for (const pat of include.split(',').map(p => p.trim())) {
|
|
6530
|
+
for (const pat of expandIncludePattern(include)) {
|
|
6445
6531
|
grepArgs.push('--include=' + pat);
|
|
6446
6532
|
}
|
|
6447
6533
|
grepArgs.push('--', args.pattern, searchPath);
|
|
6448
6534
|
let output;
|
|
6535
|
+
const grepStart = Date.now();
|
|
6449
6536
|
try {
|
|
6450
6537
|
output = execFileSync('grep', grepArgs, {
|
|
6451
6538
|
cwd: root, encoding: 'utf-8', timeout: 30000,
|
|
@@ -6455,9 +6542,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6455
6542
|
} catch (err) {
|
|
6456
6543
|
// grep returns exit code 1 when no matches found
|
|
6457
6544
|
output = err.stdout || '';
|
|
6545
|
+
if (err.killed) logToFile('WARN', `grep: timed out after 30s for pattern "${args.pattern}"`);
|
|
6458
6546
|
}
|
|
6547
|
+
const grepElapsed = Date.now() - grepStart;
|
|
6459
6548
|
const lines = output.trim().split('\n').filter(Boolean);
|
|
6460
6549
|
const total = lines.length;
|
|
6550
|
+
if (grepElapsed > 5000) logToFile('WARN', `grep: slow query "${args.pattern}" — ${total} matches in ${grepElapsed}ms`);
|
|
6461
6551
|
const truncated = lines.slice(0, maxResults);
|
|
6462
6552
|
let text = filesOnly
|
|
6463
6553
|
? `## grep (files only): \`${args.pattern}\`\nFound **${total}** file(s)${total > maxResults ? ` (showing first ${maxResults})` : ''}. Use magento_read with methodName to read specific methods.\n\n`
|
|
@@ -6619,6 +6709,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6619
6709
|
const filePath = path.join(root, args.path);
|
|
6620
6710
|
let content;
|
|
6621
6711
|
try { content = readFileSync(filePath, 'utf-8'); } catch (err) {
|
|
6712
|
+
logToFile('WARN', `read: file not found: ${args.path} (${err.code || err.message})`);
|
|
6622
6713
|
return { content: [{ type: 'text', text: `File not found: ${args.path}` }], isError: true };
|
|
6623
6714
|
}
|
|
6624
6715
|
|
|
@@ -6626,6 +6717,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
6626
6717
|
if (args.methodName) {
|
|
6627
6718
|
const body = readFullMethodBody(filePath, args.methodName);
|
|
6628
6719
|
if (!body) {
|
|
6720
|
+
logToFile('WARN', `read: method "${args.methodName}" not found in ${args.path}`);
|
|
6629
6721
|
return { content: [{ type: 'text', text: `## ${args.path}\n\nMethod \`${args.methodName}\` not found in file.` }] };
|
|
6630
6722
|
}
|
|
6631
6723
|
// Find line number of the method
|