# How to Create Custom Coded Tools in Neuro SAN: A Complete Developer Guide

> Create custom coded tools in Neuro SAN by implementing the async_invoke method and registering your class. Follow this developer guide for a complete walkthrough.

- Repository: [Cognizant AI Lab/neuro-san-studio](https://github.com/cognizant-ai-lab/neuro-san-studio)
- Tags: how-to-guide
- Published: 2026-02-27

---

**Create custom coded tools in Neuro SAN by subclassing the `CodedTool` interface, implementing the asynchronous `async_invoke` method, and registering the class in your agent network's HOCON configuration.**

Neuro SAN, the open-source agent orchestration framework from the `cognizant-ai-lab/neuro-san-studio` repository, allows developers to extend agent capabilities beyond LLM reasoning by implementing custom coded tools. These Python components execute inside the Neuro SAN runtime, enabling agents to query external APIs, access databases, or perform complex computations while maintaining seamless integration with the conversational flow.

## What Are Coded Tools in Neuro SAN?

Coded tools are specialized Python classes that inherit from the **`CodedTool`** interface located at `neuro_san.interfaces.coded_tool.CodedTool`. Unlike declarative tool definitions that rely solely on LLM function calling, coded tools provide deterministic, code-driven execution environments where developers control the exact logic, error handling, and external integrations.

When an agent invokes a coded tool, Neuro SAN automatically instantiates the tool class (once per agent network load), passes the user-provided arguments and private session data, executes the asynchronous `async_invoke` method, and returns the result back to the calling agent as a standard message.

## Implementing the CodedTool Interface

### The async_invoke Method Contract

Every custom tool must implement the **`async_invoke`** method with the following signature:

```python
async def async_invoke(self, args: Dict[str, Any], sly_data: Dict[str, Any]) -> Any:

```

The method receives two dictionaries:

- **`args`**: Contains the arguments supplied by the agent prompt (e.g., `{"city": "Paris"}`). These are visible to the LLM and extracted from the natural language request.
- **`sly_data`**: Contains private "sly data" such as API keys, user session tokens, or internal metadata. This data is **never exposed to the LLM** and is used for secure credential passing or context management.

The method can return strings, dictionaries, lists, or any JSON-serializable object that the agent can incorporate into its reasoning.

### Tool Lifecycle and Instantiation

Neuro SAN instantiates coded tool classes **once per agent network load**, not per invocation. This design pattern allows tools to maintain persistent connections, cache data, or initialize expensive resources (like database pools) efficiently. The `__init__` method receives no special arguments from the framework, though some advanced patterns (like `CodedToolAgentCaller`) may inject dependencies during instantiation.

## Step-by-Step: Building Your First Custom Tool

### 1. Create the Tool Class

Create a new Python file under `coded_tools/tools/` and subclass `CodedTool`. Here is a minimal "Hello" tool implementation:

```python

# coded_tools/tools/hello_tool.py

import logging
from typing import Any, Dict

from neuro_san.interfaces.coded_tool import CodedTool


class HelloTool(CodedTool):
    """
    Simple example: returns a greeting.
    """

    def __init__(self):
        self.logger = logging.getLogger(__name__)

    async def async_invoke(self, args: Dict[str, Any], sly_data: Dict[str, Any]) -> str:
        name = args.get("name", "world")
        self.logger.info("HelloTool invoked with name=%s", name)
        return f"👋 Hello, {name}!"

```

### 2. Expose the Tool in the Package

Import the class in [`coded_tools/tools/__init__.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/__init__.py) to make it available for dynamic loading:

```python

# coded_tools/tools/__init__.py

from .hello_tool import HelloTool

# other tool imports...

```

### 3. Register in HOCON Network Configuration

Reference the fully-qualified class name in your agent network definition (e.g., `network.hocon`):

```hocon
agents {
  greeter {
    class = "myagents.Greeter"
    tools = [ "coded_tools.tools.HelloTool" ]
  }
}

```

Start Neuro SAN (`python -m run`). When the greeter agent issues a tool call like `{{tool: HelloTool name="Alice"}}`, Neuro SAN executes your custom logic and returns the formatted greeting.

## Advanced Patterns for Coded Tools

### Calling External APIs with Async HTTP

For real-world integrations, implement asynchronous HTTP calls using `aiohttp` or `httpx`. This example demonstrates a weather lookup tool that securely accesses API keys through `sly_data`:

```python

# coded_tools/tools/weather_lookup.py

import logging
import aiohttp
from typing import Any, Dict

from neuro_san.interfaces.coded_tool import CodedTool


class WeatherLookup(CodedTool):
    """
    Calls the OpenWeatherMap API and returns the current temperature.
    """

    def __init__(self):
        self.logger = logging.getLogger(__name__)

    async def async_invoke(self, args: Dict[str, Any], sly_data: Dict[str, Any]) -> str:
        city = args.get("city")
        if not city:
            return "Error: `city` argument is required."

        api_key = sly_data.get("openweather_api_key")
        if not api_key:
            return "Error: OpenWeatherMap API key not provided in sly_data."

        endpoint = (
            f"https://api.openweathermap.org/data/2.5/weather"
            f"?q={city}&units=metric&appid={api_key}"
        )

        async with aiohttp.ClientSession() as session:
            async with session.get(endpoint) as resp:
                if resp.status != 200:
                    return f"Error: Weather service returned {resp.status}"
                data = await resp.json()

        temp = data["main"]["temp"]
        description = data["weather"][0]["description"]
        return f"The current temperature in {city} is {temp} °C with {description}."

```

### Orchestrating Sub-Agents with CodedToolAgentCaller

Complex workflows may require a tool to delegate tasks to other agents in the network. Use **`CodedToolAgentCaller`** to invoke agents from within `async_invoke`:

```python

# coded_tools/tools/agent_bridge.py

import logging
from typing import Any, Dict

from neuro_san.interfaces.coded_tool import CodedTool
from coded_tools.tools.coded_tool_agent_caller import CodedToolAgentCaller


class AgentBridge(CodedTool):
    """
    Calls a secondary agent and returns its reply.
    """

    def __init__(self, branch_activation):
        # branch_activation is injected by Neuro SAN during instantiation

        self.caller = CodedToolAgentCaller(branch_activation)
        self.logger = logging.getLogger(__name__)

    async def async_invoke(self, args: Dict[str, Any], sly_data: Dict[str, Any]) -> str:
        target = args.get("target_agent")
        query = args.get("query")
        
        if not target or not query:
            return "Error: `target_agent` and `query` are required."

        response = await self.caller.call_agent(target, {"query": query}, sly_data)
        return f"Response from {target}: {response}"

```

### Wrapping OpenAI Built-in Tools

For integrations with OpenAI's native capabilities (image generation, code interpreter, web search), extend the **`OpenAITool`** wrapper class located at [`coded_tools/tools/openai_tool.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/openai_tool.py). This base class handles authentication and provides the `arun` method for executing OpenAI tool calls asynchronously.

Reference implementations include:
- **[`openai_image_generation.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/openai_image_generation.py)** – Generates images using DALL-E
- **[`wikipedia_rag.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/wikipedia_rag.py)** – Implements retrieval-augmented generation using LangChain retrievers

## Key Reference Files in the Repository

| File | Purpose | Location |
|------|---------|----------|
| [`coded_tools/tools/openai_image_generation.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/openai_image_generation.py) | Wraps OpenAI image generation capabilities | [View source](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/openai_image_generation.py) |
| [`coded_tools/tools/wikipedia_rag.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/wikipedia_rag.py) | RAG implementation using LangChain Wikipedia retriever | [View source](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/wikipedia_rag.py) |
| [`coded_tools/tools/coded_tool_agent_caller.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/coded_tool_agent_caller.py) | Helper for invoking other agents from within tools | [View source](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/coded_tool_agent_caller.py) |
| [`coded_tools/tools/openai_tool.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/openai_tool.py) | Base wrapper for OpenAI built-in tools | [View source](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/openai_tool.py) |
| [`run.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/run.py) | Entry point that loads HOCON networks and registers tools | [View source](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/run.py) |

## Summary

- **Create custom coded tools in Neuro SAN** by subclassing `CodedTool` from `neuro_san.interfaces.coded_tool` and implementing the `async_invoke` method.
- Place tool implementations in `coded_tools/tools/<your_tool>.py` and expose them via [`coded_tools/tools/__init__.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/__init__.py) to enable dynamic loading.
- Reference tools in your HOCON network configuration using fully-qualified class names (e.g., `coded_tools.tools.WeatherLookup`).
- Use `sly_data` for secure credential passing (API keys, tokens) that must remain hidden from the LLM, while `args` contains user-visible parameters.
- Leverage `CodedToolAgentCaller` to orchestrate sub-agents from within tools, and extend `OpenAITool` for wrapping OpenAI native capabilities.

## Frequently Asked Questions

### What is the difference between args and sly_data in async_invoke?

The `args` dictionary contains parameters extracted from the agent's natural language request (e.g., `{"city": "Paris"}`), which are visible to the LLM during tool selection. The `sly_data` dictionary contains private session data such as API keys, authentication tokens, or internal metadata that is **never exposed to the LLM**. This separation ensures sensitive credentials remain secure while allowing the agent to reason about tool parameters.

### Can I use synchronous libraries inside a coded tool?

While you can technically use synchronous code, the `async_invoke` method is designed for asynchronous execution. For I/O-bound operations like HTTP requests or database queries, use `async` libraries such as `aiohttp` or `asyncpg` to avoid blocking the Neuro SAN event loop. If you must use synchronous code, wrap it in `asyncio.to_thread()` or similar executors to maintain runtime performance.

### How do I debug a coded tool during development?

Since coded tools are instantiated once per network load and executed within the Neuro SAN runtime, use standard Python logging via `logging.getLogger(__name__)` within your tool class. Start the server with `python -m run` and monitor stdout for log output. For deeper debugging, you can add breakpoints in `async_invoke` and attach a debugger, or temporarily add print statements (though logging is preferred for production code).

### Can coded tools call other agents in the network?

Yes. Use the **`CodedToolAgentCaller`** helper class (located at [`coded_tools/tools/coded_tool_agent_caller.py`](https://github.com/cognizant-ai-lab/neuro-san-studio/blob/main/coded_tools/tools/coded_tool_agent_caller.py)) to invoke other agents from within `async_invoke`. When Neuro SAN instantiates your tool, it injects the current `BranchActivation` context, which you pass to `CodedToolAgentCaller` to enable agent-to-agent communication. This pattern is essential for complex orchestration workflows where a tool needs to delegate sub-tasks to specialized agents.