RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /How-to
  5. /How to Register and Call Custom Tools in Agents
HOW-TO · RAG

How to Register and Call Custom Tools in Agents

intermediate·15 min·By Fredoline Eruo
Target environment
Ubuntu 24.04 · Ollama 0.4.x
PREREQUISITES

Custom tool functions defined, agent framework installed, Python 3.10+

What this does

Tool registration creates a discoverable catalog that the agent's LLM can reference when deciding which tool to call. This separates tool definition from tool invocation.

Steps

  • Create a tool registry. A central registry maps tool names to functions and schemas.
class ToolRegistry:
    def __init__(self):
        self._tools = {}
        self._schemas = []

    def register(self, tool):
        self._tools[tool.name] = tool
        self._schemas.append({
            "type": "function",
            "function": {
                "name": tool.name,
                "description": tool.description,
                "parameters": tool.args_schema.schema() if tool.args_schema else {
                    "type": "object",
                    "properties": {
                        k: {"type": v.__name__} for k, v in tool.args.items()
                    }
                }
            }
        })

    def get_schemas(self) -> list[dict]:
        return self._schemas

    def call(self, name: str, **kwargs):
        if name not in self._tools:
            raise KeyError(f"Tool '{name}' not registered")
        return self._tools[name].invoke(kwargs)
  • Register tools with the agent. Pass the registry when creating the agent.
from langchain.tools import tool

@tool
def search_web(query: str) -> str:
    """Search the web for information."""
    return f"Results for: {query}"

@tool
def calculate(expression: str) -> str:
    """Evaluate a math expression."""
    return str(eval(expression))

registry = ToolRegistry()
registry.register(search_web)
registry.register(calculate)

# Pass schemas to the LLM
llm_with_tools = llm.bind_tools(registry.get_schemas())
  • Dispatch tool calls. On receiving a tool call from the LLM, look up and execute.
def handle_tool_calls(tool_calls: list, registry: ToolRegistry) -> list[dict]:
    results = []
    for tc in tool_calls:
        try:
            result = registry.call(tc.function.name, **json.loads(tc.function.arguments))
            results.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": str(result)
            })
        except Exception as e:
            results.append({
                "role": "tool",
                "tool_call_id": tc.id,
                "content": json.dumps({"error": str(e)})
            })
    return results
  • Validate tool existence before calling. Provide a friendly error if the tool is missing.
def safe_call(registry: ToolRegistry, name: str, args: dict) -> str:
    if name not in registry._tools:
        return json.dumps({"error": f"Unknown tool '{name}'. Available: {list(registry._tools.keys())}"})
    return registry.call(name, **args)
  • List registered tools for inspection.
def list_tools(registry: ToolRegistry) -> str:
    lines = ["Available tools:"]
    for name, tool in registry._tools.items():
        lines.append(f"  - {name}: {tool.description}")
    return "\n".join(lines)

Verification

python -c "
from langchain.tools import tool
@tool
def echo(msg: str) -> str:
    return msg
r = {'echo': echo}
result = r['echo'].invoke({'msg': 'hello'})
print(result)
# Expected: hello
"

Common failures

  • Tool name collision. Two tools with the same name silently overwrite. Use a namespace prefix (e.g., db_query, web_search).
  • Args schema mismatch. The JSON schema generated by LangChain may differ from what the LLM expects. Validate by printing tool.args_schema.schema().
  • Forgetting to register. A tool defined but not registered returns "Unknown tool" errors. Always check the registry after setup.
  • Version mismatch - The installed package or runtime differs from the command shown; check the version first and rerun the smallest verification command.
  • Local environment drift - Another service, virtual environment, model, or path is being used; print the active binary path and configuration before changing the guide steps.

Related guides

  • How to Build Custom Tools for Agents
  • How to Use OpenAI Function Calling with Tools
← All how-to guidesCourses →