Make your custom tools accessible across multiple protocols, including MCP, REST and CLI.
Project description
toolaccess
Define your Python functions once, expose them as REST APIs, MCP servers, and CLI commands — simultaneously, with zero boilerplate duplication.
When to use this
You have Python functions that need to be callable from more than one interface. Common scenarios:
- AI/LLM tool servers — you want the same tools available over MCP (for agents) and REST (for web apps) and CLI (for local testing).
- Internal tooling — a set of utility functions your team invokes from scripts, HTTP clients, and AI assistants.
- Rapid prototyping — skip the plumbing and get a working API + MCP server + CLI in minutes.
Without toolaccess you'd write separate FastAPI routes, a FastMCP server, and Typer commands that all call the same underlying code. This library removes that duplication.
Install
pip install toolaccess
Or from source:
pip install -e .
Quick start
from toolaccess import (
ServerManager,
ToolService,
ToolDefinition,
OpenAPIServer,
SSEMCPServer,
CLIServer,
)
# 1. Write plain functions (sync or async)
def add(a: int, b: int) -> int:
"""Add two numbers."""
return a + b
async def greet(name: str) -> str:
"""Return a greeting."""
return f"Hello, {name}!"
# 2. Group them into a service
service = ToolService("math", [add, greet])
# 3. Create servers and mount the service
rest = OpenAPIServer(path_prefix="/api", title="Math API")
rest.mount(service)
mcp = SSEMCPServer("math")
mcp.mount(service)
cli = CLIServer("math")
cli.mount(service)
# 4. Wire everything into the manager
manager = ServerManager(name="my-tools")
manager.add_server(rest)
manager.add_server(mcp)
manager.add_server(cli)
# 5. Run
manager.run()
That single file gives you:
| Interface | Access |
|---|---|
| REST API | POST /api/add, POST /api/greet |
| OpenAPI docs | GET /api/docs |
| MCP (SSE) | http://localhost:8000/mcp/math/sse |
| MCP (stdio) | python app.py mcp-run --name math |
| CLI | python app.py math add 1 2 |
| Health check | GET /health |
Starting the HTTP server
python app.py start # default 127.0.0.1:8000
python app.py start --host 0.0.0.0 --port 9000
Running MCP over stdio
python app.py mcp-run --name math
Use this when connecting from Claude Desktop, Cursor, or any MCP client that expects a stdio transport.
Core concepts
ToolDefinition
Wraps a callable with metadata. If you pass a bare function to ToolService, one is created automatically using the function name and docstring. Use it explicitly when you need control over the HTTP method or name:
ToolDefinition(func=add, name="add_numbers", http_method="POST", description="Sum two ints")
ToolService
A named group of tools. Mount the same service onto multiple servers to keep them in sync:
service = ToolService("admin", [check_health, restart_worker])
Servers
| Class | Protocol | Notes |
|---|---|---|
OpenAPIServer |
HTTP / REST | Backed by FastAPI. Set path_prefix to namespace routes. |
SSEMCPServer |
MCP (SSE + stdio) | Backed by FastMCP. Mounted at /mcp/{name}/sse. |
CLIServer |
CLI | Backed by Typer. Async functions are handled automatically. |
ServerManager
The runtime host. It owns a FastAPI app, a Typer CLI, and a dynamic ASGI dispatcher that routes requests to the correct sub-app by path prefix.
Servers can be added and removed at runtime:
manager.add_server(new_api) # immediately routable
manager.remove_server(new_api) # immediately gone
Multiple isolated groups
You can create separate servers for different audiences and mount different services onto each:
public_api = OpenAPIServer("/public", "Public API")
public_api.mount(public_service)
admin_api = OpenAPIServer("/admin", "Admin API")
admin_api.mount(admin_service)
manager.add_server(public_api)
manager.add_server(admin_api)
The same pattern works for MCP — create multiple SSEMCPServer instances with different names.
Lifespan support
Pass an async context manager to ServerManager to run setup/teardown logic (database connections, model loading, etc.). The lifespan is entered for both the HTTP server and CLI command execution:
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app):
db = await connect_db()
yield
await db.close()
manager = ServerManager(name="my-service", lifespan=lifespan)
Mounting custom ASGI apps
Use MountableApp to add an existing FastAPI or ASGI application alongside your tool servers:
from toolaccess.toolaccess import MountableApp
from fastapi import FastAPI
dashboard = FastAPI()
@dashboard.get("/")
async def index():
return {"page": "dashboard"}
manager.add_server(MountableApp(dashboard, path_prefix="/dashboard", name="dashboard"))
Requirements
- Python >= 3.10
- fastapi
- fastmcp
- pydantic
- typer
- uvicorn
Development
pip install -e ".[dev]"
pytest
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toolaccess-0.1.0.tar.gz.
File metadata
- Download URL: toolaccess-0.1.0.tar.gz
- Upload date:
- Size: 12.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4e3224ec6454e0bcb218e12e61adc75b90470ed87fbdf7c4ee0f0df8fc3f599
|
|
| MD5 |
cfe2e4573bce160755012641f788b612
|
|
| BLAKE2b-256 |
29c067eec37b908d42ab66572541c068affe905d3f84358a7a91c41443bed8c5
|
File details
Details for the file toolaccess-0.1.0-py3-none-any.whl.
File metadata
- Download URL: toolaccess-0.1.0-py3-none-any.whl
- Upload date:
- Size: 8.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aeed876db66fbdb0a023d528578b9453c2eca939b284f8258e0e3ea8f4563cfa
|
|
| MD5 |
a6f2375d9998af4917871f78995136f1
|
|
| BLAKE2b-256 |
a8002f5a55db975e29e41ee22510d8e9b20d069593275b21ae94c8ec1f9b0609
|