Skip to content
Changelog

Page tools now return typed results with error and completeness status

September 23, 20261 min read

Agno's PageFileSystem.run_command_result() and arun_command_result() tell an application whether a page command failed, stopped early or got clipped. The page command tools used to return plain strings. An application that wanted to show a missing page as an error, or report an incomplete grep over MCP, had to guess from the text, and a documentation page that happened to mention page_unavailable looked like a failure.

PageCommandResult has these fields:

FieldTypeDefault
textstr
is_errorboolFalse
errorsTuple[str, ...]()
partialboolFalse
truncatedboolFalse
continuationOptional[str]None
stop_reasonOptional[str]None

Agno sets these fields from the way each command ran. It never reads them from the page text. Running three commands against a local page store with both methods gives the same text, and only the typed result says which one failed:

run_command("cat /errors.md")
  "==> /errors.md <==\n# Errors\n\nA tool returns page_unavailable when storage is down.\n"
run_command_result("cat /errors.md")
  is_error=false  errors=[]  partial=false  stop_reason=null
 
run_command("cat /no-such-page")
  "/no-such-page: no such file. Use ls/tree to explore, or rg to search."
run_command_result("cat /no-such-page")
  is_error=true  errors=["page_not_found"]  partial=true  stop_reason="file_error"
 
run_command_result("rg page_unavailable /")
  is_error=false  errors=[]  text="[1 matching lines in 1 files]\n/errors.md:3:..."

A grep that stops early stays successful but reports partial=true with a stop_reason, so an incomplete result can't pass as proof that nothing matched. max_output_bytes (default 32000) bounds the complete UTF-8 JSON of the result, metadata included. When Agno clips the text, it sets truncated=true and clears continuation, because a line-based continuation command would skip text you never saw. run_command() and the default chat tool still return the same strings as before.

MCP tools that report errors

PageFileSystem.tools(transport="mcp") builds a command tool with a native MCP output schema. A failed command comes back to the MCP client with isError set.

Knowledge.get_tools(page_results=True) returns one page search tool that answers with a ranked SearchResult. With the default transport="chat" the tool returns SearchResult JSON. With transport="mcp" it publishes the SearchResult output schema and returns the same object as structured content:

from agno.agent import Agent
from agno.os import AgentOS, MCPConfig
 
search_docs = knowledge.get_tools(page_results=True, tool_name="search_docs", transport="mcp")[0]
 
agent_os = AgentOS(
    agents=[Agent(id="docs")],
    mcp=MCPConfig(default_tools=False, tools=[search_docs]),
)

Calling search_docs through an MCP client returned this structured content (hashes shortened, second hit left out):

{
  "schema_version": 1,
  "results": [
    {
      "schema_version": 1,
      "path": "/agent.md",
      "url": "https://docs.example.com/agent",
      "title": "Agent",
      "revision": "99c5aba1...",
      "chunk_id": "5595243a...",
      "content": "Agent: Use Agent with tools.\n\nAgent\n\n# Agent\n\nUse Agent with tools.",
      "score": 0.9374999990686774,
      "rank": 1
    }
  ],
  "partial": false,
  "truncated": false,
  "omitted_count": 0,
  "warnings": []
}

The search tool takes a query and optional alternatives, and you set its name and description with tool_name and tool_description. Pass async_mode=True for an async tool. Each search also records its references on the run when you pass run_response. Agno exposes none of these tools on its own. You pass each one to Agent.tools or MCPConfig.tools. For a worked example of page tools behind chat and MCP, see how we built the Agno Docs Agent.

See the cookbook, and learn more in the Published Pages reference.

Frequently asked questions

Call PageFileSystem.run_command_result() or arun_command_result(). The returned PageCommandResult sets is_error and lists codes such as page_not_found in errors. Agno sets them from how the command ran, so a page whose text mentions an error still returns is_error=false.

Call knowledge.get_tools(page_results=True, tool_name="search_docs", transport="mcp") and pass the tool to MCPConfig(tools=[...]). The tool publishes the SearchResult output schema and returns ranked hits as structured content.

Yes. max_output_bytes caps the size of the whole UTF-8 JSON result. It defaults to 32000 and can't go below 1024. If Agno has to clip the text, it marks the result truncated=true and drops continuation, so a follow-up command can't skip text you never saw.

Shipped around the same time