Define custom tools and serve them via OpenAPI or MCP interfaces. Built on ToolRegistry.
toolregistry-server lets you register Python functions as tools and expose them as services through multiple protocols. It provides:
- Registry Builder: Protocol-agnostic config loading and source registration (
registry_builder) - Protocol Adapters:
OpenAPIAdapter(FastAPI/REST) andMCPAdapter(Model Context Protocol) - App Orchestration:
Appclass for building registries and dispatching to any adapter; subclassprepare_registry()for custom registries - Authentication: Unified Bearer token support (
auth.load_tokens) - CLI:
toolregistry-server openapi/toolregistry-server mcpwith--config,--profile, and more
| Package | Description | PyPI | Docs |
|---|---|---|---|
| toolregistry | Core library — tool registration, schema generation, execution | Docs | |
| toolregistry-server | Server adapters — expose tools via OpenAPI & MCP | Docs | |
| toolregistry-hub | Ready-to-use tools — calculator, web search, file ops, etc. | Docs |
toolregistry (core)
↓
toolregistry-server (tool server)
↓
toolregistry-hub (tool collection + server config)
# Base (RouteTable, registry_builder, auth)
pip install toolregistry-server
# With OpenAPI support
pip install toolregistry-server[openapi]
# With MCP support
pip install toolregistry-server[mcp]
# Full
pip install toolregistry-server[all]from toolregistry import ToolRegistry
from toolregistry_server import RouteTable
from toolregistry_server.adapters.openapi import OpenAPIAdapter
registry = ToolRegistry()
@registry.register
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
route_table = RouteTable(registry)
adapter = OpenAPIAdapter(route_table)
adapter.run(host="0.0.0.0", port=8000)import asyncio
from toolregistry import ToolRegistry
from toolregistry_server import RouteTable
from toolregistry_server.adapters.mcp import MCPAdapter
registry = ToolRegistry()
# ... register tools ...
route_table = RouteTable(registry)
adapter = MCPAdapter(route_table)
adapter.run(transport="stdio") # blocking
# or: asyncio.run(adapter.run_async(transport="sse", host="0.0.0.0", port=8000))from toolregistry_server.app import App
# From a config file
App().serve_openapi(config_path="tools.yaml", host="0.0.0.0", port=8000)
App().serve_mcp(config_path="tools.yaml", transport="stdio")
# From a pre-built registry
from toolregistry import ToolRegistry
registry = ToolRegistry()
# ... register tools ...
App().serve_openapi(registry=registry, port=9000)Override prepare_registry to add built-in tools, hooks, or metadata:
from toolregistry_server.app import App
class MyApp(App):
def prepare_registry(self, **kwargs):
from toolregistry import ToolRegistry
registry = ToolRegistry()
registry.register(my_builtin_tool)
# optionally apply user config on top
if kwargs.get("config_path"):
from toolregistry_server import apply_config, load_config
apply_config(registry, load_config(kwargs["config_path"]))
return registry
MyApp().serve_openapi(host="0.0.0.0", port=8000)# OpenAPI server from config file
toolregistry-server openapi --config tools.yaml --port 8000
# MCP server (stdio)
toolregistry-server mcp --config tools.yaml --transport stdio
# MCP server (SSE)
toolregistry-server mcp --config tools.yaml --transport sse --port 8000
# With deployment profile (disables network/filesystem tools)
toolregistry-server openapi --config tools.yaml --profile remote
# With Bearer token auth
toolregistry-server openapi --config tools.yaml --tokens /path/to/tokens.txtJSONC and YAML are both supported. Three source types: python, mcp, openapi.
mode: denylist # or "allowlist"
disabled: [] # namespaces to exclude (denylist mode)
tools:
# Python module — all public functions
- type: python
module: my_package.tools
namespace: my_tools
# Python class
- type: python
class: my_package.Calculator
namespace: calculator
# MCP server (stdio subprocess)
- type: mcp
transport: stdio
command: ["python", "-m", "my_mcp_server"]
namespace: mcp_tools
# MCP server (SSE / streamable-http)
- type: mcp
transport: http
url: http://localhost:8080/mcp
namespace: remote_mcp
# OpenAPI endpoint
- type: openapi
url: https://api.example.com/openapi.json
namespace: external_api
auth:
type: bearer
token_env: EXTERNAL_API_TOKENSee examples/config.yaml and examples/config.jsonc for full examples.
┌─────────────────────────────────────────────────────────────┐
│ registry_builder │
│ load_config · apply_config · register_*_source │
│ apply_profile · PROFILE_DISABLE_TAGS │
└─────────────────────────┬───────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ RouteTable │
│ (central routing layer) │
└─────────────────────────┬───────────────────────────────────┘
│
┌───────────────┼───────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ OpenAPIAdapter │ │ MCPAdapter │ │ (your adapter) │
│ (FastAPI) │ │ stdio/sse/http │ │ Adapter ABC │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ HTTP Clients │ │ MCP Clients │
└─────────────────┘ └─────────────────┘
- Subclass
Adapterfromtoolregistry_server.adapters - Implement
run(**kwargs)andcreate_and_run(cls, route_table, **kwargs) - Optionally implement
add_cli_arguments(parser)for CLI integration - Call
App().serve(MyAdapter, ...)— no changes toAppneeded
--profile applies tag-based tool filtering at startup:
| Profile | Disables |
|---|---|
remote |
FILE_SYSTEM, DESTRUCTIVE, PRIVILEGED tagged tools |
local |
NETWORK tagged tools |
Contributions are welcome! Please see our Contributing Guide for details.
MIT — see LICENSE.
- ToolRegistry — Core library
- toolregistry-hub — Built-in tool collection
- Model Context Protocol — MCP specification