Ama
Ama is a local-first code-intelligence server that speaks the Model Context Protocol (MCP). It parses a codebase into a queryable knowledge graph of symbols and their relationships, then hands that graph to AI coding agents — so an agent answers "who calls this?", "what breaks if I change this?", and "which tests cover this route?" in a single tool call instead of dozens of file reads.
Named after a puppy: small, eager, and a little smarter every day.
Status: 0.5 — deep TypeScript analysis; deep Java source semantics with bytecode support and honest baseline fallback; a baseline call graph for Python (incl. FastAPI route→handler→test impact); framework-route detection across TypeScript, Python, Go, PHP, Java, and Rust; syntactic breadth for a dozen more languages; an embeddable library API; 27 MCP tools; an
amaCLI (with self-update viaama upgrade); persistent, auto-syncing incremental indexing; and self-contained, no-Node install bundles for macOS/Linux/Windows — all able to index Ama's own source cleanly as the built-in regression test.
1. Install Ama — a self-contained bundle (no Node needed) or via npm (Node 24+):
# macOS / Linux — self-contained, no Node required
curl -fsSL https://raw.githubusercontent.com/mka-rainmaker/ama/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/mka-rainmaker/ama/main/install.ps1 | iex
# …or, if you already have Node 24+
npm install -g @mka-rainmaker/ama
2. Wire it into your agent:
ama install # auto-configures Claude Code / Cursor / Windsurf / Codex
3. Ask your agent structural questions — "who calls createServer?", "what breaks if I change AmaSession?" It runs index_repository once, then queries the graph; Ama re-indexes automatically as you edit.
(No install at all? npx -y @mka-rainmaker/ama mcp works as the MCP command — see Configure your coding agent.)
Why Ama
Most code-graph tools parse every language the same way: one fast, syntactic pass that sees shapes but not meaning. Ama takes a different bet — use each language's real compiler when one exists.
- Deep tier — language-specific semantic analysis. TypeScript via the TypeScript Compiler API today; Java via a TypeScript source-semantic resolver plus JVM bytecode/classfile parsing; .NET via Roslyn next. This resolves types, overloads, imports/re-exports, interface dispatch, and cross-file symbol binding that a purely syntactic parser cannot.
- Baseline tier — universal syntactic analysis for breadth. Every other language gets parsed for structure (and, where it pays off, a heuristic call graph), so the whole repo is navigable from day one.
Every answer reports which tier produced it, so partial coverage never quietly masquerades as complete. And because it's built for agents:
- 100% local. No external APIs, no API keys, no telemetry. Your code never leaves your machine.
- Cheaper and faster. Fewer tool calls and fewer tokens per question — one graph query replaces a pile of file reads.
Language support
Each analyzer declares a tier, and every result is tagged with the tier that produced it — so partial coverage never looks complete. Deep = semantic, via a compiler API, bytecode, or a language-specific resolver (resolves types, overloads, imports, dispatch). Baseline = syntactic, via tree-sitter, with a heuristic call/type graph where it pays off.
| Language | Tier | Symbols | Imports | Call graph | Type hierarchy | Type usage | Routes |
|---|---|---|---|---|---|---|---|
TypeScript (.ts, .tsx) |
deep |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Prisma (.prisma) |
deep |
✓ | — | — | — | ✓ | — |
Java (.java) |
deep |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Python (.py) |
baseline |
✓ | ✓ | ✓ ¹ | — | — | ✓ |
Go (.go) |
baseline |
✓ | ✓ | — | — | — | ✓ |
PHP (.php) |
baseline |
✓ | ✓ | — | — | — | ✓ |
Rust (.rs) |
baseline |
✓ | ✓ | — | — | — | ✓ |
JavaScript (.js, .jsx) |
baseline |
✓ | ✓ | — | — | — | — |
C# (.cs) |
baseline |
✓ | ✓ | — | — | — | — |
Kotlin (.kt) |
baseline |
✓ | ✓ | — | — | — | — |
C / C++ (.c, .cpp, .h) |
baseline |
✓ | ✓ | — | — | — | — |
Vue / Svelte (SFC <script>) |
baseline |
✓ | ✓ | — | — | — | — |
Swift (.swift) |
baseline |
✓ | — | — | — | — | — |
¹ Heuristic call graph — resolves within-file calls by name and cross-file calls through the import graph. An empty find_callers on baseline-tier code can mean "not resolved," not "none."
Java is now a deep-tier analyzer: the default path is implemented in TypeScript and builds a source type/method index over tree-sitter, resolving in-repo package/import names and exact overloaded calls where receiver and argument types are known. A pure TypeScript JVM classfile reader backs the bytecode path for compiled classes. When neither semantic path can produce a reliable result for a project, Ama returns the Java baseline result and tags that result as baseline instead of overstating coverage. Generics, full Java overload conversion rules, and external JAR/source-root resolution are still growing toward full compiler parity.
Benchmarks
On Ama's own repo (5 representative questions): ~99% fewer tokens and ~96% fewer tool calls to obtain an answer than the grep-then-read an agent falls back to without a graph — one focused, structured result instead of pulling whole files into context. That headline is a ceiling (the baseline reads every grep-matching file in full); docs/benchmarks has the methodology and honest caveats, and node scripts/benchmark.mjs reproduces it.
How auto-sync works
Ama keeps the graph current while connected, so you never run a manual sync:
- A native file watcher debounces bursts of edits, then re-indexes each changed file in place (no full rebuild).
- On reconnect, Ama reconciles anything that changed while it was away (size/mtime + content-hash diff).
index_status()reports what's indexed, per-language coverage + tier, and how many edits are pending.
Framework-aware routes
Ama maps HTTP routes to their handlers across stacks — always as a Route → handler reference, never a fabricated call — so "who handles POST /reports?" is one query:
| Stack | Tier | Frameworks |
|---|---|---|
| TypeScript | deep |
Express, NestJS, Fastify, Hapi, Koa, Hono, tRPC, GraphQL; filename routers — Next.js (Pages & App), Nuxt, Astro, SvelteKit |
| Python | baseline |
Flask, FastAPI, Django (urls.py); FastAPI TestClient links test → route → handler, so impact_analysis / affected reach route tests |
| Java | deep |
Spring MVC (@GetMapping/…), JAX-RS (@GET + @Path), Javalin |
| Go | baseline |
Gin, chi, echo |
| PHP | baseline |
Laravel |
| Rust | baseline |
actix-web (attribute macros) |
Configure your coding agent
Fastest — let Ama wire itself in:
ama install # detects Claude Code / Cursor / Windsurf / Codex and writes their MCP config
ama install --dry-run # preview what it would change, writing nothing
ama uninstall # remove it again
Or configure manually — every MCP client spawns the same stdio command (ama mcp), only the config location differs.
Claude Code — add it with one command:
claude mcp add ama -- ama mcp
…or add it to a project .mcp.json:
{ "mcpServers": { "ama": { "command": "ama", "args": ["mcp"] } } }
Cursor — .cursor/mcp.json (project) or ~/.cursor/mcp.json (global):
{ "mcpServers": { "ama": { "command": "ama", "args": ["mcp"] } } }
Windsurf — ~/.codeium/windsurf/mcp_config.json:
{ "mcpServers": { "ama": { "command": "ama", "args": ["mcp"] } } }
Any other MCP client — spawn ama mcp over stdio. If you didn't install globally, run it via npx:
{ "mcpServers": { "ama": { "command": "npx", "args": ["-y", "@mka-rainmaker/ama", "mcp"] } } }
Fewer tools, lower token cost
Ama exposes 27 tools by default. To trade that for a small high-signal set, set AMA_MCP_TOOLS on the server — minimal (just explore + indexing), or a comma-separated list of tool names (the indexing tools are always included):
{ "mcpServers": { "ama": { "command": "ama", "args": ["mcp"], "env": { "AMA_MCP_TOOLS": "minimal" } } } }
MCP tools
| Tool | What it does |
|---|---|
index_repository(path) |
Build the graph for a directory or project. Run first. |
index_status() |
What's indexed: node/edge counts, per-language coverage + tier, pending edits. |
search_symbol(query) / search_code(query) |
Find symbols by name (add exact: true for a precise dotted match), or search snippet text. |
find_callers / find_callees |
Who calls a function/method, and what it calls. |
find_referrers / find_imports / find_importers |
Where a symbol is used / what a file imports / who imports it. |
impact_analysis(symbol) |
Transitive blast radius for change/test selection. |
explore(question) |
Relevant symbols, a relationship map, and blast radius in one call. |
get_code_snippet(symbol) / file_skeleton(file) |
A symbol's source, or a file's symbol outline. |
…and more (overrides, interfaces, type users, routes → handlers, circular imports). Call get_graph_schema() for the full node/edge model, or ama --help for the CLI.
CLI
The same package ships an ama CLI mirroring the query surface, for one-shot use and scripting (e.g. git diff --name-only | ama affected):
ama --help # list commands
ama mcp # run the MCP server over stdio (what coding agents spawn)
ama upgrade # update Ama in place (npm or bundle); --check to see the latest release
Programmatic API
Embed Ama as a library — index a repo and query its graph from your own code (the same surface the MCP server and CLI use):
import { index } from "@mka-rainmaker/ama";
const ama = await index("/path/to/repo");
ama.searchSymbol("createServer"); // GraphNode[]
ama.findCallers("createServer"); // who calls it
ama.impactAnalysis("AmaSession"); // transitive blast radius
ama.close(); // release resources when done
index(root) returns a transport-free AmaSession (aliased Ama) with the full query surface; open(root) reuses a persisted index. See src/api.ts for the exported types.
How it works
source files ──▶ analyzer (deep | baseline) ──▶ graph (nodes + edges) ──▶ store ──▶ query ──▶ MCP tools
- Graph model — language-agnostic
nodes(File, Module, Class, Interface, Enum, Function, Method, Route, …) andedges(Defines, Calls, Inherits, Implements, UsesType, Imports, References, …). Each symbol gets a stable, location-independent id. - Analyzers — pluggable per language, each declaring its tier (
deeporbaseline). The TypeScript deep analyzer is the reference implementation. - Store — in-memory, or SQLite with full-text search (FTS5) for persistence.
- Query service — search, call-graph traversal, code snippets, and impact analysis.
- MCP server — exposes the query surface over stdio.
How Ama is built
Ama improves itself by dogfooding: each change is made by using Ama's own tools on Ama's own source, and is only considered done once Ama can re-index itself cleanly — that self-index is the project's built-in regression test. Lessons learned along the way are recorded in docs/insights/ so they compound over time. See docs/SELF_IMPROVEMENT_LOOP.md for the workflow and AGENTS.md for contributor and agent conventions.
Development
Requires Node.js 24+.
git clone https://github.com/mka-rainmaker/ama.git
cd ama
npm install
npm run build # compile TypeScript to dist/
npm test # run the test suite (vitest)
npm run typecheck # type-check without emitting
Issues and the backlog live in GitHub Issues; see CONTRIBUTING.md for the workflow.
License
MIT 2026 Mykhaylo Katruk