<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anirbaan Chowdhury</title>
    <description>The latest articles on DEV Community by Anirbaan Chowdhury (@anirbaan_chowdhury_58a600).</description>
    <link>https://dev.to/anirbaan_chowdhury_58a600</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100979%2Fec07ae8c-37bc-4605-a3e0-4caaf2b151db.jpg</url>
      <title>DEV Community: Anirbaan Chowdhury</title>
      <link>https://dev.to/anirbaan_chowdhury_58a600</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anirbaan_chowdhury_58a600"/>
    <language>en</language>
    <item>
      <title>Teaching an LLM to pull MCP Resources and Prompts on demand (instead of drowning it in context)</title>
      <dc:creator>Anirbaan Chowdhury</dc:creator>
      <pubDate>Sun, 30 Aug 2026 05:19:03 +0000</pubDate>
      <link>https://dev.to/anirbaan_chowdhury_58a600/teaching-an-llm-to-pull-mcp-resources-and-prompts-on-demand-instead-of-drowning-it-in-context-591l</link>
      <guid>https://dev.to/anirbaan_chowdhury_58a600/teaching-an-llm-to-pull-mcp-resources-and-prompts-on-demand-instead-of-drowning-it-in-context-591l</guid>
      <description>&lt;h1&gt;
  
  
  Teaching an LLM to &lt;em&gt;pull&lt;/em&gt; MCP Resources and Prompts on demand (instead of drowning it in context)
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;How we wired the Model Context Protocol's "application-controlled" primitives into a model-controlled tool-calling loop — and why that small shift changes everything about context hygiene.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The Model Context Protocol (MCP) gives a server three ways to expose capability: &lt;strong&gt;tools&lt;/strong&gt;, &lt;strong&gt;resources&lt;/strong&gt;, and &lt;strong&gt;prompts&lt;/strong&gt;. Tools drop straight into an LLM's function-calling loop. Resources and prompts don't — they're &lt;em&gt;application-controlled&lt;/em&gt;, so most integrations just &lt;strong&gt;dump every resource's content into the system prompt&lt;/strong&gt; and hope for the best.&lt;/p&gt;

&lt;p&gt;That approach bloats context, truncates large documents, breaks on binary files, and gives the model zero say in what it actually needs.&lt;/p&gt;

&lt;p&gt;Our fix: &lt;strong&gt;promote resources and prompts into synthetic, auto-approved LLM tools&lt;/strong&gt; — &lt;code&gt;read_resource(uri)&lt;/code&gt; and &lt;code&gt;invoke_prompt(name)&lt;/code&gt;. The system prompt now carries only a lightweight &lt;em&gt;catalog&lt;/em&gt; (URIs + descriptions). The model reads a resource &lt;strong&gt;only when it decides it needs one&lt;/strong&gt;, through the exact same tool-calling machinery it already uses. On-demand, selective, full-fidelity.&lt;/p&gt;




&lt;h2&gt;
  
  
  The three MCP primitives — and the control model nobody talks about
&lt;/h2&gt;

&lt;p&gt;MCP defines three server capabilities, but the interesting part is &lt;em&gt;who's in control&lt;/em&gt; of each:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Who decides when it's used&lt;/th&gt;
&lt;th&gt;Natural fit for tool-calling?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;model&lt;/strong&gt; (it calls them)&lt;/td&gt;
&lt;td&gt;✅ Yes — this is what function calling &lt;em&gt;is&lt;/em&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resources&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;application / user&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;❌ No native hook in the loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The &lt;strong&gt;user&lt;/strong&gt; (usually a slash-command)&lt;/td&gt;
&lt;td&gt;❌ No native hook in the loop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tool calling is &lt;em&gt;model-controlled&lt;/em&gt; by design: the LLM emits a &lt;code&gt;tool_use&lt;/code&gt; block, you execute it, you feed the result back. Beautiful.&lt;/p&gt;

&lt;p&gt;Resources and prompts are &lt;em&gt;application-controlled&lt;/em&gt;. The spec's mental model is a human clicking "attach this file" or "/use this prompt template." There is no obvious place for them inside an autonomous agent's reasoning loop. So what do most integrations do?&lt;/p&gt;




