[{"data":1,"prerenderedAt":805},["ShallowReactive",2],{"\u002Fdocs\u002Fadvanced\u002Fcustom-tools":3},{"id":4,"title":5,"body":6,"description":798,"extension":799,"meta":800,"navigation":69,"path":801,"seo":802,"stem":803,"__hash__":804},"docs\u002Fdocs\u002F5.advanced\u002F1.custom-tools.md","Custom Tools",{"type":7,"value":8,"toc":791},"minimark",[9,13,17,22,381,385,442,445,449,550,554,627,638,642,653,710,716,787],[10,11,5],"h1",{"id":12},"custom-tools",[14,15,16],"p",{},"You can extend Mango with any tool you need — an HTTP call, a second database, a calculation engine. The LLM will call your tool exactly like the built-in ones.",[18,19,21],"h2",{"id":20},"minimal-example","Minimal example",[23,24,29],"pre",{"className":25,"code":26,"language":27,"meta":28,"style":28},"language-python shiki shiki-themes github-dark","from mango.tools.base import Tool, ToolResult\nfrom mango.llm import ToolDef, ToolParam\n\nclass GetExchangeRateTool(Tool):\n\n    @property\n    def definition(self) -> ToolDef:\n        return ToolDef(\n            name=\"get_exchange_rate\",\n            description=\"Get the current exchange rate between two currencies.\",\n            params=[\n                ToolParam(name=\"from_currency\", type=\"string\", description=\"Base currency (e.g. USD)\", required=True),\n                ToolParam(name=\"to_currency\", type=\"string\", description=\"Target currency (e.g. EUR)\", required=True),\n            ],\n        )\n\n    async def execute(self, from_currency: str, to_currency: str) -> ToolResult:\n        # Your logic here\n        rate = fetch_rate(from_currency, to_currency)\n        return ToolResult(success=True, data={\"rate\": rate, \"pair\": f\"{from_currency}\u002F{to_currency}\"})\n","python","",[30,31,32,51,64,71,90,95,105,117,126,143,156,167,215,254,260,266,271,297,304,315],"code",{"__ignoreMap":28},[33,34,37,41,45,48],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"snl16","from",[33,42,44],{"class":43},"s95oV"," mango.tools.base ",[33,46,47],{"class":39},"import",[33,49,50],{"class":43}," Tool, ToolResult\n",[33,52,54,56,59,61],{"class":35,"line":53},2,[33,55,40],{"class":39},[33,57,58],{"class":43}," mango.llm ",[33,60,47],{"class":39},[33,62,63],{"class":43}," ToolDef, ToolParam\n",[33,65,67],{"class":35,"line":66},3,[33,68,70],{"emptyLinePlaceholder":69},true,"\n",[33,72,74,77,81,84,87],{"class":35,"line":73},4,[33,75,76],{"class":39},"class",[33,78,80],{"class":79},"svObZ"," GetExchangeRateTool",[33,82,83],{"class":43},"(",[33,85,86],{"class":79},"Tool",[33,88,89],{"class":43},"):\n",[33,91,93],{"class":35,"line":92},5,[33,94,70],{"emptyLinePlaceholder":69},[33,96,98,101],{"class":35,"line":97},6,[33,99,100],{"class":79},"    @",[33,102,104],{"class":103},"sDLfK","property\n",[33,106,108,111,114],{"class":35,"line":107},7,[33,109,110],{"class":39},"    def",[33,112,113],{"class":79}," definition",[33,115,116],{"class":43},"(self) -> ToolDef:\n",[33,118,120,123],{"class":35,"line":119},8,[33,121,122],{"class":39},"        return",[33,124,125],{"class":43}," ToolDef(\n",[33,127,129,133,136,140],{"class":35,"line":128},9,[33,130,132],{"class":131},"s9osk","            name",[33,134,135],{"class":39},"=",[33,137,139],{"class":138},"sU2Wk","\"get_exchange_rate\"",[33,141,142],{"class":43},",\n",[33,144,146,149,151,154],{"class":35,"line":145},10,[33,147,148],{"class":131},"            description",[33,150,135],{"class":39},[33,152,153],{"class":138},"\"Get the current exchange rate between two currencies.\"",[33,155,142],{"class":43},[33,157,159,162,164],{"class":35,"line":158},11,[33,160,161],{"class":131},"            params",[33,163,135],{"class":39},[33,165,166],{"class":43},"[\n",[33,168,170,173,176,178,181,184,187,189,192,194,197,199,202,204,207,209,212],{"class":35,"line":169},12,[33,171,172],{"class":43},"                ToolParam(",[33,174,175],{"class":131},"name",[33,177,135],{"class":39},[33,179,180],{"class":138},"\"from_currency\"",[33,182,183],{"class":43},", ",[33,185,186],{"class":131},"type",[33,188,135],{"class":39},[33,190,191],{"class":138},"\"string\"",[33,193,183],{"class":43},[33,195,196],{"class":131},"description",[33,198,135],{"class":39},[33,200,201],{"class":138},"\"Base currency (e.g. USD)\"",[33,203,183],{"class":43},[33,205,206],{"class":131},"required",[33,208,135],{"class":39},[33,210,211],{"class":103},"True",[33,213,214],{"class":43},"),\n",[33,216,218,220,222,224,227,229,231,233,235,237,239,241,244,246,248,250,252],{"class":35,"line":217},13,[33,219,172],{"class":43},[33,221,175],{"class":131},[33,223,135],{"class":39},[33,225,226],{"class":138},"\"to_currency\"",[33,228,183],{"class":43},[33,230,186],{"class":131},[33,232,135],{"class":39},[33,234,191],{"class":138},[33,236,183],{"class":43},[33,238,196],{"class":131},[33,240,135],{"class":39},[33,242,243],{"class":138},"\"Target currency (e.g. EUR)\"",[33,245,183],{"class":43},[33,247,206],{"class":131},[33,249,135],{"class":39},[33,251,211],{"class":103},[33,253,214],{"class":43},[33,255,257],{"class":35,"line":256},14,[33,258,259],{"class":43},"            ],\n",[33,261,263],{"class":35,"line":262},15,[33,264,265],{"class":43},"        )\n",[33,267,269],{"class":35,"line":268},16,[33,270,70],{"emptyLinePlaceholder":69},[33,272,274,277,280,283,286,289,292,294],{"class":35,"line":273},17,[33,275,276],{"class":39},"    async",[33,278,279],{"class":39}," def",[33,281,282],{"class":79}," execute",[33,284,285],{"class":43},"(self, from_currency: ",[33,287,288],{"class":103},"str",[33,290,291],{"class":43},", to_currency: ",[33,293,288],{"class":103},[33,295,296],{"class":43},") -> ToolResult:\n",[33,298,300],{"class":35,"line":299},18,[33,301,303],{"class":302},"sAwPA","        # Your logic here\n",[33,305,307,310,312],{"class":35,"line":306},19,[33,308,309],{"class":43},"        rate ",[33,311,135],{"class":39},[33,313,314],{"class":43}," fetch_rate(from_currency, to_currency)\n",[33,316,318,320,323,326,328,330,332,335,337,340,343,346,349,352,355,358,360,363,366,369,371,374,376,378],{"class":35,"line":317},20,[33,319,122],{"class":39},[33,321,322],{"class":43}," ToolResult(",[33,324,325],{"class":131},"success",[33,327,135],{"class":39},[33,329,211],{"class":103},[33,331,183],{"class":43},[33,333,334],{"class":131},"data",[33,336,135],{"class":39},[33,338,339],{"class":43},"{",[33,341,342],{"class":138},"\"rate\"",[33,344,345],{"class":43},": rate, ",[33,347,348],{"class":138},"\"pair\"",[33,350,351],{"class":43},": ",[33,353,354],{"class":39},"f",[33,356,357],{"class":138},"\"",[33,359,339],{"class":103},[33,361,362],{"class":43},"from_currency",[33,364,365],{"class":103},"}",[33,367,368],{"class":138},"\u002F",[33,370,339],{"class":103},[33,372,373],{"class":43},"to_currency",[33,375,365],{"class":103},[33,377,357],{"class":138},[33,379,380],{"class":43},"})\n",[18,382,384],{"id":383},"register-it","Register it",[23,386,388],{"className":25,"code":387,"language":27,"meta":28,"style":28},"tools = ToolRegistry()\n\n# Built-in tools\nfor tool in build_mongo_tools(db, memory):\n    tools.register(tool)\n\n# Your custom tool\ntools.register(GetExchangeRateTool())\n",[30,389,390,400,404,409,423,428,432,437],{"__ignoreMap":28},[33,391,392,395,397],{"class":35,"line":36},[33,393,394],{"class":43},"tools ",[33,396,135],{"class":39},[33,398,399],{"class":43}," ToolRegistry()\n",[33,401,402],{"class":35,"line":53},[33,403,70],{"emptyLinePlaceholder":69},[33,405,406],{"class":35,"line":66},[33,407,408],{"class":302},"# Built-in tools\n",[33,410,411,414,417,420],{"class":35,"line":73},[33,412,413],{"class":39},"for",[33,415,416],{"class":43}," tool ",[33,418,419],{"class":39},"in",[33,421,422],{"class":43}," build_mongo_tools(db, memory):\n",[33,424,425],{"class":35,"line":92},[33,426,427],{"class":43},"    tools.register(tool)\n",[33,429,430],{"class":35,"line":97},[33,431,70],{"emptyLinePlaceholder":69},[33,433,434],{"class":35,"line":107},[33,435,436],{"class":302},"# Your custom tool\n",[33,438,439],{"class":35,"line":119},[33,440,441],{"class":43},"tools.register(GetExchangeRateTool())\n",[14,443,444],{},"The LLM will automatically see your tool's name and description and call it when relevant.",[18,446,448],{"id":447},"toolparam-reference","ToolParam reference",[23,450,452],{"className":25,"code":451,"language":27,"meta":28,"style":28},"@dataclass\nclass ToolParam:\n    name: str\n    type: str           # \"string\" | \"integer\" | \"number\" | \"boolean\" | \"array\" | \"object\"\n    description: str\n    required: bool = True\n    enum: list | None = None     # restrict to specific values\n    items: dict | None = None    # for array types, describe the items\n",[30,453,454,459,469,477,489,496,510,531],{"__ignoreMap":28},[33,455,456],{"class":35,"line":36},[33,457,458],{"class":79},"@dataclass\n",[33,460,461,463,466],{"class":35,"line":53},[33,462,76],{"class":39},[33,464,465],{"class":79}," ToolParam",[33,467,468],{"class":43},":\n",[33,470,471,474],{"class":35,"line":66},[33,472,473],{"class":43},"    name: ",[33,475,476],{"class":103},"str\n",[33,478,479,482,484,486],{"class":35,"line":73},[33,480,481],{"class":103},"    type",[33,483,351],{"class":43},[33,485,288],{"class":103},[33,487,488],{"class":302},"           # \"string\" | \"integer\" | \"number\" | \"boolean\" | \"array\" | \"object\"\n",[33,490,491,494],{"class":35,"line":92},[33,492,493],{"class":43},"    description: ",[33,495,476],{"class":103},[33,497,498,501,504,507],{"class":35,"line":97},[33,499,500],{"class":43},"    required: ",[33,502,503],{"class":103},"bool",[33,505,506],{"class":39}," =",[33,508,509],{"class":103}," True\n",[33,511,512,515,518,521,524,526,528],{"class":35,"line":107},[33,513,514],{"class":43},"    enum: ",[33,516,517],{"class":103},"list",[33,519,520],{"class":39}," |",[33,522,523],{"class":103}," None",[33,525,506],{"class":39},[33,527,523],{"class":103},[33,529,530],{"class":302},"     # restrict to specific values\n",[33,532,533,536,539,541,543,545,547],{"class":35,"line":119},[33,534,535],{"class":43},"    items: ",[33,537,538],{"class":103},"dict",[33,540,520],{"class":39},[33,542,523],{"class":103},[33,544,506],{"class":39},[33,546,523],{"class":103},[33,548,549],{"class":302},"    # for array types, describe the items\n",[18,551,553],{"id":552},"toolresult-reference","ToolResult reference",[23,555,557],{"className":25,"code":556,"language":27,"meta":28,"style":28},"@dataclass\nclass ToolResult:\n    success: bool\n    data: Any            # any JSON-serializable value\n    error: str | None = None\n\n    def as_text(self) -> str:\n        \"\"\"How the result is shown to the LLM.\"\"\"\n",[30,558,559,563,572,580,588,604,608,622],{"__ignoreMap":28},[33,560,561],{"class":35,"line":36},[33,562,458],{"class":79},[33,564,565,567,570],{"class":35,"line":53},[33,566,76],{"class":39},[33,568,569],{"class":79}," ToolResult",[33,571,468],{"class":43},[33,573,574,577],{"class":35,"line":66},[33,575,576],{"class":43},"    success: ",[33,578,579],{"class":103},"bool\n",[33,581,582,585],{"class":35,"line":73},[33,583,584],{"class":43},"    data: Any            ",[33,586,587],{"class":302},"# any JSON-serializable value\n",[33,589,590,593,595,597,599,601],{"class":35,"line":92},[33,591,592],{"class":43},"    error: ",[33,594,288],{"class":103},[33,596,520],{"class":39},[33,598,523],{"class":103},[33,600,506],{"class":39},[33,602,603],{"class":103}," None\n",[33,605,606],{"class":35,"line":97},[33,607,70],{"emptyLinePlaceholder":69},[33,609,610,612,615,618,620],{"class":35,"line":107},[33,611,110],{"class":39},[33,613,614],{"class":79}," as_text",[33,616,617],{"class":43},"(self) -> ",[33,619,288],{"class":103},[33,621,468],{"class":43},[33,623,624],{"class":35,"line":119},[33,625,626],{"class":138},"        \"\"\"How the result is shown to the LLM.\"\"\"\n",[14,628,629,630,633,634,637],{},"Return ",[30,631,632],{},"success=False"," with a descriptive ",[30,635,636],{},"error"," message to let the LLM know the tool failed and potentially retry with different arguments.",[18,639,641],{"id":640},"async-tools","Async tools",[14,643,644,645,648,649,652],{},"All tools are ",[30,646,647],{},"async",". For synchronous operations, just return directly — no need to use ",[30,650,651],{},"await",":",[23,654,656],{"className":25,"code":655,"language":27,"meta":28,"style":28},"async def execute(self, **kwargs) -> ToolResult:\n    result = some_sync_function()  # sync is fine\n    return ToolResult(success=True, data=result)\n",[30,657,658,675,688],{"__ignoreMap":28},[33,659,660,662,664,666,669,672],{"class":35,"line":36},[33,661,647],{"class":39},[33,663,279],{"class":39},[33,665,282],{"class":79},[33,667,668],{"class":43},"(self, ",[33,670,671],{"class":39},"**",[33,673,674],{"class":43},"kwargs) -> ToolResult:\n",[33,676,677,680,682,685],{"class":35,"line":53},[33,678,679],{"class":43},"    result ",[33,681,135],{"class":39},[33,683,684],{"class":43}," some_sync_function()  ",[33,686,687],{"class":302},"# sync is fine\n",[33,689,690,693,695,697,699,701,703,705,707],{"class":35,"line":66},[33,691,692],{"class":39},"    return",[33,694,322],{"class":43},[33,696,325],{"class":131},[33,698,135],{"class":39},[33,700,211],{"class":103},[33,702,183],{"class":43},[33,704,334],{"class":131},[33,706,135],{"class":39},[33,708,709],{"class":43},"result)\n",[14,711,712,713,652],{},"For blocking I\u002FO (database calls, HTTP), use ",[30,714,715],{},"asyncio.to_thread",[23,717,719],{"className":25,"code":718,"language":27,"meta":28,"style":28},"import asyncio\n\nasync def execute(self, collection: str) -> ToolResult:\n    docs = await asyncio.to_thread(self._db_call, collection)\n    return ToolResult(success=True, data=docs)\n",[30,720,721,728,732,747,766],{"__ignoreMap":28},[33,722,723,725],{"class":35,"line":36},[33,724,47],{"class":39},[33,726,727],{"class":43}," asyncio\n",[33,729,730],{"class":35,"line":53},[33,731,70],{"emptyLinePlaceholder":69},[33,733,734,736,738,740,743,745],{"class":35,"line":66},[33,735,647],{"class":39},[33,737,279],{"class":39},[33,739,282],{"class":79},[33,741,742],{"class":43},"(self, collection: ",[33,744,288],{"class":103},[33,746,296],{"class":43},[33,748,749,752,754,757,760,763],{"class":35,"line":73},[33,750,751],{"class":43},"    docs ",[33,753,135],{"class":39},[33,755,756],{"class":39}," await",[33,758,759],{"class":43}," asyncio.to_thread(",[33,761,762],{"class":103},"self",[33,764,765],{"class":43},"._db_call, collection)\n",[33,767,768,770,772,774,776,778,780,782,784],{"class":35,"line":92},[33,769,692],{"class":39},[33,771,322],{"class":43},[33,773,325],{"class":131},[33,775,135],{"class":39},[33,777,211],{"class":103},[33,779,183],{"class":43},[33,781,334],{"class":131},[33,783,135],{"class":39},[33,785,786],{"class":43},"docs)\n",[788,789,790],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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);}",{"title":28,"searchDepth":53,"depth":53,"links":792},[793,794,795,796,797],{"id":20,"depth":53,"text":21},{"id":383,"depth":53,"text":384},{"id":447,"depth":53,"text":448},{"id":552,"depth":53,"text":553},{"id":640,"depth":53,"text":641},"Build your own tools and register them alongside the built-ins.","md",{},"\u002Fdocs\u002Fadvanced\u002Fcustom-tools",{"title":5,"description":798},"docs\u002F5.advanced\u002F1.custom-tools","dCdiOKXw5lSLY2tN2wWjMCNKTSy2N9DC_4DwtVjCdSg",1776189332178]