[{"data":1,"prerenderedAt":366},["ShallowReactive",2],{"navigation_docs":3,"-hosts-pi":48,"-hosts-pi-surround":307,"host-views":312},[4,24],{"title":5,"path":6,"stem":7,"children":8,"icon":23},"Guide","\u002Fguide","1.guide\u002F01.index",[9,11,15,19],{"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},"Moving a Package Over","\u002Fguide\u002Fmigrating","1.guide\u002F04.migrating","i-lucide-book-open",{"title":25,"path":26,"stem":27,"children":28,"icon":47},"Hosts","\u002Fhosts","2.hosts\u002F00.index",[29,31,35,39,43],{"title":30,"path":26,"stem":27},"Every host",{"title":32,"path":33,"stem":34},"MCP","\u002Fhosts\u002Fmcp","2.hosts\u002F01.mcp",{"title":36,"path":37,"stem":38},"Pi","\u002Fhosts\u002Fpi","2.hosts\u002F02.pi",{"title":40,"path":41,"stem":42},"OMP","\u002Fhosts\u002Fomp","2.hosts\u002F03.omp",{"title":44,"path":45,"stem":46},"AI SDK","\u002Fhosts\u002Fai-sdk","2.hosts\u002F04.ai-sdk","i-lucide-table",{"id":49,"title":36,"body":50,"description":301,"extension":302,"links":303,"meta":304,"navigation":129,"path":37,"seo":305,"stem":38,"__hash__":306},"docs\u002F2.hosts\u002F02.pi.md",{"type":51,"value":52,"toc":297},"minimark",[53,57,171,202,207,218,221,240,251,255,268,290,293],[54,55],"host-facts",{"name":56},"pi",[58,59,65],"pre",{"className":60,"code":61,"filename":62,"language":63,"meta":64,"style":64},"language-ts shiki shiki-themes tools tools tools","import type { ExtensionAPI } from \"@earendil-works\u002Fpi-coding-agent\";\nimport { registerPiTools } from \"@agntn\u002Ftools\u002Fpi\";\nimport { tools } from \"..\u002F..\u002F..\u002Fsrc\u002Ftools.ts\";\n\nexport default function (pi: ExtensionAPI) {\n  registerPiTools(pi, tools);\n}\n","packages\u002Fpi\u002Fextensions\u002Fmypackage.ts","ts","",[66,67,68,94,109,124,131,156,165],"code",{"__ignoreMap":64},[69,70,73,77,80,84,87,91],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"skH_V","import",[69,78,79],{"class":75}," type",[69,81,83],{"class":82},"s38Sx"," { ExtensionAPI } ",[69,85,86],{"class":75},"from",[69,88,90],{"class":89},"shU9J"," \"@earendil-works\u002Fpi-coding-agent\"",[69,92,93],{"class":82},";\n",[69,95,97,99,102,104,107],{"class":71,"line":96},2,[69,98,76],{"class":75},[69,100,101],{"class":82}," { registerPiTools } ",[69,103,86],{"class":75},[69,105,106],{"class":89}," \"@agntn\u002Ftools\u002Fpi\"",[69,108,93],{"class":82},[69,110,112,114,117,119,122],{"class":71,"line":111},3,[69,113,76],{"class":75},[69,115,116],{"class":82}," { tools } ",[69,118,86],{"class":75},[69,120,121],{"class":89}," \"..\u002F..\u002F..\u002Fsrc\u002Ftools.ts\"",[69,123,93],{"class":82},[69,125,127],{"class":71,"line":126},4,[69,128,130],{"emptyLinePlaceholder":129},true,"\n",[69,132,134,137,140,143,146,149,153],{"class":71,"line":133},5,[69,135,136],{"class":75},"export",[69,138,139],{"class":75}," default",[69,141,142],{"class":75}," function",[69,144,145],{"class":82}," (pi",[69,147,148],{"class":75},":",[69,150,152],{"class":151},"sK71F"," ExtensionAPI",[69,154,155],{"class":82},") {\n",[69,157,159,162],{"class":71,"line":158},6,[69,160,161],{"class":151},"  registerPiTools",[69,163,164],{"class":82},"(pi, tools);\n",[69,166,168],{"class":71,"line":167},7,[69,169,170],{"class":82},"}\n",[172,173,174,175,178,179,182,183,186,187,178,190,193,194,197,198,201],"p",{},"Pi gets ",[66,176,177],{},"name",", ",[66,180,181],{},"label"," from ",[66,184,185],{},"title",", the ",[66,188,189],{},"description",[66,191,192],{},"promptSnippet"," and ",[66,195,196],{},"promptGuidelines"," when you set them, and the schema as ",[66,199,200],{},"parameters",". Same JSON Schema every other host gets. Pi's own validator checks it, then the core checks it again, because a second check costs nothing and a missing one costs an afternoon.",[203,204,206],"h2",{"id":205},"failures","Failures",[172,208,209,210,213,214,217],{},"Up to 0.98, Pi reads a returned object as a success no matter what's in it. A tool that returns ",[66,211,212],{},"isError: true"," would look fine to the model. So by default the adapter throws the result's text when ",[66,215,216],{},"isError"," is set, and Pi records an error.",[172,219,220],{},"On Pi 0.99 or newer you can keep the result as it is:",[58,222,224],{"className":60,"code":223,"language":63,"meta":64,"style":64},"registerPiTools(pi, tools, { failures: \"return\" });\n",[66,225,226],{"__ignoreMap":64},[69,227,228,231,234,237],{"class":71,"line":72},[69,229,230],{"class":151},"registerPiTools",[69,232,233],{"class":82},"(pi, tools, { failures: ",[69,235,236],{"class":89},"\"return\"",[69,238,239],{"class":82}," });\n",[172,241,242,243,246,247,250],{},"That keeps ",[66,244,245],{},"details"," on a failure too. Only do it with a peer range of ",[66,248,249],{},">=0.99.0",", since Pi doesn't check peer ranges when it installs a package.",[203,252,254],{"id":253},"renderers","Renderers",[172,256,257,258,193,261,264,265,267],{},"Pass Pi's own ",[66,259,260],{},"renderCall",[66,262,263],{},"renderResult"," per tool through ",[66,266,253],{},". They reach Pi unchanged:",[58,269,271],{"className":60,"code":270,"language":63,"meta":64,"style":64},"registerPiTools(pi, tools, {\n  renderers: { books_text: { renderResult: preview } },\n});\n",[66,272,273,280,285],{"__ignoreMap":64},[69,274,275,277],{"class":71,"line":72},[69,276,230],{"class":151},[69,278,279],{"class":82},"(pi, tools, {\n",[69,281,282],{"class":71,"line":96},[69,283,284],{"class":82},"  renderers: { books_text: { renderResult: preview } },\n",[69,286,287],{"class":71,"line":111},[69,288,289],{"class":82},"});\n",[172,291,292],{},"Handy when a tool returns a lot of text and you want the collapsed row to stay short.",[294,295,296],"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 .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":64,"searchDepth":96,"depth":96,"links":298},[299,300],{"id":205,"depth":96,"text":206},{"id":253,"depth":96,"text":254},"registerPiTools puts every tool on Pi with its prompt snippet and guidelines. And throws on failure because older Pi only listens to throws","md",null,{},{"title":36,"description":301},"TR8TWECu0qSQpsgHzCChBe3gl6RIYx0oUaGAxAcmk6c",[308,310],{"title":32,"path":33,"stem":34,"description":309,"children":-1},"createMcpServer builds an MCP server on SDK v2 for any list of tools. With the error handling the SDK's own McpServer gets wrong",{"title":40,"path":41,"stem":42,"description":311,"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",[313,329,341,354],{"key":314,"rows":315,"registration":326,"fields":327},"mcp",[316,318,321,324],{"label":185,"value":317},"Text Slug",{"label":319,"value":320,"accent":129},"hints","readOnly, idempotent",{"label":322,"value":323},"inputSchema","text, separator",{"label":325,"value":212},"failure","{\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    \"readOnlyHint\": true,\n    \"destructiveHint\": false,\n    \"idempotentHint\": true,\n    \"openWorldHint\": false\n  }\n}",{"name":177,"label":185,"schema":322,"effect":328,"prompt":303,"run":303},"annotations",{"key":56,"rows":330,"registration":338,"fields":339},[331,332,335,336],{"label":181,"value":317},{"label":333,"value":334,"accent":129},"prompt","snippet, 1 rule",{"label":200,"value":323},{"label":325,"value":337},"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":177,"label":181,"schema":200,"effect":303,"prompt":192,"run":340},"execute",{"key":342,"rows":343,"registration":352,"fields":353},"omp",[344,345,348,350],{"label":181,"value":317},{"label":346,"value":347,"accent":129},"approval","read",{"label":200,"value":349},"Type.Unsafe(json)",{"label":325,"value":351},"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":177,"label":181,"schema":200,"effect":346,"prompt":303,"run":340},{"key":355,"rows":356,"registration":364,"fields":365},"ai",[357,358,360,363],{"label":185,"value":317},{"label":322,"value":359,"accent":129},"jsonSchema()",{"label":361,"value":362},"output","details + text",{"label":325,"value":337},"{\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}",{"name":303,"label":185,"schema":322,"effect":303,"prompt":303,"run":340},1790784242042]