[{"data":1,"prerenderedAt":494},["ShallowReactive",2],{"navigation_docs":3,"-guide-migrating":48,"-guide-migrating-surround":489},[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":20,"body":50,"description":482,"extension":483,"links":484,"meta":485,"navigation":219,"path":21,"seo":487,"stem":22,"__hash__":488},"docs\u002F1.guide\u002F04.migrating.md",{"type":51,"value":52,"toc":474},"minimark",[53,57,66,88,99,103,131,138,142,270,359,362,395,399,463,467,470],[54,55,56],"p",{},"You have a package with an MCP server, an AI SDK file, a Pi extension and an OMP extension. Each declares the same tools again, with its own schema, its own descriptions and its own idea of an error. Here's how to get to one file. Your executors stay exactly as they are, this is only about the layer on top.",[58,59,61,62],"h2",{"id":60},"_1-one-schema-from-the-right-type","1. One schema, from the right ",[63,64,65],"code",{},"Type",[54,67,68,69,71,72,75,76,79,80,83,84,87],{},"Put the schemas in one module and build them with ",[63,70,65],{}," from ",[63,73,74],{},"@agntn\u002Ftools",". Take the types (",[63,77,78],{},"Static",", ",[63,81,82],{},"TObject",") from there too. Close every object with properties. Swap every union of literals for ",[63,85,86],{},"Type.Enum",".",[54,89,90,91,94,95,98],{},"If the old MCP or AI SDK surface used Zod, compare the semantics, not just the shape. Zod's ",[63,92,93],{},".trim().min(1)"," rejects a string of spaces and trims the value. A TypeBox ",[63,96,97],{},"minLength: 1"," accepts spaces. Decide which one you meant, write it down once, and let the executor do the trimming.",[58,100,102],{"id":101},"_2-the-definitions","2. The definitions",[54,104,105,106,109,110,113,114,117,118,121,122,125,126,87],{},"A ",[63,107,108],{},"tools.ts"," with one ",[63,111,112],{},"defineTool"," per tool. Descriptions come from the most complete surface you had. Usually that's MCP, because it had nothing else to say it with. The Pi snippet and guidelines move into ",[63,115,116],{},"snippet"," and ",[63,119,120],{},"guidelines",". ",[63,123,124],{},"execute"," loads the executors lazily, see ",[127,128,130],"a",{"href":129},"\u002Fguide\u002Fdefining-tools#keep-the-executor-lazy","Defining tools",[54,132,133,134,137],{},"Pick one text for the model and use it everywhere. If MCP answered with raw JSON and Pi with a formatted summary, one of them was wrong for somebody. The formatted text plus the data in ",[63,135,136],{},"details"," works for every host.",[58,139,141],{"id":140},"_3-the-surfaces","3. The surfaces",[143,144,150],"pre",{"className":145,"code":146,"filename":147,"language":148,"meta":149,"style":149},"language-ts shiki shiki-themes tools tools tools","import type { Server } from \"@modelcontextprotocol\u002Fserver\";\nimport { createMcpServer as createToolServer } from \"@agntn\u002Ftools\u002Fmcp\";\nimport { tools } from \".\u002Ftools.ts\";\n\nexport function createMcpServer(): Server {\n  return createToolServer({ name: \"mypackage\", version }, tools);\n}\n","src\u002Fmcp.ts","ts","",[63,151,152,178,199,214,221,246,264],{"__ignoreMap":149},[153,154,157,161,164,168,171,175],"span",{"class":155,"line":156},"line",1,[153,158,160],{"class":159},"skH_V","import",[153,162,163],{"class":159}," type",[153,165,167],{"class":166},"s38Sx"," { Server } ",[153,169,170],{"class":159},"from",[153,172,174],{"class":173},"shU9J"," \"@modelcontextprotocol\u002Fserver\"",[153,176,177],{"class":166},";\n",[153,179,181,183,186,189,192,194,197],{"class":155,"line":180},2,[153,182,160],{"class":159},[153,184,185],{"class":166}," { createMcpServer ",[153,187,188],{"class":159},"as",[153,190,191],{"class":166}," createToolServer } ",[153,193,170],{"class":159},[153,195,196],{"class":173}," \"@agntn\u002Ftools\u002Fmcp\"",[153,198,177],{"class":166},[153,200,202,204,207,209,212],{"class":155,"line":201},3,[153,203,160],{"class":159},[153,205,206],{"class":166}," { tools } ",[153,208,170],{"class":159},[153,210,211],{"class":173}," \".\u002Ftools.ts\"",[153,213,177],{"class":166},[153,215,217],{"class":155,"line":216},4,[153,218,220],{"emptyLinePlaceholder":219},true,"\n",[153,222,224,227,230,234,237,240,243],{"class":155,"line":223},5,[153,225,226],{"class":159},"export",[153,228,229],{"class":159}," function",[153,231,233],{"class":232},"sK71F"," createMcpServer",[153,235,236],{"class":166},"()",[153,238,239],{"class":159},":",[153,241,242],{"class":232}," Server",[153,244,245],{"class":166}," {\n",[153,247,249,252,255,258,261],{"class":155,"line":248},6,[153,250,251],{"class":159},"  return",[153,253,254],{"class":232}," createToolServer",[153,256,257],{"class":166},"({ name: ",[153,259,260],{"class":173},"\"mypackage\"",[153,262,263],{"class":166},", version }, tools);\n",[153,265,267],{"class":155,"line":266},7,[153,268,269],{"class":166},"}\n",[143,271,274],{"className":145,"code":272,"filename":273,"language":148,"meta":149,"style":149},"import { toAiTool, type AiToolOutput } from \"@agntn\u002Ftools\u002Fai\";\nimport type { Tool } from \"ai\";\n\nexport const searchTool: Tool\u003CSearchParams, AiToolOutput\u003CSearchDetails>> = toAiTool(searchDefinition);\n","src\u002Fai.ts",[63,275,276,296,312,316],{"__ignoreMap":149},[153,277,278,280,283,286,289,291,294],{"class":155,"line":156},[153,279,160],{"class":159},[153,281,282],{"class":166}," { toAiTool, ",[153,284,285],{"class":159},"type",[153,287,288],{"class":166}," AiToolOutput } ",[153,290,170],{"class":159},[153,292,293],{"class":173}," \"@agntn\u002Ftools\u002Fai\"",[153,295,177],{"class":166},[153,297,298,300,302,305,307,310],{"class":155,"line":180},[153,299,160],{"class":159},[153,301,163],{"class":159},[153,303,304],{"class":166}," { Tool } ",[153,306,170],{"class":159},[153,308,309],{"class":173}," \"ai\"",[153,311,177],{"class":166},[153,313,314],{"class":155,"line":201},[153,315,220],{"emptyLinePlaceholder":219},[153,317,318,320,323,326,328,331,334,337,339,342,344,347,350,353,356],{"class":155,"line":216},[153,319,226],{"class":159},[153,321,322],{"class":159}," const",[153,324,325],{"class":166}," searchTool",[153,327,239],{"class":159},[153,329,330],{"class":232}," Tool",[153,332,333],{"class":166},"\u003C",[153,335,336],{"class":232},"SearchParams",[153,338,79],{"class":166},[153,340,341],{"class":232},"AiToolOutput",[153,343,333],{"class":166},[153,345,346],{"class":232},"SearchDetails",[153,348,349],{"class":166},">> ",[153,351,352],{"class":159},"=",[153,354,355],{"class":232}," toAiTool",[153,357,358],{"class":166},"(searchDefinition);\n",[54,360,361],{},"Annotate exported AI SDK tools with your own parameter and details types. Inferred, the type points into the bundled TypeBox and your declaration build can't name it.",[54,363,364,365,368,369,372,373,376,377,380,381,384,385,117,388,391,392,87],{},"The Pi extension becomes one ",[63,366,367],{},"registerPiTools(pi, tools)"," call. The OMP one is ",[63,370,371],{},"registerOmpTools(pi, tools, { Text })",", with ",[63,374,375],{},"Text"," imported from ",[63,378,379],{},"@oh-my-pi\u002Fpi-coding-agent"," in the extension file itself. Status line summaries go into ",[63,382,383],{},"renderers"," with ",[63,386,387],{},"describeCall",[63,389,390],{},"describeResult",". A result preview of your own goes in as ",[63,393,394],{},"renderResult",[58,396,398],{"id":397},"_4-the-traps-in-the-order-they-bit","4. The traps, in the order they bit",[400,401,402,422,439,449,460],"ul",{},[403,404,405,406,409,410,413,414,417,418,421],"li",{},"MCP SDK v2. ",[63,407,408],{},"@modelcontextprotocol\u002Fsdk"," becomes ",[63,411,412],{},"@modelcontextprotocol\u002Fserver",", the stdio transport comes from ",[63,415,416],{},"@modelcontextprotocol\u002Fserver\u002Fstdio"," and the test client from ",[63,419,420],{},"@modelcontextprotocol\u002Fclient",". The dependency list shrinks a lot on the way.",[403,423,105,424,427,428,431,432,434,435,438],{},[63,425,426],{},"text"," field in details. The AI SDK output is ",[63,429,430],{},"{ ...details, text }",", so a details object with its own ",[63,433,426],{}," would get overwritten. The adapter throws instead. Rename it (",[63,436,437],{},"slice"," works).",[403,440,441,444,445,448],{},[63,442,443],{},"\u002Ftui"," in the OMP extension. Compiled OMP gives an extension only the package root. An import from ",[63,446,447],{},"@oh-my-pi\u002Fpi-coding-agent\u002Ftui"," stops the whole extension from loading. The adapter draws the status line with the host theme, so you don't need it.",[403,450,451,452,455,456,459],{},"A test double for OMP's TypeBox. Standalone omptype's ",[63,453,454],{},"Type.Unsafe"," hands back one shared object for every call. Patch ",[63,457,458],{},"safeParse"," on it and every tool validates against whichever schema registered last. Give each document its own callable.",[403,461,462],{},"Providers that register on import. If your MCP server used to import the providers, the lazy executor now does it on the first call. A test that looks a provider up before any call has to import them itself.",[58,464,466],{"id":465},"_5-prove-it","5. Prove it",[54,468,469],{},"Your existing tests are the contract. They should pass with at most the error text changed, because the validation lines now come from the core. Then load the extensions in the real hosts, from a checkout and from a packed tarball, and call a tool in each. A green unit suite says nothing about whether compiled OMP agrees to load your extension. Ask OMP.",[471,472,473],"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":149,"searchDepth":180,"depth":180,"links":475},[476,478,479,480,481],{"id":60,"depth":180,"text":477},"1. One schema, from the right Type",{"id":101,"depth":180,"text":102},{"id":140,"depth":180,"text":141},{"id":397,"depth":180,"text":398},{"id":465,"depth":180,"text":466},"Take a package that declares its tools four times and bring it down to one definition. Step by step with the traps marked","md",null,{"icon":486},"i-lucide-layers",{"title":20,"description":482},"mt_Z5-khWv8_zchVAIkXifJC6Iz-Iq8445fI2_hDFpc",[490,492],{"title":16,"path":17,"stem":18,"description":491,"children":-1},"How the core checks every call and how each host reports a failure. And why error text never gets a second line",{"title":30,"path":26,"stem":27,"description":493,"children":-1},"The four hosts one tool definition reaches. What each adapter gives its host and which peer it needs",1790784242012]