navigation-agent-mcp
A minimal MCP server providing a normalized API for code navigation and repository inspection through tools like symbol searching and call tracing. It enables AI agents to perform structured analysis of codebases by mapping endpoints, inspecting file trees, and tracing symbol relationships.
README
navigation-agent-mcp
Minimal MCP server scaffold focused on code navigation and repository inspection.
Purpose
This project exposes a public, normalized MCP API for analysis tools under the code.* namespace.
V1 scope:
- Navigation / analysis / inspection only
- First public tool:
code.find_symbol - Tree inspection tool:
code.inspect_tree - Endpoint index tool:
code.list_endpoints - Forward trace tool:
code.trace_symbol - Reverse trace tool:
code.trace_callers - Text search tool:
code.search_text - Structured responses for agent-friendly automation
Tech Stack
- Python
uvproject layout- Official MCP Python SDK (
FastMCP, v1.x style) - Pydantic models for normalized contracts
Project Layout
src/navigation_mcp/
├── adapters/internal_tools/ # wrappers around existing internal analyzers
├── contracts/ # public request/response models
├── services/ # application orchestration and normalization
├── tools/ # public MCP tool registration
├── app.py # FastMCP assembly
└── server.py # CLI entrypoint
Documentation
docs/overview.md— product scope, philosophy, and public toolsdocs/v1-summary.md— shipped V1 surface, limitations, and tradeoffsdocs/release-checklist.md— future release checklistdocs/testing.md— test layout and commands
Run
Stdio
uv run navigation-mcp --transport stdio
Streamable HTTP
uv run navigation-mcp --transport streamable-http --host 127.0.0.1 --port 8000 --path /mcp
Optional environment variables
NAVIGATION_MCP_WORKSPACE_ROOT: workspace root to analyze. Defaults to the current working directory.NAVIGATION_MCP_FIND_SYMBOL_SCRIPT: override the internal adapter script path.NAVIGATION_MCP_LIST_ENDPOINTS_SCRIPT: override the internal list_endpoints adapter script path.NAVIGATION_MCP_TRACE_CALLERS_SCRIPT: override the internal trace_callers adapter script path.NAVIGATION_MCP_TRACE_SYMBOL_SCRIPT: override the internal trace_symbol adapter script path.
Test in local environments
If you want to try this MCP on another PC with the same OpenCode setup, the simplest path is to install it as a user command and then register it in OpenCode.
Required programs
You need these programs installed on the machine:
python3.12+uvripgrepopencode(if you want to use it from OpenCode)
Install on Arch Linux
sudo pacman -S python uv ripgrep opencode
If opencode is not available in your environment yet, install it using the official OpenCode method described in their docs.
Install this MCP locally
From the root of this repository:
uv tool install .
If it was already installed and you want to refresh it after changes:
uv tool install --reinstall .
This installs the navigation-mcp command in your user environment.
Quick local verification
Check that the command is available:
navigation-mcp --help
You can also start it manually over stdio:
navigation-mcp --transport stdio
Enable it in OpenCode
Add this to ~/.config/opencode/opencode.json on the target machine:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"navigation": {
"type": "local",
"command": ["navigation-mcp", "--transport", "stdio"],
"enabled": true
}
}
}
If you want to pin the analyzed workspace explicitly, add an environment variable:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"navigation": {
"type": "local",
"command": ["navigation-mcp", "--transport", "stdio"],
"enabled": true,
"environment": {
"NAVIGATION_MCP_WORKSPACE_ROOT": "/path/to/workspace"
}
}
}
}
Verify OpenCode detects it
opencode mcp list
Then open OpenCode in your project and ask it to use the navigation MCP for code analysis.
Repeat on another PC
If the other machine uses the same OpenCode configuration style, the migration steps are:
- Install
python,uv,ripgrep, andopencode - Clone this repository
- Run
uv tool install . - Copy or recreate the
opencode.jsonMCP entry - Copy your project skill/rules if you want the same behavior defaults
Public Tool Contract
All tools return the same envelope shape:
{
"tool": "code.find_symbol",
"status": "ok",
"summary": "Found 2 symbol definitions for 'loader'.",
"data": {},
"errors": [],
"meta": {
"query": {},
"resolvedPath": null,
"truncated": false,
"counts": {},
"detection": {}
}
}
Stable meta contract
query: normalized request payloadresolvedPath: workspace-relative resolved scope when apathwas providedtruncated:truewhen data was truncated or safety-prunedcounts: stable machine-readable count metadatadetection: normalized derived metadata such as effective language/framework when meaningful
Stable path semantics
If a scoped path is provided and it does not exist inside the configured workspace, the tool returns status: "error" with FILE_NOT_FOUND.
If the provided path resolves outside the workspace, the tool returns status: "error" with PATH_OUTSIDE_WORKSPACE.
code.find_symbol
Find symbol definitions in the workspace.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
symbol |
string | yes | Symbol name to locate |
language |
typescript | javascript | java |
no | Optional language filter |
framework |
react-router | spring |
no | Optional framework hint; can infer language |
kind |
any | class | interface | function | method | type | enum | constructor | annotation |
no | Stable public kind filter |
match |
exact | fuzzy |
no | Match mode; default exact |
path |
string | no | Workspace-relative or absolute scope |
limit |
integer | no | Default 50, max 200 |
Data semantics
count: compatibility field; equalstotalMatchedreturnedCount: number of returned itemstotalMatched: full matched count before limit truncationitems[*].kind: normalized to the stable public symbol kinds above
code.inspect_tree
Inspect the workspace tree without reading file contents.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
path |
string | no | Workspace-relative or absolute file/directory scope |
max_depth |
integer | no | Default 3, max 20 |
extensions |
string[] | no | File extension filter; directories remain visible |
file_pattern |
string | no | Filename glob such as *.py |
include_stats |
boolean | no | Include stat metadata |
include_hidden |
boolean | no | Include hidden entries except hard-ignored directories |
Data semantics
entryCount: number of returned entriesmeta.counts.returnedCount: same asentryCountmeta.counts.totalMatched: present only when known; omitted asnullwhen the safety cap prevents a full count
code.list_endpoints
List backend endpoints and frontend routes in the workspace.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
path |
string | no | Workspace-relative or absolute scope |
language |
typescript | javascript | java |
no | Optional language filter |
framework |
react-router | spring |
no | Optional framework hint |
kind |
any | graphql | rest | route |
no | Stable public kind filter |
limit |
integer | no | Default 50, max 200 |
Data semantics
totalCount: full matched count before limit truncationreturnedCount: number of returned itemscounts.byKind|byLanguage|byFramework: grouped counts across the full matched set
Backend-specific subtypes are normalized to the stable public kinds:
- GraphQL queries/mutations →
graphql - REST verbs and request mappings →
rest - React Router loaders/actions/layouts/resource routes/components →
route
code.search_text
Search plain text or regex patterns across workspace files.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
query |
string | yes | Plain text or regex pattern |
path |
string | no | Workspace-relative or absolute scope |
language |
typescript | javascript | java |
no | Optional language filter |
framework |
react-router | spring |
no | Optional framework hint |
include |
string | no | Additional include glob such as *.tsx or src/** |
regex |
boolean | no | Default false |
context |
integer | no | Default 1, max 10 |
limit |
integer | no | Default 50, max 200 |
Data semantics
fileCount: returned matched file countmatchCount: returned matched line counttotalFileCount: full matched file count before limit truncationtotalMatchCount: full matched line count before limit truncation
code.trace_symbol
Trace a symbol forward from a known starting file.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
path |
string | yes | Workspace-relative or absolute starting file path |
symbol |
string | yes | Symbol/function/method name to trace forward |
language |
typescript | javascript | java |
no | Optional language hint |
framework |
react-router | spring |
no | Optional framework hint |
Data semantics
fileCount: returned related file countmeta.counts.returnedCount: same asfileCount
code.trace_callers
Trace incoming callers from a known starting file.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
path |
string | yes | Workspace-relative or absolute starting file path |
symbol |
string | yes | Symbol/function/method name to trace incoming callers for |
language |
typescript | javascript | java |
no | Optional language hint |
framework |
react-router | spring |
no | Optional framework hint |
recursive |
boolean | no | Enable recursive reverse traversal |
max_depth |
integer | no | Only used with recursive=true; default 3, min 1, max 8 |
Data semantics
count: compatibility field; equalsreturnedCountreturnedCount: direct caller count returned initems- Recursive mode is opt-in
- Recursive payloads may be safety-pruned; when that happens the tool returns
status: "partial",RESULT_TRUNCATED, and the recursive summary counts remain authoritative even if arrays were sliced
Status semantics
ok: request succeeded, including zero resultspartial: request succeeded with truncated or safety-pruned dataerror: request could not be completed
Error semantics
Errors are always structured with:
code: stable machine-readable codemessage: human-readable explanationretryable: whether retrying may helpsuggestion: concrete next step for the caller
Coverage notes
find_symbol,trace_symbol, andtrace_callerscurrently cover Java and TypeScript-family source files supported by the internal analyzerssearch_textis powered by ripgrep behind the normalized public contract- Internal analyzer paths, commands, raw payloads, and local implementation details are intentionally NOT exposed in the public contract
Sample usage
{
"name": "code.search_text",
"arguments": {
"query": "useLoaderData",
"language": "typescript",
"include": "app/routes/**/*.tsx",
"context": 1,
"limit": 20
}
}
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。