[{"data":1,"prerenderedAt":411},["ShallowReactive",2],{"navigation_docs":3,"-hosts-mcp":48,"-hosts-mcp-surround":348,"host-views":353},[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":32,"body":50,"description":342,"extension":343,"links":344,"meta":345,"navigation":126,"path":33,"seo":346,"stem":34,"__hash__":347},"docs\u002F2.hosts\u002F01.mcp.md",{"type":51,"value":52,"toc":337},"minimark",[53,57,183,198,203,218,259,269,273,296,318,322,333],[54,55],"host-facts",{"name":56},"mcp",[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 { createMcpServer } from \"@agntn\u002Ftools\u002Fmcp\";\nimport { StdioServerTransport } from \"@modelcontextprotocol\u002Fserver\u002Fstdio\";\nimport { tools } from \".\u002Ftools.ts\";\n\nconst server = createMcpServer({ name: \"mypackage\", version: \"1.0.0\" }, tools);\nawait server.connect(new StdioServerTransport());\n","server.ts","ts","",[66,67,68,91,106,121,128,159],"code",{"__ignoreMap":64},[69,70,73,77,81,84,88],"span",{"class":71,"line":72},"line",1,[69,74,76],{"class":75},"skH_V","import",[69,78,80],{"class":79},"s38Sx"," { createMcpServer } ",[69,82,83],{"class":75},"from",[69,85,87],{"class":86},"shU9J"," \"@agntn\u002Ftools\u002Fmcp\"",[69,89,90],{"class":79},";\n",[69,92,94,96,99,101,104],{"class":71,"line":93},2,[69,95,76],{"class":75},[69,97,98],{"class":79}," { StdioServerTransport } ",[69,100,83],{"class":75},[69,102,103],{"class":86}," \"@modelcontextprotocol\u002Fserver\u002Fstdio\"",[69,105,90],{"class":79},[69,107,109,111,114,116,119],{"class":71,"line":108},3,[69,110,76],{"class":75},[69,112,113],{"class":79}," { tools } ",[69,115,83],{"class":75},[69,117,118],{"class":86}," \".\u002Ftools.ts\"",[69,120,90],{"class":79},[69,122,124],{"class":71,"line":123},4,[69,125,127],{"emptyLinePlaceholder":126},true,"\n",[69,129,131,134,137,140,144,147,150,153,156],{"class":71,"line":130},5,[69,132,133],{"class":75},"const",[69,135,136],{"class":79}," server ",[69,138,139],{"class":75},"=",[69,141,143],{"class":142},"sK71F"," createMcpServer",[69,145,146],{"class":79},"({ name: ",[69,148,149],{"class":86},"\"mypackage\"",[69,151,152],{"class":79},", version: ",[69,154,155],{"class":86},"\"1.0.0\"",[69,157,158],{"class":79}," }, tools);\n",[69,160,162,165,168,171,174,177,180],{"class":71,"line":161},6,[69,163,164],{"class":75},"await",[69,166,167],{"class":79}," server.",[69,169,170],{"class":142},"connect",[69,172,173],{"class":79},"(",[69,175,176],{"class":75},"new",[69,178,179],{"class":142}," StdioServerTransport",[69,181,182],{"class":79},"());\n",[184,185,186,189,190,193,194,197],"p",{},[66,187,188],{},"createMcpServer(info, tools)"," gives you an unconnected server. One ",[66,191,192],{},"tools\u002Flist"," handler, one ",[66,195,196],{},"tools\u002Fcall"," handler, every tool in the list. Connect it to stdio, to Streamable HTTP or to a pair in memory for tests. This site does the last one, in your tab.",[199,200,202],"h2",{"id":201},"what-the-client-sees","What the client sees",[184,204,205,206,209,210,213,214,217],{},"The schema goes out exactly as you wrote it, ",[66,207,208],{},"additionalProperties: false"," and ",[66,211,212],{},"pattern"," included. The annotations come from ",[66,215,216],{},"effect",":",[219,220,221,231,240,250],"ul",{},[222,223,224,227,228],"li",{},[66,225,226],{},"readOnlyHint"," when the effect is ",[66,229,230],{},"read",[222,232,233,236,237],{},[66,234,235],{},"destructiveHint"," when it's ",[66,238,239],{},"destructive",[222,241,242,245,246,249],{},[66,243,244],{},"idempotentHint"," from ",[66,247,248],{},"idempotent",", which defaults to reads",[222,251,252,245,255,258],{},[66,253,254],{},"openWorldHint",[66,256,257],{},"openWorld",", off unless you say so",[184,260,261,264,265,268],{},[66,262,263],{},"details"," never reaches the client and ",[66,266,267],{},"structuredContent"," is never set. Clients that see structured output tend to prefer it over the text and hide the readable answer, so the text is the answer. Everything the next call needs goes in it.",[199,270,272],{"id":271},"why-not-mcpserver","Why not McpServer",[184,274,275,276,279,280,283,284,287,288,291,292,295],{},"SDK v2's ",[66,277,278],{},"McpServer.registerTool"," would take the schema. It also answers a call to ",[66,281,282],{},"toString",", ",[66,285,286],{},"constructor"," or ",[66,289,290],{},"__proto__"," with \"Tool toString disabled\", because its lookup reaches ",[66,293,294],{},"Object.prototype",". It echoes a tool name with a newline and an escape code straight back to the client. It glues validation problems into one line and passes a thrown message through with its newline intact.",[184,297,298,299,302,303,306,307,309,310,313,314,317],{},"So the adapter sits on the low-level ",[66,300,301],{},"Server",", which the SDK marks ",[66,304,305],{},"@deprecated",", and does those parts itself. An unknown name, ",[66,308,282],{}," included, gets ",[66,311,312],{},"Unknown \u003Cserver> tool: \"\u003Cname>\""," with the name escaped. Validation gets one line per problem. A throw becomes ",[66,315,316],{},"\u003Ctool> failed: \u003Cmessage>"," on one line.",[199,319,321],{"id":320},"errors","Errors",[184,323,324,325,328,329,332],{},"A bad input, a returned ",[66,326,327],{},"isError"," and a thrown error all come back as ",[66,330,331],{},"isError: true"," with text. Nothing escapes as a JSON-RPC error, so the model always gets something to read and a reason to try again.",[334,335,336],"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":93,"depth":93,"links":338},[339,340,341],{"id":201,"depth":93,"text":202},{"id":271,"depth":93,"text":272},{"id":320,"depth":93,"text":321},"createMcpServer builds an MCP server on SDK v2 for any list of tools. With the error handling the SDK's own McpServer gets wrong","md",null,{},{"title":32,"description":342},"G98b7yQli38yJbzxRoWDLUJgT2Oly8kTaNmI0Ze5f9U",[349,351],{"title":30,"path":26,"stem":27,"description":350,"children":-1},"The four hosts one tool definition reaches. What each adapter gives its host and which peer it needs",{"title":36,"path":37,"stem":38,"description":352,"children":-1},"registerPiTools puts every tool on Pi with its prompt snippet and guidelines. And throws on failure because older Pi only listens to throws",[354,371,387,399],{"key":56,"rows":355,"registration":367,"fields":368},[356,359,362,365],{"label":357,"value":358},"title","Text Slug",{"label":360,"value":361,"accent":126},"hints","readOnly, idempotent",{"label":363,"value":364},"inputSchema","text, separator",{"label":366,"value":331},"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":369,"label":357,"schema":363,"effect":370,"prompt":344,"run":344},"name","annotations",{"key":372,"rows":373,"registration":383,"fields":384},"pi",[374,376,379,381],{"label":375,"value":358},"label",{"label":377,"value":378,"accent":126},"prompt","snippet, 1 rule",{"label":380,"value":364},"parameters",{"label":366,"value":382},"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":369,"label":375,"schema":380,"effect":344,"prompt":385,"run":386},"promptSnippet","execute",{"key":388,"rows":389,"registration":397,"fields":398},"omp",[390,391,393,395],{"label":375,"value":358},{"label":392,"value":230,"accent":126},"approval",{"label":380,"value":394},"Type.Unsafe(json)",{"label":366,"value":396},"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":369,"label":375,"schema":380,"effect":392,"prompt":344,"run":386},{"key":400,"rows":401,"registration":409,"fields":410},"ai",[402,403,405,408],{"label":357,"value":358},{"label":363,"value":404,"accent":126},"jsonSchema()",{"label":406,"value":407},"output","details + text",{"label":366,"value":382},"{\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":344,"label":357,"schema":363,"effect":344,"prompt":344,"run":386},1790784241443]