hermes - 💡(How to fix) Fix [Feature]: Fix for: Hermes Agent Silently Drops `EmbeddedResource` Content Blocks from MCP Tool Results

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

  1. Result: {"result": ""} — empty string, no error

Root Cause

Working alternatives: mcp_qmd_read_resource and mcp_qmd_query are unaffected because they use different code paths (resource reading and search result formatting respectively).

Fix Action

Fix

Two spots in tools/mcp_tool.py need patching:

Code Example

# MCP SDK test confirming the block structure:
from mcp.types import EmbeddedResource, TextResourceContents

resource = TextResourceContents(
    uri="qmd://vault/test.md",
    name="test.md",
    mimeType="text/markdown",
    text="Hello from qmd!",
)
block = EmbeddedResource(type="resource", resource=resource)

hasattr(block, "text")       # False — no top-level .text
hasattr(block, "resource")   # True
block.resource.text          # "Hello from qmd!" — content is here

---

# BEFORE (broken):
parts: List[str] = []
for block in (result.content or []):
    if hasattr(block, "text") and block.text:
        parts.append(block.text)
        continue
    image_tag = _cache_mcp_image_block(block)
    if image_tag:
        parts.append(image_tag)

# AFTER (fixed):
parts: List[str] = []
for block in (result.content or []):
    # TextContent blocks carry .text directly
    if hasattr(block, "text") and block.text:
        parts.append(block.text)
        continue
    # EmbeddedResource blocks carry the payload in .resource.text
    # (MCP spec: type="resource", resource={uri, mimeType, text/blob}).
    if hasattr(block, "resource"):
        resource = block.resource
        if hasattr(resource, "text") and resource.text:
            parts.append(resource.text)
            continue
        if hasattr(resource, "blob") and resource.blob:
            pass  # handled by _cache_mcp_image_block below
    image_tag = _cache_mcp_image_block(block)
    if image_tag:
        parts.append(image_tag)

---

# BEFORE (broken):
@staticmethod
def _extract_tool_result_text(block) -> str:
    """Extract text from a ToolResultContent block."""
    if not hasattr(block, "content") or block.content is None:
        return ""
    items = block.content if isinstance(block.content, list) else [block.content]
    return "\n".join(item.text for item in items if hasattr(item, "text"))

# AFTER (fixed):
@staticmethod
def _extract_tool_result_text(block) -> str:
    """Extract text from a ToolResultContent block.
    Handles both TextContent blocks (.text) and EmbeddedResourceContent
    blocks (.resource.text) per the MCP spec.
    """
    if not hasattr(block, "content") or block.content is None:
        return ""
    items = block.content if isinstance(block.content, list) else [block.content]
    parts: List[str] = []
    for item in items:
        if hasattr(item, "text") and item.text:
            parts.append(item.text)
        elif hasattr(item, "resource"):
            resource = item.resource
            if hasattr(resource, "text") and resource.text:
                parts.append(resource.text)
    return "\n".join(parts)

---
RAW_BUFFERClick to expand / collapse

Problem or Use Case

Hermes Agent's MCP client (tools/mcp_tool.py) only extracts text from TextContent and ImageContent blocks in tool call results. MCP servers that return type: "resource" content blocks (EmbeddedResource in the MCP SDK) have their results silently dropped, producing empty tool output.

Affected tools: mcp_qmd_get, mcp_qmd_multi_get, and any other MCP server tool that returns resource-typed content blocks (e.g., Playwright MCP, filesystem servers embedding file content as resources).

Working alternatives: mcp_qmd_read_resource and mcp_qmd_query are unaffected because they use different code paths (resource reading and search result formatting respectively).

Proposed Solution

Reproduction

  1. Install qmd v2.1.0 and configure it as an MCP server for Hermes
  2. Call mcp_qmd_get with any valid docid or path — e.g., file="v-02-areas/daily-notes/2026-03-10.md"
  3. Result: {"result": ""} — empty string, no error
  4. Call mcp_qmd_read_resource with uri="qmd://vault/v-02-areas/daily-notes/2026-03-10.md" — returns content correctly

This affects every tool that uses type: "resource" blocks. qmd's get and multi_get both return EmbeddedResource blocks, so they always appear empty from Hermes' perspective.

Verified Cause

# MCP SDK test confirming the block structure:
from mcp.types import EmbeddedResource, TextResourceContents

resource = TextResourceContents(
    uri="qmd://vault/test.md",
    name="test.md",
    mimeType="text/markdown",
    text="Hello from qmd!",
)
block = EmbeddedResource(type="resource", resource=resource)

hasattr(block, "text")       # False — no top-level .text
hasattr(block, "resource")   # True
block.resource.text          # "Hello from qmd!" — content is here

The type: "resource" block has no .text attribute at the top level. Content is nested inside .resource.text.

Fix

Two spots in tools/mcp_tool.py need patching:

1. Main tool result extraction loop (~line 2265)

# BEFORE (broken):
parts: List[str] = []
for block in (result.content or []):
    if hasattr(block, "text") and block.text:
        parts.append(block.text)
        continue
    image_tag = _cache_mcp_image_block(block)
    if image_tag:
        parts.append(image_tag)

# AFTER (fixed):
parts: List[str] = []
for block in (result.content or []):
    # TextContent blocks carry .text directly
    if hasattr(block, "text") and block.text:
        parts.append(block.text)
        continue
    # EmbeddedResource blocks carry the payload in .resource.text
    # (MCP spec: type="resource", resource={uri, mimeType, text/blob}).
    if hasattr(block, "resource"):
        resource = block.resource
        if hasattr(resource, "text") and resource.text:
            parts.append(resource.text)
            continue
        if hasattr(resource, "blob") and resource.blob:
            pass  # handled by _cache_mcp_image_block below
    image_tag = _cache_mcp_image_block(block)
    if image_tag:
        parts.append(image_tag)

2. _extract_tool_result_text() static method (~line 637)

# BEFORE (broken):
@staticmethod
def _extract_tool_result_text(block) -> str:
    """Extract text from a ToolResultContent block."""
    if not hasattr(block, "content") or block.content is None:
        return ""
    items = block.content if isinstance(block.content, list) else [block.content]
    return "\n".join(item.text for item in items if hasattr(item, "text"))

# AFTER (fixed):
@staticmethod
def _extract_tool_result_text(block) -> str:
    """Extract text from a ToolResultContent block.
    Handles both TextContent blocks (.text) and EmbeddedResourceContent
    blocks (.resource.text) per the MCP spec.
    """
    if not hasattr(block, "content") or block.content is None:
        return ""
    items = block.content if isinstance(block.content, list) else [block.content]
    parts: List[str] = []
    for item in items:
        if hasattr(item, "text") and item.text:
            parts.append(item.text)
        elif hasattr(item, "resource"):
            resource = item.resource
            if hasattr(resource, "text") and resource.text:
                parts.append(resource.text)
    return "\n".join(parts)

Alternatives Considered

No response

Feature Type

Other

Scope

Small (single file, < 50 lines)

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Feature]: Fix for: Hermes Agent Silently Drops `EmbeddedResource` Content Blocks from MCP Tool Results