mcp
The server wraps the same Kosha methods the CLI and daemon expose, over stdio. mcp ships with kosha, so no extra is needed. kosha indexes the current repo and its venv, so launch it from the project root with the project’s environment (uv run does both):
uv add --dev koshas
uv run kosha-mcp # stdio server; --http for Streamable HTTPIndex management
sync
def sync(
dir:str | None=None, pkgs:list | None=None, embed:bool=True, force:bool=False, sync_graph:bool=False
)->dict:Sync repo + env packages + call graph into the .kosha/ index. Incremental — only changed files and new package versions re-index. Returns post-sync status.
status
def status()->dict:Index freshness: {files, packages, graph_nodes, stale_files, stale_pkgs}. Call this first; if anything is stale, sync before querying (stale looks like missing).
Context search
where_to_add
def where_to_add(
description:str, limit:int=5
)->list:Likely file:line insertion points for new code matching a description, with co_dispatched peers to pattern-match.
env_context
def env_context(
query:str, limit:int=10
)->list:Semantic search over installed packages only — check whether a dependency already does something before implementing it. Bare package names in the query auto-detect as soft filters.
repo_context
def repo_context(
query:str, limit:int=10
)->list:Semantic + keyword search over indexed repo code only.
context
def context(
query:str, limit:int=10, repo:bool=True, env:bool=True, graph:bool=True, compact:bool=False
)->list:Fan-out semantic + keyword search over repo and installed packages, graph-enriched (callers/callees/pagerank). The right default once a task touches more than one module. compact=True returns slim dicts (mod_name, signature, docstring, lineno) for triage.
Call graph
pkg_url
def pkg_url(
pkg:str
)->str:Best repo/docs URL for an installed package — feed it to a web-fetch tool for docs, changelogs, or migration guides.
dep_stack
def dep_stack(
seeds:list | None=None, depth:int=1
)->list:BFS dependency layers from seed packages (default: pyproject deps), ordered by coupling strength.
api_paths
def api_paths(
from_pkg:str, to_pkg:str, k:int=15
)->dict:Shortest call-graph paths from one package’s public API to another’s — how two packages actually connect.
top_nodes
def top_nodes(
pkg:str, k:int=5
)->list:Top-k public API nodes for a package ranked by PageRank in the call graph.
public_api
def public_api(
pkg:str, limit:int=200
)->list:A package’s real public API — all entries plus @patch-derived methods — with docstrings. Works for a package (‘fastcore’) or submodule (‘fastcore.basics’).
short_path
def short_path(
src:str, tgt:str
)->list:Shortest call-graph path between two fully-qualified nodes (how do these connect?).
neighbors
def neighbors(
node:str, depth:int=1
)->list:Callers + callees of a fully-qualified node up to depth hops.
node_info
def node_info(
mod_name:str
)->dict:Callers, callees, co_dispatched peers, and pagerank for a fully-qualified node, e.g. fastcore.basics.merge. pagerank = blast radius: high means load-bearing, touch carefully.
main
def main():Entry point for the kosha-mcp console script. stdio by default; pass –http for Streamable HTTP.
Connect a client
The server must start at the project root with the project’s venv, so the launch command is uv run kosha-mcp rather than uvx.
Claude Code (run inside the project)
claude mcp add kosha -- uv run kosha-mcpCodex (~/.codex/config.toml; Codex launches servers from your session’s working directory, so start it at the project root)
[mcp_servers.kosha]
command = "uv"
args = ["run", "kosha-mcp"]Claude Desktop (claude_desktop_config.json — pin the project explicitly)
{"mcpServers": {"kosha": {"command": "uv", "args": ["run", "--project", "/path/to/your/repo", "kosha-mcp"]}}}