Native tools — your own functions
A native tool wraps a plain function: return a string (→ output) or throw (→ error result). It joins the toolkit like any other tool.
import { defineTool } from "toolnexus"
tk.register(defineTool({ name: "add", description: "Add two numbers and return the sum.", inputSchema: { type: "object", properties: { a: { type: "number" }, b: { type: "number" } }, required: ["a", "b"], }, run: ({ a, b }) => `${Number(a) + Number(b)}`,}))from toolnexus import define_tool
def add(a: float, b: float) -> str: """Add two numbers and return the sum.""" return str(a + b)
tk.register(define_tool(add, name="add"))type addArgs struct { A float64 `json:"a"`; B float64 `json:"b"` }
tk.Register(toolnexus.NativeToolReflect("add", "Add two numbers and return the sum.", func(_ context.Context, in addArgs) (string, error) { return strconv.FormatFloat(in.A+in.B, 'f', -1, 64), nil }))import io.github.muthuishere.toolnexus.NativeTool;import java.util.Map;
Map<String, Object> schema = Map.of("type", "object", "properties", Map.of("a", Map.of("type", "number"), "b", Map.of("type", "number")), "required", java.util.List.of("a", "b"));
tk.register(NativeTool.of("add", "Add two numbers and return the sum.", schema, args -> String.valueOf(((Number) args.get("a")).doubleValue() + ((Number) args.get("b")).doubleValue())));tk.Register(NativeTool.Of("add", "Add two numbers and return the sum.", new Dictionary<string, object?> { ["type"] = "object", ["properties"] = new Dictionary<string, object?> { ["a"] = new Dictionary<string, object?> { ["type"] = "number" }, ["b"] = new Dictionary<string, object?> { ["type"] = "number" } }, ["required"] = new List<object?> { "a", "b" }, }, args => (Convert.ToDouble(args["a"]) + Convert.ToDouble(args["b"])).ToString()));add = Toolnexus.define_tool( name: "add", description: "Add two numbers and return the sum.", input_schema: %{"type" => "object", "properties" => %{"a" => %{"type" => "number"}, "b" => %{"type" => "number"}}, "required" => ["a", "b"]}, execute: fn %{"a" => a, "b" => b}, _ctx -> to_string(a + b) end)tk = Toolnexus.Toolkit.register(tk, [add])(require '[toolnexus.core :as tn] '[toolnexus.native :as native])
(def add (native/native-tool {:name "add" :description "Add two numbers and return the sum." :input-schema {:type "object" :properties {:a {:type "number"} :b {:type "number"}} :required ["a" "b"]} :run (fn [{:keys [a b]}] (str (+ a b)))}));; hand your tools to the toolkit builder:(def tk (tn/build {:tools [add]}))use serde::Deserialize;use serde_json::json;use toolnexus::{BuiltinsConfig, ToolContext, ToolkitOptions, create_toolkit, define_typed_tool};
#[derive(Deserialize)]struct Weather { city: String }
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { let weather = define_typed_tool( "get_weather", "Current weather for a city", json!({"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}), |w: Weather, _ctx| async move { Ok::<_, String>(format!("Sunny in {}", w.city)) }, ); let tk = create_toolkit(ToolkitOptions { extra_tools: vec![weather], builtins: Some(BuiltinsConfig::off()), ..Default::default() }).await?;
let ok = tk.execute("get_weather", json!({"city": "Chennai"}), ToolContext::default()).await; assert_eq!(ok.output, "Sunny in Chennai"); let bad = tk.execute("get_weather", json!({}), ToolContext::default()).await; // bad args => isError assert!(bad.is_error); println!("ok: {}", ok.output); Ok(())}Java and C# also support annotation-driven tools (@ToolMethod / [ToolMethod] on a
class). See Native tools.
Next: HTTP / REST tools.