CLAUDE LABJP
VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1VERSION — v2.1.227 landed on August 10 with no new features, just fixes around plan detection and CI behaviorAUTO — Two days remain until August 14, when auto mode becomes the default in Claude Code for Pro, Max, and TeamCI — Bash commands no longer fail across the board under claude-code-action with allowed_non_write_users on GitHub-hosted runnersBILLING — Sessions started with an expired token could misread your plan and nudge Max users toward usage credits; that is now fixedSUNSET — The legacy Workbench and the experimental prompt tool APIs retire on August 17, five days outPRICE — Sonnet 5 promo pricing at $2/$10 per Mtok runs through August 31, moving to $3/$15 on September 1
Articles/API & SDK
API & SDK/2026-06-19Advanced

Grounding Claude on Your Own Knowledge Base with search_result Blocks

How to stop your own RAG setup from losing track of which article it cited, using Claude's search_result content block and structured citations — with real numbers from running it across four sites.

Claude API116CitationsRAG4search_resultindie developer19

Premium Article

This starts with an internal agent I built on the Claude API to pick related articles and fact-check claims across the content I publish. I run four technical blogs as an indie developer, and on the Japanese side alone I have more than 600 articles on hand. The usual self-hosted RAG shape: concatenate the search hits into a prompt and ask, "Which of these articles supports this claim?"

The first version worked well enough. But a habit surfaced over time that I could not ignore. Claude would answer "this is described in article X" — and sometimes article X was not among the candidates I had passed in. I also could not trace which part of the concatenated text it had actually read. The provenance dissolved into prose and could not be verified after the fact. For an internal tool, that lack of honesty still bothered me.

The mechanism that forces "which source, which passage" to come back as structured data is Claude's Citations feature, specifically the search_result content block. Here are the notes from swapping it into production and running it for a few weeks, including the potholes along the way.

Why prose citations can't be verified

When you concatenate body text yourself, Claude sees one long string. Headers like ## Article A convey a semantic boundary, but they are not machine-readable reference handles. So the grounding comes back as natural language — "according to Article A" — and you need post-processing to map that back to the source with regular expressions.

That post-processing was brittle. When Claude paraphrased a title slightly, or merged several articles into a vague "based on these," matching failed. In my setup the mechanical match rate plateaued around 70%. The remaining 30% had to be checked by hand, which halved the point of automating it.

The root cause is simply that I was not passing reference IDs. If you don't send them, you can't get them back. The search_result block exists precisely to pass documents in as search results that carry an ID.

Anatomy of the search_result block

search_result is a dedicated block you can place in a message's content array. A single result is expressed as three parts: a source identifier, a title, and an array of text fragments.

search_result_block = {
    "type": "search_result",
    "source": "https://claudelab.net/articles/api-sdk/claude-api-prompt-caching-monthly-cost-half-guide",
    "title": "Halving monthly cost with prompt caching",
    "content": [
        {"type": "text", "text": "Putting a 5-minute TTL cache breakpoint at the end of system..."},
        {"type": "text", "text": "A 1-hour TTL suits large static context. Pricing is..."},
    ],
    "citations": {"enabled": True},
}

The key point is that content is an array of text blocks, not a string. Claude returns the index of which entry it relied on, so how you split the fragments becomes the granularity of your citations. Splitting per paragraph makes it easy to link back to "this paragraph of the article" later. Note too that only blocks with citations.enabled set to true are eligible to be cited.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
Make Claude attach which document and which passage it relied on as structured data, every time, via search_result blocks
Move off hand-rolled context concatenation and cut request tokens by ~40% while reducing missed citations
Wire the citations array back into article links, plus three search_result gotchas I hit in production
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

API & SDK2026-07-09
When the RAG Started Being Confidently Wrong — Field Notes on Measuring Retrieval Misses With Groundedness
In a Claude API RAG, the answers stay fluent while the facts drift. Often the cause is a silent recall decay on the retrieval side, missing the document that holds the answer. Field notes on measuring groundedness and retrieval hit rate and walking the system back, with working code and real numbers.
API & SDK2026-06-28
Measure Streaming CPU and Dropped Chunks to Stabilize Long Batch Jobs
You start an overnight batch, and by morning only half of it finished. The culprits were CPU pinned during streaming and a quiet connection drop. Here is a monitor wrapper that measures stream CPU and throughput, and resumes from interruptions.
API & SDK2026-06-25
Reach a Remote MCP Server in a Single API Request: Implementing the Messages API MCP Connector
How to call a remote MCP server's tools using only the Messages API's mcp_servers and mcp_toolset—no local MCP client. Covers allowlist/denylist design, response handling, and the pitfalls to avoid before unattended production use.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →