HTTP / REST tools
{placeholder} in the URL is filled from the args; ${ENV} in a header expands
from the environment at call time and is never logged. Non-2xx becomes an
HTTP <status>: <body> error result.
import { httpTool } from "toolnexus"
tk.register(httpTool({ name: "get_post", description: "Fetch a placeholder blog post by id.", method: "GET", url: "https://jsonplaceholder.typicode.com/posts/{id}", inputSchema: { type: "object", properties: { id: { type: "number" } }, required: ["id"], },}))from toolnexus import http_tool
tk.register(http_tool( name="get_post", description="Fetch a placeholder blog post by id.", method="GET", url="https://jsonplaceholder.typicode.com/posts/{id}", input_schema={ "type": "object", "properties": {"id": {"type": "number"}}, "required": ["id"], },))tk.Register(toolnexus.HTTPTool(toolnexus.HTTPToolOptions{ Name: "get_post", Description: "Fetch a placeholder blog post by id.", Method: "GET", URL: "https://jsonplaceholder.typicode.com/posts/{id}", InputSchema: toolnexus.JSONSchema{ "type": "object", "properties": map[string]any{"id": map[string]any{"type": "number"}}, "required": []string{"id"}},}))import io.github.muthuishere.toolnexus.HttpTool;
HttpTool.Options o = new HttpTool.Options();o.name = "get_post";o.description = "Fetch a placeholder blog post by id.";o.method = "GET";o.url = "https://jsonplaceholder.typicode.com/posts/{id}";o.inputSchema = Map.of("type", "object", "properties", Map.of("id", Map.of("type", "number")), "required", java.util.List.of("id"));tk.register(HttpTool.of(o));tk.Register(HttpTool.Of(new HttpTool.Options { Name = "get_post", Description = "Fetch a placeholder blog post by id.", Method = "GET", Url = "https://jsonplaceholder.typicode.com/posts/{id}", InputSchema = new Dictionary<string, object?> { ["type"] = "object", ["properties"] = new Dictionary<string, object?> { ["id"] = new Dictionary<string, object?> { ["type"] = "number" }, }, ["required"] = new List<object?> { "id" }, },}));get_post = Toolnexus.Http.tool( name: "get_post", description: "Fetch a placeholder blog post by id.", method: "GET", url: "https://jsonplaceholder.typicode.com/posts/{id}", input_schema: %{"type" => "object", "properties" => %{"id" => %{"type" => "integer"}}, "required" => ["id"]})tk = Toolnexus.Toolkit.register(tk, [get_post])(require '[toolnexus.core :as tn] '[toolnexus.http :as http])
(def get-post (http/http-tool {:name "get_post" :description "Fetch a placeholder blog post by id." :method :get :url "https://jsonplaceholder.typicode.com/posts/{id}" :input-schema {:type "object" :properties {:id {:type "number"}} :required ["id"]}}))(def tk (tn/build {:tools [get-post]}))use serde_json::json;use tokio::io::{AsyncReadExt, AsyncWriteExt};use toolnexus::http::{HttpToolOptions, http_tool};use toolnexus::{BuiltinsConfig, ToolContext, ToolkitOptions, create_toolkit};
#[tokio::main]async fn main() -> Result<(), Box<dyn std::error::Error>> { // A loopback stand-in for api.github.com: echoes the request line and auth header. let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await?; let base = format!("http://{}", listener.local_addr()?); tokio::spawn(async move { let (mut s, _) = listener.accept().await.unwrap(); let mut buf = vec![0; 4096]; let n = s.read(&mut buf).await.unwrap(); let req = String::from_utf8_lossy(&buf[..n]).to_string(); let auth = req.lines().find(|l| l.to_lowercase().starts_with("authorization:")).unwrap_or("").to_string(); let body = json!({"line": req.lines().next().unwrap_or(""), "auth": auth}).to_string(); let resp = format!("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{body}", body.len()); s.write_all(resp.as_bytes()).await.unwrap(); }); if std::env::var("GITHUB_TOKEN").is_err() { unsafe { std::env::set_var("GITHUB_TOKEN", "test-key") } // demo only; normally set by your shell }
let repo = http_tool(HttpToolOptions { name: "repo_info".into(), description: "Fetch a GitHub repository".into(), url: format!("{base}/repos/{{owner}}/{{repo}}"), // real: https://api.github.com/repos/{owner}/{repo} headers: [("Authorization".to_string(), "Bearer ${GITHUB_TOKEN}".to_string())].into(), input_schema: Some(json!({"type": "object", "properties": { "owner": {"type": "string"}, "repo": {"type": "string"}}, "required": ["owner", "repo"]})), ..Default::default() }); // ${GITHUB_TOKEN} is read from the environment at call time and never logged let tk = create_toolkit(ToolkitOptions { extra_tools: vec![repo], builtins: Some(BuiltinsConfig::off()), ..Default::default() }).await?; let r = tk.execute("repo_info", json!({"owner": "muthuishere", "repo": "toolnexus"}), ToolContext::default()).await; assert!(!r.is_error, "{}", r.output); assert!(r.output.contains("/repos/muthuishere/toolnexus") && r.output.contains("Bearer "), "{}", r.output); println!("ok: {}", r.output); Ok(())}An authenticated endpoint uses ${ENV} in a header — e.g. a POST with
"Authorization": "Bearer ${API_TOKEN}". See HTTP / REST tools.
Next: Bring your own HTTP client.