magector 2.3.0 → 2.4.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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Technology-aware MCP server for Magento 2 and Adobe Commerce with intelligent indexing and search.**
4
4
 
5
- Magector is a Model Context Protocol (MCP) server that deeply understands Magento 2 and Adobe Commerce. It builds a semantic vector index of your entire codebase — 18,000+ files across hundreds of modules — and exposes 28 tools that let AI assistants search, navigate, and understand the code with domain-specific intelligence. Instead of grepping for keywords, your AI asks *"how are checkout totals calculated?"* and gets ranked, relevant results in under 50ms, enriched with Magento pattern detection (plugins, observers, controllers, DI preferences, layout XML, and 20+ more).
5
+ Magector is a Model Context Protocol (MCP) server that deeply understands Magento 2 and Adobe Commerce. It builds a semantic vector index of your entire codebase — 18,000+ files across hundreds of modules — and exposes 34 tools that let AI assistants search, navigate, and understand the code with domain-specific intelligence. Instead of grepping for keywords, your AI asks *"how are checkout totals calculated?"* and gets ranked, relevant results in under 50ms, enriched with Magento pattern detection (plugins, observers, controllers, DI preferences, layout XML, and 20+ more).
6
6
 
7
7
  [![Rust](https://img.shields.io/badge/rust-1.75+-orange.svg)](https://www.rust-lang.org)
8
8
  [![Node.js](https://img.shields.io/badge/node-18+-green.svg)](https://nodejs.org)
@@ -58,7 +58,7 @@ The result: your AI assistant calls one MCP tool and gets ranked, pattern-enrich
58
58
  - **Complexity analysis** -- cyclomatic complexity, function count, and hotspot detection across modules
59
59
  - **Fast** -- 10-45ms queries via persistent serve process, batched ONNX embedding with adaptive thread scaling
60
60
  - **LLM description enrichment** -- generate natural-language descriptions of di.xml files using Claude, stored in SQLite, and prepend them to embedding text so descriptions influence vector search ranking (not just post-retrieval display)
61
- - **MCP server** -- 28 tools integrating with Claude Code, Cursor, and any MCP-compatible AI tool
61
+ - **MCP server** -- 34 tools integrating with Claude Code, Cursor, and any MCP-compatible AI tool
62
62
  - **Clean architecture** -- Rust core handles all indexing/search, Node.js MCP server delegates to it
63
63
 
64
64
  ---
@@ -70,7 +70,7 @@ flowchart LR
70
70
  subgraph node ["Node.js Layer"]
71
71
  direction TB
72
72
  G["CLI<br/>init · index · search · describe"]
73
- E["MCP Server<br/>28 tools · LRU cache"]
73
+ E["MCP Server<br/>34 tools · LRU cache"]
74
74
  F["Persistent Serve Process"]
75
75
  G --> F
76
76
  E --> F
@@ -369,7 +369,7 @@ npx magector index --force
369
369
 
370
370
  ## MCP Server Tools
371
371
 
372
- The MCP server exposes 28 tools for AI-assisted Magento 2 and Adobe Commerce development. All search tools return **structured JSON** with file paths, class names, methods, role badges, and content snippets -- enabling AI clients to parse results programmatically and minimize file-read round-trips.
372
+ The MCP server exposes 34 tools for AI-assisted Magento 2 and Adobe Commerce development. All search tools return **structured JSON** with file paths, class names, methods, role badges, and content snippets -- enabling AI clients to parse results programmatically and minimize file-read round-trips.
373
373
 
374
374
  ### Output Format
375
375
 
@@ -432,7 +432,10 @@ All search tools return structured JSON:
432
432
  | `magento_trace_flow` | Trace execution flow from an entry point (route, API, GraphQL, event, cron) -- maps controller → plugins → observers → templates in one call |
433
433
  | `magento_trace_dependency` | Trace DI graph for a class/interface -- preferences, plugins, virtualTypes, argument overrides (parses all di.xml, no index needed) |
434
434
  | `magento_find_event_flow` | Trace complete event chain: dispatchers → observers → handler PHP classes (parses events.xml + vector search) |
435
+ | `magento_find_event_dispatchers` | Find all PHP locations where a specific event is dispatched -- exact grep matching with method context and surrounding code **(v2.3)** |
435
436
  | `magento_find_layout` | Find layout XML files by handle or content -- lists blocks, containers, and referenceBlock declarations |
437
+ | `magento_trace_data_flow` | Trace how a data attribute flows: find all setters (magic setter, setData, addData) and getters (magic getter, getData) across PHP and XML **(v2.3)** |
438
+ | `magento_trace_call_chain` | Trace internal method call chain: follows `$this->method()`, `$this->dep->method()`, and `dispatch()` calls to build an execution tree **(v2.2)** |
436
439
 
437
440
  Auto-detects entry type from pattern (`/V1/...` → API, `snake_case` → event, `camelCase` → GraphQL, `path/segments` → route), or override with `entryType`. Use `depth: "shallow"` (entry + config + plugins) or `depth: "deep"` (adds observers, layout, templates, DI preferences).
438
441
 
@@ -442,6 +445,9 @@ Auto-detects entry type from pattern (`/V1/...` → API, `snake_case` → event,
442
445
  |------|-------------|
443
446
  | `magento_impact_analysis` | Analyze impact of changing a class -- finds use statements, DI references, direct instantiations, and type hints across the codebase |
444
447
  | `magento_find_test` | Find PHPUnit tests for a given class/method -- searches Test/ directories for coverage, mocks, and assertions |
448
+ | `magento_find_implementors` | Find all classes implementing a given PHP interface -- scans `implements` keywords and di.xml `<preference>` declarations **(v2.2)** |
449
+ | `magento_find_callers` | Find all call sites of a method across PHP and XML files -- `->method()` and `::method()` calls **(v2.2)** |
450
+ | `magento_find_di_wiring` | Complete DI picture for a class: preferences, plugins, constructor args, virtual types, and argument overrides from di.xml **(v2.2)** |
445
451
 
446
452
  ### Diagnostics
447
453
 
@@ -470,9 +476,22 @@ Auto-detects entry type from pattern (`/V1/...` → API, `snake_case` → event,
470
476
 
471
477
  - **Hybrid BM25+vector search** -- combines text frequency scoring with semantic vector similarity for better exact class name matches
472
478
  - **Query expansion** -- automatically expands queries with Magento domain synonyms (plugin → interceptor, checkout → cart/quote/totals, etc.)
473
- - **Module filtering** -- `moduleFilter` parameter on `magento_search` to limit results by vendor/module pattern (supports wildcards, e.g., `"Vendor_*"`)
479
+ - **Module filtering** -- `moduleFilter` parameter on `magento_search` to limit results by vendor/module pattern. Accepts a single string or array of strings. Supports wildcards, e.g., `"Vendor_*"` or `["Acme_PaymentGateway", "Acme_FreeShipping"]`
474
480
  - **Non-blocking reindex** -- old index stays usable during background rebuild; new index is built to a temp path and swapped in atomically on completion
475
481
 
482
+ ### Deep Code Analysis (v2.2)
483
+
484
+ - **`magento_find_implementors`** -- find all classes implementing a PHP interface (PHP `implements` + di.xml `<preference>`)
485
+ - **`magento_find_callers`** -- find all call sites of a method across PHP and XML files
486
+ - **`magento_find_di_wiring`** -- complete DI picture: preferences, plugins, constructor args, virtual types, argument overrides
487
+ - **`magento_trace_call_chain`** -- trace internal method execution chain: `$this->method()`, `$this->dep->method()`, and `dispatch()` calls with event→observer resolution
488
+
489
+ ### Data Flow & Event Tracing (v2.3)
490
+
491
+ - **`magento_trace_data_flow`** -- trace all setters and getters for a data attribute (magic methods, setData/getData, addData, constants, XML references). Answers "who writes/reads `custom_discounted_price_incl_tax` on `Quote\Address`?"
492
+ - **`magento_find_event_dispatchers`** -- grep-based exact search for all PHP locations dispatching a specific event, with method context and surrounding code. Complements `magento_find_event_flow` with higher precision.
493
+ - **`magento_find_plugin` area context** -- enriched output shows DI area (frontend/adminhtml/global/graphql) and explicit di.xml plugin registrations when `targetClass` is provided
494
+
476
495
  ### Tool Cross-References
477
496
 
478
497
  Each tool description includes "See also" hints to help AI clients chain tools effectively:
@@ -549,6 +568,12 @@ magento_trace_flow({ entryPoint: "checkout/cart/add", depth: "deep" })
549
568
  magento_trace_flow({ entryPoint: "/V1/products" })
550
569
  magento_trace_flow({ entryPoint: "placeOrder", entryType: "graphql" })
551
570
  magento_trace_flow({ entryPoint: "sales_order_place_after" })
571
+ magento_trace_data_flow({ attributeKey: "custom_discounted_price_incl_tax", modelClass: "Quote\\Address" })
572
+ magento_find_event_dispatchers({ eventName: "custom_discount_rule_validation_before" })
573
+ magento_find_implementors({ interfaceName: "ProductRepositoryInterface" })
574
+ magento_find_callers({ methodName: "collectTotals", className: "TotalsCollector" })
575
+ magento_find_di_wiring({ className: "CartManagementInterface" })
576
+ magento_trace_call_chain({ className: "Magento\\Quote\\Model\\QuoteManagement", methodName: "submit" })
552
577
  ```
553
578
 
554
579
  ---
@@ -629,7 +654,7 @@ cd rust-core && cargo run --release -- validate -m ./magento2 --skip-index
629
654
  magector/
630
655
  ├── src/ # Node.js source
631
656
  │ ├── cli.js # CLI entry point (npx magector <command>)
632
- │ ├── mcp-server.js # MCP server (20 tools, structured JSON output)
657
+ │ ├── mcp-server.js # MCP server (34 tools, structured JSON output)
633
658
  │ ├── binary.js # Platform binary resolver
634
659
  │ ├── model.js # ONNX model resolver/downloader
635
660
  │ ├── init.js # Full init command (index + IDE config)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "magector",
3
- "version": "2.3.0",
3
+ "version": "2.4.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.3.0",
37
- "@magector/cli-linux-x64": "2.3.0",
38
- "@magector/cli-linux-arm64": "2.3.0",
39
- "@magector/cli-win32-x64": "2.3.0"
36
+ "@magector/cli-darwin-arm64": "2.4.0",
37
+ "@magector/cli-linux-x64": "2.4.0",
38
+ "@magector/cli-linux-arm64": "2.4.0",
39
+ "@magector/cli-win32-x64": "2.4.0"
40
40
  },
41
41
  "keywords": [
42
42
  "magento",
package/src/mcp-server.js CHANGED
@@ -32,6 +32,8 @@ import {
32
32
  } from 'ruvector/dist/analysis/complexity.js';
33
33
  import { resolveBinary } from './binary.js';
34
34
  import { resolveModels } from './model.js';
35
+ import { createRequire } from 'module';
36
+ const __pkg = createRequire(import.meta.url)('../package.json');
35
37
 
36
38
  const config = {
37
39
  dbPath: process.env.MAGECTOR_DB || './.magector/index.db',
@@ -2816,7 +2818,7 @@ async function traceCallChain(startClass, startMethod, maxDepth = 3) {
2816
2818
  const server = new Server(
2817
2819
  {
2818
2820
  name: 'magector',
2819
- version: '2.1.1'
2821
+ version: __pkg.version
2820
2822
  },
2821
2823
  {
2822
2824
  capabilities: {
@@ -2848,7 +2850,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
2848
2850
  { type: 'string' },
2849
2851
  { type: 'array', items: { type: 'string' } }
2850
2852
  ],
2851
- description: 'Filter results by vendor/module pattern(s). Accepts a single string or array of strings. Supports wildcards and vendor prefix matching. Uses "/" or "_" interchangeably as separator. Examples: "Vendor_*", ["drmax_paymentrestrictions", "drmax_module-free-shipping"], "Magento_Catalog".'
2853
+ description: 'Filter results by vendor/module pattern(s). Accepts a single string or array of strings. Supports wildcards and vendor prefix matching. Uses "/" or "_" interchangeably as separator. Examples: "Vendor_*", ["Acme_PaymentGateway", "Acme_FreeShipping"], "Magento_Catalog".'
2852
2854
  },
2853
2855
  expand: {
2854
2856
  type: 'boolean',
@@ -3086,7 +3088,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3086
3088
  },
3087
3089
  {
3088
3090
  name: 'magento_find_db_schema',
3089
- description: 'Find database table definitions, columns, indexes, and constraints declared in db_schema.xml (Magento declarative schema). See also: magento_find_class (model/resource model for the table).',
3091
+ description: 'Find database table definitions, columns, indexes, and constraints declared in db_schema.xml (Magento declarative schema) AND legacy Setup scripts (InstallSchema, UpgradeSchema). Covers both modern declarative schema and legacy $setup->newTable() / addColumn() table definitions. See also: magento_find_trigger (DB triggers), magento_find_table_usage (cross-module table references).',
3090
3092
  inputSchema: {
3091
3093
  type: 'object',
3092
3094
  properties: {
@@ -3098,6 +3100,37 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3098
3100
  required: ['tableName']
3099
3101
  }
3100
3102
  },
3103
+ {
3104
+ name: 'magento_find_trigger',
3105
+ description: 'Find MySQL database trigger definitions in Magento Setup scripts. Detects triggers created via TriggerFactory (setName, setTable, setEvent, setTime, addStatement, createTrigger). Returns trigger name, target table, event type (INSERT/UPDATE/DELETE), timing (BEFORE/AFTER), and SQL statements. Use when investigating DB-level automation, trigger chains, or performance issues caused by cascading triggers.',
3106
+ inputSchema: {
3107
+ type: 'object',
3108
+ properties: {
3109
+ triggerName: {
3110
+ type: 'string',
3111
+ description: 'Optional trigger name or pattern to search for. If omitted, finds all triggers in the codebase.'
3112
+ },
3113
+ tableName: {
3114
+ type: 'string',
3115
+ description: 'Optional table name to find triggers targeting this table.'
3116
+ }
3117
+ }
3118
+ }
3119
+ },
3120
+ {
3121
+ name: 'magento_find_table_usage',
3122
+ description: 'Find all code that references a database table — across db_schema.xml, Setup scripts (InstallSchema/UpgradeSchema), raw SQL (Zend_Db_Expr, $connection->query), getTable() calls, and resource model definitions. Builds a cross-module dependency map showing who reads/writes/creates a given table. Essential for impact analysis of schema changes.',
3123
+ inputSchema: {
3124
+ type: 'object',
3125
+ properties: {
3126
+ tableName: {
3127
+ type: 'string',
3128
+ description: 'Database table name to find all references for. Examples: "salesrule_ordered", "catalog_product_entity", "quote_item"'
3129
+ }
3130
+ },
3131
+ required: ['tableName']
3132
+ }
3133
+ },
3101
3134
  {
3102
3135
  name: 'magento_module_structure',
3103
3136
  description: 'Get the complete structure of a Magento module — lists all controllers, models, blocks, plugins, observers, API classes, XML configs, and templates. Provides an overview of module architecture.',
@@ -3330,7 +3363,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3330
3363
  properties: {
3331
3364
  methodName: {
3332
3365
  type: 'string',
3333
- description: 'Method name to find callers for. Examples: "execute", "save", "collectTotals", "copySalesRuleIdsFromParentToDrmaxQuote"'
3366
+ description: 'Method name to find callers for. Examples: "execute", "save", "collectTotals", "copySalesRuleIdsFromParentToChildQuote"'
3334
3367
  },
3335
3368
  className: {
3336
3369
  type: 'string',
@@ -3362,7 +3395,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3362
3395
  properties: {
3363
3396
  className: {
3364
3397
  type: 'string',
3365
- description: 'Full PHP class name (FQCN) to start tracing from. Examples: "DrmaxMarketplace\\OrderSplit\\Model\\CreateOrder\\CreateChildOrder", "Magento\\Quote\\Model\\QuoteManagement"'
3398
+ description: 'Full PHP class name (FQCN) to start tracing from. Examples: "Vendor\\OrderSplit\\Model\\CreateOrder\\CreateChildOrder", "Magento\\Quote\\Model\\QuoteManagement"'
3366
3399
  },
3367
3400
  methodName: {
3368
3401
  type: 'string',
@@ -3379,13 +3412,13 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3379
3412
  },
3380
3413
  {
3381
3414
  name: 'magento_trace_data_flow',
3382
- description: 'Trace how a data attribute flows through the Magento codebase: find all PHP files that set (via magic setter, setData, addData) and get (via magic getter, getData) a specific attribute key. Shows which classes write vs read the attribute, in which methods, and whether XML configs reference it. Use this to understand data dependencies — e.g., who sets drmax_discounted_price_incl_tax on Quote\\Address and who reads it.',
3415
+ description: 'Trace how a data attribute flows through the Magento codebase: find all PHP files that set (via magic setter, setData, addData) and get (via magic getter, getData) a specific attribute key. Shows which classes write vs read the attribute, in which methods, and whether XML configs reference it. Use this to understand data dependencies — e.g., who sets custom_discounted_price_incl_tax on Quote\\Address and who reads it.',
3383
3416
  inputSchema: {
3384
3417
  type: 'object',
3385
3418
  properties: {
3386
3419
  attributeKey: {
3387
3420
  type: 'string',
3388
- description: 'The snake_case data attribute key to trace. Examples: "drmax_discounted_price_incl_tax", "base_grand_total", "drmax_free_shipping_price", "subtotal_with_discount"'
3421
+ description: 'The snake_case data attribute key to trace. Examples: "custom_discounted_price_incl_tax", "base_grand_total", "custom_free_shipping_price", "subtotal_with_discount"'
3389
3422
  },
3390
3423
  modelClass: {
3391
3424
  type: 'string',
@@ -3403,7 +3436,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
3403
3436
  properties: {
3404
3437
  eventName: {
3405
3438
  type: 'string',
3406
- description: 'Magento event name to find dispatchers for. Examples: "sales_order_place_after", "drmax_discount_rule_validation_before", "checkout_cart_add_product_complete"'
3439
+ description: 'Magento event name to find dispatchers for. Examples: "sales_order_place_after", "custom_discount_rule_validation_before", "checkout_cart_add_product_complete"'
3407
3440
  }
3408
3441
  },
3409
3442
  required: ['eventName']
@@ -3807,17 +3840,160 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
3807
3840
  }
3808
3841
 
3809
3842
  case 'magento_find_db_schema': {
3810
- const query = `db_schema.xml table ${args.tableName} column declarative schema`;
3811
- const raw = await rustSearchAsync(query, 40);
3812
- let results = raw.map(normalizeResult).filter(r =>
3843
+ // Search both declarative schema (db_schema.xml) and legacy Setup scripts
3844
+ const declQuery = `db_schema.xml table ${args.tableName} column declarative schema`;
3845
+ const legacyQuery = `create_table ${args.tableName} legacy_schema table_created ${args.tableName} setup install schema newTable addColumn`;
3846
+ const [declRaw, legacyRaw] = await Promise.all([
3847
+ rustSearchAsync(declQuery, 40),
3848
+ rustSearchAsync(legacyQuery, 30)
3849
+ ]);
3850
+
3851
+ let declResults = declRaw.map(normalizeResult).filter(r =>
3813
3852
  r.path?.includes('db_schema.xml')
3814
3853
  );
3815
- results = rerank(results, { fileType: 'xml', pathContains: ['db_schema.xml'] });
3854
+ declResults = rerank(declResults, { fileType: 'xml', pathContains: ['db_schema.xml'] });
3855
+
3856
+ let legacyResults = legacyRaw.map(normalizeResult).filter(r => {
3857
+ const p = r.path || '';
3858
+ return (p.includes('/Setup/') || p.includes('InstallSchema') ||
3859
+ p.includes('UpgradeSchema') || p.includes('/Patch/')) &&
3860
+ (r.snippet?.toLowerCase().includes(args.tableName.toLowerCase()) ||
3861
+ r.searchText?.toLowerCase().includes(args.tableName.toLowerCase()));
3862
+ });
3863
+
3864
+ // Deduplicate by path
3865
+ const seen = new Set(declResults.map(r => r.path));
3866
+ for (const r of legacyResults) {
3867
+ if (!seen.has(r.path)) {
3868
+ seen.add(r.path);
3869
+ declResults.push(r);
3870
+ }
3871
+ }
3872
+
3873
+ // Add section headers
3874
+ let output = '';
3875
+ const xmlResults = declResults.filter(r => r.path?.includes('db_schema.xml'));
3876
+ const setupResults = declResults.filter(r => !r.path?.includes('db_schema.xml'));
3877
+
3878
+ if (xmlResults.length > 0) {
3879
+ output += formatSearchResults(xmlResults.slice(0, 10));
3880
+ }
3881
+ if (setupResults.length > 0) {
3882
+ output += `\n\n### Legacy Setup Scripts (InstallSchema/UpgradeSchema)\n`;
3883
+ output += formatSearchResults(setupResults.slice(0, 10));
3884
+ }
3816
3885
 
3817
3886
  return {
3818
3887
  content: [{
3819
3888
  type: 'text',
3820
- text: formatSearchResults(results.slice(0, 15))
3889
+ text: output || formatSearchResults([])
3890
+ }]
3891
+ };
3892
+ }
3893
+
3894
+ case 'magento_find_trigger': {
3895
+ // Search for DB trigger definitions in Setup scripts
3896
+ const triggerTerm = args.triggerName || args.tableName || '';
3897
+ const query = `db_trigger create_trigger sql_trigger TriggerFactory createTrigger setName setEvent ${triggerTerm} setup database_trigger trigger`;
3898
+ const raw = await rustSearchAsync(query, 50);
3899
+ let results = raw.map(normalizeResult).filter(r => {
3900
+ const p = r.path || '';
3901
+ const s = (r.searchText || '') + ' ' + (r.snippet || '');
3902
+ // Must be a Setup file that contains trigger-related terms
3903
+ return (p.includes('/Setup/') || p.includes('InstallSchema') ||
3904
+ p.includes('UpgradeSchema') || p.includes('/Patch/')) &&
3905
+ (s.toLowerCase().includes('trigger') || s.includes('db_trigger') || s.includes('create_trigger'));
3906
+ });
3907
+
3908
+ // If searching for specific trigger/table, filter further
3909
+ if (triggerTerm) {
3910
+ const term = triggerTerm.toLowerCase();
3911
+ const filtered = results.filter(r => {
3912
+ const s = ((r.searchText || '') + ' ' + (r.snippet || '')).toLowerCase();
3913
+ return s.includes(term);
3914
+ });
3915
+ if (filtered.length > 0) results = filtered;
3916
+ }
3917
+
3918
+ results = rerank(results, { pathContains: ['/Setup/', 'UpgradeSchema', 'InstallSchema'] });
3919
+
3920
+ return {
3921
+ content: [{
3922
+ type: 'text',
3923
+ text: results.length > 0
3924
+ ? `### DB Trigger Definitions\n\n` + formatSearchResults(results.slice(0, 15)) +
3925
+ `\n\n**Tip:** Read the matched Setup files to see trigger names, target tables, events (INSERT/UPDATE/DELETE), timing (BEFORE/AFTER), and SQL statements.`
3926
+ : `No database triggers found${triggerTerm ? ` matching "${triggerTerm}"` : ''}. Triggers are defined in Setup scripts using TriggerFactory.`
3927
+ }]
3928
+ };
3929
+ }
3930
+
3931
+ case 'magento_find_table_usage': {
3932
+ const table = args.tableName;
3933
+ // Multi-query strategy: search declarative schema, setup scripts, and inline SQL references
3934
+ const queries = [
3935
+ `table ${table} db_schema.xml declarative schema column`,
3936
+ `sql_table ${table} table_reference ${table} Zend_Db_Expr getTable`,
3937
+ `create_table ${table} legacy_schema setup trigger ${table}`,
3938
+ `${table.replace(/_/g, ' ')} resource model collection`,
3939
+ ];
3940
+
3941
+ const rawResults = await Promise.all(queries.map(q => rustSearchAsync(q, 25)));
3942
+ const allResults = rawResults.flat().map(normalizeResult);
3943
+
3944
+ // Deduplicate by path
3945
+ const pathMap = new Map();
3946
+ for (const r of allResults) {
3947
+ if (!r.path) continue;
3948
+ const s = ((r.searchText || '') + ' ' + (r.snippet || '')).toLowerCase();
3949
+ if (s.includes(table.toLowerCase()) || r.path.includes('db_schema.xml')) {
3950
+ if (!pathMap.has(r.path) || (r.score || 0) > (pathMap.get(r.path).score || 0)) {
3951
+ pathMap.set(r.path, r);
3952
+ }
3953
+ }
3954
+ }
3955
+ const unique = Array.from(pathMap.values());
3956
+
3957
+ // Categorize results
3958
+ const categories = {
3959
+ 'Declarative Schema (db_schema.xml)': unique.filter(r => r.path?.includes('db_schema.xml')),
3960
+ 'Setup Scripts (InstallSchema/UpgradeSchema/Patch)': unique.filter(r => {
3961
+ const p = r.path || '';
3962
+ return !p.includes('db_schema.xml') &&
3963
+ (p.includes('/Setup/') || p.includes('InstallSchema') ||
3964
+ p.includes('UpgradeSchema') || p.includes('/Patch/'));
3965
+ }),
3966
+ 'PHP Code (raw SQL, getTable, Zend_Db_Expr)': unique.filter(r => {
3967
+ const p = r.path || '';
3968
+ return p.endsWith('.php') && !p.includes('db_schema.xml') &&
3969
+ !p.includes('/Setup/') && !p.includes('InstallSchema') &&
3970
+ !p.includes('UpgradeSchema') && !p.includes('/Patch/');
3971
+ }),
3972
+ 'XML Config': unique.filter(r => {
3973
+ const p = r.path || '';
3974
+ return p.endsWith('.xml') && !p.includes('db_schema.xml');
3975
+ }),
3976
+ };
3977
+
3978
+ let output = `### Table Usage: \`${table}\`\n\n`;
3979
+ output += `Found ${unique.length} file(s) referencing this table.\n\n`;
3980
+
3981
+ for (const [category, items] of Object.entries(categories)) {
3982
+ if (items.length > 0) {
3983
+ output += `#### ${category} (${items.length})\n`;
3984
+ output += formatSearchResults(items.slice(0, 8));
3985
+ output += '\n';
3986
+ }
3987
+ }
3988
+
3989
+ if (unique.length === 0) {
3990
+ output += `No references found for table "${table}". Try a broader search with magento_search.`;
3991
+ }
3992
+
3993
+ return {
3994
+ content: [{
3995
+ type: 'text',
3996
+ text: output
3821
3997
  }]
3822
3998
  };
3823
3999
  }