An MCP server that gives AI agents type-resolved Python navigation, backed by ty's language server. Definitions, references and call sites go through real type inference (imports, dependency-injected parameters, dataclass fields), not text grep.
The same resolution powers its edits: rename a symbol, change a signature, or move a function or module, and every usage follows, including overriding methods and injected dependencies. Edits are type-checked and previewable before they are written, and can be undone.
ty is installed with the package, so all you need is
uv (or pipx):
uvx codenav-mcpRegister it with your MCP host. For Claude Code, from the project root:
claude mcp add codenav -- uvx codenav-mcpOr add it to a project .mcp.json (Claude Code) or .cursor/mcp.json (Cursor):
{
"mcpServers": {
"codenav": {
"command": "uvx",
"args": ["codenav-mcp"],
"env": {
"CODENAV_MCP_SOURCE_ROOT": "src"
}
}
}
}In Cursor, also set "CODENAV_MCP_WORKSPACE": "${workspaceFolder}", because
Cursor may start MCP servers with your home directory as the working directory.
Start with the name-based tools:
| Tool | Answers |
|---|---|
symbol_info |
What is this? Header, hover, definition and references in one call |
outline |
What's in this file? (classes, methods, functions) |
callers |
Who actually calls this function? (call hierarchy, not imports) |
implementations |
Which classes structurally implement this Protocol? (type-checked) |
search_symbol |
Workspace symbol search by name (ranked, capped; optional kind= / path= filters; production code before tests; fuzzy-only hits summarised unless fuzzy=true) |
workspace |
Which directory is being navigated, and why |
Then use the position tools once you have a path:line:col:
| Tool | Answers |
|---|---|
hover |
Type and docs at a position |
definition |
Go to definition (resolves through injected parameters) |
references |
All usages across the workspace |
diagnostics |
ty type-check diagnostics for one file |
name and query are accepted as aliases on the name-based tools
(port_name / name / query on implementations). A missing or wrong
parameter gets a short hint back instead of a validation error. Dotted names
may nest (Outer.Inner.method). implementations also takes file_path to
pick one port when the name exists in several files, and counts inherited
members and dataclass/self.x fields toward a port's required names.
The navigation tools above are read-only. These change code, and share one rule: the edit is applied to in-memory copies first, ty re-checks the edited files and the files importing them, and the response says which diagnostics the edit would add or fix. Nothing is written until that is on the table.
| Tool | Does |
|---|---|
edit |
Type-checked text edit of a Python file: old_string/new_string or a whole new_text. For anything that is not a whole definition |
edit_symbol |
action="replace" / "insert" / "delete" a function, method or class by name: re-indented to the file's style, PEP 8 spacing, imports=[...] added, syntax-checked. Delete refuses while anything uses the symbol and prunes now-dead imports |
rename_symbol |
Rename across the workspace (imports, aliases, keyword arguments, parameter= for a function's parameter). Also renames what ty's rename misses: overriding methods, super().name() calls, and Protocol members together with the classes that satisfy them (linked=false to opt out). Lists remaining mentions of the old name (untyped accesses, strings, comments, docs; other worktrees are skipped) |
change_signature |
Add / remove / reorder parameters and rewrite every call site (found through ty's references, so injected dependencies and typed attributes work). Overrides follow. *args calls and uses as a value are listed as manual work |
move |
With name: move a module-level function or class to another file with the imports it needs; without it, move or rename the module file_path. Rewrites imports everywhere, drops imports only the moved code used, warns about import cycles |
quick_fix |
Apply ty's own fixes (missing imports) with correct placement; ambiguous fixes are listed instead of guessed |
verify_changes |
After editing with any tool: which type errors the working tree gained or lost since a git revision (default HEAD) |
apply_edit / undo_edit |
Write a previewed edit by id; revert an applied one |
One rule for every write tool: it writes when the edit adds no more than max_new_errors
(default 0) type errors in the edited files and their importers. Otherwise nothing is
written and you get the diagnostics plus an id for apply_edit. apply=false always
previews. Hint-level diagnostics (unused, deprecated) are not counted.
Safety rules, enforced in code:
- Only
.py/.pyifiles inside the workspace are written; virtualenvs,site-packages, typeshed and symlinks that leave the workspace are refused. - A preview remembers the exact bytes of every file;
apply_editrefuses if any file changed since. Files are replaced atomically, and a failure part-way restores the ones already written. - Every applied edit is undoable (
undo_edit) unless the files were changed after it. - Output is never written if it is not valid Python.
- Edits touching more than 40 files need
allow_large=true. CODENAV_MCP_READ_ONLY=1turns every write into a refusal (previews still work).
New files an edit would create are written to disk for the few moments of the type check (ty finds modules through the file system) and removed again, even on errors.
Positions are 1-indexed. column is a UTF-16 character offset (a leading
tab counts as one character).
Python only (.py / .pyi).
- The project's own
.venv/bin/ty(.venv\Scripts\ty.exeon Windows), so the ty version matches the project's pin and config. - The
tyinstalled alongside codenav-mcp. tyonPATH.uvx ty serveras a last resort.
| Variable | Default | Purpose |
|---|---|---|
CODENAV_MCP_WORKSPACE |
unset: follows the client's MCP roots when they name a worktree of the same git repository, else CLAUDE_PROJECT_DIR, else the working directory |
Pins the project root (never overridden). See the workspace tool |
CODENAV_MCP_SOURCE_ROOT |
whole workspace | Directory scanned for implementations candidates and used to derive dotted import paths (e.g. src) |
CODENAV_MCP_READ_ONLY |
unset | 1/true: write tools refuse to write (previews and checks still work) |
CODENAV_MCP_EXTRA_SOURCE_ROOTS |
none | Comma-separated directories (e.g. tests) that implementations also scans; matches (test doubles) are listed under a separate heading |
- Python ≥ 3.11
- Installed automatically:
mcp,ty,ruff(sorts imports in files a move touched, only where they were already sorted),mcp-nav-shared
webnav-mcp: the same kind of navigation for JS/TS/HTML/CSS.- Design notes (Protocol conformance probe, positioning, output formats): docs/agent-tooling.md.
Source: github.com/illescasDaniel/codenav-mcp. From a
checkout: uv sync --group dev, then uv run codenav-mcp.
MIT. See LICENSE.