Content
# CAL Dependency MCP Server
[](https://opensource.org/licenses/MIT)
[](#development)
A Model Context Protocol (MCP) server for analyzing **C/AL (Microsoft Dynamics NAV 2009-2018)** text files. This server enables AI assistants to search, explore, and map dependencies within NAV codebases.
## Features
- **Package Management** - Load, select, unload, and inspect multiple package-scoped registries
- **Object Search** - Wildcard search across all C/AL object types with pagination
- **Usage Search** - Search semantic usages of objects, procedures, and variables across packages
- **Dependency Mapping** - Analyze incoming and outgoing references between objects
- **Reference Tracking** - Find where fields/objects are used (TableRelation, CalcFormula, Record variables)
- **Code Search** - Regex-based search within procedure bodies
- **Table Relations** - Map complex TableRelation and CalcFormula links
- **Impact and Path Analysis** - Explore object-level reachability and shortest structural paths
- **Optional Local UI** - Launch a thin in-process browser UI for package/object/usages inspection
- **Token Efficient** - Summary modes for LLM context optimization
## Supported Object Types
- Table
- Page
- Form (Legacy)
- Codeunit
- Report
- XMLport
- Query
- MenuSuite
## Installation
```bash
# Clone the repository
git clone https://github.com/marosgonda/CAL-Dependency-MCP-Server.git
cd CAL-Dependency-MCP-Server
# Install dependencies
bun install
# Build
bun run build
```
## Configuration
### Claude Desktop
Add to your `claude_desktop_config.json`:
**Windows:**
```json
{
"mcpServers": {
"cal-analyzer": {
"command": "bun",
"args": ["run", "C:/path/to/CAL-Dependency-MCP-Server/dist/index.js"]
}
}
}
```
**macOS/Linux:**
```json
{
"mcpServers": {
"cal-analyzer": {
"command": "bun",
"args": ["run", "/path/to/CAL-Dependency-MCP-Server/dist/index.js"]
}
}
}
```
### Other MCP Clients
Use stdio transport with command:
```bash
bun run /path/to/dist/index.js
```
### Optional Local UI
You can run the MCP server and the minimal local UI from the same process:
```bash
bun run /path/to/dist/index.js --ui
```
Use `--ui-port=<port>` to change the default UI port (`3080`):
```bash
bun run /path/to/dist/index.js --ui --ui-port=3090
```
The UI is intentionally thin: it calls the same package, object, and usage endpoints that back the MCP tools.
## Available Tools
### Core Tools
#### `cal_files` - Load and manage C/AL files
```json
{
"action": "load",
"path": "C:/NAV/Objects/",
"autoDiscover": true
}
```
Actions: `load`, `list`, `stats`
#### `cal_packages` - Manage package-scoped registries
Use `cal_packages` when you want explicit package lifecycle control instead of the legacy single active registry flow from `cal_files`.
Load a package from a file or directory:
```json
{
"action": "load",
"path": "C:/NAV/Objects/",
"packageId": "nav-base",
"label": "NAV Base Objects",
"autoDiscover": true,
"forceReload": true
}
```
List loaded packages:
```json
{
"action": "list"
}
```
Select the active package for subsequent queries:
```json
{
"action": "select",
"packageId": "nav-base"
}
```
Get stats for the active package or an explicit package:
```json
{
"action": "stats",
"packageId": "nav-base"
}
```
Unload a package:
```json
{
"action": "unload",
"packageId": "nav-base"
}
```
#### `cal_search_objects` - Search objects by pattern
```json
{
"pattern": "Cust*",
"objectType": "Table",
"limit": 20,
"offset": 0,
"summaryMode": true
}
```
#### `cal_get_object_definition` - Get full object definition
```json
{
"objectType": "Table",
"objectId": 18,
"summaryMode": false
}
```
#### `cal_find_references` - Find cross-object references
```json
{
"targetName": "Customer",
"fieldName": "No.",
"referenceType": "TableRelation"
}
```
#### `cal_search_object_members` - Search fields/procedures
```json
{
"objectName": "Customer",
"memberType": "fields",
"pattern": "*No*"
}
```
#### `cal_get_object_summary` - Token-efficient overview
```json
{
"objectName": "Sales-Post",
"objectType": "Codeunit"
}
```
#### `cal_search_usages` - Search semantic usages across packages
Search for variable usages in the active package:
```json
{
"symbolType": "variable",
"symbolName": "ComplexMetadata",
"limit": 20
}
```
Search for object usages in a specific package with an owner filter:
```json
{
"symbolType": "object",
"symbolName": "Customer",
"objectType": "Codeunit",
"objectName": "Sales-Post",
"packageId": "nav-base",
"scope": "active",
"limit": 20
}
```
### Extended Tools
#### `cal_search_code` - Search in procedure bodies
```json
{
"pattern": "ERROR\(.*\)",
"objectType": "Codeunit",
"limit": 20
}
```
#### `cal_get_dependencies` - Get dependency graph
```json
{
"objectType": "Table",
"objectId": 18,
"direction": "both"
}
```
Directions: `incoming`, `outgoing`, `both`
#### `cal_get_table_relations` - Map table relationships
```json
{
"tableId": 36,
"includeCalcFormula": true
}
```
#### `cal_get_impact` - Traverse object-level structural impact
Returns the shared graph traversal contract directly.
```json
{
"packageId": "nav-base",
"node": {
"objectType": "Table",
"objectId": 18
},
"direction": "outgoing",
"maxDepth": 2,
"limit": 10,
"edgeKinds": ["TableRelation", "CalcFormula"]
}
```
Response shape:
```json
{
"direction": "outgoing",
"origin": {
"packageId": "nav-base",
"objectType": "Table",
"objectId": 18,
"objectName": "Customer"
},
"nodes": [],
"edges": [],
"warnings": [
{
"code": "unsupported-edge-family",
"skippedFamily": "SourceExpr",
"message": "Object-graph v1 does not traverse SourceExpr."
}
],
"coverage": {
"contractVersion": "v1",
"nodeScope": "object-level-only",
"supportedEdgeKinds": ["TableRelation", "CalcFormula", "RecordVariable", "SourceTable", "DataItemTable"],
"supportedEdgeKindCount": 5,
"skippedEdgeFamilies": ["SourceExpr", "procedure-call-body-matches", "trigger-body-matches", "event-publisher-subscriber-links", "control-runobject-links", "query-dataitem-links", "xmlport-node-links", "menusuite-links", "cross-package-traversal"],
"skippedEdgeFamilyCount": 9,
"diagnostics": {
"unresolvedEdgeCount": 0,
"unresolvedEdgesByKind": {},
"excludedHeuristicEdgeCount": 2,
"excludedHeuristicEdgeFamilies": ["procedure-call-body-matches", "trigger-body-matches"],
"unsupportedParserAreaWarningCount": 9
},
"partialAnalysis": true,
"warnings": []
},
"diagnostics": {
"unresolvedEdgeCount": 0,
"unresolvedEdgesByKind": {},
"excludedHeuristicEdgeCount": 2,
"excludedHeuristicEdgeFamilies": ["procedure-call-body-matches", "trigger-body-matches"],
"unsupportedParserAreaWarningCount": 9
},
"truncation": {
"truncated": false,
"reasons": [],
"maxDepth": 2,
"limit": 10
},
"explainability": {
"visitedNodes": [],
"includedEdgeKinds": ["TableRelation", "CalcFormula"]
}
}
```
`direction` supports `incoming`, `outgoing`, and `reachable`. The traversal is object-level only and uses structural edges only.
The sample above shows one warning, but the real response includes every skipped edge family in `coverage.warnings`.
#### `cal_find_path` - Find the shortest structural path between two objects
Returns the shared path contract directly.
```json
{
"packageId": "nav-base",
"source": {
"objectType": "Table",
"objectId": 18
},
"target": {
"objectType": "Table",
"objectId": 27
},
"maxDepth": 6,
"includeCoverage": false
}
```
Response shape:
```json
{
"source": {
"packageId": "nav-base",
"objectType": "Table",
"objectId": 18,
"objectName": "Customer"
},
"target": {
"packageId": "nav-base",
"objectType": "Table",
"objectId": 27,
"objectName": "Item"
},
"path": {
"found": true,
"hopCount": 2,
"nodes": [],
"edges": [],
"steps": []
},
"warnings": [],
"explainability": {
"visitedNodes": [],
"includedEdgeKinds": ["TableRelation"]
}
}
```
When no path exists, `path.found` is `false` and `nodes`, `edges`, and `steps` stay empty. If `includeCoverage` is `false`, the response omits `coverage` and `diagnostics`.
### Impact and path limits
- Object-level nodes only, keyed by package, object type, and object ID.
- Structural edges only, not procedure or trigger call graphs.
- Unsupported families stay explicit in coverage and warnings.
- No persistent index is stored between runs.
- The local UI is still a thin inspector, not a graph explorer.
## Usage Example
1. **Load a package of C/AL files:**
```
Use cal_packages with action "load" to load C:/NAV/AllObjects.txt as packageId "nav-base"
```
2. **Inspect package stats:**
```
Use cal_packages with action "stats" for packageId "nav-base"
```
3. **Search for objects:**
```
Search for all tables starting with "Sales" in package nav-base
```
4. **Search for usages:**
```
Search for variable usages of ComplexMetadata in package nav-base
```
5. **Analyze dependencies:**
```
What references the Customer table?
```
6. **Find code patterns:**
```
Find all ERROR calls in codeunits
```
## Product Docs
- [Product Vision](docs/product/vision.md) - long-term direction and design principles
- [Product Roadmap](docs/product/roadmap.md) - prioritized next steps for the product
- [Improvement Backlog](docs/product/improvement-backlog.md) - important ideas, research areas, and candidate initiatives
## Development
```bash
# Install dependencies
bun install
# Run tests
bun test
# Run tests with watch
bun test --watch
# Type check
bun run type-check
# Build
bun run build
# Development mode (auto-rebuild)
bun run dev
```
## Project Structure
```
src/
├── index.ts # MCP server entry point
├── types/ # TypeScript interfaces
├── parser/ # C/AL parsers (8 object types)
├── core/
│ ├── symbol-database.ts # In-memory symbol storage
│ ├── reference-extractor.ts # Cross-reference analysis
│ └── file-loader.ts # File loading with streaming
└── tools/
└── mcp-tools.ts # MCP tool implementations
```
## Test Coverage
- **388 tests** passing
- **1 test** skipped
- **9000 assertions**
- All parsers, database, reference extraction, MCP tools, and UI/API flows covered
## License
MIT License - see [LICENSE](LICENSE) file for details.
## Author
Created by [marosgonda](https://github.com/marosgonda)
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
everything-claude-code
Complete Claude Code configuration collection - agents, skills, hooks,...
markitdown
MarkItDown-MCP is a lightweight server for converting URIs to Markdown.
cc-switch
All-in-One Assistant for Claude Code, Codex & Gemini CLI across platforms.
servers
Model Context Protocol Servers
servers
Model Context Protocol Servers
Time
A Model Context Protocol server for time and timezone conversions.