Define tools once. Export to OpenAI, Anthropic, Gemini — with effect-safe filtering built in.
Building tool-using AI agents today means fighting the ecosystem. NAIL is the missing layer.
Write your tool spec in NAIL once. nail fc convert
outputs the correct schema for any provider.
# tools.nail fn search_web( query: String, max_results: Int = 10 ) @NET -> List[Result] { "Search the web and return ranked results." } fn read_file( path: String, encoding: String = "utf-8" ) @FS -> String { "Read a file from disk and return its contents." }
# nail fc convert tools.nail --provider openai [ { "type": "function", "function": { "name": "search_web", "description": "Search the web and return ranked results.", "parameters": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": "integer", "default": 10 } }, "required": ["query"] } } } ]
# nail fc convert tools.nail --provider anthropic [ { "name": "search_web", "description": "Search the web and return ranked results.", "input_schema": { "type": "object", "properties": { "query": { "type": "string" }, "max_results": { "type": "integer", "default": 10 } }, "required": ["query"] } } ]
# nail fc convert tools.nail --provider gemini { "function_declarations": [ { "name": "search_web", "description": "Search the web and return ranked results.", "parameters": { "type": "OBJECT", "properties": { "query": { "type": "STRING" }, "max_results": { "type": "INTEGER" } }, "required": ["query"] } } ] }
Declare what your tools can do. Block everything else — at check time, before a single token is generated.
from nail_lang import filter_by_effects # Allow only file system and I/O — block network and process calls safe_tools = filter_by_effects(all_tools, allowed=["FS", "IO"]) # → Blocks NET, PROC. Unannotated tools excluded by default. response = client.chat.completions.create( tools=safe_tools, messages=messages, ... )
NAIL checks your tool specs at four levels before they ever reach a model.
JSON schema conformance check. Malformed specs are rejected immediately.
Parameter types resolved: int / string / bool. No implicit coercions.
IO in a pure function is a compile error. Effects must be declared explicitly.
Any loop in a tool spec must provably halt. Infinite loops are rejected at compile time.
Install, check your existing tools, and convert to any provider — all from the CLI.
pip install nail-lang # Check tools against a provider nail fc check tools.nail --provider openai # Convert to any provider nail fc convert tools.nail --provider anthropic > tools.json nail fc convert tools.nail --provider gemini > tools.json # Import existing schemas nail fc import openai_tools.json --from openai