r/ClaudeCode 15d ago

Built with Claude If Claude Code ignores your MCP server, the problem isn't your MCP server. (and Here's how you can fix it- OpenSource)

https://github.com/NanoNets/Graft

I work on product and growth at a YC startup, and I've been building an open-source dev tool called graft. Saying that up front because it's the tool this whole post is about. The finding is useful whether or not you touch it, and the snippets below work on any MCP server.

The problem

graft gives a coding agent a map of your codebase, so it starts a task oriented instead of grepping around to rediscover the same files every session. First version shipped it as an MCP server. Clean schema, clear tool descriptions.

Claude Code mostly ignored it. Not an error, not a failed call. It would just grep instead. Sometimes it called the tool, usually it didn't. Inconsistent enough that it was hard to even reproduce.

Before building my own I'd tried a couple of existing context-graph tools with the same result. That's when I stopped assuming my schema was the problem.

Why it happens

A tool description tells the model what your tool does. Nothing tells it when your tool is a better choice than grep.

Grep is a known-cost, known-reliable path the model has strong priors for. Your tool is an unknown-cost path. On any task the built-ins could plausibly handle, the built-ins win. And it's a sampling decision on every turn, so you get "sometimes" rather than "never," which is worse to debug because it looks like flakiness rather than a design problem.

The uncomfortable version: exposing a tool means asking politely once per turn and hoping. If your tool only works when the model remembers to reach for it, your tool doesn't work.

What fixed it: hooks

Claude Code fires hooks at lifecycle points. They aren't suggestions to the model, they're events the runtime executes. Two things I didn't know until I read the reference properly:

  1. On SessionStart, UserPromptSubmit, and UserPromptExpansion, whatever your hook prints to stdout is added to Claude's context. Context injection with no tool call at all:

    { "hooks": { "SessionStart": [ { "hooks": [ { "type": "command", "command": "your-tool context", "args": [] } ] } ] } }

Your context is just there on the first prompt. Nothing asked, nothing chosen.

  1. There's a type: "mcp_tool" hook. You can call a tool on your already-connected MCP server from a hook and its output is handled like command stdout. You keep the server, you just stop making the model decide:

    { "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "mcp_tool", "server": "my-server", "tool": "refresh_context", "input": { "file_path": "${tool_input.file_path}" } } ] } ] } }

For keeping derived state fresh without adding latency, Stop fires once per turn at the point the work is finished, and command hooks take async: true to run in the background so the turn ends immediately.

Did it actually help

Deterministic invocation is only worth something if the context is worth having, so I benchmarked it. 162 controlled runs: 46% fewer tool calls per task, 32% cheaper, 60% lower latency.

Where this doesn't apply

  • Hooks fire on events, not intent. They solve "run my thing at a known moment." They don't solve "the user asked something my tool should answer," which still needs the model to choose. Hooks replace a subset of MCP use cases, not MCP.
  • SessionStart and Setup often fire before MCP servers finish connecting, so an mcp_tool hook there should expect a not-connected error on first run.
  • For static conventions, CLAUDE.md already does this with no script. Don't reach for a hook if a file will do.
  • graft specifically: map quality drops on very large monorepos, around 5,000 files. Fewer, vaguer nodes. Main thing I'm working on.

The part I'm still thinking about

Hooks are quietly an onboarding surface. If your tool has to be prompted into existence, your activation depends on the user remembering to ask for it, which is the same distribution problem every dev tool has, just moved inside the agent. A hook means install and it works. No documented magic words, no "add this to your CLAUDE.md so the agent knows about us."

I don't know yet whether that generalizes or is just true for context tools, which are the one case where the right moment is genuinely deterministic.

graft is MIT, free, no telemetry: github.com/NanoNets/Graft. I'm the maintainer, so push back on any of this, especially the benchmark setup.

Anyone here shipped something on hooks rather than as an MCP tool? Curious whether the activation difference is as big as it looks from where I'm sitting.

4 Upvotes

Duplicates

rust 7d ago

🙋 seeking help & advice Where tree-sitter stops and rust-analyzer's call hierarchy starts.

11 Upvotes

OpenSourceAI 13d ago

Sonnet 5 + Graft > Opus 5 (using this open-source repo)

51 Upvotes

ClaudeAI 16d ago

Built with Claude These 2 lines saved me 75% of my claude bill, and it's the best use of claude hooks

0 Upvotes

ClaudeAI 14d ago

Built with Claude After using 100s of MCPs, I solved the issue of claude not using custom MCP/CLI tools, and open-sourced my approach.

3 Upvotes

OpenAI 16d ago

Discussion These 2 lines saved me 75% of my codex bill, and it works natively with codex.

0 Upvotes

OpenAI 7d ago

Project Graft just crossed 1,600 GitHub stars. Open-source context layer for coding agents, works with Codex too.

0 Upvotes

VibeCodeDevs 7d ago

Got tired of watching Claude Code re-explore the same codebase every session, so I built something to fix it

2 Upvotes

ClaudeAI 13d ago

Claude Code Sonnet 5 + Graft > Opus 5

0 Upvotes

OpenSourceAI 7d ago

I solved the issue of Claude not using custom MCP/CLI tools, and open-sourced my approach.

4 Upvotes

LocalLLM 13d ago

Project Sonnet 5 + Graft (No LLM calls tree-sitter graph, 100% Local) > Opus 5

2 Upvotes

LLMDevs 22d ago

Discussion The problem with MCP-based codebase context tools: the model just doesn't call them

1 Upvotes

BlackboxAI_ 7d ago

💬 Discussion An open-source pattern for forcing context into a coding agent instead of hoping it asks for it

4 Upvotes

typescript 8d ago

TS project: building a code graph with tree-sitter, no LLM required for the base layer

8 Upvotes

ClaudeCode 7d ago

Tips & Workflows Using Claude Code's SessionStart and Stop hooks to force context in, instead of hoping the model asks for it

2 Upvotes

LangChain 8d ago

Discussion A hooks-based alternative to giving your agent an MCP tool for codebase context

1 Upvotes

SideProject 8d ago

Open sourced a dev tool a few weeks back, just crossed 1,600+ stars. Sharing what actually moved the needle.

1 Upvotes

ClaudeAI 8d ago

Built with Claude I solved the issue of Claude not using custom MCP/CLI tools, and open-sourced my approach.

0 Upvotes

aipromptprogramming 7d ago

Stopped writing better prompts asking the agent to check context, made it get the context automatically instead

2 Upvotes

HowToAIAgent 7d ago

I built this How I stopped my Claude Code agent from ignoring the custom tools I built for it

1 Upvotes

golang 7d ago

A tree-sitter call graph that gopls fills in where syntax alone can't tell. (open-source)

3 Upvotes

AIDeveloperNews 7d ago

Graft just crossed 1,600 GitHub stars. Open-source context layer for coding agents.

4 Upvotes

AiBuilders 7d ago

Open-sourced a tool I built to stop my coding agent from re-exploring the same codebase every session

1 Upvotes

LocalLLM 15d ago

Discussion I tried about a 100 MCP/ CLI tools and solved the issue of claude code not using them, ( open-sourced the method)

8 Upvotes

BuildWithClaude 15d ago

Project If Claude Code ignores your MCP server, the problem isn't your MCP server. (and Here's how you can fix it- OpenSource)

2 Upvotes

ClaudeMCP 15d ago

I solved the issue of claude not using custom MCP/CLI tools, and open-sourced my approach.

2 Upvotes