navigation-agent-mcp
README.md
# @navigation-agent/mcp-server
Workspace-only MCP server for structural code navigation and repository inspection. It exposes a stable public `code.*` tool surface for finding symbol definitions, tracing upstream callers for impact analysis, tracing downstream execution flow before logic changes, listing routes/endpoints, searching text, and inspecting workspace trees without opening files blindly.
**npm:** [`@navigation-agent/mcp-server`](https://www.npmjs.com/package/@navigation-agent/mcp-server)
---
## Installation
The server runs via `npx`.
### Requirements
- **Node.js 18+**
- **[ripgrep](https://github.com/BurntSushi/ripgrep)** (`rg`) — optional, only needed for `code.search_text`
### Claude Code
```bash
claude mcp add --transport stdio navigation-agent -- npx -y @navigation-agent/mcp-server
```
### OpenCode
Add to `~/.config/opencode/opencode.json`:
```json
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"navigation-agent": {
"type": "local",
"command": ["npx", "-y", "@navigation-agent/mcp-server"],
"enabled": true,
"timeout": 30000
}
}
}
```
### Gemini CLI
```bash
gemini mcp add navigation-agent npx -- -y @navigation-agent/mcp-server
```
Or add it manually to `~/.gemini/settings.json` or `.gemini/settings.json`:
```json
{
"mcpServers": {
"navigation-agent": {
"command": "npx",
"args": ["-y", "@navigation-agent/mcp-server"],
"timeout": 30000
}
}
}
```
Use the hyphenated server name `navigation-agent`. Avoid underscores in Gemini MCP server names because Gemini derives fully-qualified tool names from the server name.
### Cursor
Add to `~/.cursor/mcp.json` or `.cursor/mcp.json`:
```json
{
"mcpServers": {
"navigation-agent": {
"command": "npx",
"args": ["-y", "@navigation-agent/mcp-server"]
}
}
}
```
### OpenAI Codex
```bash
codex mcp add navigation-agent -- npx -y @navigation-agent/mcp-server
```
Or add to `~/.codex/config.toml`:
```toml
[mcp_servers.navigation-agent]
command = "npx"
args = ["-y", "@navigation-agent/mcp-server"]
startup_timeout_sec = 30
tool_timeout_sec = 60
```
### Workspace root
By default the server analyzes the current working directory. To pin a specific project, set `NAVIGATION_MCP_WORKSPACE_ROOT` in your MCP config.
---
## Agent usage guide
This server is built for **model-controlled MCP tool use**: agents should discover and invoke its tools before opening source files when the task is about workspace code structure.
It publishes both per-tool descriptions and server instructions so MCP clients can teach the model the workflow without relying on a private skill registry.
### How agents learn to use it
MCP clients give the model guidance through a few standard channels:
| Channel | What this server provides |
|---|---|
| MCP `initialize.result.instructions` | A concise workflow: use navigation before reading files, which tool to pick, supported languages/frameworks, and workspace-only limits. |
| Tool descriptions and input schemas | Each `code.*` tool explains when to use it and lists supported `language` / `framework` filters. |
| Structured tool results | Every tool returns the stable envelope `tool`, `status`, `summary`, `data`, `errors`, and `meta` so agents can chain outputs safely. |
| Optional client rules/skills | Clients such as OpenCode, Codex, Cursor, and Gemini can add project rules, but this server does not require a private registry to be useful. |
The important part: the server instructions are part of the MCP handshake, so clients that honor MCP instructions can inject them into the model before tool selection.
`skills/navigation-mcp/SKILL.md` is an optional portable skill template for clients that support skills. It is not required for normal MCP operation; for OpenCode specifically, skills are discovered from `.opencode/skills/<name>/SKILL.md`, global OpenCode skills, or Claude/agents-compatible skill directories.
### Quick path for agents
1. Use `code.inspect_tree` to orient in an unknown module or directory without reading files.
2. Use `code.find_symbol` when you know a class, function, method, type, enum, or annotation name but not the defining file.
3. Pass `find_symbol`'s returned `items[].path` into:
- `code.trace_callers` for upstream impact: **who calls this?**
- `code.trace_flow` for downstream behavior: **what does this call or reach?**
4. Use `code.list_endpoints` before changing REST, GraphQL, or route surfaces.
5. Use `code.search_text` for textual patterns, imports, decorators, or when symbol lookup is not enough.
6. Read only the relevant files returned by the navigation tools.
### Fallbacks agents should use
| Situation | Correct fallback |
|---|---|
| `code.find_symbol` returns zero for constants, config keys, decorators, imports, or generated names | Use `code.search_text` scoped by `path`, `include`, and `language`. |
| A trace result is too broad or noisy | Narrow `path`, `language`, `framework`, or `symbol`; for `trace_callers`, lower `max_depth`. |
| A route or endpoint inventory returns zero | Retry with a narrower `path` and the most specific `framework` or `kind` before concluding there is no public surface. |
| A navigation result has `truncated: true` | Narrow the query before reading files or increasing `limit`. |
Do not treat an empty result as proof by itself. Use one scoped fallback, then explain the limitation if results still stay empty.
### Tool naming in clients
The canonical public contract is `code.*`. Some clients expose MCP tools with a server prefix or normalized separators, for example `navigation-agent_code_find_symbol` or `mcp_navigation-agent_code.find_symbol`. Treat those names as aliases of the same canonical tools.
Use `navigation-agent` as the server name in examples. It is readable, avoids collisions, and avoids underscore-related parser issues in clients that derive fully-qualified tool names from the server id.
### Client convention notes
| Client | Convention checked |
|---|---|
| Claude Code | Local stdio command uses `claude mcp add --transport stdio <name> -- <command> <args...>`. Server instructions help Claude's MCP tool search decide when to load these tools. |
| OpenCode | Local MCP servers live under the `mcp` config key with `type: "local"` and `command` as an array. MCP tools are exposed with a server-name prefix, so prompts/rules can say “use `navigation-agent`”. |
| Gemini CLI | MCP servers live under `mcpServers`; stdio uses `command` + `args`. Gemini appends MCP server instructions to system instructions and assigns names like `mcp_{serverName}_{toolName}`. |
| Cursor | MCP servers are configured in `mcp.json` with `command` + `args` for stdio or `url` + `headers` for remote servers. |
| OpenAI Codex | MCP servers live under `[mcp_servers.<name>]` in `config.toml`; `codex mcp add <name> -- <command>` is the CLI form. |
### Supported filters agents should know
- Languages: `typescript`, `javascript`, `go`, `java`, `php`, `python`, `rust`, `csharp`
- Frameworks: `react-router`, `spring`
Do not use this MCP for web search, external repositories, arbitrary filesystem access, or reading file contents. It is a workspace-only navigation layer.
---
## Compatibility matrix
This table MUST stay in the README because it is the fastest way to understand the public support surface. It is intentionally organized with **languages as rows** and **tools as columns** so adding more languages grows downward instead of widening the table.
Tool columns omit the `code.` prefix to keep the matrix readable.
| Language | `inspect_tree` | `find_symbol` | `search_text` | `list_endpoints` | `trace_flow` | `trace_callers` |
|---|---:|---:|---:|---:|---:|---:|
| Java | ✅ | ✅ | ✅ | ✅ Spring REST/GraphQL | ✅ | ✅ |
| TypeScript | ✅ | ✅ | ✅ | ✅ React Router | ✅ | ✅ |
| JavaScript | ✅ | ✅ | ✅ | ✅ React Router | ✅ | ✅ |
| PHP | ✅ | ✅ | ✅ | ⚠️ Public, not re-verified for endpoints | ✅ | ✅ |
| Python | ✅ | ✅ | ✅ | ✅ FastAPI/Flask-style decorators | ✅ | ✅ |
| Rust | ✅ | ✅ | ✅ | ⚠️ Target-dependent / non-web target returned zero | ✅ Qualified symbols | ✅ Qualified symbols |
| Go | ✅ | ✅ | ✅ | ⚠️ No useful endpoint inventory in current example | ✅ | ✅ |
| C# | ✅ | ✅ | ✅ | ⚠️ Stub implementation | ✅ | ✅ |
Legend:
- ✅ = verified in a real project during this documentation sync
- ⚠️ = publicly exposed, but not re-verified in this pass, not meaningful on the chosen validation project, or still has caveats
- ❌ = not working as public support today
- `code.inspect_tree` and `code.search_text` also work without a language filter across general workspace files.
Important:
- Public language filters are `typescript`, `javascript`, `go`, `java`, `php`, `python`, `rust`, and `csharp`.
- Go, PHP, Python, Rust, Java, TypeScript, JavaScript, and C# are part of the public contract; the matrix above shows current verification level per tool.
- Rust trace tools work well, but method/impl symbols should be queried with their qualified name (for example `JavaProjectIndex::build`).
---
## Public tools
The public contract exposes exactly these six tools:
- `code.inspect_tree`
- `code.list_endpoints`
- `code.find_symbol`
- `code.search_text`
- `code.trace_flow`
- `code.trace_callers`
Use `snake_case` parameters such as `max_depth`, `include_hidden`, and `file_pattern`.
### Before changing a function or method
Use this workflow whenever you need to understand behavior or impact inside the workspace:
1. `code.find_symbol` — resolve the exact defining file first.
2. `code.trace_callers` — inspect upstream impact before renaming, deleting, or changing a signature.
3. `code.trace_flow` — inspect downstream execution before changing logic.
4. `read` only the files returned by the trace results that actually matter.
Rule of thumb:
- choose `code.trace_callers` for **who depends on this?**
- choose `code.trace_flow` for **what does this reach or invoke?**
- if you need both impact and behavior, run both before editing
Concrete workspace example:
1. Resolve the symbol definition:
```json
{
"symbol": "create_order",
"language": "python",
"kind": "function",
"path": "examples/python"
}
```
2. Inspect upstream impact before changing the function:
```json
{
"path": "examples/python/app/api/endpoints.py",
"symbol": "create_order",
"language": "python",
"recursive": true,
"max_depth": 3
}
```
3. Inspect downstream behavior before changing the logic:
```json
{
"path": "examples/python/app/api/endpoints.py",
"symbol": "create_order",
"language": "python"
}
```
Expected agent behavior:
- use `code.find_symbol` first when the defining file is not already known
- use `code.trace_callers` first when the risk is breaking callers
- use `code.trace_flow` next when the risk is changing downstream behavior
- only then `read` the traced files you actually need
React Router example:
```json
{
"symbol": "action",
"kind": "function",
"framework": "react-router",
"path": "app/routes"
}
```
```json
{
"path": "app/routes/change-password.tsx",
"symbol": "action",
"framework": "react-router",
"recursive": true,
"max_depth": 2
}
```
```json
{
"path": "app/routes/change-password.tsx",
"symbol": "action",
"framework": "react-router"
}
```
### `code.search_text` response style
`code.search_text` is optimized for agents:
- results are grouped by file
- each match returns only `line` plus exact `spans`
- `topFiles` highlights the densest files first
- contextual `before` / `after` text is intentionally omitted from the public response to reduce noise and token cost
Example shape:
```json
{
"fileCount": 3,
"matchCount": 19,
"totalFileCount": 3,
"totalMatchCount": 19,
"topFiles": [
{
"path": "examples/go/internal/http/handlers/user_handler.go",
"language": "go",
"matchCount": 11
}
],
"items": [
{
"path": "examples/go/internal/http/handlers/user_handler.go",
"language": "go",
"matchCount": 11,
"matches": [
{
"line": 28,
"spans": [{ "colInit": 23, "colEnd": 32 }]
}
]
}
]
}
```
### Quick examples
```json
{
"symbol": "RootUserGraphQLController",
"language": "java",
"kind": "class"
}
```
```json
{
"path": "app/routes/change-password.tsx",
"symbol": "action",
"framework": "react-router"
}
```
```json
{
"path": "src/main/java/com/example/FooController.java",
"symbol": "getFoo",
"framework": "spring"
}
```
---
## Verified real-world behavior
These checks were verified against real projects instead of toy stubs:
### Java (`~/sias/app/back`)
- `code.inspect_tree` works on real module trees
- `code.find_symbol` works on real Spring classes
- `code.search_text` works on real Java source
- `code.list_endpoints` inventories framework-detectable Spring REST controllers and GraphQL resolvers as likely public entrypoints
- `code.trace_flow` works on real controller/resolver entrypoints
- `code.trace_callers` works on Java use cases and can identify probable public entrypoints
Verified example:
- `RootUserGraphQLController#getUsersByDependency`
- traced into `RootGetUserUseCase#getUsers`
### TypeScript / React Router (`~/sias/app/front`)
- `code.inspect_tree` works on real route trees
- `code.find_symbol` works on route-module exports
- `code.search_text` works on real route files
- `code.list_endpoints` inventories React Router route-module `loader` / `action` exports as likely route entrypoints
- `code.trace_flow` works for same-file route flow extraction
- `code.trace_callers` works for same-file helpers and marks route exports as probable entrypoints
Verified example:
- `app/routes/change-password.tsx#action`
- found calls to `getUserIdAndTokenFromSession`, `changeMyPassword`, `getSession`, `commitSession`, `getRoleRoute`
- reverse-traced `getRoleRoute <- action`
### Python (`examples/python`)
- `code.inspect_tree` works on Python module trees
- `code.find_symbol` works on Python classes, functions, and methods
- `code.search_text` works on Python source files
- `code.list_endpoints` inventories FastAPI-style route decorators (`@router.get`, `@app.post`, etc.) as likely public API entrypoints
- `code.trace_flow` works end-to-end for multi-module Python scenarios, capturing deep recursive trees including branching calls, instance methods (`self`), and cross-file resolution.
- `code.trace_callers` works end-to-end for impact analysis, supporting recursive reverse-tracing across the entire workspace.
Verified example (Forward Trace):
- `app/api/endpoints.py#create_order`
- traced into `order_service.process_order(...)` -> `_handle_payment(...)` -> `payment_service.authorize_payment(...)`
- deep tree captures cross-file calls to `AuditService`, `InventoryService`, `ProductRepository`, etc.
Verified example (Backward Trace):
- `app/services/audit.py#log_action`
- reverse-traced to callers in `UserService`, `OrderService`
- recursively identifies entrypoints in `app/api/endpoints.py` (`get_user`, `create_order`)
### PHP (`examples/php`)
- `code.inspect_tree` works on PHP project trees
- `code.find_symbol` works on PHP classes and methods
- `code.search_text` works on PHP source files
- `code.trace_flow` works end-to-end for PHP service-to-repository calls
- `code.trace_callers` works end-to-end for PHP impact analysis
Notes:
- `code.list_endpoints` is publicly exposed for PHP but was not re-verified for useful endpoint inventory in the current example
Verified example:
- `src/Service/UserService.php#UserService::persistUser`
- traced `$this->repository->save($user)` to `src/Repository/MemoryUserRepository.php#save`
### Rust (this repository)
- `code.inspect_tree` works on real Rust source trees
- `code.find_symbol` works on Rust types/functions
- `code.search_text` works on real Rust source
- `code.trace_flow` works on real Rust methods when queried with the correct qualified symbol
- `code.trace_callers` works on real Rust methods when queried with the correct qualified symbol
Notes:
- `code.list_endpoints` returned zero results on this repository, which is expected for the chosen validation target because it is not a Rust web app
Verified example:
- `crates/navigation-engine/src/capabilities/trace_flow.rs#JavaProjectIndex::build`
- traced `Self::new_empty()`, `index.scan_project(workspace_root)`, and `index.is_empty()`
- reverse-traced `JavaProjectIndex::scan_project <- JavaProjectIndex::build`
### C# (`./examples/csharp`)
- `code.inspect_tree` works
- `code.search_text` works
- `code.find_symbol` works for method lookup such as `OrderWorkflowService.ProcessOrderAsync`
- `code.trace_flow` works end-to-end on the example app and returns the recursive internal call tree
- `code.trace_callers` works end-to-end on the example app
### Go (`./examples/go`)
Real behavior today against `examples/go`:
- `code.inspect_tree` works
- `code.search_text` works
- `code.find_symbol` works for method lookup such as `CreateUser`
- `code.trace_flow` works end-to-end on the example app and returns the recursive internal call tree
- `code.trace_callers` works end-to-end on the example app, including callback/method-value references and interface-to-implementation reverse matches
- `code.list_endpoints` still returns no useful entrypoint inventory for the current Go example
---
## Public language and framework filters
Current public language filters:
- `typescript`
- `javascript`
- `go`
- `java`
- `php`
- `python`
- `rust`
- `csharp`
Current public framework filters:
- `react-router`
- `spring`
---
## Response shape
Every tool returns the same top-level envelope:
```json
{
"tool": "code.trace_flow",
"status": "ok",
"summary": "Traced 5 callees for 'action' from 'app/routes/change-password.tsx'.",
"data": {},
"errors": [],
"meta": {
"query": {},
"resolvedPath": "app/routes/change-password.tsx",
"truncated": false,
"counts": {},
"detection": {}
}
}
```
Status meanings:
- `ok` — request succeeded, including zero-result success
- `partial` — request succeeded but was truncated/pruned
- `error` — request failed and includes stable error codes
Notes:
- `code.trace_flow` returns a rooted recursive tree under `data.root`
- `code.trace_callers` returns direct callers plus recursive reverse-trace metadata
- `code.search_text` returns compact grouped matches and `topFiles`, not full context blocks
---
## Architecture
This repository has two main layers:
1. **TypeScript MCP runtime** (`packages/mcp-server/`)
- validates the public `code.*` contract
- exposes stdio / stdio-legacy transports
- normalizes responses
2. **Rust engine** (`crates/navigation-engine/`)
- parses source with tree-sitter
- hosts language analyzers
- includes internal AST/debug binaries under `crates/navigation-engine/src/bin/`
Important:
- `packages/mcp-server/src/bin/` contains the runtime entrypoint (`navigation-mcp.ts`)
- AST inspection/debug binaries live in `crates/navigation-engine/src/bin/`, not in the TypeScript runtime
---
## Contributing / local development
Key local commands:
```bash
npm install
npm --workspace @navigation-agent/mcp-server run check
npm --workspace @navigation-agent/mcp-server run test
cargo test --manifest-path crates/navigation-engine/Cargo.toml
```
Useful local runtime checks:
```bash
npx tsx packages/mcp-server/src/bin/navigation-mcp.ts --describe-tools
npx tsx packages/mcp-server/src/bin/navigation-mcp.ts --transport stdio-legacy --workspace-root /path/to/workspace
```
## License
MIT
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues