[{"data":1,"prerenderedAt":362},["ShallowReactive",2],{"navigation_docs":3,"-hosts-ai-sdk":52,"-hosts-ai-sdk-surround":301,"host-views":304},[4,28],{"title":5,"path":6,"stem":7,"children":8,"icon":27},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19,23],{"title":10,"path":6,"stem":7},"Getting Started",{"title":12,"path":13,"stem":14},"Defining Tools","\u002Fguide\u002Fdefining-tools","1.guide\u002F02.defining-tools",{"title":16,"path":17,"stem":18},"Validation and Errors","\u002Fguide\u002Fvalidation","1.guide\u002F03.validation",{"title":20,"path":21,"stem":22},"CLI","\u002Fguide\u002Fcli","1.guide\u002F04.cli",{"title":24,"path":25,"stem":26},"Moving a Package Over","\u002Fguide\u002Fmigrating","1.guide\u002F05.migrating","i-lucide-book-open",{"title":29,"path":30,"stem":31,"children":32,"icon":51},"Hosts","\u002Fhosts","2.hosts\u002F00.index",[33,35,39,43,47],{"title":34,"path":30,"stem":31},"Every host",{"title":36,"path":37,"stem":38},"MCP","\u002Fhosts\u002Fmcp","2.hosts\u002F01.mcp",{"title":40,"path":41,"stem":42},"Pi","\u002Fhosts\u002Fpi","2.hosts\u002F02.pi",{"title":44,"path":45,"stem":46},"OMP","\u002Fhosts\u002Fomp","2.hosts\u002F03.omp",{"title":48,"path":49,"stem":50},"AI SDK","\u002Fhosts\u002Fai-sdk","2.hosts\u002F04.ai-sdk","i-lucide-table",{"id":53,"title":48,"body":54,"description":295,"extension":296,"links":297,"meta":298,"navigation":115,"path":49,"seo":299,"stem":50,"__hash__":300},"docs\u002F2.hosts\u002F04.ai-sdk.md",{"type":55,"value":56,"toc":288},"minimark",[57,61,156,164,169,191,209,212,222,226,236,240,257,261,270,274,284],[58,59],"host-facts",{"name":60},"ai",[62,63,69],"pre",{"className":64,"code":65,"filename":66,"language":67,"meta":68,"style":68},"language-ts shiki shiki-themes tools tools tools","import { toAiTool, toAiTools } from \"@agntn\u002Ftools\u002Fai\";\nimport { slugTool, tools } from \".\u002Ftools.ts\";\n\nconst one = toAiTool(slugTool);\nconst all = toAiTools(tools); \u002F\u002F keyed by tool name\n","agent.ts","ts","",[70,71,72,95,110,117,136],"code",{"__ignoreMap":68},[73,74,77,81,85,88,92],"span",{"class":75,"line":76},"line",1,[73,78,80],{"class":79},"skH_V","import",[73,82,84],{"class":83},"s38Sx"," { toAiTool, toAiTools } ",[73,86,87],{"class":79},"from",[73,89,91],{"class":90},"shU9J"," \"@agntn\u002Ftools\u002Fai\"",[73,93,94],{"class":83},";\n",[73,96,98,100,103,105,108],{"class":75,"line":97},2,[73,99,80],{"class":79},[73,101,102],{"class":83}," { slugTool, tools } ",[73,104,87],{"class":79},[73,106,107],{"class":90}," \".\u002Ftools.ts\"",[73,109,94],{"class":83},[73,111,113],{"class":75,"line":112},3,[73,114,116],{"emptyLinePlaceholder":115},true,"\n",[73,118,120,123,126,129,133],{"class":75,"line":119},4,[73,121,122],{"class":79},"const",[73,124,125],{"class":83}," one ",[73,127,128],{"class":79},"=",[73,130,132],{"class":131},"sK71F"," toAiTool",[73,134,135],{"class":83},"(slugTool);\n",[73,137,139,141,144,146,149,152],{"class":75,"line":138},5,[73,140,122],{"class":79},[73,142,143],{"class":83}," all ",[73,145,128],{"class":79},[73,147,148],{"class":131}," toAiTools",[73,150,151],{"class":83},"(tools); ",[73,153,155],{"class":154},"scIB-","\u002F\u002F keyed by tool name\n",[157,158,159,160,163],"p",{},"The schema goes through the AI SDK's ",[70,161,162],{},"jsonSchema()"," with the core's validator attached. So the model sees the same JSON Schema every other host sees, and a bad input gets refused with the same lines. No second schema in Zod. That used to be the fourth copy.",[165,166,168],"h2",{"id":167},"the-output","The output",[157,170,171,174,175,178,179,182,183,186,187,190],{},[70,172,173],{},"execute"," returns the details with the text added: ",[70,176,177],{},"{ ...details, text }",". When ",[70,180,181],{},"details"," isn't an object, you get ",[70,184,185],{},"{ details, text }",". That's the object your code finds in ",[70,188,189],{},"toolResults",".",[157,192,193,194,197,198,201,202,205,206,208],{},"The model gets less. ",[70,195,196],{},"toModelOutput"," hands it ",[70,199,200],{},"text"," and nothing else, the same text MCP, Pi and OMP put in front of their models. Why not the whole object? The text already says it. A ",[70,203,204],{},"books_text"," slice used to land twice. Once as text, once escaped as JSON inside ",[70,207,181],{},". That came to 2.1 times the characters for one passage. Paying twice to read the same book is a strange hobby.",[157,210,211],{},"No text at all, an image and nothing else? Then the model gets the whole object as JSON, same as before.",[157,213,214,215,217,218,221],{},"A details object with its own ",[70,216,200],{}," field would lose it under the spread, so the adapter throws a ",[70,219,220],{},"ToolDefinitionError"," instead of guessing. Rename the field.",[165,223,225],{"id":224},"failures","Failures",[157,227,228,229,231,232,235],{},"A bad input is refused by the input schema before ",[70,230,173],{},". A result with ",[70,233,234],{},"isError"," throws its text, and the AI SDK turns any throw into a tool error the model can read.",[165,237,239],{"id":238},"progress","Progress",[157,241,242,243,245,246,249,250,252,253,256],{},"There's nowhere to put it. ",[70,244,238],{}," is ",[70,247,248],{},"undefined"," in ",[70,251,173],{},", and a tool that calls it with ",[70,254,255],{},"?."," just keeps going.",[165,258,260],{"id":259},"questions","Questions",[157,262,263,264,245,267,269],{},"Same story. The AI SDK has no person on the line, so ",[70,265,266],{},"ask",[70,268,248],{},". Who'd answer? A tool that checks first does its best without one.",[165,271,273],{"id":272},"types","Types",[157,275,276,279,280,283],{},[70,277,278],{},"toAiTool"," keeps the input and details types. When you export the tool from a package, annotate it with your own types, ",[70,281,282],{},"Tool\u003CSearchParams, AiToolOutput\u003CSearchDetails>>",". The inferred type points into the bundled TypeBox, and your declaration build can't name it.",[285,286,287],"style",{},"html pre.shiki code .skH_V, html code.shiki .skH_V{--shiki-light:var(--shiki-token-keyword);--shiki-default:var(--shiki-token-keyword);--shiki-dark:var(--shiki-token-keyword)}html pre.shiki code .s38Sx, html code.shiki .s38Sx{--shiki-light:var(--ui-text-highlighted);--shiki-default:var(--ui-text-highlighted);--shiki-dark:var(--ui-text-highlighted)}html pre.shiki code .shU9J, html code.shiki .shU9J{--shiki-light:var(--shiki-token-string);--shiki-default:var(--shiki-token-string);--shiki-dark:var(--shiki-token-string)}html pre.shiki code .sK71F, html code.shiki .sK71F{--shiki-light:var(--shiki-token-function);--shiki-default:var(--shiki-token-function);--shiki-dark:var(--shiki-token-function)}html pre.shiki code .scIB-, html code.shiki .scIB-{--shiki-light:var(--shiki-token-comment);--shiki-default:var(--shiki-token-comment);--shiki-dark:var(--shiki-token-comment)}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":68,"searchDepth":97,"depth":97,"links":289},[290,291,292,293,294],{"id":167,"depth":97,"text":168},{"id":224,"depth":97,"text":225},{"id":238,"depth":97,"text":239},{"id":259,"depth":97,"text":260},{"id":272,"depth":97,"text":273},"toAiTool turns a definition into an AI SDK tool. The TypeBox schema goes in as it is and nobody writes it again in Zod","md",null,{},{"title":48,"description":295},"JAqRjq5Q-6d60itOFXjXKn2qE_aMYL8g1XuIbmZpkUU",[302,297],{"title":44,"path":45,"stem":46,"description":303,"children":-1},"registerOmpTools puts every tool on OMP with an approval tier and a status line. Built around the TypeBox rewrite OMP does behind your back",[305,324,339,352],{"key":306,"rows":307,"registration":320,"fields":321},"mcp",[308,311,314,317],{"label":309,"value":310},"title","Text Slug",{"label":312,"value":313,"accent":115},"hints","readOnly, idempotent",{"label":315,"value":316},"inputSchema","text, separator",{"label":318,"value":319},"failure","isError: true","{\n  \"name\": \"text_slug\",\n  \"title\": \"Text Slug\",\n  \"description\": \"Turn a title into a lowercase URL slug. Letters and digits stay, accents lose their marks, the rest becomes the separator.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"properties\": {\n      \"text\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"maxLength\": 200,\n        \"pattern\": \"\\\\S\",\n        \"description\": \"Title to slug\"\n      },\n      \"separator\": {\n        \"enum\": [\n          \"-\",\n          \"_\",\n          \".\"\n        ],\n        \"description\": \"Joins the words. '-' when omitted\"\n      }\n    },\n    \"required\": [\n      \"text\"\n    ],\n    \"additionalProperties\": false\n  },\n  \"annotations\": {\n    \"title\": \"Text Slug\",\n    \"readOnlyHint\": true,\n    \"destructiveHint\": false,\n    \"idempotentHint\": true,\n    \"openWorldHint\": false\n  }\n}",{"name":322,"label":309,"schema":315,"effect":323,"prompt":297,"run":297},"name","annotations",{"key":325,"rows":326,"registration":336,"fields":337},"pi",[327,329,332,334],{"label":328,"value":310},"label",{"label":330,"value":331,"accent":115},"prompt","snippet, 1 rule",{"label":333,"value":316},"parameters",{"label":318,"value":335},"thrown","{\n  \"name\": \"text_slug\",\n  \"label\": \"Text Slug\",\n  \"description\": \"Turn a title into a lowercase URL slug. Letters and digits stay, accents lose their marks, the rest becomes the separator.\",\n  \"promptSnippet\": \"Use text_slug to turn a title into a URL slug.\",\n  \"promptGuidelines\": [\n    \"Pass separator '_' for identifiers. The default '-' is for URLs.\"\n  ],\n  \"parameters\": {\n    \"type\": \"object\",\n    \"required\": [\n      \"text\"\n    ],\n    \"properties\": {\n      \"text\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"maxLength\": 200,\n        \"pattern\": \"\\\\S\",\n        \"description\": \"Title to slug\"\n      },\n      \"separator\": {\n        \"enum\": [\n          \"-\",\n          \"_\",\n          \".\"\n        ],\n        \"description\": \"Joins the words. '-' when omitted\"\n      }\n    },\n    \"additionalProperties\": false\n  },\n  \"execute\": \"[function]\"\n}",{"name":322,"label":328,"schema":333,"effect":297,"prompt":338,"run":173},"promptSnippet",{"key":340,"rows":341,"registration":350,"fields":351},"omp",[342,343,346,348],{"label":328,"value":310},{"label":344,"value":345,"accent":115},"approval","read",{"label":333,"value":347},"Type.Unsafe(json)",{"label":318,"value":349},"isError or thrown","{\n  \"name\": \"text_slug\",\n  \"label\": \"Text Slug\",\n  \"description\": \"Turn a title into a lowercase URL slug. Letters and digits stay, accents lose their marks, the rest becomes the separator.\",\n  \"parameters\": {\n    \"unsafe\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"text\"\n      ],\n      \"properties\": {\n        \"text\": {\n          \"type\": \"string\",\n          \"minLength\": 1,\n          \"maxLength\": 200,\n          \"pattern\": \"\\\\S\",\n          \"description\": \"Title to slug\"\n        },\n        \"separator\": {\n          \"enum\": [\n            \"-\",\n            \"_\",\n            \".\"\n          ],\n          \"description\": \"Joins the words. '-' when omitted\"\n        }\n      },\n      \"additionalProperties\": false\n    }\n  },\n  \"approval\": \"read\",\n  \"execute\": \"[function]\",\n  \"renderCall\": \"[function]\",\n  \"renderResult\": \"[function]\"\n}",{"name":322,"label":328,"schema":333,"effect":344,"prompt":297,"run":173},{"key":60,"rows":353,"registration":360,"fields":361},[354,355,356,359],{"label":309,"value":310},{"label":315,"value":162,"accent":115},{"label":357,"value":358},"output","details + text",{"label":318,"value":335},"{\n  \"title\": \"Text Slug\",\n  \"description\": \"Turn a title into a lowercase URL slug. Letters and digits stay, accents lose their marks, the rest becomes the separator.\",\n  \"inputSchema\": {\n    \"type\": \"object\",\n    \"required\": [\n      \"text\"\n    ],\n    \"properties\": {\n      \"text\": {\n        \"type\": \"string\",\n        \"minLength\": 1,\n        \"maxLength\": 200,\n        \"pattern\": \"\\\\S\",\n        \"description\": \"Title to slug\"\n      },\n      \"separator\": {\n        \"enum\": [\n          \"-\",\n          \"_\",\n          \".\"\n        ],\n        \"description\": \"Joins the words. '-' when omitted\"\n      }\n    },\n    \"additionalProperties\": false\n  },\n  \"execute\": \"[function]\",\n  \"toModelOutput\": \"[function]\"\n}",{"name":297,"label":309,"schema":315,"effect":297,"prompt":297,"run":173},1791624638305]