Skip to content

Commit 908cd40

Browse files
authored
Merge pull request #63 from universal-tool-calling-protocol/dev
Update docs
2 parents 7ba8b3c + 76b07db commit 908cd40

File tree

5 files changed

+211
-5
lines changed

5 files changed

+211
-5
lines changed

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,4 +538,61 @@ The build process now involves building each package (`core` and `plugins`) sepa
538538
4. Run the build: `python -m build`.
539539
5. The distributable files (`.whl` and `.tar.gz`) will be in the `dist/` directory.
540540

541+
## OpenAPI Ingestion - Zero Infrastructure Tool Integration
542+
543+
🚀 **Transform any existing REST API into UTCP tools without server modifications!**
544+
545+
UTCP's OpenAPI ingestion feature automatically converts OpenAPI 2.0/3.0 specifications into UTCP tools, enabling AI agents to interact with existing APIs directly - no wrapper servers, no API changes, no additional infrastructure required.
546+
547+
### Quick Start with OpenAPI
548+
549+
```python
550+
from utcp_http.openapi_converter import OpenApiConverter
551+
import aiohttp
552+
553+
# Convert any OpenAPI spec to UTCP tools
554+
async def convert_api():
555+
async with aiohttp.ClientSession() as session:
556+
async with session.get("https://api.github.com/openapi.json") as response:
557+
openapi_spec = await response.json()
558+
559+
converter = OpenApiConverter(openapi_spec)
560+
manual = converter.convert()
561+
562+
print(f"Generated {len(manual.tools)} tools from GitHub API!")
563+
return manual
564+
565+
# Or use UTCP Client configuration for automatic detection
566+
from utcp.utcp_client import UtcpClient
567+
568+
client = await UtcpClient.create(config={
569+
"manual_call_templates": [{
570+
"name": "github",
571+
"call_template_type": "http",
572+
"url": "https://api.github.com/openapi.json"
573+
}]
574+
})
575+
```
576+
577+
### Key Benefits
578+
579+
-**Zero Infrastructure**: No servers to deploy or maintain
580+
-**Direct API Calls**: Native performance, no proxy overhead
581+
-**Automatic Conversion**: OpenAPI schemas → UTCP tools
582+
-**Authentication Preserved**: API keys, OAuth2, Basic auth supported
583+
-**Multi-format Support**: JSON, YAML, OpenAPI 2.0/3.0
584+
-**Batch Processing**: Convert multiple APIs simultaneously
585+
586+
### Multiple Ingestion Methods
587+
588+
1. **Direct Converter**: `OpenApiConverter` class for full control
589+
2. **Remote URLs**: Fetch and convert specs from any URL
590+
3. **Client Configuration**: Include specs directly in UTCP config
591+
4. **Batch Processing**: Process multiple specs programmatically
592+
5. **File-based**: Convert local JSON/YAML specifications
593+
594+
📖 **[Complete OpenAPI Ingestion Guide](docs/openapi-ingestion.md)** - Detailed examples and advanced usage
595+
596+
---
597+
541598
## [Contributors](https://www.utcp.io/about)