&lt;h2&gt;
  
  
  The naive approach (and why it hurts)
&lt;/h2&gt;

&lt;p&gt;The path of least resistance is to fetch &lt;strong&gt;every&lt;/strong&gt; resource at startup and paste it into the system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Available Resources
### Resource: SUM ABAP Test Matrix
URI: sap-btp://sum-abap-v1
Content:
&amp;lt;... 11,000 characters of markdown ...&amp;gt;
### Resource: API Docs
URI: sap-btp://api-docs
Content:
&amp;lt;... more ...&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four problems show up fast:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Context bloat.&lt;/strong&gt; Every request pays for every resource, whether or not it's relevant. Ten resources × a few thousand tokens each = a system prompt that dwarfs the actual conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Truncation.&lt;/strong&gt; To keep bloat sane, you cap content (&lt;code&gt;content[:2000]&lt;/code&gt;) — and now large documents are silently chopped. In our case the SUM ABAP matrix lost its entire product list and output-format section below the 2,000-char line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary breaks.&lt;/strong&gt; A PDF or PNG resource has no meaningful text form. Naive extractors try &lt;code&gt;blob.as_string()&lt;/code&gt;, hit a &lt;code&gt;UnicodeDecodeError&lt;/code&gt;, and quietly emit &lt;code&gt;"[No content available]"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No agency.&lt;/strong&gt; The model can't say "I don't need any of these right now" or "give me &lt;em&gt;that&lt;/em&gt; one, in full." It's force-fed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The insight: make resources and prompts look like tools
&lt;/h2&gt;

&lt;p&gt;Here's the shift. The LLM already has a clean, well-understood way to ask for something on demand: &lt;strong&gt;it calls a tool.&lt;/strong&gt; So instead of fighting the control model, we &lt;em&gt;translate&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;We register two &lt;strong&gt;DARA-internal&lt;/strong&gt; tools that don't exist on any MCP server — they're synthesized client-side:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;read_resource(uri)&lt;/code&gt;&lt;/strong&gt; → fetches one resource's content when the model asks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;invoke_prompt(name)&lt;/code&gt;&lt;/strong&gt; → injects a named prompt template into the conversation when the model asks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system prompt now advertises only a &lt;strong&gt;catalog&lt;/strong&gt; — names, URIs, and descriptions, &lt;em&gt;no content&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Available Resources
The following resources can be read on demand. To read one, call the
`read_resource` tool with its exact URI. Do not assume a resource's
contents until you have read it.

### Resource: SUM ABAP Test Matrix
URI: sap-btp://sum-abap-v1
Description: SUM (Software Update Manager) test matrix specification for ABAP products
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model sees what exists, then reaches for exactly what it needs — through the tool loop it already speaks fluently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Implementation walkthrough
&lt;/h2&gt;

&lt;p&gt;We built this on top of LangGraph + &lt;code&gt;langchain-mcp-adapters&lt;/code&gt;, but the pattern is framework-agnostic.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Synthesize the tool with a schema
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;read_resource&lt;/code&gt; is a &lt;code&gt;StructuredTool&lt;/code&gt; with a one-field schema. Its &lt;code&gt;func&lt;/code&gt; is a no-op lambda — we never actually &lt;em&gt;run&lt;/em&gt; it as a function; we intercept it in the graph (see step 3).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pydantic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain_core.tools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StructuredTool&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_ReadResourceArgs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The exact URI of the MCP resource to read, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;e.g. &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sap-btp://sum-abap-v1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;read_resource_tool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;StructuredTool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;# placeholder — handled in the graph
&lt;/span&gt;    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read the full contents of an MCP resource by its URI. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Call this when the user asks to read, open, or summarize a resource, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;or when you need a resource&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s contents to answer. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Only resources listed under &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Available Resources&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; can be read.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;args_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_ReadResourceArgs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;read_resource_tool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;allowed_tools_without_review&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# auto-approve, no human gate
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things matter here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The description is the UX.&lt;/strong&gt; It's the only instruction the model gets on &lt;em&gt;when&lt;/em&gt; to call this. Write it like a prompt, because it is one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-approval.&lt;/strong&gt; In an agent with human-in-the-loop review, reading a read-only resource shouldn't require a click. We add it to the "no review" allow-list so the graph routes straight to execution.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Build a content map once, keyed by URI
&lt;/h3&gt;

&lt;p&gt;We fetch resources once (the adapter already returns their content as &lt;code&gt;Blob&lt;/code&gt; objects) and index them by URI so the on-demand read is an O(1) lookup — no second network round-trip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;resources_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
    &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_extract_resource_fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# URIs can arrive as pydantic AnyUrl objects — normalize to str so the
&lt;/span&gt;    &lt;span class="c1"&gt;# plain-string URI the LLM passes actually matches the dict key. (Gotcha!)
&lt;/span&gt;    &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resources_content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Intercept the tool call in the graph
&lt;/h3&gt;

&lt;p&gt;When the model emits a &lt;code&gt;read_resource&lt;/code&gt; call, we don't invoke a function — we look up the content and hand it back as a &lt;strong&gt;tool message&lt;/strong&gt;. Because a tool result flows naturally back into the model's context, the content lands &lt;em&gt;only when requested&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;args&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;uri&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resources_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;new_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;          &lt;span class="c1"&gt;# full content, no truncation
&lt;/span&gt;            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_call_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resources_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;new_messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_resource&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resource &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not found. Available URIs: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;available&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_call_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mirror image for prompts: &lt;code&gt;invoke_prompt&lt;/code&gt; injects the template's messages as real &lt;code&gt;Human&lt;/code&gt;/&lt;code&gt;AI&lt;/code&gt; turns, which is exactly how the MCP spec intends prompts to be surfaced.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Handle binary honestly (don't decode bytes as text)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;langchain-mcp-adapters&lt;/code&gt; collapses every resource into a &lt;code&gt;Blob&lt;/code&gt;: text lands in &lt;code&gt;.data&lt;/code&gt; as a &lt;code&gt;str&lt;/code&gt;, binary as raw &lt;code&gt;bytes&lt;/code&gt;, with the media type on the &lt;strong&gt;&lt;code&gt;.mimetype&lt;/code&gt; attribute&lt;/strong&gt; (not in metadata — a common trip-up). So we branch on the mime type instead of blindly calling &lt;code&gt;.as_string()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_is_text_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;+yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/xml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# inside the extractor, for a Blob:
&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;_is_text_mime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Binary (PDF, PNG, ...) → an honest descriptor, NOT raw bytes/base64
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[Binary resource: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;mime&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/octet-stream&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;_human_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. This is not text and cannot be inlined; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open it with a client that handles its media type.]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is aligned with the MCP spec itself: binary payloads belong in typed media content blocks or are referenced by URI — never stuffed into a text field. A model reading &lt;code&gt;[Binary resource: application/pdf, 240.0 KB]&lt;/code&gt; knows exactly what it's looking at and can decide what to do, instead of choking on garbage or getting a misleading "no content."&lt;/p&gt;




