A modular tool registration system that separates core tool metadata handling from specific tool format implementations. This system provides decorator-based tool registration with automatic metadata extraction, tool discovery, and export capabilities for integration with external systems like Ollama.
- Tool Registration: Decorate functions with
@toolto register them with metadata - Export Control: Use
@exposeto mark tools for external consumption - Metadata Management: Automatic parameter analysis and optional parameter detection
- Tool Invocation: Execute registered tools by name with parameter validation
- Ollama Integration: Export tools in Ollama-compatible format
- HTTP Server: RESTful API for tool discovery and execution
- Module Scanning: Automatic discovery of tools within modules using frame inspection
- Caching: Built-in caching for tool metadata and Ollama tool exports
# Clone the repository
git clone https://github.com/yourusername/tool-registration.git
cd tool-registration
# Install in development mode
pip install -e .
# Or install normally
pip install .Add to your requirements.txt:
tool-registration>=0.1.0
Or install directly:
pip install tool-registrationfrom tool_registration import tool, expose
@expose
@tool(
name="hello_world",
description="A simple greeting tool",
parameters={
"name": {
"type": "string",
"description": "Name to greet",
"default": "World"
}
},
version="1.0.0"
)
def hello_world(name: str = "World") -> str:
"""Return a greeting message."""
return f"Hello, {name}!"from tool_registration import get_registered_tool_names, invoke_tool
# Get all registered tools
tool_names = get_registered_tool_names()
print(f"Available tools: {tool_names}")
# Invoke a tool
result = invoke_tool("hello_world", name="Alice")
print(result) # Output: Hello, Alice!from tool_registration import get_tools_for_ollama, get_tool_for_ollama
# Get all tools in Ollama format
ollama_tools = get_tools_for_ollama()
print(ollama_tools)
# Get a specific tool in Ollama format
specific_tool = get_tool_for_ollama("hello_world")
print(specific_tool)from tool_registration import create_server
# Create and start the HTTP server
server = create_server(host="localhost", port=5002)
server.run(debug=True)
# Available endpoints:
# GET /tools - List all exportable tools
# GET /ollama/tools - List tools in Ollama format
# GET /health - Health check
# POST /tool/<tool_name> - Execute a tool
# POST /ollama/tool/<tool_name> - Execute a tool via Ollama registrytool-registration/
├── src/ # Source code
│ └── tool_registration/ # Main package
│ ├── __init__.py # Package initialization and exports
│ ├── ToolRegistry.py # Core tool registration system
│ ├── ToolExportServer.py # HTTP server for tool export
│ ├── OllamaTools.py # Ollama integration
│ └── utils/ # Utility modules
│ ├── __init__.py
│ └── LoggerUtils.py # Logging configuration
├── examples/ # Example implementations
│ ├── ExampleTools.py # Sample tool definitions
│ ├── ExampleTools2.py # Additional examples
│ └── ExampleUsage.py # Usage demonstration
├── tests/ # Test suite
├── pyproject.toml # Build configuration
├── setup.py # Alternative setup script
└── README.md # This file
@tool(name, description, parameters, version): Decorator to register a tool@expose: Decorator to mark a tool as exportableregister_tools_from_module(module_name): Register tools from a specific modulescan_and_register_tools(): Scan and register tools from current context
get_registered_tool_names(): Get list of all registered tool namesget_tool_count(): Get total number of registered toolsget_tool_metadata(tool_name): Get metadata for a specific toolget_all_tool_metadata(): Get metadata for all registered toolsget_exportable_tool_names(): Get list of exportable tool namesget_exportable_tool_count(): Get total number of exportable toolsget_exportable_tool_metadata(tool_name): Get metadata for a specific exportable toolget_all_exportable_tool_metadata(): Get metadata for all exportable tools
invoke_tool(tool_name, **kwargs): Execute a tool by nameinvoke_tool_from_registry(tool_name, **kwargs): Execute a tool via Ollama registry
get_tools_for_ollama(exportable_only=False): Get tools in Ollama formatget_tool_for_ollama(tool_name): Get a specific tool in Ollama formatget_ollama_tool_count(): Get count of registered Ollama toolsget_registered_ollama_tool_names(): Get list of registered Ollama tool namesclear_ollama_tools(): Clear the Ollama tools registry
ToolExportServer(host, port): Create an HTTP server instancecreate_server(host, port): Factory function to create a serverAPI_VERSION: Current API versionDEFAULT_HOST: Default server hostDEFAULT_PORT: Default server port
clear_tool_registry(): Clear the entire tool registryget_registry_timestamp(): Get timestamp of last registry scan
The parameters argument in the @tool decorator should be a dictionary with the following structure:
parameters={
"param_name": {
"type": "string|number|boolean|array|object",
"description": "Parameter description",
"default": "default_value", # Optional
"enum": ["value1", "value2"], # Optional, for enum types
"optional": True # Auto-detected from function signature
}
}The examples/ directory contains complete working examples demonstrating how to use the tool registration system:
ExampleTools.py- Sample tools with various parameter types and decoratorsExampleTools2.py- Additional tools showing different use casesExampleUsage.py- Complete demonstration script showing external project integration
# Run the complete demonstration
python examples/ExampleUsage.py
# Or import and use individual example modules
python -c "from examples import ExampleTools; print('Tools loaded successfully')"Here are complete working examples of how to use the tool registration system:
from tool_registration import tool, expose
# Define a simple tool
@expose
@tool(
name="hello_world",
description="A simple greeting tool",
parameters={
"name": {
"type": "string",
"description": "Name to greet",
"default": "World"
}
},
version="1.0.0"
)
def hello_world(name: str = "World") -> str:
"""Return a greeting message."""
return f"Hello, {name}!"
# Define a tool with multiple parameters
@expose
@tool(
name="calculate",
description="Perform basic arithmetic operations",
parameters={
"operation": {
"type": "string",
"description": "Arithmetic operation (add, subtract, multiply, divide)",
"enum": ["add", "subtract", "multiply", "divide"]
},
"a": {
"type": "number",
"description": "First number"
},
"b": {
"type": "number",
"description": "Second number"
}
},
version="1.0.0"
)
def calculate(operation: str, a: float, b: float) -> float:
"""Perform arithmetic operation on two numbers."""
if operation == "add":
return a + b
elif operation == "subtract":
return a - b
elif operation == "multiply":
return a * b
elif operation == "divide":
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
else:
raise ValueError(f"Unknown operation: {operation}")from tool_registration import get_registered_tool_names, invoke_tool, register_tools_from_module
# Register tools from a module (assuming the tools are defined in a module called 'my_tools')
register_tools_from_module('my_tools')
# Get all registered tools
tool_names = get_registered_tool_names()
print(f"Available tools: {tool_names}")
# Invoke a tool
result = invoke_tool("hello_world", name="Alice")
print(result) # Output: Hello, Alice!
# Invoke with default parameter
result = invoke_tool("hello_world")
print(result) # Output: Hello, World!
# Invoke the calculate tool
result = invoke_tool("calculate", operation="add", a=10, b=5)
print(result) # Output: 15.0from tool_registration import (
get_tool_metadata,
get_all_tool_metadata,
get_exportable_tool_names,
get_exportable_tool_count
)
# Get metadata for a specific tool
metadata = get_tool_metadata("hello_world")
print(f"Tool metadata: {metadata}")
# Get all tool metadata
all_metadata = get_all_tool_metadata()
print(f"Total tools: {len(all_metadata)}")
# Get exportable tools (those decorated with @expose)
exportable_tools = get_exportable_tool_names()
print(f"Exportable tools: {exportable_tools}")
# Get count of exportable tools
exportable_count = get_exportable_tool_count()
print(f"Exportable tool count: {exportable_count}")- Frame Inspection: The automatic tool discovery relies on Python frame inspection, which can be unreliable in certain environments (e.g., some IDEs, debuggers, or complex import scenarios)
- Module Scanning: Tools must be defined in modules that can be imported and scanned. Dynamic or runtime-generated tools may not be discovered automatically
- Tool Name Constraints: Tool names must exactly match the function name they decorate - no aliasing is supported
- Cache Expiration: Tool registry cache expires after 24 hours, which may cause performance issues in long-running applications
- No Input Sanitization: Limited input validation beyond basic type checking
- No Rate Limiting: The HTTP server doesn't implement rate limiting or request throttling
- Global State: The system uses global variables for tool storage, which can cause issues in multi-threaded environments
- No Persistence: Tool metadata is not persisted between application restarts
- Limited Error Handling: Some error conditions may not be handled gracefully
- No Tool Versioning: While tools can specify a version, there's no version management or compatibility checking
- Format Constraints: Ollama tool export is limited to the specific format expected by Ollama
- Parameter Type Mapping: Some complex parameter types may not map perfectly to Ollama's expected format
- No Dynamic Updates: Ollama tools cache may not reflect real-time changes to the tool registry
- TODO Items: Several features are marked as TODO and not yet implemented (authentication, configuration)
- Hacky Implementation: Some module scanning logic is marked as "too hacky" and needs refactoring
- Limited Documentation: Some internal APIs lack comprehensive documentation
# Install test dependencies
pip install -e ".[test]"
# Run tests
pytest
# Run with coverage
pytest --cov=src# Build source distribution
python setup.py sdist
# Build wheel
python setup.py bdist_wheel
# Or use modern tools
pip install build
python -m buildWhen contributing to this project, please be aware of the current limitations and consider:
- Implementing proper authentication for the HTTP server
- Adding comprehensive error handling
- Improving the module scanning mechanism
- Adding persistence for tool metadata
- Implementing proper configuration management