docs/openapi-ingestion.md

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# OpenAPI Ingestion Methods in python-utcp
2+
3+
UTCP automatically converts OpenAPI 2.0/3.0 specifications into UTCP tools, enabling AI agents to interact with REST APIs without requiring server modifications or additional infrastructure.
4+
5+
## Method 1: Direct OpenAPI Converter
6+
7+
Use the `OpenApiConverter` class for maximum control over the conversion process.
8+
9+
```python
10+
from utcp_http.openapi_converter import OpenApiConverter # utcp-http plugin
11+
import json
12+
13+
# From local JSON file
14+
with open("api_spec.json", "r") as f:
15+
openapi_spec = json.load(f)
16+
17+
converter = OpenApiConverter(openapi_spec)
18+
manual = converter.convert()
19+
20+
print(f"Generated {len(manual.tools)} tools")
21+
```
22+
23+
```python
24+
from utcp_http.openapi_converter import OpenApiConverter # utcp-http plugin
25+
import yaml
26+
27+
# From YAML file (can also be JSON)
28+
with open("api_spec.yaml", "r") as f:
29+
openapi_spec = yaml.safe_load(f)
30+
31+
converter = OpenApiConverter(openapi_spec)
32+
manual = converter.convert()
33+
```
34+
35+
## Method 2: Remote OpenAPI Specification
36+
37+
Fetch and convert OpenAPI specifications from remote URLs.
38+
39+
```python
40+
import aiohttp
41+
from utcp_http.openapi_converter import OpenApiConverter # utcp-http plugin
42+
43+
async def load_remote_spec(url):
44+
async with aiohttp.ClientSession() as session:
45+
async with session.get(url) as response:
46+
response.raise_for_status()
47+
openapi_spec = await response.json()
48+
49+
converter = OpenApiConverter(openapi_spec, spec_url=url)
50+
return converter.convert()
51+
52+
# Usage
53+
manual = await load_remote_spec("https://api.example.com/openapi.json")
54+
```
55+
56+
## Method 3: UTCP Client Configuration
57+
58+
Include OpenAPI specs directly in your UTCP client configuration.
59+
60+
```python
61+
from utcp.utcp_client import UtcpClient # core utcp package
62+
63+
config = {
64+
"manual_call_templates": [
65+
{
66+
"name": "weather_api",
67+
"call_template_type": "http",
68+
"url": "https://api.weather.com/openapi.json",
69+
"http_method": "GET"
70+
}
71+
]
72+
}
73+
74+
client = await UtcpClient.create(config=config)
75+
```
76+
77+
```python
78+
# With authentication
79+
config = {
80+
"manual_call_templates": [
81+
{
82+
"name": "authenticated_api",
83+
"call_template_type": "http",
84+
"url": "https://api.example.com/openapi.json",
85+
"auth": {
86+
"auth_type": "api_key",
87+
"api_key": "${API_KEY}",
88+
"var_name": "Authorization",
89+
"location": "header"
90+
}
91+
}
92+
]
93+
}
94+
```
95+
96+
## Method 4: Batch Processing
97+
98+
Process multiple OpenAPI specifications programmatically.
99+
100+
```python
101+
import aiohttp
102+
from utcp_http.openapi_converter import OpenApiConverter # utcp-http plugin
103+
from utcp.data.utcp_manual import UtcpManual # core utcp package
104+
105+
async def process_multiple_specs(spec_urls):
106+
all_tools = []
107+
108+
for i, url in enumerate(spec_urls):
109+
async with aiohttp.ClientSession() as session:
110+
async with session.get(url) as response:
111+
openapi_spec = await response.json()
112+
113+
converter = OpenApiConverter(openapi_spec, spec_url=url, call_template_name=f"api_{i}")
114+
manual = converter.convert()
115+
all_tools.extend(manual.tools)
116+
117+
return UtcpManual(tools=all_tools)
118+
119+
# Usage
120+
spec_urls = [
121+
"https://api.github.com/openapi.json",
122+
"https://api.stripe.com/openapi.yaml"
123+
]
124+
125+
combined_manual = await process_multiple_specs(spec_urls)
126+
```
127+
128+
## Key Features
129+
130+
### Authentication Mapping
131+
OpenAPI security schemes automatically convert to UTCP auth objects:
132+
133+
- `apiKey``ApiKeyAuth`
134+
- `http` (basic) → `BasicAuth`
135+
- `http` (bearer) → `ApiKeyAuth`
136+
- `oauth2``OAuth2Auth`
137+
138+
### Multi-format Support
139+
- **OpenAPI 2.0 & 3.0**: Full compatibility
140+
- **JSON & YAML**: Automatic format detection
141+
- **Local & Remote**: Files or URLs
142+
143+
### Schema Resolution
144+
- Handles `$ref` references automatically
145+
- Resolves nested object definitions
146+
- Detects circular references
147+
148+
## Examples
149+
150+
See the [examples repository](https://github.com/universal-tool-calling-protocol/utcp-examples) for complete working examples.

plugins/communication_protocols/cli/src/utcp_cli/cli_call_template.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import traceback
88

99
class CommandStep(BaseModel):
10-
"""Configuration for a single command step in a CLI execution flow.
10+
"""REQUIRED
11+
Configuration for a single command step in a CLI execution flow.
1112
1213
Attributes:
1314
command: The command string to execute. Can contain UTCP_ARG_argname_UTCP_END

plugins/communication_protocols/cli/src/utcp_cli/cli_communication_protocol.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
- Tool discovery by running a command that outputs a UTCP manual.
1111
- Flexible argument formatting for different CLI conventions.
1212
- Support for environment variables and custom working directories.
13-
- Automatic parsing of JSON output with a fallback to raw text.
1413
- Cross-platform command parsing for Windows and Unix-like systems.
1514
1615
Security Considerations:
@@ -588,8 +587,7 @@ async def call_tool(self, caller, tool_name: str, tool_args: Dict[str, Any], too
588587
Returns:
589588
The result of the command execution. If the command exits with a code
590589
of 0, it returns the content of stdout. If the exit code is non-zero,
591-
it returns the content of stderr. The output is parsed as JSON if
592-
possible; otherwise, it is returned as a raw string.
590+
it returns the content of stderr.
593591
594592
Raises:
595593
ValueError: If `tool_call_template` is not an instance of

plugins/communication_protocols/mcp/tests/test_mcp_transport.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async def test_register_manual_discovers_tools(transport: McpCommunicationProtoc
5555
assert len(register_result.manual.tools) == 4
5656

5757
# Find the echo tool
58-
echo_tool = next((tool for tool in register_result.manual.tools if tool.name ==f"{SERVER_NAME}.echo"), None)
58+
echo_tool = next((tool for tool in register_result.manual.tools if tool.name == f"{SERVER_NAME}.echo"), None)
5959
assert echo_tool is not None
6060
assert "echoes back its input" in echo_tool.description
6161

0 commit comments

Comments
 (0)