MCP Hub
Back to servers

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.

glama
Updated
Mar 30, 2026

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
  • uv project 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 tools
  • docs/v1-summary.md — shipped V1 surface, limitations, and tradeoffs
  • docs/release-checklist.md — future release checklist
  • docs/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:

  • python 3.12+
  • uv
  • ripgrep
  • opencode (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:

  1. Install python, uv, ripgrep, and opencode
  2. Clone this repository
  3. Run uv tool install .
  4. Copy or recreate the opencode.json MCP entry
  5. 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 payload
  • resolvedPath: workspace-relative resolved scope when a path was provided
  • truncated: true when data was truncated or safety-pruned
  • counts: stable machine-readable count metadata
  • detection: 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

FieldTypeRequiredNotes
symbolstringyesSymbol name to locate
languagetypescript | javascript | javanoOptional language filter
frameworkreact-router | springnoOptional framework hint; can infer language
kindany | class | interface | function | method | type | enum | constructor | annotationnoStable public kind filter
matchexact | fuzzynoMatch mode; default exact
pathstringnoWorkspace-relative or absolute scope
limitintegernoDefault 50, max 200

Data semantics

  • count: compatibility field; equals totalMatched
  • returnedCount: number of returned items
  • totalMatched: full matched count before limit truncation
  • items[*].kind: normalized to the stable public symbol kinds above

code.inspect_tree

Inspect the workspace tree without reading file contents.

Input

FieldTypeRequiredNotes
pathstringnoWorkspace-relative or absolute file/directory scope
max_depthintegernoDefault 3, max 20
extensionsstring[]noFile extension filter; directories remain visible
file_patternstringnoFilename glob such as *.py
include_statsbooleannoInclude stat metadata
include_hiddenbooleannoInclude hidden entries except hard-ignored directories

Data semantics

  • entryCount: number of returned entries
  • meta.counts.returnedCount: same as entryCount
  • meta.counts.totalMatched: present only when known; omitted as null when the safety cap prevents a full count

code.list_endpoints

List backend endpoints and frontend routes in the workspace.

Input

FieldTypeRequiredNotes
pathstringnoWorkspace-relative or absolute scope
languagetypescript | javascript | javanoOptional language filter
frameworkreact-router | springnoOptional framework hint
kindany | graphql | rest | routenoStable public kind filter
limitintegernoDefault 50, max 200

Data semantics

  • totalCount: full matched count before limit truncation
  • returnedCount: number of returned items
  • counts.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

FieldTypeRequiredNotes
querystringyesPlain text or regex pattern
pathstringnoWorkspace-relative or absolute scope
languagetypescript | javascript | javanoOptional language filter
frameworkreact-router | springnoOptional framework hint
includestringnoAdditional include glob such as *.tsx or src/**
regexbooleannoDefault false
contextintegernoDefault 1, max 10
limitintegernoDefault 50, max 200

Data semantics

  • fileCount: returned matched file count
  • matchCount: returned matched line count
  • totalFileCount: full matched file count before limit truncation
  • totalMatchCount: full matched line count before limit truncation

code.trace_symbol

Trace a symbol forward from a known starting file.

Input

FieldTypeRequiredNotes
pathstringyesWorkspace-relative or absolute starting file path
symbolstringyesSymbol/function/method name to trace forward
languagetypescript | javascript | javanoOptional language hint
frameworkreact-router | springnoOptional framework hint

Data semantics

  • fileCount: returned related file count
  • meta.counts.returnedCount: same as fileCount

code.trace_callers

Trace incoming callers from a known starting file.

Input

FieldTypeRequiredNotes
pathstringyesWorkspace-relative or absolute starting file path
symbolstringyesSymbol/function/method name to trace incoming callers for
languagetypescript | javascript | javanoOptional language hint
frameworkreact-router | springnoOptional framework hint
recursivebooleannoEnable recursive reverse traversal
max_depthintegernoOnly used with recursive=true; default 3, min 1, max 8

Data semantics

  • count: compatibility field; equals returnedCount
  • returnedCount: direct caller count returned in items
  • 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 results
  • partial: request succeeded with truncated or safety-pruned data
  • error: request could not be completed

Error semantics

Errors are always structured with:

  • code: stable machine-readable code
  • message: human-readable explanation
  • retryable: whether retrying may help
  • suggestion: concrete next step for the caller

Coverage notes

  • find_symbol, trace_symbol, and trace_callers currently cover Java and TypeScript-family source files supported by the internal analyzers
  • search_text is 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
  }
}

Reviews

No reviews yet

Sign in to write a review