Oxagen Docs

Agentic coding with Oxagen

Three end-to-end agent threads — performance triage, cross-repo refactor, and stack-trace triage — with the exact MCP tool calls and the response shapes the agent sees.

Agentic coding with Oxagen

Three real agent threads, each one MCP call away from a complete answer on Oxagen, ten or more tool calls away on a vanilla setup. The MCP tool names and input fields below are taken verbatim from mcp.oxagen.ai — see services/mcp-server/_schemas.py for the canonical Pydantic shapes.

Prerequisites

  • An Oxagen workspace with a GitHub connection that has finished a backfill.
  • An Oxagen API key (Settings → API Keys).
  • Claude Code, Cursor, VS Code, Windsurf, or Codex with the Oxagen MCP server installed — see MCP Server for one-line installs.

Thread A — "Why is processOrder slow?"

The agent has a function name and a vague performance complaint. It needs the signature, the recent commits that touched the function, the callers, the tests, and the docs that describe behaviour — enough context to form a hypothesis without reading source.

One MCP call on Oxagen

// tool: ontology.explain_function
{
  "name": "processOrder"
}

The response is the full edge neighbourhood for the function — what it calls, throws, reads, writes, and returns — plus typed records the agent reads directly:

{
  "function": {
    "id": "a3f2c1d8-...",
    "name": "processOrder",
    "signature": "async function processOrder(input: OrderInput): Promise<OrderResult>",
    "file": "services/api/lib/orders.ts",
    "is_exported": true,
    "is_async": true,
    "kind": "function"
  },
  "calls": [
    { "id": "b8c1...", "name": "validateCard" },
    { "id": "c4d9...", "name": "chargeStripe" },
    { "id": "d1f8...", "name": "writeOrderRow" }
  ],
  "throws": [
    { "id": "e5b7...", "name": "PaymentError" }
  ],
  "reads": [
    { "id": "f2a8...", "name": "OrderInput" }
  ],
  "writes": [
    { "id": "9b3c...", "name": "Order" }
  ],
  "returns": [
    { "id": "1d6e...", "name": "OrderResult" }
  ],
  "tested_by": [
    { "id": "7a2b...", "name": "processOrder_handles_decline" }
  ],
  "recent_commits": [
    {
      "sha": "9a4c1f...",
      "short_sha": "9a4c1f10",
      "message": "fix(api): retry chargeStripe on transient 5xx",
      "authored_at": "2026-04-22T14:03:12Z",
      "author_name": "Alex Rivera",
      "files_changed": 3,
      "insertions": 41,
      "deletions": 12
    }
  ]
}

The agent now has an immediate hypothesis: the recent commit added a retry around chargeStripe. With one follow-up call — code.find_callers { target: { node_id: "a3f2c1d8-..." }, depth: 2 } — it pulls every transitive caller and decides whether the latency lives in processOrder or upstream.

Note. recent_commits requires the GitHub connection's commit ingestion to have finished — until backfill completes for a repo, the array will be empty. The other fields populate as soon as the symbol is parsed.

The same task on a vanilla coding agent

Without a code graph the agent reproduces the answer from primary sources, paying for every step:

  1. read_file services/api/lib/orders.ts — locate the function.
  2. bash grep -rn "processOrder(" services/ — find callers.
  3. bash git log --follow --oneline -n 5 services/api/lib/orders.ts — recent history.
  4. bash git show 9a4c1f10 — read the diff.
  5. bash grep -rn "PaymentError" services/api/ — find error sites.
  6. bash grep -rn "describe.*processOrder\|test.*processOrder" services/api/ — find tests.
  7. read_file × N to actually read each candidate.

Twelve-plus tool calls, a 50 KB context window of file dumps, and the agent still has to read each blob to assemble what Oxagen returned in one typed payload. Run the comparison yourself — methodology and numbers live in oxagenai/oxagen-evals.

Thread B — "Refactor User.email to User.contactEmail"

The agent must enumerate every reference site across functions, files, tests, and docs. Regex catches the easy cases and quietly misses the inconvenient ones — a doc that says the User's email field, a test that destructures { email } from a fixture, a re-export that aliases the symbol.

One MCP call on Oxagen

First, anchor on the symbol node:

// tool: ontology.explain_function
// (works for any named symbol — function, class, or type)
{
  "name": "User"
}

Then traverse the typed edges that constitute a "reference":

// tool: ontology.traverse
{
  "from_node_id": "8c4f1a2b-...",
  "max_hops": 2,
  "edge_types": ["calls", "imports", "references"],
  "limit": 200
}

The response is an ordered list of paths, shortest-first, with walk-ordered nodes and edges:

{
  "paths": [
    {
      "nodes": [
        { "id": "8c4f...", "type": "code.symbol", "name": "User" },
        { "id": "d1f8...", "type": "code.file", "name": "services/api/routes/users.ts" }
      ],
      "edges": [
        { "type": "imports", "source_id": "d1f8...", "target_id": "8c4f..." }
      ]
    },
    {
      "nodes": [
        { "id": "8c4f...", "type": "code.symbol", "name": "User" },
        { "id": "e5b7...", "type": "code.doc", "name": "docs/architecture/users.md" }
      ],
      "edges": [
        { "type": "references", "source_id": "e5b7...", "target_id": "8c4f..." }
      ]
    },
    {
      "nodes": [
        { "id": "8c4f...", "type": "code.symbol", "name": "User" },
        { "id": "7a2b...", "type": "code.function", "name": "test_user_email_is_required" }
      ],
      "edges": [
        { "type": "imports", "source_id": "f2a8...", "target_id": "8c4f..." },
        { "type": "calls", "source_id": "7a2b...", "target_id": "f2a8..." }
      ]
    }
  ]
}

The doc-to-symbol references edge is what catches the markdown that a regex sweep misses. Once the agent has the paths, it does the rename deterministically — every site is named in the response, with file path and line range available on the underlying node properties.

For the reverse direction — every function that mutates the symbolontology.impact_of { name: "User" } returns the inbound reads / writes / modifies / calculates set in one call.

Note. Internal markdown links materialise as file-to-file references edges between code.file nodes — see Code Graph.

Thread C — "Triage a stack trace from production"

A pager fires. The on-call engineer pastes the stack trace into the agent. The agent must produce a suspect set — the failing function, its callers, the recent commits that touched any of them — without a clone or a git log.

One MCP call on Oxagen

Stack trace:
  TypeError: Cannot read property 'amount' of undefined
    at calculateTotal (services/api/lib/totals.ts:42:18)
    at processOrder (services/api/lib/orders.ts:104:24)
    at handleCheckout (services/api/routes/checkout.ts:67:11)

The agent extracts calculateTotal and runs:

// tool: ontology.traverse
{
  "from_node_id": "<resolved-id-for-calculateTotal>",
  "max_hops": 3,
  "edge_types": ["calls"],
  "limit": 100
}

The traversal returns every function reachable from calculateTotal within three hops, walked along calls edges. The agent pairs that with one more call to gather the recent diffs:

// tool: ontology.ask
{
  "prompt": "Which commits in the last 14 days modified calculateTotal, processOrder, or handleCheckout?",
  "top_k": 30
}

The response cites commit nodes by UUID:

{
  "intent": "query",
  "answer": {
    "narrative": "Three commits touched these functions in the window. 9a4c1f10 (Alex Rivera, 2026-04-22) added a retry around chargeStripe in processOrder. 3b2e8d77 (Sam Park, 2026-04-25) refactored calculateTotal to read from a new line-item shape and is the most recent change to the file mentioned in the stack frame.",
    "citations": [
      { "node_id": "3b2e8d77-...", "type": "code.commit", "name": "3b2e8d77" },
      { "node_id": "9a4c1f10-...", "type": "code.commit", "name": "9a4c1f10" }
    ],
    "candidate_count": 17,
    "retrieval_mode": "hybrid"
  }
}

Two MCP calls, deterministic node IDs the on-call engineer can paste into the dashboard. No clone. No git log. The agent's next step is a one-line fix or a git revert with a SHA in hand.

Note. code.commit nodes ship with the code-graph rollout. Until then, ontology.ask answers the same question from semantic context — the commit citations appear post-rollout.

Patterns the threads share

  • Identity comes from the bearer token. None of the calls pass workspace_id or user_id — the MCP server resolves them from the verified token. Cross-workspace queries return empty.
  • Node IDs are stable. Once the agent has a UUID for processOrder, it can cite it for the rest of the session. The graph is deterministic; there is no embedding-hit-or-miss.
  • Caps are conservative. max_hops defaults to 3 (cap 5), limit defaults to 50 (cap 500). The MCP server refuses queries that would exceed the cap so agent-side pagination is predictable.
  • Edges are typed. calls, imports, references, tests, is_tested_by, throws, is_thrown_by are exact strings. Pass them as edge_types filters; do not invent new ones.

Run the evals

The cost and tool-call deltas above are reproducible. The methodology and the harness live in oxagenai/oxagen-evals — clone it, point it at your workspace, run the three threads on Haiku and on Opus, compare the cost-per-query and accuracy numbers in the generated report. Numbers we cite anywhere in the docs come from this harness; if they shift, the docs page shifts with them.

Where to go next

  • Code Graph — the canonical reference for nodes, edges, and the agent query.
  • Cheaper models with Oxagen — the eval methodology and the cost argument.
  • Agent Memory — how the agent's past sessions feed back into the next call's context.

Get started free · Read the docs

On this page