Skip to content

Add process_tool_call hook to MCP servers to modify tool args, metadata, and return value #1962

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

hayk-corpusant
Copy link
Contributor

@hayk-corpusant hayk-corpusant commented Jun 11, 2025

The goal here is to allow a persistent Agent to be used within a multi-user app where user-specific metadata/auth should get passed through to the MCP server in a way not visible to an LLM.

To do this, we use the _meta field of the request. See this discussion, method 2. Use the JSON‑RPC _meta field
https://github.com/orgs/modelcontextprotocol/discussions/309

In our app we directly call agent.iter and have verified that this works to then in a FastMCP server tool we can call ctx.request_context.meta to access the contents.

Fixes #1872

@hayk-corpusant
Copy link
Contributor Author

Also noting that while we would love to merge this, we can't actually use the tip of pydantic-ai until we get streamable HTTP back

@hayk-corpusant
Copy link
Contributor Author

@DouweM

@DouweM
Copy link
Contributor

DouweM commented Jun 12, 2025

@hayk-corpusant Thanks for the PR!

I think using _meta for this makes sense. I don't love that it's not officially covered in the MCP documentation (modelcontextprotocol/modelcontextprotocol#414), but there seems to be some precedent and it's used in https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/utilities/progress.mdx.

Instead of an MCP-specific property on GraphAgentDeps, though, I'd like to make this more generically useful for processing MCP tool calls before they go out.

I imagine a new process_tool_call property on MCPServer that takes a callable that's passed the RunContext, the tool name and arguments, and a reference to the function that will actually call the tool on the MCP server. That method takes a tool name, arguments, and a new optional metadata dict that maps to _meta. You can then store whatever you want on your own deps object available from ctx.deps and pass it as metadata:

@dataclass
class MyDeps:
    user_id: int

async def process_tool_call(ctx: RunContext[MyDeps], call_tool: Callable[[str, dict[str, Any]], CallToolResult], tool_name: str, args: dict[str, Any]) -> str:
    return await call_tool(tool_name, args, {"user_id": ctx.deps.user_id})

server = MCPServerHTTP(..., process_tool_call=process_tool_call)

This function would be called from inside the run_tool function inside the _tool_from_mcp_server function in _agent_graph.py.

Would that work for you?

@hayk-corpusant hayk-corpusant force-pushed the hayk/metadata_debug branch 2 times, most recently from 2646f9f to 80f57f6 Compare June 14, 2025 20:19
@hayk-corpusant
Copy link
Contributor Author

hayk-corpusant commented Jun 14, 2025

How's this @DouweM? It doesn't seem like the types are quite right but the rest works.

We thought it was strange that we couldn't find an existing way to pass through user-level auth for a persistent agent. Seems like a common use case if you're running an app serving many users and the agent is on the backend.

@hayk-corpusant hayk-corpusant changed the title Expose metadata headers for tool calls Expose metadata for MCP tool calls Jun 14, 2025
@stevenh
Copy link
Contributor

stevenh commented Jun 16, 2025

This would solve our issue in the creation of a AG-UI adapter, as it too requires metadata about the call, currently I have to workaround by renaming the method calls, which is far from ideal.

I wonder if the default should be to always pass deps as metadata though, that way users can customise but for the majority of cases it will just work without any additional work from the developer.

Thoughts?

@stevenh
Copy link
Contributor

stevenh commented Jun 16, 2025

@hayk-corpusant if I can help get this over the line in any way let me know, as this would allow us get a good solution for tool call tracking.

@DouweM
Copy link
Contributor

DouweM commented Jun 16, 2025

@hayk-corpusant Thanks, the implementation looks good! Can you please look add tests and docs, and look at the typing issue? Let me know if you'd like some guidance.

@DouweM
Copy link
Contributor

DouweM commented Jun 16, 2025

I wonder if the default should be to always pass deps as metadata though, that way users can customise but for the majority of cases it will just work without any additional work from the developer.

@stevenh Passing all deps through has been suggested before (#1872), but (as I wrote there), I think it'd be unexpected to both users and MCP servers if we unconditionally passed all of the deps. Note also that deps may not be serializable.

@DouweM DouweM mentioned this pull request Jun 16, 2025
@DouweM DouweM changed the title Expose metadata for MCP tool calls Add process_tool_call hook to MCP servers to modify tool args, metadata, and return value Jun 16, 2025
@DouweM DouweM mentioned this pull request Jun 16, 2025
@hayk-corpusant
Copy link
Contributor Author

I also think passing through all deps is problematic, would rather it's explicit. @DouweM I'm quite busy this week so I'll get to it if I can, but I can't promise urgent action here so anyone else wanting to move it forward is welcome to

@stevenh
Copy link
Contributor

stevenh commented Jun 17, 2025

I've created a PR off the back of this one, which I believe achieves what @hayk-corpusant set out to do, with tests and additional docs.

You can find it here: #2000, its even got a nice round number 😄

@DouweM
Copy link
Contributor

DouweM commented Jun 17, 2025

@stevenh Thank you Steven! I'll close this PR in favor of that one then.

@hayk-corpusant Thanks for making the start on this :)

@DouweM DouweM closed this Jun 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature request: Pass metadata to MCP server tool calls
3 participants