Skip to content

Conversation

Mashiro2000
Copy link

Description

#881
Add a new meta field to ToolResultContent

meta: NotRequired[Dict[str, Any]]

Include the new MCP feature field _meta in the returned fields of _map_mcp_content_to_tool_result_content

result = {"text": content.text}
if content.meta is not None:
    result["_meta"] = content.meta
return result

I believe adding the meta field to ToolResultContent aligns with MCP's design principles.

# mcp sdk
ContentBlock = TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource

class CallToolResult(Result):
    """The server's response to a tool call."""

    content: list[ContentBlock]
    structuredContent: dict[str, Any] | None = None
    """An optional JSON object that represents the structured result of the tool call."""
    isError: bool = False

class TextContent(BaseModel):
    """Text content for a message."""

    type: Literal["text"]
    text: str
    """The text content of the message."""
    annotations: Annotations | None = None
    meta: dict[str, Any] | None = Field(alias="_meta", default=None)

This is the official MCP SDK's return for a tool call, where content is a list that stores ContentBlocks, and ContentBlock includes a _meta field.

# strands
class ToolResultContent(TypedDict, total=False):

    document: DocumentContent
    image: ImageContent
    json: Any
    meta: NotRequired[Dict[str, Any]] # new field
    text: str

class MCPToolResult(ToolResult):
    content: list[ToolResultContent]
    status: ToolResultStatus
    toolUseId: str
    structuredContent: NotRequired[Dict[str, Any]]

The content attribute of ToolResult is of type list[ToolResultContent]. Previously, ToolResultContent lacked the meta field, so this update introduces that change.

Do not include this in the LLM's response, as it is an MCP business parameter that can be used to carry the token usage for tool execution.
I handle the business parameter in the meta field by implementing AfterToolInvocationEvent and using it in the post hook.

Related Issues

Issues #881

Documentation PR

Type of Change

Bug fix

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…ously incorrectly written as meta. This did not comply with the MCP specification.
document: DocumentContent
image: ImageContent
json: Any
_meta: NotRequired[Dict[str, Any]]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, in the current state, we are going to need to do something similar to https://github.com/strands-agents/sdk-python/pull/818/files since if we try to pass _meta to certain models we will get a validation exception.

After #836 this may change. So let's wait until 836 is merged and then I'll re review.

Copy link
Member

@dbschmigelski dbschmigelski Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So #818 has since been merged as has #836.

I want to understand your needs though.

I see that you stated the following, indicating that you are aware that MCPToolResult has optional fields like structuredContent.

# strands
class ToolResultContent(TypedDict, total=False):

    document: DocumentContent
    image: ImageContent
    json: Any
    meta: NotRequired[Dict[str, Any]] # new field
    text: str

class MCPToolResult(ToolResult):
    content: list[ToolResultContent]
    status: ToolResultStatus
    toolUseId: str
    structuredContent: NotRequired[Dict[str, Any]]

Does adding meta to MCPToolResult not satisfy your needs? I could entertain meta being added more generally to the base ToolResult but I'm trying to understand the desire for meta to be a ContentType.

This comes down to the following question: Is meta used by the application, in things like Hooks or other AgentTools, or do we want this exposed to the LLM?

class MCPToolResult(ToolResult):
    content: list[ToolResultContent]
    status: ToolResultStatus
    toolUseId: str
    structuredContent: NotRequired[Dict[str, Any]]
    meta: NotRequired[Dict[str, Any]]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, our ContentType is a discriminated union. We could change that with a meta special case - would need some convincing. But lets chat more about your needs and we can work back from that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So #818 has since been merged as has #836.

I want to understand your needs though.

I see that you stated the following, indicating that you are aware that MCPToolResult has optional fields like structuredContent.

# strands
class ToolResultContent(TypedDict, total=False):

    document: DocumentContent
    image: ImageContent
    json: Any
    meta: NotRequired[Dict[str, Any]] # new field
    text: str

class MCPToolResult(ToolResult):
    content: list[ToolResultContent]
    status: ToolResultStatus
    toolUseId: str
    structuredContent: NotRequired[Dict[str, Any]]

Does adding meta to MCPToolResult not satisfy your needs? I could entertain meta being added more generally to the base ToolResult but I'm trying to understand the desire for meta to be a ContentType.

This comes down to the following question: Is meta used by the application, in things like Hooks or other AgentTools, or do we want this exposed to the LLM?

class MCPToolResult(ToolResult):
    content: list[ToolResultContent]
    status: ToolResultStatus
    toolUseId: str
    structuredContent: NotRequired[Dict[str, Any]]
    meta: NotRequired[Dict[str, Any]]
  1. Here’s the situation: I found that the official MCP’s CallToolResult content is a list, where each item is a TextContent object that contains a _meta field. This indicates that the tool results in MCP can be a list, and each item in the list may include its own meta information. So my idea is to implement it according to the MCP protocol.

  2. Putting it into MCPToolResult would work, but I think it should be a list: NotRequired[List[Dict[str, Any]]].

  3. I don’t think this should be exposed to the LLM, since it’s an MCP reserved field 🤔 meant to carry business parameters like token_usage. I believe a hook can carry such business-specific information in a non-intrusive way, so there’s no need for the LLM to be aware of it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dbschmigelski I’d like to know the latest updates about this _meta. I think my idea aligns with the MCP standards.

Copy link
Author

@Mashiro2000 Mashiro2000 Sep 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does adding meta to MCPToolResult not satisfy your needs? I could entertain meta being added more generally to the base ToolResult but I'm trying to understand the desire for meta to be a ContentType.

I believe my business needs have already been met, because CallToolResult also has a meta field.

The meta field in CallToolResult corresponds to the one you added:

class MCPToolResult(ToolResult):
content: list[ToolResultContent]
status: ToolResultStatus
toolUseId: str
structuredContent: NotRequired[Dict[str, Any]]
meta: NotRequired[Dict[str, Any]]

@dbschmigelski dbschmigelski self-assigned this Sep 18, 2025
@Mashiro2000 Mashiro2000 reopened this Sep 26, 2025
@Mashiro2000 Mashiro2000 deleted the fix/mcp-meta-support branch September 28, 2025 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants