OPAL REST Tool Service - Automatically generates tools from OpenAPI 3.x specifications
Project description
OPAL REST Tool Service
A robust implementation of REST API tools for the OPAL SDK that automatically generates tools from OpenAPI specifications.
Overview
The RESTToolService extends the standard OPAL ToolsService to support automatic tool generation from OpenAPI 3.x specifications. Instead of manually writing wrapper functions for each API endpoint, you can simply provide an OpenAPI spec and get all operations as callable tools.
Features
- ✅ Zero-code tool generation - REST APIs become tools automatically
- ✅ OpenAPI 3.x support - Parse JSON or YAML specifications
- ✅ Flexible authentication - Support for API keys, OAuth2, Bearer tokens
- ✅ Parameter mapping - Convert OpenAPI parameters to OPAL Parameter types
- ✅ HTTP client integration - Built-in async HTTP client for API calls
- ✅ Error handling - Proper status code and error response handling
- ✅ Backward compatibility - Works alongside traditional @tool decorators
- ✅ Configuration options - Filter operations, custom base URLs, auth config
REST Tool Flow
The OPAL REST Tool Service follows a clear pipeline that transforms OpenAPI specifications into executable tools:
1. Data Flow Architecture
┌─────────────────┐ ┌────────────────────┐ ┌─────────────────┐
│ OpenAPI │───▶│ OpenAPISpecParser │───▶│ ParsedOperation │
│ Specification │ │ │ │ List │
│ (JSON/YAML) │ │ │ │ │
└─────────────────┘ └────────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌────────────────────┐ ┌─────────────────┐
│ HTTP Client │◀───│ RESTHandler │◀───│ RESTToolService │
│ (httpx) │ │ │ │ │
└─────────────────┘ └────────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌────────────────────┐ ┌─────────────────┐
│ FastAPI │◀───│ RESTToolRegistry │◀───│ Tool Handler │
│ Endpoints │ │ │ │ Functions │
└─────────────────┘ └────────────────────┘ └─────────────────┘
2. Processing Steps
- OpenAPI Parsing:
OpenAPISpecParserreads the specification and extracts operations - Operation Registration: Each operation becomes a
ParsedOperationwith parameters and metadata - Tool Creation: Operations are registered as FastAPI endpoints through
RESTToolService - Registry Management:
RESTToolRegistrytracks all operations with filtering and statistics - Request Execution:
RESTHandlerexecutes HTTP calls to target APIs - Response Handling: Results are formatted and returned to the client
3. Component Interaction
Request → FastAPI Endpoint → Tool Handler → RESTHandler → Target API
↓
Response ← Tool Handler ← RESTHandler ← Target API Response
Installation
- Install required dependencies:
pip install -r requirements.txt
- Configure environment variables (optional):
# Copy the example environment file
cp .env.example .env
# Edit .env with your API keys and configuration
# BEARER_AUTHENTICATION=your-api-key-here
Quick Start
Get started with REST tools in 3 simple steps:
1. Run the Example
# Run the Optimizely API example
python -m rest_tools_example
How to Add New Tools
There are multiple ways to add new REST tools to the OPAL system, depending on your use case:
Method 1: From OpenAPI Specification URL
The easiest way to add tools from a publicly accessible OpenAPI spec:
from fastapi import FastAPI
from core.rest_tool_service import create_service_from_urls
from core.rest_models import OpenAPIConfig
app = FastAPI(title="My REST Tools Service")
# Configure which operations to include
config = OpenAPIConfig(
base_url="https://api.example.com/v1", # Override spec base URL if needed
filter_operations=["list_users", "get_user"], # Only include these operations
# exclude_operations=["delete_all"] # Or exclude specific operations
)
# Create service from URL
service = await create_service_from_urls(
app,
{"my-api": "https://api.example.com/openapi.json"}, # spec_name: url
config
)
await service.startup()
Method 2: From Local OpenAPI Specification File
When you have the OpenAPI spec as a local file:
from pathlib import Path
from core.rest_tool_service import create_rest_tool_service
# Load from file path
spec_path = Path("my-api-spec.json") # or .yaml
service = create_rest_tool_service(app, spec_path, config)
# Or load and filter the spec first
import json
with open("full-api-spec.json") as f:
full_spec = json.load(f)
# Filter to specific paths
filtered_spec = {
**full_spec,
"paths": {path: methods for path, methods in full_spec["paths"].items()
if path.startswith("/users")} # Only /users endpoints
}
service = create_rest_tool_service(app, filtered_spec, config)
Method 3: Multiple API Specifications
When you need to combine multiple APIs into one service:
from core.rest_tool_service import create_multi_spec_service
# Multiple specs with names
specs = {
"github-api": github_openapi_spec,
"slack-api": slack_openapi_spec,
"custom-api": Path("custom-api.yaml")
}
service = create_multi_spec_service(app, specs, config)
await service.startup()
# Access operations by spec
github_ops = service.get_operations(spec_name="github-api")
slack_ops = service.get_operations(spec_name="slack-api")
Method 4: Dynamic Registration
Add specs dynamically to a running service:
# Start with empty service
service = RESTToolService(app, config)
await service.startup()
# Add specs as needed
count1 = service.register_openapi_spec(api_spec_1, "api-v1")
count2 = await service.register_openapi_url("https://api.example.com/spec", "api-v2")
# Bulk registration
batch_specs = [
{"spec": spec_1, "name": "spec1"},
{"url": "https://api.example.com/spec2", "name": "spec2"}
]
results = service.register_multiple_specs(batch_specs)
Configuration Options
Control which operations become tools using OpenAPIConfig:
config = OpenAPIConfig(
base_url="https://custom-base.com", # Override API base URL
filter_operations=["op1", "op2"], # Only include these operations
exclude_operations=["dangerous_op"], # Exclude specific operations
endpoint_prefix="/custom/api/path", # Custom endpoint prefix
)
Authentication Setup
Configure authentication for API calls:
# In your tool calls, provide auth data:
auth_data = {
# Direct headers
"Authorization": "Bearer your-token",
"X-API-Key": "your-api-key",
# Or structured auth (matches OpenAPI security schemes)
"api_key": "your-key",
"token": "bearer-token",
"access_token": "oauth-token"
}
# The RESTHandler will automatically apply the appropriate headers
result = await service.rest_handler.execute_operation(
operation=my_operation,
parameters={"param1": "value1"},
auth_data=auth_data
)
Environment Variables
The system supports configuration via environment variables:
# Set in your .env file or environment
# Authentication
BEARER_AUTHENTICATION=your-api-key-here
# REST API Configuration
RESTAPI_ENDPOINT_PREFIX=/rest-tools # Default endpoint prefix for tools
Configuration details:
BEARER_AUTHENTICATION: API authentication token (loaded byget_auth_value())RESTAPI_ENDPOINT_PREFIX: Configurable endpoint prefix for REST tools (default:/rest-tools)
Configurable Endpoint Prefix
You can customize the endpoint prefix for REST tools using the RESTAPI_ENDPOINT_PREFIX environment variable:
# Examples:
RESTAPI_ENDPOINT_PREFIX=/rest-tools # Default
RESTAPI_ENDPOINT_PREFIX=/experimental/rest-tools
RESTAPI_ENDPOINT_PREFIX=/api/v2/tools
This affects all tool endpoints. For example, with /experimental/rest-tools:
list_projectsbecomes/experimental/rest-tools/list_projectsget_userbecomes/experimental/rest-tools/get_user
You can also override in code:
config = OpenAPIConfig(
endpoint_prefix="/custom/api/path" # Overrides environment variable
)
Core Components
The system consists of several key components that work together to transform OpenAPI specs into executable tools:
1. OpenAPISpecParser (niteco/opal_rest_tool/openapi_parser.py:12)
Parses OpenAPI 3.x specifications and extracts operations into ParsedOperation objects:
Key Features:
- Supports JSON and YAML OpenAPI specs
- Extracts parameters from paths, query strings, and request bodies
- Maps OpenAPI security schemes to authentication requirements, can omit this (for Opal Hackathon)
- Applies operation filtering based on configuration
from core.openapi_parser import OpenAPISpecParser
from core.rest_models import OpenAPIConfig
config = OpenAPIConfig(filter_operations=["list_users", "get_user"])
parser = OpenAPISpecParser(config)
operations = parser.parse(openapi_spec) # Returns List[ParsedOperation]
2. RESTHandler (niteco/opal_rest_tool/rest_handler.py:13)
Executes HTTP requests for parsed operations using the httpx client:
Key Features:
- Builds URLs with path parameter substitution
- Handles bearer authentication
- Separates parameters into query params and request body
- Formats responses with error handling
from core.rest_handler import RESTHandler
handler = RESTHandler(timeout=30)
result = await handler.execute_operation(
operation=parsed_operation,
parameters={"user_id": 123, "include_details": True},
auth_data={"Authorization": "Bearer token"}
)
3. ParsedOperation (niteco/opal_rest_tool/rest_models.py:10)
Data model representing a parsed API operation with all necessary metadata:
@dataclass
class ParsedOperation:
name: str # Generated tool name (snake_case)
description: str # Operation description
method: str # HTTP method (GET, POST, etc.)
path: str # API path with parameters
base_url: str # Base URL for API calls
parameters: List[Parameter] # Extracted parameters
auth_requirements: Optional[List[AuthRequirement]] # Auth requirements
operation_id: Optional[str] # Original OpenAPI operationId
request_body_schema: Optional[Dict[str, Any]] # Request body schema
response_schema: Optional[Dict[str, Any]] # Response schema
tags: Optional[List[str]] # OpenAPI tags
4. RESTToolService (niteco/opal_rest_tool/rest_tool_service.py:117)
Main service class that extends the OPAL ToolsService to support REST API integration:
Key Features:
- Auto-registration of OpenAPI operations as FastAPI endpoints
- Operation registry with filtering and management
- Health monitoring and validation
- Multi-spec support with bulk operations
- Lifecycle management (startup/shutdown)
from core.rest_tool_service import RESTToolService
from core.rest_models import OpenAPIConfig
app = FastAPI()
config = OpenAPIConfig(base_url="https://api.example.com")
service = RESTToolService(app, config)
# Register specs
count = service.register_openapi_spec(openapi_spec, "my-api")
count = await service.register_openapi_url("https://api.example.com/spec", "api-v2")
# Manage operations
operations = service.get_operations(spec_name="my-api")
service.unregister_operation("unwanted_tool")
5. RESTToolRegistry (niteco/opal_rest_tool/rest_tool_service.py:26)
Internal registry that manages REST operations with metadata tracking:
Key Features:
- Operation storage with name-based lookup
- Tag-based and spec-based filtering
- Statistics and health monitoring
- Bulk operations (clear, unregister)
# Accessed through RESTToolService
stats = service.registry.get_stats()
operations_by_tag = service.registry.get_by_tag("users")
operations_by_spec = service.registry.get_by_spec("github-api")
6. OpenAPIConfig (niteco/opal_rest_tool/rest_models.py:38)
Configuration object for customizing OpenAPI parsing and tool generation:
@dataclass
class OpenAPIConfig:
base_url: Optional[str] = None # Override API base URL
auth_config: Optional[Dict[str, Any]] = None # Auth configuration
filter_operations: Optional[List[str]] = None # Include only these operations
exclude_operations: Optional[List[str]] = None # Exclude these operations
Testing
Run the test suite to see the RESTToolService in action:
# Test the parser only
python test_rest_service.py
# Choose option 1
# Test the full service with HTTP calls
python test_rest_service.py
# Choose option 2
The test uses the JSONPlaceholder API (https://jsonplaceholder.typicode.com) to demonstrate:
- GET requests with query parameters
- POST requests with request body
- Path parameter substitution
- Response handling
Authentication Support
The RESTToolService supports bearer authentication method for now.
Error Handling
- HTTP errors (4xx, 5xx) are captured and included in responses
- Network errors are logged and re-raised as HTTPException
- Invalid OpenAPI specs raise ValueError with details
- Missing required parameters are logged as warnings
Discovery Endpoint
All generated tools are automatically discoverable via the /discovery endpoint:
{
"functions": [
{
"name": "get_users",
"description": "Get all users",
"parameters": [...],
"endpoint": "/rest-tools/get_users",
"http_method": "POST"
}
]
}
Limitations
- Currently supports OpenAPI 3.x (not 2.x/Swagger)
- Reference resolution is simplified (no external refs)
- Authentication mapping may need customization for complex schemes
- Response schemas are captured but not validated
Future Enhancements
- Full $ref reference resolution
- OpenAPI 2.x support
- Response validation against schemas
- Request/response transformation middleware
- Rate limiting and retry logic
- Caching for API responses
Project Structure
PyPI Package Structure (niteco.opal-rest-tool)
niteco/opal_rest_tool/ # Core system components (included in PyPI)
├── __init__.py # Package initialization
├── rest_models.py # Data models (ParsedOperation, OpenAPIConfig)
├── openapi_parser.py # OpenAPI 3.x specification parser
├── rest_handler.py # HTTP client for REST API execution
└── rest_tool_service.py # Main service (RESTToolService, Registry)
Development Repository Structure
Niteco.Opal-REST-tool/
├── niteco/opal_rest_tool/ # Core system components (PyPI package)
│ ├── __init__.py # Package initialization
│ ├── rest_models.py # Data models (ParsedOperation, OpenAPIConfig)
│ ├── openapi_parser.py # OpenAPI 3.x specification parser
│ ├── rest_handler.py # HTTP client for REST API execution
│ └── rest_tool_service.py # Main service (RESTToolService, Registry)
├── rest_tools_example/ # Example implementations (dev only)
│ ├── __init__.py # Package initialization
│ ├── __main__.py # Example entry point
│ ├── optimizely_web_experimentation.py # Optimizely API integration example
│ └── opal tool.postman_collection.json # Postman collection for testing
├── test/ # Test files and specifications (dev only)
│ ├── Optimizely API - v1.0.json # Sample OpenAPI specification
│ └── test_rest.py # Comprehensive test suite
├── .env.example # Environment configuration template
├── pyproject.toml # Package configuration and PyPI metadata
├── requirements.txt # Python dependencies
└── README.md # This documentation
Core Files Description
niteco/opal_rest_tool/rest_models.py- Data classes includingParsedOperation(line 10) andOpenAPIConfig(line 38)niteco/opal_rest_tool/openapi_parser.py- OpenAPI spec parser withOpenAPISpecParserclass (line 12)niteco/opal_rest_tool/rest_handler.py- HTTP execution engine withRESTHandlerclass (line 13)niteco/opal_rest_tool/rest_tool_service.py- Main service classes:RESTToolRegistry(line 26) - Operation registry managementRESTToolService(line 117) - Main service extending OPAL ToolsService- Factory functions (lines 503-571) - Convenience service creators
test/test_rest.py- Complete test suite demonstrating all featuresrest_tools_example/optimizely_web_experimentation.py- Real-world example using Optimizely API
License
This implementation is part of the OPAL project.
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 niteco_opal_rest_tool-0.1.6.tar.gz.
File metadata
- Download URL: niteco_opal_rest_tool-0.1.6.tar.gz
- Upload date:
- Size: 23.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6709b67b5439fc9872a144131380e987e7f203fd2ef4ce8fd4df1aac6d6f289
|
|
| MD5 |
e3c4314813ab76d932080044fd91c8d9
|
|
| BLAKE2b-256 |
b3a908a9395a53698bf63584437cc4e33fb2976a24fae0417a0056355aee7eb1
|
File details
Details for the file niteco_opal_rest_tool-0.1.6-py3-none-any.whl.
File metadata
- Download URL: niteco_opal_rest_tool-0.1.6-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
346392f771960d09e01742e70d5d4878d8901b260d164678c80ed1196345e2b7
|
|
| MD5 |
5acec615292bc446847358632ebdd9d6
|
|
| BLAKE2b-256 |
4fbb9003b41bd70442dd9c0c82569aead00fb015f5d28ab282dc0bd9e50e0ddd
|