diff --git a/docs/guardrails.md b/docs/guardrails.md index 5ae8485628..c2eb755dee 100644 --- a/docs/guardrails.md +++ b/docs/guardrails.md @@ -57,12 +57,13 @@ Terminal function-tool output needs additional handling because the tool has alr ## Tool guardrails -Tool guardrails wrap **`FunctionTool` instances** and let you validate or block calls to those tools before and after execution. They are configured on the tool itself and run every time that tool is invoked. +Tool guardrails wrap **`FunctionTool` instances** and let you validate or block calls to those tools before and after execution. They are configured on the tool itself — or, for locally executed MCP tools, on the MCP server — and run every time that tool is invoked. - Input tool guardrails run before the tool executes and can skip the call, replace the output with a message, or raise a tripwire. - Output tool guardrails run after the tool executes and can replace the output or raise a tripwire. - If a function tool requires approval, input tool guardrails normally run after approval and immediately before execution. Set [`RunConfig.tool_execution`][agents.run.RunConfig.tool_execution] to [`ToolExecutionConfig(pre_approval_tool_input_guardrails=True)`][agents.run.ToolExecutionConfig] when you want those input checks to run before the pending approval interruption is emitted. Calls that pass this pre-approval check are still checked again after approval before the tool executes. -- Tool guardrails apply only to function tools created with [`function_tool`][agents.tool.function_tool]. Handoffs run through the SDK's handoff pipeline rather than the normal function-tool pipeline, so tool guardrails do not apply to the handoff call itself. Hosted tools (`WebSearchTool`, `FileSearchTool`, `HostedMCPTool`, `CodeInterpreterTool`, `ImageGenerationTool`) and built-in execution tools (`ComputerTool`, `ShellTool`, `ApplyPatchTool`, `LocalShellTool`) also do not use this guardrail pipeline, and [`Agent.as_tool()`][agents.agent.Agent.as_tool] does not currently expose tool-guardrail options directly. +- Locally executed MCP server tools can opt in by passing `tool_input_guardrails` / `tool_output_guardrails` to the MCP server (for example [`MCPServerStdio`][agents.mcp.server.MCPServerStdio] or [`MCPServerStreamableHttp`][agents.mcp.server.MCPServerStreamableHttp]). Those guardrails are attached to every tool the server exposes when the SDK converts MCP tools into `FunctionTool`s, so they run on the same pipeline and with the same approval ordering, tripwire behavior, and `tool_input_guardrail_results` / `tool_output_guardrail_results` reporting described here. Guardrails receive the normal [`ToolContext`][agents.tool_context.ToolContext], so branch on `context.tool_name` when a policy should apply to only some of a server's tools. `HostedMCPTool` is not covered, because the Python process does not own that round trip. +- Otherwise, tool guardrails apply only to function tools created with [`function_tool`][agents.tool.function_tool]. Handoffs run through the SDK's handoff pipeline rather than the normal function-tool pipeline, so tool guardrails do not apply to the handoff call itself. Hosted tools (`WebSearchTool`, `FileSearchTool`, `HostedMCPTool`, `CodeInterpreterTool`, `ImageGenerationTool`) and built-in execution tools (`ComputerTool`, `ShellTool`, `ApplyPatchTool`, `LocalShellTool`) also do not use this guardrail pipeline, and [`Agent.as_tool()`][agents.agent.Agent.as_tool] does not currently expose tool-guardrail options directly. See the code snippet below for details. diff --git a/docs/mcp.md b/docs/mcp.md index 3104f023eb..30295c53d0 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -297,6 +297,35 @@ server = MCPServerStreamableHttp( If your run context is a Pydantic model, dataclass, or custom class, read the tenant ID with attribute access instead. +### Tool guardrails for local MCP servers + +Locally executed MCP tools are converted into `FunctionTool`s by the SDK, so they can opt into the same [tool guardrails](guardrails.md#tool-guardrails) used by function tools. Guardrails passed to the server apply to every tool it exposes: input guardrails run immediately before `call_tool()`, and output guardrails run on the result before it becomes model-visible. + +```python +from agents.mcp import MCPServerStreamableHttp +from agents.tool_guardrails import ( + ToolGuardrailFunctionOutput, + ToolInputGuardrailData, + tool_input_guardrail, +) + + +@tool_input_guardrail +def block_pii(data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput: + if "ssn" in data.context.tool_arguments.lower(): + return ToolGuardrailFunctionOutput.reject_content(message="Blocked: argument contains PII") + return ToolGuardrailFunctionOutput.allow() + + +server = MCPServerStreamableHttp( + name="Billing", + params={"url": "http://localhost:8000/mcp"}, + tool_input_guardrails=[block_pii], +) +``` + +Guardrails receive the normal `ToolContext`, so branch on `data.context.tool_name` to apply a policy to only some of the server's tools. These checks are client-side and complement, rather than replace, authorization in the MCP server itself. `HostedMCPTool` is not covered, since the Python process does not own that round trip. + ### MCP tool outputs: text, images, and other content When an MCP result uses its content blocks, the SDK forwards text content as text output and maps image content to image-type entries in the tool output. For other MCP content block types, including audio and resource blocks, the SDK forwards a text output whose value is the block's valid JSON serialization. Responses that contain multiple content blocks are forwarded as a list of output items. If `use_structured_content=True` selects a non-empty, non-error `structuredContent` payload, that structured payload takes precedence over these content blocks. Missing or empty structured content falls back to the content blocks.