Skip to content

Commit 9d80c32

Browse files
authored
Merge pull request #77 from universal-tool-calling-protocol/dev
Update UTCP to 1.1
2 parents 629621e + 2dc9c02 commit 9d80c32

File tree

6 files changed

+580
-25
lines changed

6 files changed

+580
-25
lines changed

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ Configuration examples for each protocol. Remember to replace `provider_type` wi
377377
"url": "https://api.example.com/users/{user_id}", // Required
378378
"http_method": "POST", // Required, default: "GET"
379379
"content_type": "application/json", // Optional, default: "application/json"
380+
"allowed_communication_protocols": ["http"], // Optional, defaults to [call_template_type]. Restricts which protocols tools can use.
380381
"auth": { // Optional, authentication for the HTTP request (example using ApiKeyAuth for Bearer token)
381382
"auth_type": "api_key",
382383
"api_key": "Bearer $API_KEY", // Required
@@ -514,6 +515,81 @@ Note the name change from `http_stream` to `streamable_http`.
514515
}
515516
```
516517

518+
## Security: Protocol Restrictions
519+
520+
UTCP provides fine-grained control over which communication protocols each manual can use through the `allowed_communication_protocols` field. This prevents potentially dangerous protocol escalation (e.g., an HTTP-based manual accidentally calling CLI tools).
521+
522+
### Default Behavior (Secure by Default)
523+
524+
When `allowed_communication_protocols` is not set or is empty, a manual can only register and call tools that use the **same protocol type** as the manual itself:
525+
526+
```python
527+
from utcp_http.http_call_template import HttpCallTemplate
528+
529+
# This manual can ONLY register/call HTTP tools (default restriction)
530+
http_manual = HttpCallTemplate(
531+
name="my_api",
532+
call_template_type="http",
533+
url="https://api.example.com/utcp"
534+
# allowed_communication_protocols not set → defaults to ["http"]
535+
)
536+
```
537+
538+
### Allowing Multiple Protocols
539+
540+
To allow a manual to work with tools from multiple protocols, explicitly set `allowed_communication_protocols`:
541+
542+
```python
543+
from utcp_http.http_call_template import HttpCallTemplate
544+
545+
# This manual can register/call both HTTP and CLI tools
546+
multi_protocol_manual = HttpCallTemplate(
547+
name="flexible_manual",
548+
call_template_type="http",
549+
url="https://api.example.com/utcp",
550+
allowed_communication_protocols=["http", "cli"] # Explicitly allow both
551+
)
552+
```
553+
554+
### JSON Configuration
555+
556+
```json
557+
{
558+
"name": "my_api",
559+
"call_template_type": "http",
560+
"url": "https://api.example.com/utcp",
561+
"allowed_communication_protocols": ["http", "cli", "mcp"]
562+
}
563+
```
564+
565+
### Behavior Summary
566+
567+
| `allowed_communication_protocols` | Manual Type | Allowed Tool Protocols |
568+
|----------------------------------|-------------|------------------------|
569+
| Not set / `null` | `"http"` | Only `"http"` |
570+
| `[]` (empty) | `"http"` | Only `"http"` |
571+
| `["http", "cli"]` | `"http"` | `"http"` and `"cli"` |
572+
| `["http", "cli", "mcp"]` | `"cli"` | `"http"`, `"cli"`, and `"mcp"` |
573+
574+
### Registration Filtering
575+
576+
During `register_manual()`, tools that don't match the allowed protocols are automatically filtered out with a warning:
577+
578+
```
579+
WARNING - Tool 'dangerous_tool' uses communication protocol 'cli' which is not in
580+
allowed protocols ['http'] for manual 'my_api'. Tool will not be registered.
581+
```
582+
583+
### Call-Time Validation
584+
585+
Even if a tool somehow exists in the repository, calling it will fail if its protocol is not allowed:
586+
587+
```python
588+
# Raises ValueError: Tool 'my_api.some_cli_tool' uses communication protocol 'cli'
589+
# which is not allowed by manual 'my_api'. Allowed protocols: ['http']
590+
await client.call_tool("my_api.some_cli_tool", {"arg": "value"})
591+
```
592+
517593
## Testing
518594

519595
The testing structure has been updated to reflect the new core/plugin split.

core/README.md

Lines changed: 171 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ UTCP supports multiple communication protocols through dedicated plugins:
8686
| [`utcp-cli`](plugins/communication_protocols/cli/) | Command-line tools | ✅ Stable | [CLI Plugin README](plugins/communication_protocols/cli/README.md) |
8787
| [`utcp-mcp`](plugins/communication_protocols/mcp/) | Model Context Protocol | ✅ Stable | [MCP Plugin README](plugins/communication_protocols/mcp/README.md) |
8888
| [`utcp-text`](plugins/communication_protocols/text/) | Local file-based tools | ✅ Stable | [Text Plugin README](plugins/communication_protocols/text/README.md) |
89+
| [`utcp-websocket`](plugins/communication_protocols/websocket/) | WebSocket real-time bidirectional communication | ✅ Stable | [WebSocket Plugin README](plugins/communication_protocols/websocket/README.md) |
8990
| [`utcp-socket`](plugins/communication_protocols/socket/) | TCP/UDP protocols | 🚧 In Progress | [Socket Plugin README](plugins/communication_protocols/socket/README.md) |
9091
| [`utcp-gql`](plugins/communication_protocols/gql/) | GraphQL APIs | 🚧 In Progress | [GraphQL Plugin README](plugins/communication_protocols/gql/README.md) |
9192

@@ -376,12 +377,19 @@ Configuration examples for each protocol. Remember to replace `provider_type` wi
376377
"url": "https://api.example.com/users/{user_id}", // Required
377378
"http_method": "POST", // Required, default: "GET"
378379
"content_type": "application/json", // Optional, default: "application/json"
379-
"auth": { // Optional, example using ApiKeyAuth for a Bearer token. The client must prepend "Bearer " to the token.
380+
"allowed_communication_protocols": ["http"], // Optional, defaults to [call_template_type]. Restricts which protocols tools can use.
381+
"auth": { // Optional, authentication for the HTTP request (example using ApiKeyAuth for Bearer token)
380382
"auth_type": "api_key",
381383
"api_key": "Bearer $API_KEY", // Required
382384
"var_name": "Authorization", // Optional, default: "X-Api-Key"
383385
"location": "header" // Optional, default: "header"
384386
},
387+
"auth_tools": { // Optional, authentication for converted tools, if this call template points to an openapi spec that should be automatically converted to a utcp manual (applied only to endpoints requiring auth per OpenAPI spec)
388+
"auth_type": "api_key",
389+
"api_key": "Bearer $TOOL_API_KEY", // Required
390+
"var_name": "Authorization", // Optional, default: "X-Api-Key"
391+
"location": "header" // Optional, default: "header"
392+
},
385393
"headers": { // Optional
386394
"X-Custom-Header": "value"
387395
},
@@ -437,31 +445,34 @@ Note the name change from `http_stream` to `streamable_http`.
437445

438446
```json
439447
{
440-
"name": "my_cli_tool",
448+
"name": "multi_step_cli_tool",
441449
"call_template_type": "cli", // Required
442-
"commands": [ // Required - array of commands to execute in sequence
450+
"commands": [ // Required - sequential command execution
443451
{
444-
"command": "cd UTCP_ARG_target_dir_UTCP_END",
445-
"append_to_final_output": false // Optional, default is false if not last command
452+
"command": "git clone UTCP_ARG_repo_url_UTCP_END temp_repo",
453+
"append_to_final_output": false
446454
},
447455
{
448-
"command": "my-command --input UTCP_ARG_input_file_UTCP_END"
449-
// append_to_final_output defaults to true for last command
456+
"command": "cd temp_repo && find . -name '*.py' | wc -l"
457+
// Last command output returned by default
450458
}
451459
],
452460
"env_vars": { // Optional
453-
"MY_VAR": "my_value"
461+
"GIT_AUTHOR_NAME": "UTCP Bot",
462+
"API_KEY": "${MY_API_KEY}"
454463
},
455-
"working_dir": "/path/to/working/directory", // Optional
464+
"working_dir": "/tmp", // Optional
456465
"auth": null // Optional (always null for CLI)
457466
}
458467
```
459468

460-
**Notes:**
461-
- Commands execute in a single subprocess (PowerShell on Windows, Bash on Unix)
462-
- Use `UTCP_ARG_argname_UTCP_END` placeholders for arguments
463-
- Reference previous command output with `$CMD_0_OUTPUT`, `$CMD_1_OUTPUT`, etc.
464-
- Only the last command's output is returned by default
469+
**CLI Protocol Features:**
470+
- **Multi-command execution**: Commands run sequentially in single subprocess
471+
- **Cross-platform**: PowerShell on Windows, Bash on Unix/Linux/macOS
472+
- **State preservation**: Directory changes (`cd`) persist between commands
473+
- **Argument placeholders**: `UTCP_ARG_argname_UTCP_END` format
474+
- **Output referencing**: Access previous outputs with `$CMD_0_OUTPUT`, `$CMD_1_OUTPUT`
475+
- **Flexible output control**: Choose which command outputs to include in final result
465476

466477
### Text Call Template
467478

@@ -470,7 +481,13 @@ Note the name change from `http_stream` to `streamable_http`.
470481
"name": "my_text_manual",
471482
"call_template_type": "text", // Required
472483
"file_path": "./manuals/my_manual.json", // Required
473-
"auth": null // Optional (always null for Text)
484+
"auth": null, // Optional (always null for Text)
485+
"auth_tools": { // Optional, authentication for generated tools from OpenAPI specs
486+
"auth_type": "api_key",
487+
"api_key": "Bearer ${API_TOKEN}",
488+
"var_name": "Authorization",
489+
"location": "header"
490+
}
474491
}
475492
```
476493

@@ -498,6 +515,81 @@ Note the name change from `http_stream` to `streamable_http`.
498515
}
499516
```
500517

518+
## Security: Protocol Restrictions
519+
520+
UTCP provides fine-grained control over which communication protocols each manual can use through the `allowed_communication_protocols` field. This prevents potentially dangerous protocol escalation (e.g., an HTTP-based manual accidentally calling CLI tools).
521+
522+
### Default Behavior (Secure by Default)
523+
524+
When `allowed_communication_protocols` is not set or is empty, a manual can only register and call tools that use the **same protocol type** as the manual itself:
525+
526+
```python
527+
from utcp_http.http_call_template import HttpCallTemplate
528+
529+
# This manual can ONLY register/call HTTP tools (default restriction)
530+
http_manual = HttpCallTemplate(
531+
name="my_api",
532+
call_template_type="http",
533+
url="https://api.example.com/utcp"
534+
# allowed_communication_protocols not set → defaults to ["http"]
535+
)
536+
```
537+
538+
### Allowing Multiple Protocols
539+
540+
To allow a manual to work with tools from multiple protocols, explicitly set `allowed_communication_protocols`:
541+
542+
```python
543+
from utcp_http.http_call_template import HttpCallTemplate
544+
545+
# This manual can register/call both HTTP and CLI tools
546+
multi_protocol_manual = HttpCallTemplate(
547+
name="flexible_manual",
548+
call_template_type="http",
549+
url="https://api.example.com/utcp",
550+
allowed_communication_protocols=["http", "cli"] # Explicitly allow both
551+
)
552+
```
553+
554+
### JSON Configuration
555+
556+
```json
557+
{
558+
"name": "my_api",
559+
"call_template_type": "http",
560+
"url": "https://api.example.com/utcp",
561+
"allowed_communication_protocols": ["http", "cli", "mcp"]
562+
}
563+
```
564+
565+
### Behavior Summary
566+
567+
| `allowed_communication_protocols` | Manual Type | Allowed Tool Protocols |
568+
|----------------------------------|-------------|------------------------|
569+
| Not set / `null` | `"http"` | Only `"http"` |
570+
| `[]` (empty) | `"http"` | Only `"http"` |
571+
| `["http", "cli"]` | `"http"` | `"http"` and `"cli"` |
572+
| `["http", "cli", "mcp"]` | `"cli"` | `"http"`, `"cli"`, and `"mcp"` |
573+
574+
### Registration Filtering
575+
576+
During `register_manual()`, tools that don't match the allowed protocols are automatically filtered out with a warning:
577+
578+
```
579+
WARNING - Tool 'dangerous_tool' uses communication protocol 'cli' which is not in
580+
allowed protocols ['http'] for manual 'my_api'. Tool will not be registered.
581+
```
582+
583+
### Call-Time Validation
584+
585+
Even if a tool somehow exists in the repository, calling it will fail if its protocol is not allowed:
586+
587+
```python
588+
# Raises ValueError: Tool 'my_api.some_cli_tool' uses communication protocol 'cli'
589+
# which is not allowed by manual 'my_api'. Allowed protocols: ['http']
590+
await client.call_tool("my_api.some_cli_tool", {"arg": "value"})
591+
```
592+
501593
## Testing
502594

503595
The testing structure has been updated to reflect the new core/plugin split.
@@ -535,4 +627,68 @@ The build process now involves building each package (`core` and `plugins`) sepa
535627
4. Run the build: `python -m build`.
536628
5. The distributable files (`.whl` and `.tar.gz`) will be in the `dist/` directory.
537629

630+
## OpenAPI Ingestion - Zero Infrastructure Tool Integration
631+
632+
🚀 **Transform any existing REST API into UTCP tools without server modifications!**
633+
634+
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.
635+
636+
### Quick Start with OpenAPI
637+
638+
```python
639+
from utcp_http.openapi_converter import OpenApiConverter
640+
import aiohttp
641+
642+
# Convert any OpenAPI spec to UTCP tools
643+
async def convert_api():
644+
async with aiohttp.ClientSession() as session:
645+
async with session.get("https://api.github.com/openapi.json") as response:
646+
openapi_spec = await response.json()
647+
648+
converter = OpenApiConverter(openapi_spec)
649+
manual = converter.convert()
650+
651+
print(f"Generated {len(manual.tools)} tools from GitHub API!")
652+
return manual
653+
654+
# Or use UTCP Client configuration for automatic detection
655+
from utcp.utcp_client import UtcpClient
656+
657+
client = await UtcpClient.create(config={
658+
"manual_call_templates": [{
659+
"name": "github",
660+
"call_template_type": "http",
661+
"url": "https://api.github.com/openapi.json",
662+
"auth_tools": { # Authentication for generated tools requiring auth
663+
"auth_type": "api_key",
664+
"api_key": "Bearer ${GITHUB_TOKEN}",
665+
"var_name": "Authorization",
666+
"location": "header"
667+
}
668+
}]
669+
})
670+
```
671+
672+
### Key Benefits
673+
674+
-**Zero Infrastructure**: No servers to deploy or maintain
675+
-**Direct API Calls**: Native performance, no proxy overhead
676+
-**Automatic Conversion**: OpenAPI schemas → UTCP tools
677+
-**Selective Authentication**: Only protected endpoints get auth, public endpoints remain accessible
678+
-**Authentication Preserved**: API keys, OAuth2, Basic auth supported
679+
-**Multi-format Support**: JSON, YAML, OpenAPI 2.0/3.0
680+
-**Batch Processing**: Convert multiple APIs simultaneously
681+
682+
### Multiple Ingestion Methods
683+
684+
1. **Direct Converter**: `OpenApiConverter` class for full control
685+
2. **Remote URLs**: Fetch and convert specs from any URL
686+
3. **Client Configuration**: Include specs directly in UTCP config
687+
4. **Batch Processing**: Process multiple specs programmatically
688+
5. **File-based**: Convert local JSON/YAML specifications
689+
690+
📖 **[Complete OpenAPI Ingestion Guide](docs/openapi-ingestion.md)** - Detailed examples and advanced usage
691+
692+
---
693+
538694
## [Contributors](https://www.utcp.io/about)

core/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "utcp"
7-
version = "1.0.4"
7+
version = "1.1.0"
88
authors = [
99
{ name = "UTCP Contributors" },
1010
]

core/src/utcp/data/call_template.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,17 @@ class CallTemplate(BaseModel):
4040
Should be unique across all providers and recommended to be set to a human-readable name.
4141
Can only contain letters, numbers and underscores. All special characters must be replaced with underscores.
4242
call_template_type: The transport protocol type used by this provider.
43+
allowed_communication_protocols: Optional list of communication protocol types that tools
44+
registered under this manual are allowed to use. If None or empty, defaults to only allowing
45+
the same protocol type as the manual's call_template_type. This provides fine-grained security
46+
control - e.g., set to ["http", "cli"] to allow both HTTP and CLI tools, or leave unset to
47+
restrict tools to the manual's own protocol type.
4348
"""
4449

4550
name: str = Field(default_factory=lambda: uuid.uuid4().hex)
4651
call_template_type: str
4752
auth: Optional[Auth] = None
53+
allowed_communication_protocols: Optional[List[str]] = None
4854

4955
@field_serializer("auth")
5056
def serialize_auth(self, auth: Optional[Auth]):

0 commit comments

Comments
 (0)