&lt;h2&gt;
  
  
  The flow, end to end
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   ┌─────────────────┐
   │  User message   │
   └────────┬────────┘
            │
            ▼
   ┌──────────────────────────────────────────┐
   │ System prompt = resource CATALOG only     │
   │ (URIs + descriptions, NO content)         │
   └────────┬─────────────────────────────────┘
            │
            ▼
      ┌───────────────┐
      │  LLM decides  │
      └──┬─────────┬──┘
         │         │
 needs a │         │ doesn't need one
 resource│         └──────────────► Answer directly
         ▼
 ┌──────────────────────────┐
 │ tool_use: read_resource  │
 │        (uri)             │
 └────────────┬─────────────┘
              ▼
 ┌──────────────────────────────┐
 │ Graph intercepts the call     │
 │ (auto-approved, no HITL gate) │
 └────────────┬─────────────────┘
              ▼
 ┌──────────────────────────────┐
 │ Look up URI in content map    │
 └───────┬───────────────┬──────┘
         │ text          │ binary
         ▼               ▼
 ┌────────────────┐  ┌──────────────────────┐
 │ Full content   │  │ Descriptor:          │
 │ as tool message│  │ mime type + size     │
 └───────┬────────┘  └───────────┬──────────┘
         │                       │
         └───────────┬───────────┘
                     ▼
              (back to LLM ──► answer)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the &lt;em&gt;"needs a resource"&lt;/em&gt; branch ever pays the content cost — and it pays the &lt;strong&gt;full&lt;/strong&gt; cost, untruncated, for &lt;strong&gt;just&lt;/strong&gt; that resource.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is nice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Token-efficient.&lt;/strong&gt; The system prompt holds a catalog (tens of tokens per resource), not a library. Content enters context only on read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective.&lt;/strong&gt; The model — or the user, phrasing a request — decides what to load. Ten irrelevant resources cost almost nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full fidelity.&lt;/strong&gt; No truncation cap needed, because you're no longer defending against ten simultaneous dumps. The one resource you asked for arrives whole.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary-safe.&lt;/strong&gt; Mime-driven handling means PDFs and images degrade to a clear descriptor instead of a crash or a lie.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec-aligned.&lt;/strong&gt; Application-controlled primitives stay application-controlled — we just expose an &lt;em&gt;affordance&lt;/em&gt; for the model to request them, rather than pre-deciding on its behalf.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uniform mental model.&lt;/strong&gt; Resources, prompts, and real tools all flow through one loop. No special-case rendering paths, no bespoke context-stuffing logic.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Gotchas worth stealing
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AnyUrl&lt;/code&gt; vs &lt;code&gt;str&lt;/code&gt;.&lt;/strong&gt; MCP resource URIs often surface as pydantic &lt;code&gt;AnyUrl&lt;/code&gt; objects. If your content map is keyed by &lt;code&gt;AnyUrl&lt;/code&gt; and the LLM passes a plain string, &lt;code&gt;.get()&lt;/code&gt; silently misses. Normalize to &lt;code&gt;str&lt;/code&gt; on &lt;strong&gt;both&lt;/strong&gt; sides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mime lives on &lt;code&gt;.mimetype&lt;/code&gt;, not metadata.&lt;/strong&gt; For &lt;code&gt;langchain-mcp-adapters&lt;/code&gt; &lt;code&gt;Blob&lt;/code&gt;s, the media type is an attribute; metadata only carries the &lt;code&gt;uri&lt;/code&gt;. Read the right field or every binary looks like &lt;code&gt;application/octet-stream&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The tool description &lt;em&gt;is&lt;/em&gt; the routing logic.&lt;/strong&gt; There's no separate policy telling the model when to read a resource — only the tool's &lt;code&gt;description&lt;/code&gt;. Invest in it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-approve read-only reads.&lt;/strong&gt; If your agent has human review gates, forcing a click to read a read-only resource kills the UX. Allow-list it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A no-op &lt;code&gt;func&lt;/code&gt; is fine.&lt;/strong&gt; The synthesized tool never executes as a function; the graph intercepts it. The lambda is just there to satisfy the schema.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Where it goes next
&lt;/h2&gt;

&lt;p&gt;The binary branch is the single hook for richer handling: extract PDF text server-side, or emit an &lt;code&gt;ImageContent&lt;/code&gt; block to a vision-capable model for images. Because everything already funnels through one &lt;code&gt;read_resource&lt;/code&gt; path, adding a modality is a localized change — not a re-architecture.&lt;/p&gt;

&lt;p&gt;The bigger takeaway: when a protocol primitive doesn't fit your execution model, don't force the model to swallow it up front. Give the model an &lt;strong&gt;affordance to ask&lt;/strong&gt;, and let the loop it already understands do the rest.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built on the Model Context Protocol (2025-03-26), LangGraph, and &lt;code&gt;langchain-mcp-adapters&lt;/code&gt;. The pattern is framework-agnostic — anywhere you have tool-calling and MCP, you can do this.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>llm</category>
      <category>python</category>
    </item>
  </channel>
</rss>
