Toolcase
Type-safe, extensible tool framework for AI agents.
Features
- Async-first with sync compatibility
- Type-safe parameters via Pydantic generics
- Monadic error handling with
Result[T, E]types - Multi-framework converters (OpenAI, Anthropic, Google)
- MCP protocol & HTTP server for Cursor/Claude Desktop
- Middleware pipeline (logging, retry, timeout, rate limiting, circuit breaker)
- Agentic primitives (router, fallback, race, gate, escalation)
- Structured concurrency with TaskGroup and CancelScope
- Distributed tracing (OTLP, Datadog, Honeycomb, Zipkin)
- Caching with TTL (memory, Redis, Memcached)
- Dependency injection with scoped lifecycle
- Streaming with SSE, WebSocket, JSON Lines adapters
Installation
pip install toolcase
# Optional integrations
pip install toolcase[mcp] # MCP protocol (Cursor, Claude Desktop)
pip install toolcase[http] # HTTP REST server
pip install toolcase[langchain] # LangChain tools
pip install toolcase[redis] # Redis cache backend
pip install toolcase[otel] # OpenTelemetry exporters
pip install toolcase[all] # Everything
Quick Start
from toolcase import tool, init_tools
@tool(description="Search the web")
async def search(query: str, limit: int = 5) -> str:
return f"Found {limit} results for: {query}"
registry = init_tools(search) # Registers discovery tool + your tools
result = await registry.execute("search", {"query": "python", "limit": 3})
Class-Based Tools
from pydantic import BaseModel, Field
from toolcase import BaseTool, ToolMetadata
class SearchParams(BaseModel):
query: str = Field(..., description="Search query")
limit: int = Field(default=5, ge=1, le=20)
class SearchTool(BaseTool[SearchParams]):
metadata = ToolMetadata(name="search", description="Search the web", category="search")
params_schema = SearchParams
async def _async_run(self, params: SearchParams) -> str:
return f"Found {params.limit} results for: {params.query}"
Core Concepts
Middleware Pipeline
from toolcase import LoggingMiddleware, RetryMiddleware, TimeoutMiddleware
registry.use(LoggingMiddleware())
registry.use(RetryMiddleware(max_retries=3))
registry.use(TimeoutMiddleware(30.0))
Monadic Error Handling
from toolcase import Ok, Err, Result, try_tool_operation
def _run_result(self, params) -> Result[str, ToolError]:
return (
self._validate(params)
.flat_map(lambda p: self._fetch(p))
.map(lambda d: self._format(d))
)
# Auto-wrap exceptions
result = try_tool_operation("my_tool", lambda: risky_call())
match result:
case Ok(value): print(value)
case Err(error): print(error.message)
See docs/MONADIC_ERRORS.md for complete guide.
Agentic Composition
from toolcase import router, fallback, race, gate, Route
smart_search = router(Route(lambda p: "code" in p["query"], code_search), default=web_search)
resilient = fallback(primary_api, backup_api, cache) # Try until success
fastest = race(api_a, api_b, timeout=5.0) # First success wins
premium = gate(lambda ctx: ctx.get("is_premium"), expensive_tool)
Multi-Framework Export
from toolcase.foundation.formats import to_openai, to_anthropic, to_google
openai_tools = to_openai(registry) # OpenAI function calling
anthropic_tools = to_anthropic(registry) # Anthropic tool_use
gemini_tools = to_google(registry) # Google Gemini
MCP & HTTP Server
from toolcase.ext.mcp import serve_mcp, serve_http
serve_mcp(registry, transport="sse", port=8080) # Cursor, Claude Desktop
serve_http(registry, port=8000) # REST API
Dependency Injection
from toolcase import Container, Scope
container = Container()
container.provide("db", lambda: Database(), Scope.SINGLETON)
container.provide("http", lambda: httpx.AsyncClient(), Scope.SCOPED)
async with container.scope() as ctx:
db = await container.resolve("db", ctx)
Batch Execution
from toolcase import BatchConfig
params_list = [{"query": q} for q in ["python", "rust", "go"]]
results = await tool.batch_run(params_list, BatchConfig(concurrency=5))
print(f"Success: {results.success_rate:.0%}")
Streaming
from toolcase import sse_adapter
@tool(description="Stream response", streaming=True)
async def stream_search(query: str):
for i in range(10):
yield f"Result {i} for {query}"
async for event in sse_adapter(stream_search.stream({"query": "test"})):
print(event)
Observability
from toolcase import configure_tracing, configure_logging, TracingMiddleware
configure_tracing(exporter="otlp", endpoint="http://localhost:4317")
configure_logging(level="INFO", json=True)
registry.use(TracingMiddleware())
Testing
from toolcase import ToolTestCase, mock_tool
class TestSearch(ToolTestCase):
async def test_search(self):
tool = mock_tool("search", return_value="mocked")
result = await tool.run({"query": "test"})
self.assert_success(result)
Settings & Environment
from toolcase import get_settings, load_env
load_env() # Load .env files
settings = get_settings()
print(settings.cache.ttl, settings.retry.max_retries)
CLI Help
toolcase help # List topics
toolcase help tool # Tool creation
toolcase help middleware # Middleware pipeline
toolcase help agents # Agentic patterns
toolcase help mcp # MCP/HTTP server
toolcase help result # Monadic errors
API Reference
Core
BaseTool[T] · ToolMetadata · ToolCapabilities · @tool · init_tools
Errors
Result[T, E] · Ok · Err · ErrorCode · ToolError · try_tool_operation
Runtime
Middleware · compose · pipeline · parallel · streaming_pipeline
Agents
router · fallback · race · gate · retry_with_escalation
Concurrency
TaskGroup · CancelScope · Lock · Semaphore · run_sync
Observability
configure_tracing · configure_logging · TracingMiddleware · Span
IO
ToolCache · MemoryCache · StreamChunk · StreamEvent · BatchConfig
License
Griffin License - Made by GriffinCanCode
See LICENSE for full terms.
Metadata
Release files for toolcase 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| toolcase-0.1.0.tar.gz | 326.6 kB | Without provenance | Details for toolcase-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| toolcase-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for toolcase-0.1.0-py3-none-any.whl |
Total release size: 760.3 kB
Release files / toolcase-0.1.0.tar.gz
| Download URL | toolcase-0.1.0.tar.gz |
|---|---|
| Size | 326.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f39a8176c7928918c67e1bd5f5954d52a34b8b2584f2254cc62e4929b044ed02
|
|
BLAKE2b-256 checksum How to use checksums |
b3a44e460d5cdc866bf2c36ef69f5ce80369747fe2b549333b5c555ab9569ed4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Release files / toolcase-0.1.0-py3-none-any.whl
| Download URL | toolcase-0.1.0-py3-none-any.whl |
|---|---|
| Size | 433.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
44b67beeffa02e031599c1bae975b03753f94cd1cad428869148a4e23d6c06b0
|
|
BLAKE2b-256 checksum How to use checksums |
d41c6c46c69c7119bb18d174954fa13ab441dd587c5380938e1c94a804293f3d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Security for toolcase 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.