How to Create a Needle Agent with Custom Tools: A Complete 3-Step Guide

You can create a Needle agent with custom tools by decorating Python functions with @tool from needle/agent/tools.py, then passing them to NeedleAgent.

The Needle framework (cactus-compute/needle) provides a lightweight way to turn ordinary Python functions into LLM-executable tools. This guide walks through the complete workflow using actual source code from the repository.


What the @tool Decorator Does

The @tool decorator in needle/agent/tools.py automates three critical tasks:

  • Extracts type hints including typing.Annotated metadata from function signatures
  • Generates JSON-Schema that conforms to OpenAI's function-calling format (name, description, parameters)
  • Attaches schema metadata to the function object via fn._needle_tool

This eliminates manual schema writing and registration boilerplate. A well-typed, well-documented Python function becomes a fully specified tool automatically.


Step 1: Define Your Custom Tool

Create a standard Python function with type hints and a docstring, then apply the @tool decorator.


# file: my_tools.py

from needle.agent.tools import tool

@tool
def get_weather(city: str) -> str:
    """
    Retrieve the current weather for a city.

    Parameters
    ----------
    city : str
        Name of the city (e.g., "Paris").

    Returns
    -------
    str
        Human-readable weather description.
    """
    # In production, call a weather API here

    return f"The weather in {city} is sunny with a temperature of 23 °C."

The decorator inspects this definition and stores the generated schema at get_weather._needle_tool.


Step 2: Instantiate NeedleAgent with Your Tools

Pass the decorated function (or list of functions) to the agent constructor.


# file: run_agent.py

from needle.agent import NeedleAgent
from my_tools import get_weather

# Build agent with custom tool registration

agent = NeedleAgent(tools=[get_weather])

# The LLM decides when to invoke the tool

response = agent.run("What's the weather like in Tokyo today?")
print(response)

The NeedleAgent class in needle/agent/__init__.py scans the tools list for _needle_tool attributes and builds the complete tool-calling prompt automatically.


Step 3: Combine Multiple Custom Tools

Define additional tools using the same pattern:


# file: more_tools.py

from needle.agent.tools import tool

@tool
def calculate_sum(a: int, b: int) -> int:
    """Return the sum of two integers."""
    return a + b

@tool
def echo(message: str) -> str:
    """Return the exact message that was given."""
    return message

Then pass all tools to the agent:


# file: demo_multi.py

from needle.agent import NeedleAgent
from more_tools import calculate_sum, echo

agent = NeedleAgent(tools=[calculate_sum, echo])

# Model may use either tool, or sequence multiple calls

print(agent.run("Add 12 and 7, then repeat the result back to me."))

The core inference logic in needle/model/run.py handles function_call routing—matching the LLM's tool selection to your Python implementations and feeding results back to the model.


How Tool Execution Works Under the Hood

When agent.run() processes a prompt:

  1. Prompt construction: NeedleAgent builds a system prompt including all tool schemas from _needle_tool metadata
  2. LLM inference: The model receives context with available tools and may output function_call requests
  3. Routing: needle/model/run.py parses the function_call, looks up the matching Python function, and executes it with provided arguments
  4. Response synthesis: Tool outputs are formatted and sent back to the LLM for final answer generation

This closed-loop execution ensures type-safe, sandboxed code execution without manual intervention.


Key Source Files Reference

File Purpose Location
needle/agent/tools.py @tool decorator and build_schema() implementation needle/agent/tools.py
needle/agent/__init__.py NeedleAgent class and tool registration logic needle/agent/__init__.py
needle/model/run.py Core inference and function_call handling needle/model/run.py
needle/cli.py CLI entry point that creates NeedleAgent internally needle/cli.py
needle/model/export.py Agent export utilities including tool serialization needle/model/export.py

Summary

  • Decorate any Python function with @tool from needle/agent/tools.py to generate JSON-Schema automatically
  • Pass decorated functions to NeedleAgent(tools=[...]) for seamless LLM integration
  • Execute with agent.run(prompt)—the framework handles tool selection, calling, and response synthesis
  • Extend by adding more @tool functions; the architecture scales to arbitrary tool counts

Frequently Asked Questions

What Python types does the @tool decorator support?

The @tool decorator handles standard types (str, int, float, bool, list, dict) and their typing module equivalents. It also extracts metadata from typing.Annotated for enhanced schema descriptions. Complex nested types are converted to JSON-Schema objects according to OpenAI's function-calling specification.

Can I use async functions as Needle tools?

The source code analysis focuses on synchronous functions. Check needle/agent/tools.py for current async support—if the build_schema implementation handles coroutines, async tools may be supported directly. Otherwise, wrap async logic in asyncio.run() within a synchronous @tool function.

How do I debug when the LLM doesn't call my tool?

Verify three things: (1) your function has complete type hints and docstring, (2) the @tool decorator successfully attaches _needle_tool metadata, and (3) the tool name in your prompt matches what the LLM expects. Enable verbose logging in needle/model/run.py to trace prompt construction and function_call detection.

Can I export an agent with its tools for deployment?

Yes—needle/model/export.py provides utilities to serialize a fully configured NeedleAgent including all tool schemas to a JSON package. This enables reproducible agent deployment without recreating tool definitions at runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →