[{"data":1,"prerenderedAt":852},["ShallowReactive",2],{"navigation_docs":3,"-guide-defining-tools":48,"-guide-defining-tools-surround":847},[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":12,"body":50,"description":840,"extension":841,"links":842,"meta":843,"navigation":99,"path":13,"seo":845,"stem":14,"__hash__":846},"docs\u002F1.guide\u002F02.defining-tools.md",{"type":51,"value":52,"toc":829},"minimark",[53,61,312,317,490,512,519,558,569,588,592,597,602,625,629,647,651,665,669,672,822,825],[54,55,56,60],"p",{},[57,58,59],"code",{},"defineTool"," takes one object and hands it back typed. It also checks the schema on the spot, so a mistake shows up when the module loads, not when a model finally calls the tool.",[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 { defineTool, Type } from \"@agntn\u002Ftools\";\n\nexport const slugTool = defineTool({\n  name: \"text_slug\",\n  title: \"Text Slug\",\n  description: \"Turn a title into a lowercase URL slug.\",\n  snippet: \"Use text_slug to turn a title into a URL slug.\",\n  guidelines: [\"Pass separator '_' for identifiers. The default '-' is for URLs.\"],\n  effect: \"read\",\n  input: Type.Object(\n    {\n      text: Type.String({ minLength: 1, maxLength: 200, pattern: \"\\\\S\" }),\n      separator: Type.Optional(Type.Enum([\"-\", \"_\", \".\"])),\n    },\n    { additionalProperties: false },\n  ),\n  execute: ({ text, separator = \"-\" }) => slugify(text, separator),\n});\n","tools.ts","ts","",[57,70,71,94,101,123,135,146,157,168,180,191,203,209,227,262,268,274,280,306],{"__ignoreMap":68},[72,73,76,80,84,87,91],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"skH_V","import",[72,81,83],{"class":82},"s38Sx"," { defineTool, Type } ",[72,85,86],{"class":78},"from",[72,88,90],{"class":89},"shU9J"," \"@agntn\u002Ftools\"",[72,92,93],{"class":82},";\n",[72,95,97],{"class":74,"line":96},2,[72,98,100],{"emptyLinePlaceholder":99},true,"\n",[72,102,104,107,110,113,116,120],{"class":74,"line":103},3,[72,105,106],{"class":78},"export",[72,108,109],{"class":78}," const",[72,111,112],{"class":82}," slugTool ",[72,114,115],{"class":78},"=",[72,117,119],{"class":118},"sK71F"," defineTool",[72,121,122],{"class":82},"({\n",[72,124,126,129,132],{"class":74,"line":125},4,[72,127,128],{"class":82},"  name: ",[72,130,131],{"class":89},"\"text_slug\"",[72,133,134],{"class":82},",\n",[72,136,138,141,144],{"class":74,"line":137},5,[72,139,140],{"class":82},"  title: ",[72,142,143],{"class":89},"\"Text Slug\"",[72,145,134],{"class":82},[72,147,149,152,155],{"class":74,"line":148},6,[72,150,151],{"class":82},"  description: ",[72,153,154],{"class":89},"\"Turn a title into a lowercase URL slug.\"",[72,156,134],{"class":82},[72,158,160,163,166],{"class":74,"line":159},7,[72,161,162],{"class":82},"  snippet: ",[72,164,165],{"class":89},"\"Use text_slug to turn a title into a URL slug.\"",[72,167,134],{"class":82},[72,169,171,174,177],{"class":74,"line":170},8,[72,172,173],{"class":82},"  guidelines: [",[72,175,176],{"class":89},"\"Pass separator '_' for identifiers. The default '-' is for URLs.\"",[72,178,179],{"class":82},"],\n",[72,181,183,186,189],{"class":74,"line":182},9,[72,184,185],{"class":82},"  effect: ",[72,187,188],{"class":89},"\"read\"",[72,190,134],{"class":82},[72,192,194,197,200],{"class":74,"line":193},10,[72,195,196],{"class":82},"  input: Type.",[72,198,199],{"class":118},"Object",[72,201,202],{"class":82},"(\n",[72,204,206],{"class":74,"line":205},11,[72,207,208],{"class":82},"    {\n",[72,210,212,215,218,221,224],{"class":74,"line":211},12,[72,213,214],{"class":82},"      text: Type.",[72,216,217],{"class":118},"String",[72,219,220],{"class":82},"({ minLength: 1, maxLength: 200, pattern: ",[72,222,223],{"class":89},"\"\\\\S\"",[72,225,226],{"class":82}," }),\n",[72,228,230,233,236,239,242,245,248,251,254,256,259],{"class":74,"line":229},13,[72,231,232],{"class":82},"      separator: Type.",[72,234,235],{"class":118},"Optional",[72,237,238],{"class":82},"(Type.",[72,240,241],{"class":118},"Enum",[72,243,244],{"class":82},"([",[72,246,247],{"class":89},"\"-\"",[72,249,250],{"class":82},", ",[72,252,253],{"class":89},"\"_\"",[72,255,250],{"class":82},[72,257,258],{"class":89},"\".\"",[72,260,261],{"class":82},"])),\n",[72,263,265],{"class":74,"line":264},14,[72,266,267],{"class":82},"    },\n",[72,269,271],{"class":74,"line":270},15,[72,272,273],{"class":82},"    { additionalProperties: false },\n",[72,275,277],{"class":74,"line":276},16,[72,278,279],{"class":82},"  ),\n",[72,281,283,286,289,291,294,297,300,303],{"class":74,"line":282},17,[72,284,285],{"class":118},"  execute",[72,287,288],{"class":82},": ({ text, separator ",[72,290,115],{"class":78},[72,292,293],{"class":89}," \"-\"",[72,295,296],{"class":82}," }) ",[72,298,299],{"class":78},"=>",[72,301,302],{"class":118}," slugify",[72,304,305],{"class":82},"(text, separator),\n",[72,307,309],{"class":74,"line":308},18,[72,310,311],{"class":82},"});\n",[313,314,316],"h2",{"id":315},"the-fields","The fields",[318,319,320,333],"table",{},[321,322,323],"thead",{},[324,325,326,330],"tr",{},[327,328,329],"th",{},"Field",[327,331,332],{},"What it's for",[334,335,336,355,371,381,395,408,428,446,458,472],"tbody",{},[324,337,338,344],{},[339,340,341],"td",{},[57,342,343],{},"name",[339,345,346,347,350,351,354],{},"The wire name. Lowercase letters, digits and ",[57,348,349],{},"_",", starting with a letter, up to 64 characters. ",[57,352,353],{},"\u003Cpackage>_\u003Coperation>"," reads best",[324,356,357,362],{},[339,358,359],{},[57,360,361],{},"title",[339,363,364,365,367,368],{},"The human label. MCP calls it ",[57,366,361],{},", Pi and OMP call it ",[57,369,370],{},"label",[324,372,373,378],{},[339,374,375],{},[57,376,377],{},"description",[339,379,380],{},"The whole instruction for the model. An MCP client sees nothing else, so don't save it for the guidelines",[324,382,383,388],{},[339,384,385],{},[57,386,387],{},"snippet",[339,389,390,391,394],{},"Pi's ",[57,392,393],{},"promptSnippet",". Optional",[324,396,397,402],{},[339,398,399],{},[57,400,401],{},"guidelines",[339,403,390,404,407],{},[57,405,406],{},"promptGuidelines",". Optional. They may add advice, never a promise the description doesn't make",[324,409,410,415],{},[339,411,412],{},[57,413,414],{},"effect",[339,416,417,250,420,423,424,427],{},[57,418,419],{},"read",[57,421,422],{},"write"," or ",[57,425,426],{},"destructive",". Drives the MCP hints and the OMP approval tier",[324,429,430,435],{},[339,431,432],{},[57,433,434],{},"idempotent",[339,436,437,438,441,442,445],{},"Whether calling twice changes nothing more. Defaults to ",[57,439,440],{},"effect === \"read\"",". Set it to ",[57,443,444],{},"false"," for a read that draws something random",[324,447,448,453],{},[339,449,450],{},[57,451,452],{},"openWorld",[339,454,455,456],{},"Whether the call leaves the machine. Defaults to ",[57,457,444],{},[324,459,460,465],{},[339,461,462],{},[57,463,464],{},"input",[339,466,467,468,471],{},"A TypeBox object schema built with ",[57,469,470],{},"Type"," from this package",[324,473,474,479],{},[339,475,476],{},[57,477,478],{},"execute",[339,480,481,482,485,486,489],{},"Gets the checked input and ",[57,483,484],{},"{ signal }",", returns a ",[57,487,488],{},"ToolResult"," or a promise of one",[54,491,492,493,495,496,499,500,503,504,507,508,511],{},"A ",[57,494,488],{}," is ",[57,497,498],{},"{ content, details, isError? }",". ",[57,501,502],{},"content"," is what the model reads, text blocks or an image. ",[57,505,506],{},"details"," is the same facts as data, for the hosts that keep it. ",[57,509,510],{},"isError"," marks an answer that's a failure but not a crash, like \"nothing to slug in there\".",[313,513,515,516,518],{"id":514},"import-type-from-here-really","Import ",[57,517,470],{}," from here. Really",[62,520,522],{"className":64,"code":521,"language":67,"meta":68,"style":68},"import { Type } from \"@agntn\u002Ftools\"; \u002F\u002F yes\nimport { Type } from \"typebox\"; \u002F\u002F no, and OMP is why\n",[57,523,524,542],{"__ignoreMap":68},[72,525,526,528,531,533,535,538],{"class":74,"line":75},[72,527,79],{"class":78},[72,529,530],{"class":82}," { Type } ",[72,532,86],{"class":78},[72,534,90],{"class":89},[72,536,537],{"class":82},"; ",[72,539,541],{"class":540},"scIB-","\u002F\u002F yes\n",[72,543,544,546,548,550,553,555],{"class":74,"line":96},[72,545,79],{"class":78},[72,547,530],{"class":82},[72,549,86],{"class":78},[72,551,552],{"class":89}," \"typebox\"",[72,554,537],{"class":82},[72,556,557],{"class":540},"\u002F\u002F no, and OMP is why\n",[54,559,560,561,564,565,568],{},"OMP rewrites every bare ",[57,562,563],{},"typebox"," import in an extension, and in everything that extension imports, to its own omptype facade. Its schemas are functions, not JSON Schema. A validator from ",[57,566,567],{},"typebox\u002Fvalue",", which OMP leaves alone, then looks at a function and happily accepts any input you throw at it. No error, no warning, just validation quietly gone.",[54,570,571,574,575,577,578,580,581,250,584,587],{},[57,572,573],{},"@agntn\u002Ftools"," bundles its own TypeBox, so there's no bare import left for OMP to touch. ",[57,576,59],{}," also refuses a schema that turned out to be a function and tells you where the ",[57,579,470],{}," came from. The schema types come from here too (",[57,582,583],{},"Static",[57,585,586],{},"TObject"," and the rest), so your declarations name the same TypeBox the runtime uses.",[313,589,591],{"id":590},"the-two-schema-rules","The two schema rules",[54,593,594,596],{},[57,595,59],{}," walks the whole schema: properties, record values, array items and union branches. Two things fail it.",[598,599,601],"h3",{"id":600},"an-open-object-with-properties","An open object with properties",[54,603,604,605,608,609,612,613,616,617,620,621,624],{},"Every object that declares keys must say ",[57,606,607],{},"additionalProperties: false",". Otherwise a misspelled key just disappears. ",[57,610,611],{},"read_only"," instead of ",[57,614,615],{},"readOnly"," once ran a tool with full access, and nothing complained. An empty ",[57,618,619],{},"Type.Object({})"," stays open, because models like to send ",[57,622,623],{},"{ _: \"\" }"," to a tool without arguments and that's harmless.",[598,626,628],{"id":627},"a-union-of-literals","A union of literals",[54,630,631,634,635,638,639,642,643,646],{},[57,632,633],{},"Type.Union([Type.Literal(\"a\"), Type.Literal(\"b\")])"," has to be ",[57,636,637],{},"Type.Enum([\"a\", \"b\"])",". The union serializes as a pile of ",[57,640,641],{},"anyOf"," objects about three times the size, and when it fails TypeBox says \"must be equal to constant\" about the first literal only. The enum says ",[57,644,645],{},"must be one of a, b",". The model reads that and picks one. Good model.",[313,648,650],{"id":649},"optional-text-that-may-come-empty","Optional text that may come empty",[54,652,653,654,657,658,660,661,664],{},"Some hosts fill every optional field, and an empty string is what they fill it with. If an empty value should mean \"not given\", let the schema accept it: no ",[57,655,656],{},"minLength"," on an optional string. Then trim in ",[57,659,478],{}," and treat blank as absent. A schema that refuses ",[57,662,663],{},"\"\""," pushes the model into inventing something, and invented arguments are worse than missing ones.",[313,666,668],{"id":667},"keep-the-executor-lazy","Keep the executor lazy",[54,670,671],{},"A Pi or OMP extension imports your tools at startup to register them. If the tools module imports your whole library, every agent start pays for it, even when nobody calls the tool. Load the heavy part on the first call:",[62,673,675],{"className":64,"code":674,"filename":66,"language":67,"meta":68,"style":68},"let operations: Promise\u003Ctypeof import(\".\u002Ftool-operations.ts\")> | undefined;\n\nfunction loadOperations() {\n  operations ??= import(\".\u002Ftool-operations.ts\");\n  return operations;\n}\n\nexport const hashTool = defineTool({\n  \u002F\u002F …\n  execute: async (params) => (await loadOperations()).hashCompute(params),\n});\n",[57,676,677,717,721,732,749,757,762,766,781,786,818],{"__ignoreMap":68},[72,678,679,682,685,688,691,694,697,700,703,706,709,712,715],{"class":74,"line":75},[72,680,681],{"class":78},"let",[72,683,684],{"class":82}," operations",[72,686,687],{"class":78},":",[72,689,690],{"class":118}," Promise",[72,692,693],{"class":82},"\u003C",[72,695,696],{"class":78},"typeof",[72,698,699],{"class":78}," import",[72,701,702],{"class":82},"(",[72,704,705],{"class":89},"\".\u002Ftool-operations.ts\"",[72,707,708],{"class":82},")> ",[72,710,711],{"class":78},"|",[72,713,714],{"class":118}," undefined",[72,716,93],{"class":82},[72,718,719],{"class":74,"line":96},[72,720,100],{"emptyLinePlaceholder":99},[72,722,723,726,729],{"class":74,"line":103},[72,724,725],{"class":78},"function",[72,727,728],{"class":118}," loadOperations",[72,730,731],{"class":82},"() {\n",[72,733,734,737,740,742,744,746],{"class":74,"line":125},[72,735,736],{"class":82},"  operations ",[72,738,739],{"class":78},"??=",[72,741,699],{"class":78},[72,743,702],{"class":82},[72,745,705],{"class":89},[72,747,748],{"class":82},");\n",[72,750,751,754],{"class":74,"line":137},[72,752,753],{"class":78},"  return",[72,755,756],{"class":82}," operations;\n",[72,758,759],{"class":74,"line":148},[72,760,761],{"class":82},"}\n",[72,763,764],{"class":74,"line":159},[72,765,100],{"emptyLinePlaceholder":99},[72,767,768,770,772,775,777,779],{"class":74,"line":170},[72,769,106],{"class":78},[72,771,109],{"class":78},[72,773,774],{"class":82}," hashTool ",[72,776,115],{"class":78},[72,778,119],{"class":118},[72,780,122],{"class":82},[72,782,783],{"class":74,"line":182},[72,784,785],{"class":540},"  \u002F\u002F …\n",[72,787,788,790,793,796,799,801,804,807,809,812,815],{"class":74,"line":193},[72,789,285],{"class":118},[72,791,792],{"class":82},": ",[72,794,795],{"class":78},"async",[72,797,798],{"class":82}," (params) ",[72,800,299],{"class":78},[72,802,803],{"class":82}," (",[72,805,806],{"class":78},"await",[72,808,728],{"class":118},[72,810,811],{"class":82},"()).",[72,813,814],{"class":118},"hashCompute",[72,816,817],{"class":82},"(params),\n",[72,819,820],{"class":74,"line":205},[72,821,311],{"class":82},[54,823,824],{},"Registering the tools then costs the schema and nothing else.",[826,827,828],"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);}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)}",{"title":68,"searchDepth":96,"depth":96,"links":830},[831,832,834,838,839],{"id":315,"depth":96,"text":316},{"id":514,"depth":96,"text":833},"Import Type from here. Really",{"id":590,"depth":96,"text":591,"children":835},[836,837],{"id":600,"depth":103,"text":601},{"id":627,"depth":103,"text":628},{"id":649,"depth":96,"text":650},{"id":667,"depth":96,"text":668},"Every field of defineTool and the two schema rules it enforces. Plus the one import you must never write","md",null,{"icon":844},"i-lucide-wrench",{"title":12,"description":840},"o1SrUxW1IcuQ3038Fhp0CuKmFWh7uwjNSFyvKytQNzw",[848,850],{"title":10,"path":6,"stem":7,"description":849,"children":-1},"Install the package and serve one tool to MCP and Pi and OMP and the AI SDK from a single definition",{"title":16,"path":17,"stem":18,"description":851,"children":-1},"How the core checks every call and how each host reports a failure. And why error text never gets a second line",1790784241443]