Reducing LLM Agent Hallucinations Through Constrained API Retrieval
By Fuat Can Köseoğlu,
The Problem AI coding agents waste most of their context window on code they don't need. When an agent needs to know "what methods does GameModeManager have?", it runs Grep to find the file, then reads the entire thing — private fields, method bodies, comments, imports. A 200-line file with 9 public methods means ~190 lines of noise the LLM has to mentally filter. That filtering isn't free. It costs tokens, and worse — the agent sometimes infers wrong things from implementation detail it was never supposed to see. The Solution codesurface is an MCP server that indexes your codebase's public API at startup and serves it through compact tool responses. The agent asks "what does this class do?" and gets back only the public signatures — nothing else. pip install codesurface Supported languages: C# (.cs), Go (.go), Java (.java), Python (.py), TypeScript/TSX (.ts, .tsx) The Benchmark I tested it against a real Unity game project (129 files, 1,018 API records) on a realistic 10-step cross-cutting research workflow: implementing a feature that spans two game modes, shared events, DI wiring, and multiple services. Three strategies compared: MCP : Pre-indexed API. 1 tool call per lookup, returns only public signatures. Skilled Agent : Targeted Grep + partial Read (~40 lines). Best-case for an experienced agent. Naive Agent : Grep to find file, then Read the whole thing. Common for agents unfamiliar with a codebase. Results: The 10 questions and per-step breakdown MCP wins most on large files with few public methods (steps 3, 5, 7 — up to 91% savings). It wins least on small interfaces where reading the whole file is nearly as cheap (step 2 — only 7%). But MCP Alone Isn't Enough Honest assessment: for ~3 of the 10 steps, the agent still needs to read the actual file. MCP returns Start() and Dispose() for an entry point class — but to wire a new controller, you need to see the constructor, the init sequence, and the disposal pattern. The realistic workflow is MCP for discovery, then targeted Read for the 2-3 files you actually need : Even with follow-up reads, the hybrid approach uses 54% fewer tokens than a skilled agent. The Real Win: Fewer Hallucinations Token savings are nice. But the deeper value is determinism . Here's a concrete example. The agent needs to find what event fires when a level ends. Grep+Read agent reads GameModeManager.cs and sees: public void ReportLevelCompleted(LevelResult result) { _currentMode?.OnLevelCompleted(result); UnloadGameMode(); } It infers: "The mode probably publishes a completion event internally." It looks for LevelCompletedEvent . That event doesn't exist. The actual event is LevelWonEvent , published by a completely different class. The agent hallucinated a plausible event name from true implementation detail — the hardest kind of error to catch. MCP agent sees only: void ReportLevelCompleted(LevelResult result) No method body to over-interpret. When it needs the event, it searches search("LevelWon") and gets the authoritative answer in one call. The tool's constrained output forced the agent to look things up instead of guessing. Setup pip install codesurface Add to your .mcp.json: { "mcpServers": { "codesurface": { "command": "codesurface", "args": ["--project", "/path/to/your/csharp/src"] } } } Point it at any directory with C# (.cs), Go (.go), Java (.java), Python (.py) or TypeScript/TSX (.ts, .tsx) files. Indexes in ~0.04s for a 129-file project. Incremental reindex picks up changes in milliseconds. 5 tools: search , get_signature , get_class , get_stats , reindex . GitHub | PyPI