diff --git a/docs/for-tool-providers.md b/docs/for-tool-providers.md new file mode 100644 index 0000000..cf5b80d --- /dev/null +++ b/docs/for-tool-providers.md @@ -0,0 +1,689 @@ +--- +id: for-tool-providers +title: For Tool Providers +sidebar_position: 2 +--- + +# For Tool Providers + +:::info Language Note +This guide uses **Python** examples with the reference implementation. UTCP implementations are available in multiple languages - check the [UTCP GitHub organization](https://github.com/universal-tool-calling-protocol) for TypeScript, Go, and other language implementations. +::: + +This guide helps you expose your tools through UTCP so they can be discovered and used by AI agents and other applications. + +## Overview + +As a tool provider, you'll create a **UTCP Manual** - a standardized description of your tools that tells clients how to call them directly using their native protocols. This eliminates the need for wrapper servers and allows direct communication. + +## Quick Start + +### 1. Create a Simple Manual + +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "info": { + "title": "My API Tools", + "version": "1.0.0", + "description": "Collection of useful API tools" + }, + "tools": [ + { + "name": "get_user", + "description": "Retrieve user information by ID", + "inputs": { + "type": "object", + "properties": { + "user_id": {"type": "string", "description": "User identifier"} + }, + "required": ["user_id"] + }, + "outputs": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "email": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/users/${user_id}", + "http_method": "GET", + "headers": { + "Authorization": "Bearer ${API_TOKEN}" + } + } + } + ] +} +``` + +### 2. Expose via Discovery Endpoint + +```python +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/utcp") +def get_utcp_manual(): + return { + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": [ + # ... your tools here + ] + } + +# Your existing API endpoints remain unchanged +@app.get("/users/{user_id}") +def get_user(user_id: str): + return {"id": user_id, "name": "John Doe", "email": "john@example.com"} +``` + +## Manual Structure + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `manual_version` | string | Version of your manual (semantic versioning) | +| `utcp_version` | string | UTCP specification version | +| `tools` | array | List of available tools | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `info` | object | Metadata about your API | +| `auth` | object | Default authentication for all tools | +| `variables` | object | Default variable values | + +### Tool Definition + +Each tool must include: + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Unique tool identifier | +| `description` | string | Human-readable description | +| `inputs` | object | JSON Schema for input parameters | +| `tool_call_template` | object | How to call the tool | + +Optional tool fields: + +| Field | Type | Description | +|-------|------|-------------| +| `outputs` | object | JSON Schema for expected outputs | +| `tags` | array | Tags for categorization and search | +| `examples` | array | Usage examples | + +## Communication Protocols + +### HTTP Tools + +Most common for REST APIs: + +```json +{ + "name": "create_user", + "description": "Create a new user account", + "inputs": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "email": {"type": "string", "format": "email"} + }, + "required": ["name", "email"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/users", + "http_method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ${API_TOKEN}" + }, + "body": { + "name": "${name}", + "email": "${email}" + } + } +} +``` + +### CLI Tools + +For command-line applications: + +```json +{ + "name": "git_status", + "description": "Get git repository status", + "inputs": { + "type": "object", + "properties": { + "repo_path": {"type": "string", "description": "Path to git repository"} + }, + "required": ["repo_path"] + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "git", + "args": ["status", "--porcelain"], + "working_directory": "${repo_path}" + } +} +``` + +### WebSocket Tools + +For real-time communication: + +```json +{ + "name": "subscribe_updates", + "description": "Subscribe to real-time updates", + "inputs": { + "type": "object", + "properties": { + "channel": {"type": "string"} + }, + "required": ["channel"] + }, + "tool_call_template": { + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": { + "action": "subscribe", + "channel": "${channel}" + } + } +} +``` + +## Authentication + +### API Key Authentication + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "X-API-Key", + "location": "header" + } +} +``` + +### Bearer Token + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${ACCESS_TOKEN}", + "var_name": "Authorization", + "location": "header" + } +} +``` + +### OAuth2 + +```json +{ + "auth": { + "auth_type": "oauth2", + "client_id": "${CLIENT_ID}", + "client_secret": "${CLIENT_SECRET}", + "token_url": "https://auth.example.com/token", + "scope": "read:users write:users" + } +} +``` + +### Per-Tool Authentication + +```json +{ + "name": "admin_action", + "description": "Perform admin action", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/admin/action", + "http_method": "POST", + "auth": { + "auth_type": "api_key", + "api_key": "${ADMIN_TOKEN}", + "var_name": "Authorization", + "location": "header" + } + } +} +``` + +## Variable Substitution + +Use `${VARIABLE_NAME}` syntax for dynamic values: + +### From Tool Arguments + +```json +{ + "url": "https://api.example.com/users/${user_id}", + "body": { + "name": "${name}", + "email": "${email}" + } +} +``` + +### From Environment Variables + +```json +{ + "headers": { + "Authorization": "Bearer ${API_TOKEN}", + "X-Client-ID": "${CLIENT_ID}" + } +} +``` + +### Default Values + +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "variables": { + "base_url": "https://api.example.com", + "timeout": 30 + }, + "tools": [ + { + "name": "get_data", + "tool_call_template": { + "call_template_type": "http", + "url": "${base_url}/data", + "timeout": "${timeout}" + } + } + ] +} +``` + +## Implementation Examples + +### FastAPI Implementation (Python) + +```python +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel +from typing import List, Optional + +app = FastAPI() + +class User(BaseModel): + id: str + name: str + email: str + +# Your existing API endpoints +@app.get("/users/{user_id}") +def get_user(user_id: str) -> User: + # Your existing logic + return User(id=user_id, name="John Doe", email="john@example.com") + +@app.post("/users") +def create_user(user: User) -> User: + # Your existing logic + return user + +# UTCP Discovery endpoint +@app.get("/utcp") +def get_utcp_manual(): + return { + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "info": { + "title": "User Management API", + "version": "1.0.0", + "description": "API for managing user accounts" + }, + "tools": [ + { + "name": "get_user", + "description": "Retrieve user information by ID", + "inputs": { + "type": "object", + "properties": { + "user_id": {"type": "string"} + }, + "required": ["user_id"] + }, + "outputs": { + "type": "object", + "properties": { + "id": {"type": "string"}, + "name": {"type": "string"}, + "email": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": f"{BASE_URL}/users/{{user_id}}", + "http_method": "GET", + "headers": { + "Authorization": "Bearer ${API_TOKEN}" + } + } + }, + { + "name": "create_user", + "description": "Create a new user account", + "inputs": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "email": {"type": "string", "format": "email"} + }, + "required": ["name", "email"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": f"{BASE_URL}/users", + "http_method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ${API_TOKEN}" + }, + "body": { + "name": "${name}", + "email": "${email}" + } + } + } + ] + } +``` + +### Express.js Implementation + +```javascript +const express = require('express'); +const app = express(); + +// Your existing API endpoints +app.get('/users/:userId', (req, res) => { + // Your existing logic + res.json({ + id: req.params.userId, + name: 'John Doe', + email: 'john@example.com' + }); +}); + +// UTCP Discovery endpoint +app.get('/utcp', (req, res) => { + res.json({ + manual_version: "1.0.0", + utcp_version: "1.0.1", + info: { + title: "User Management API", + version: "1.0.0", + description: "API for managing user accounts" + }, + tools: [ + { + name: "get_user", + description: "Retrieve user information by ID", + inputs: { + type: "object", + properties: { + user_id: { type: "string" } + }, + required: ["user_id"] + }, + tool_call_template: { + call_template_type: "http", + url: `${process.env.BASE_URL}/users/\${user_id}`, + http_method: "GET", + headers: { + "Authorization": "Bearer ${API_TOKEN}" + } + } + } + ] + }); +}); + +app.listen(3000); +``` + +## OpenAPI Integration + +Convert existing OpenAPI specifications to UTCP manuals: + +### Python with utcp-http + +```python +from utcp_http.openapi_converter import OpenApiConverter + +# Convert OpenAPI spec to UTCP manual +converter = OpenApiConverter() +manual = await converter.convert_openapi_to_manual( + "https://api.example.com/openapi.json", + base_url="https://api.example.com" +) + +# Serve the converted manual +@app.get("/utcp") +def get_utcp_manual(): + return manual.model_dump() +``` + +### Customizing OpenAPI Conversion + +```python +# Convert with custom settings +manual = await converter.convert_openapi_to_manual( + "https://api.example.com/openapi.json", + base_url="https://api.example.com", + include_operations=["get", "post"], # Only include specific operations + exclude_paths=["/internal/*"], # Exclude internal paths + auth_template={ # Default auth for all tools + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "X-API-Key", + "location": "header" + } +) +``` + +## Best Practices + +### Manual Design + +1. **Clear Descriptions**: Write clear, concise tool descriptions +2. **Comprehensive Schemas**: Use detailed JSON schemas for inputs/outputs +3. **Consistent Naming**: Use consistent naming conventions +4. **Version Management**: Use semantic versioning for your manual +5. **Documentation**: Include examples and usage notes + +### Security + +1. **Authentication**: Always implement proper authentication +2. **Input Validation**: Validate all inputs on your API side +3. **Rate Limiting**: Implement rate limiting to prevent abuse +4. **HTTPS Only**: Use HTTPS for all production endpoints +5. **Credential Management**: Never hardcode credentials in manuals + +### Performance + +1. **Efficient Endpoints**: Design efficient API endpoints +2. **Caching**: Implement appropriate caching strategies +3. **Pagination**: Use pagination for large result sets +4. **Timeouts**: Set reasonable timeout values +5. **Monitoring**: Monitor API performance and usage + +### Maintenance + +1. **Versioning**: Version your manual and API together +2. **Backward Compatibility**: Maintain backward compatibility when possible +3. **Deprecation**: Provide clear deprecation notices +4. **Testing**: Test your manual with UTCP clients +5. **Documentation**: Keep documentation up to date + +## Testing Your Manual + +### Manual Validation + +```python +from utcp.data.utcp_manual import UtcpManual +import json + +# Load and validate your manual +with open('manual.json', 'r') as f: + manual_data = json.load(f) + +try: + manual = UtcpManual(**manual_data) + print("Manual is valid!") +except Exception as e: + print(f"Manual validation failed: {e}") +``` + +### Integration Testing + +```python +import pytest +from utcp.utcp_client import UtcpClient + +@pytest.mark.asyncio +async def test_manual_integration(): + client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "test_service", + "call_template_type": "http", + "url": "http://localhost:8000/utcp", + "http_method": "GET" + } + ] + }) + + # Test tool discovery + tools = await client.list_tools() + assert len(tools) > 0 + + # Test tool call + result = await client.call_tool( + "test_service.get_user", + tool_args={"user_id": "123"} + ) + assert "id" in result +``` + +## Migration Strategies + +### From OpenAPI + +1. **Automatic Conversion**: Use OpenAPI converter for initial conversion +2. **Manual Refinement**: Refine converted manual for better descriptions +3. **Authentication Setup**: Configure authentication properly +4. **Testing**: Test converted manual thoroughly + +### From MCP + +1. **Wrapper Approach**: Use UTCP-MCP plugin initially +2. **Gradual Migration**: Migrate tools one by one to native protocols +3. **Direct Implementation**: Implement tools using native UTCP protocols +4. **Deprecation**: Remove MCP dependency once migration is complete + +## Common Patterns + +### CRUD Operations + +```json +{ + "tools": [ + { + "name": "create_resource", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/resources", + "http_method": "POST" + } + }, + { + "name": "get_resource", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/resources/${id}", + "http_method": "GET" + } + }, + { + "name": "update_resource", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/resources/${id}", + "http_method": "PUT" + } + }, + { + "name": "delete_resource", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/resources/${id}", + "http_method": "DELETE" + } + } + ] +} +``` + +### Batch Operations + +```json +{ + "name": "batch_process", + "description": "Process multiple items in batch", + "inputs": { + "type": "object", + "properties": { + "items": { + "type": "array", + "items": {"type": "object"} + } + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/batch", + "http_method": "POST", + "body": { + "items": "${items}" + } + } +} +``` + +## Next Steps + +1. **Design Your Manual**: Plan your tool structure and descriptions +2. **Choose Protocols**: Select appropriate communication protocols +3. **Implement Discovery**: Add the `/utcp` endpoint to your API +4. **Test Integration**: Test with UTCP clients +5. **Monitor Usage**: Monitor how your tools are being used +6. **Iterate**: Improve based on usage patterns and feedback + +For more information, see: +- [Communication Protocols](./providers/index.md) +- [Implementation Guide](./implementation.md) +- [Security Considerations](./security.md) diff --git a/docs/implementation.md b/docs/implementation.md new file mode 100644 index 0000000..06abbba --- /dev/null +++ b/docs/implementation.md @@ -0,0 +1,580 @@ +--- +id: implementation +title: Implementation Guide +sidebar_position: 4 +--- + +# Implementation Guide + +:::info Language Note +This guide uses **Python** examples with the reference implementation. UTCP implementations are available in multiple languages - check the [UTCP GitHub organization](https://github.com/universal-tool-calling-protocol) for TypeScript, Go, and other language implementations. +::: + +This comprehensive guide walks you through implementing UTCP in your applications, whether you're creating a tool provider or building a client that consumes tools. + +## Quick Start (Python) + +### 1. Install UTCP + +```bash +# Core UTCP library +pip install utcp + +# Protocol plugins (install as needed) +pip install utcp-http utcp-cli utcp-websocket utcp-text +``` + +### 2. Create Your First Tool Provider + +```python +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/utcp") +def get_utcp_manual(): + return { + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": [ + { + "name": "get_weather", + "description": "Get current weather for a location", + "inputs": { + "type": "object", + "properties": { + "location": {"type": "string", "description": "City name"} + }, + "required": ["location"] + }, + "outputs": { + "type": "object", + "properties": { + "temperature": {"type": "number"}, + "conditions": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.weather.com/v1/current", + "http_method": "GET", + "query_params": { + "q": "${location}", + "appid": "${WEATHER_API_KEY}" + } + } + } + ] + } +``` + +### 3. Create Your First Client + +```python +import asyncio +from utcp.utcp_client import UtcpClient + +async def main(): + # Create client with manual discovery + client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "weather_service", + "call_template_type": "http", + "url": "http://localhost:8000/utcp", + "http_method": "GET" + } + ] + }) + + # Call the tool + result = await client.call_tool( + "weather_service.get_weather", + tool_args={"location": "San Francisco"} + ) + print(f"Weather: {result}") + +if __name__ == "__main__": + asyncio.run(main()) +``` + +## Other Language Implementations + +UTCP is available in multiple programming languages: + +| Language | Repository | Status | +|----------|------------|--------| +| **Python** | [python-utcp](https://github.com/universal-tool-calling-protocol/python-utcp) | ✅ Reference Implementation | +| **TypeScript** | [typescript-utcp](https://github.com/universal-tool-calling-protocol/typescript-utcp) | ✅ Stable | +| **Go** | [go-utcp](https://github.com/universal-tool-calling-protocol/go-utcp) | 🚧 In Development | +| **Rust** | [rust-utcp](https://github.com/universal-tool-calling-protocol/rust-utcp) | 🚧 In Development | + +Visit the respective repositories for language-specific documentation and examples. + +## Tool Provider Implementation (Python) + +### Manual Structure + +A UTCP Manual defines your tools and how to call them: + +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "info": { + "title": "Weather API", + "version": "1.0.0", + "description": "Weather data and forecasting tools" + }, + "tools": [ + { + "name": "get_weather", + "description": "Get current weather conditions", + "inputs": { + "type": "object", + "properties": { + "location": {"type": "string"}, + "units": {"type": "string", "enum": ["metric", "imperial"]} + }, + "required": ["location"] + }, + "outputs": { + "type": "object", + "properties": { + "temperature": {"type": "number"}, + "conditions": {"type": "string"}, + "humidity": {"type": "number"} + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.weather.com/v1/current", + "http_method": "GET", + "query_params": { + "q": "${location}", + "units": "${units}", + "appid": "${WEATHER_API_KEY}" + }, + "auth": { + "auth_type": "api_key", + "api_key": "${WEATHER_API_KEY}", + "var_name": "appid", + "location": "query" + } + } + } + ] +} +``` + +### Discovery Endpoint (Python Examples) + +Expose your manual via a discovery endpoint: + +#### FastAPI Example + +```python +from fastapi import FastAPI +from utcp.data.utcp_manual import UtcpManual +from utcp.data.tool import Tool, JsonSchema +from utcp.data.call_template import CallTemplate + +app = FastAPI() + +# Define your manual +manual = UtcpManual( + manual_version="1.0.0", + utcp_version="1.0.1", + tools=[ + Tool( + name="get_weather", + description="Get current weather", + inputs=JsonSchema( + type="object", + properties={ + "location": JsonSchema(type="string") + }, + required=["location"] + ), + tool_call_template=CallTemplate( + call_template_type="http", + url="https://api.weather.com/v1/current", + http_method="GET", + query_params={ + "q": "${location}", + "appid": "${WEATHER_API_KEY}" + } + ) + ) + ] +) + +@app.get("/utcp") +def get_manual(): + return manual.model_dump() +``` + +#### Flask Example + +```python +from flask import Flask, jsonify + +app = Flask(__name__) + +@app.route('/utcp') +def get_manual(): + return jsonify({ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": [ + # ... tool definitions + ] + }) +``` + +### Authentication Patterns (Python) + +#### API Key in Header + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "X-API-Key", + "location": "header" + } +} +``` + +#### Bearer Token + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${ACCESS_TOKEN}", + "var_name": "Authorization", + "location": "header" + } +} +``` + +#### OAuth2 + +```json +{ + "auth": { + "auth_type": "oauth2", + "client_id": "${CLIENT_ID}", + "client_secret": "${CLIENT_SECRET}", + "token_url": "https://auth.example.com/token", + "scope": "read:data" + } +} +``` + +## Client Implementation (Python) + +### Basic Client Setup + +```python +from utcp.utcp_client import UtcpClient + +# Create client with configuration +client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "my_service", + "call_template_type": "http", + "url": "https://api.example.com/utcp", + "http_method": "GET" + } + ], + "variable_loaders": [ + { + "loader_type": "env", + "prefix": "UTCP_" + } + ] +}) +``` + +### Configuration Options (Python) + +#### File-based Configuration + +```yaml +# utcp-config.yaml +manual_call_templates: + - name: weather_service + call_template_type: http + url: https://weather.example.com/utcp + http_method: GET + - name: database_service + call_template_type: http + url: https://db.example.com/utcp + http_method: GET + +variable_loaders: + - loader_type: env + prefix: UTCP_ + - loader_type: dotenv + file_path: .env +``` + +```python +client = await UtcpClient.create(config="utcp-config.yaml") +``` + +#### Programmatic Configuration + +```python +from utcp.data.utcp_client_config import UtcpClientConfig +from utcp.data.call_template import CallTemplate + +config = UtcpClientConfig( + manual_call_templates=[ + CallTemplate( + name="weather_service", + call_template_type="http", + url="https://weather.example.com/utcp", + http_method="GET" + ) + ] +) + +client = await UtcpClient.create(config=config) +``` + +### Tool Discovery and Search (Python) + +#### List Available Tools + +```python +# Get all tools +tools = await client.list_tools() +for tool in tools: + print(f"{tool.name}: {tool.description}") + +# Search tools by tag +weather_tools = await client.search_tools(tags=["weather"]) + +# Search tools by description +data_tools = await client.search_tools(description="data processing") +``` + +#### Tool Information + +```python +# Get detailed tool information +tool_info = await client.get_tool("weather_service.get_weather") +print(f"Inputs: {tool_info.inputs}") +print(f"Outputs: {tool_info.outputs}") +``` + +### Calling Tools (Python) + +#### Basic Tool Call + +```python +result = await client.call_tool( + "weather_service.get_weather", + tool_args={"location": "New York"} +) +``` + +#### Tool Call with Context + +```python +result = await client.call_tool( + "weather_service.get_weather", + tool_args={"location": "New York"}, + context={"user_id": "123", "session_id": "abc"} +) +``` + +#### Batch Tool Calls + +```python +results = await client.call_tools([ + ("weather_service.get_weather", {"location": "New York"}), + ("weather_service.get_weather", {"location": "London"}), + ("weather_service.get_weather", {"location": "Tokyo"}) +]) +``` + +### Error Handling (Python) + +```python +from utcp.exceptions import UtcpError, ToolNotFoundError, ToolCallError + +try: + result = await client.call_tool("nonexistent.tool", {}) +except ToolNotFoundError as e: + print(f"Tool not found: {e}") +except ToolCallError as e: + print(f"Tool call failed: {e}") +except UtcpError as e: + print(f"UTCP error: {e}") +``` + +## Advanced Implementation Patterns (Python) + +### Custom Communication Protocols + +```python +from utcp.interfaces.communication_protocol import CommunicationProtocol +from utcp.data.call_template import CallTemplate +from typing import Dict, Any, List + +class CustomProtocol(CommunicationProtocol): + def get_supported_call_template_types(self) -> List[str]: + return ["custom"] + + async def call_tool( + self, + call_template: CallTemplate, + tool_args: Dict[str, Any] + ) -> Any: + # Implement your custom protocol logic + return {"result": "custom protocol response"} + +# Register the protocol +from utcp.plugins.discovery import register_communication_protocol +register_communication_protocol(CustomProtocol()) +``` + +### Custom Tool Repository + +```python +from utcp.interfaces.concurrent_tool_repository import ConcurrentToolRepository +from utcp.data.tool import Tool +from typing import List, Optional + +class DatabaseToolRepository(ConcurrentToolRepository): + async def add_tool(self, tool: Tool, manual_name: str) -> None: + # Store tool in database + pass + + async def get_tool(self, namespaced_name: str) -> Optional[Tool]: + # Retrieve tool from database + pass + + async def list_tools(self) -> List[Tool]: + # List all tools from database + pass + + # ... implement other methods + +# Use custom repository +client = await UtcpClient.create( + config=config, + tool_repository=DatabaseToolRepository() +) +``` + +## Testing (Python) + +### Unit Testing Tool Providers + +```python +import pytest +from fastapi.testclient import TestClient +from your_app import app + +client = TestClient(app) + +def test_utcp_manual(): + response = client.get("/utcp") + assert response.status_code == 200 + + manual = response.json() + assert manual["manual_version"] == "1.0.0" + assert len(manual["tools"]) > 0 + + tool = manual["tools"][0] + assert "name" in tool + assert "description" in tool + assert "tool_call_template" in tool +``` + +### Integration Testing + +```python +import pytest +from utcp.utcp_client import UtcpClient + +@pytest.mark.asyncio +async def test_tool_call(): + client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "test_service", + "call_template_type": "http", + "url": "http://localhost:8000/utcp", + "http_method": "GET" + } + ] + }) + + result = await client.call_tool( + "test_service.get_weather", + tool_args={"location": "Test City"} + ) + + assert "temperature" in result + assert "conditions" in result +``` + +## Language-Specific Resources + +### Python +- **Repository**: [python-utcp](https://github.com/universal-tool-calling-protocol/python-utcp) +- **Documentation**: [Python UTCP Docs](https://python-utcp.readthedocs.io/) +- **Examples**: [Python Examples](https://github.com/universal-tool-calling-protocol/python-utcp/tree/main/examples) + +### TypeScript +- **Repository**: [typescript-utcp](https://github.com/universal-tool-calling-protocol/typescript-utcp) +- **Documentation**: [TypeScript UTCP Docs](https://typescript-utcp.readthedocs.io/) +- **Examples**: [TypeScript Examples](https://github.com/universal-tool-calling-protocol/typescript-utcp/tree/main/examples) + +### Other Languages +Check the [UTCP GitHub organization](https://github.com/universal-tool-calling-protocol) for the latest language implementations and their respective documentation. + +## Best Practices + +### Tool Provider Best Practices + +1. **Versioning**: Use semantic versioning for your manual +2. **Documentation**: Provide clear descriptions for all tools +3. **Validation**: Validate inputs using JSON Schema +4. **Error Handling**: Return meaningful error messages +5. **Rate Limiting**: Implement appropriate rate limits +6. **Monitoring**: Monitor tool usage and performance +7. **Security**: Implement proper authentication and authorization + +### Client Best Practices + +1. **Configuration Management**: Use external configuration files +2. **Error Handling**: Implement comprehensive error handling +3. **Retry Logic**: Implement retry logic for transient failures +4. **Caching**: Cache tool definitions and results when appropriate +5. **Monitoring**: Monitor client performance and errors +6. **Testing**: Write comprehensive tests for tool integrations +7. **Security**: Store credentials securely + +## Next Steps + +1. **Choose Your Language**: Select from available UTCP implementations +2. **Choose Your Protocols**: Select the communication protocols you need +3. **Design Your Tools**: Plan your tool structure and interfaces +4. **Implement Gradually**: Start with simple tools and expand +5. **Test Thoroughly**: Write comprehensive tests +6. **Monitor and Optimize**: Monitor performance and optimize as needed +7. **Scale**: Plan for scaling as your tool ecosystem grows + +For more detailed information, see: +- [Communication Protocols](./providers/index.md) +- [API Reference](./api/index.md) +- [Security Considerations](./security.md) diff --git a/docs/index.md b/docs/index.md index 4f0a4e4..ec2b57d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,152 +4,221 @@ title: Introduction sidebar_position: 1 --- -# Introduction to UTCP 1.0 +# Universal Tool Calling Protocol (UTCP) -The Universal Tool Calling Protocol (UTCP) is a lightweight, secure, and scalable standard for defining and interacting with tools across a wide variety of communication protocols. Version 1.0 introduces a modular core with a plugin-based architecture, making it more extensible, testable, and easier to package. - -## Core Components +:::info Language Examples +This documentation uses **Python** examples. UTCP is available in multiple languages - see [TypeScript](https://github.com/universal-tool-calling-protocol/typescript-utcp), [Go](https://github.com/universal-tool-calling-protocol/go-utcp), and other implementations in the [UTCP GitHub organization](https://github.com/universal-tool-calling-protocol). +::: -UTCP consists of four main components: +UTCP is a lightweight, secure, and scalable standard that enables AI agents and applications to discover and call tools directly using their native protocols - **no wrapper servers required**. -1. [**Manuals**](./api/core/utcp/data/utcp_manual.md): The standard tool provider description format that contains tool definitions -2. [**Tools**](./api/core/utcp/data/tool.md): The individual capabilities that can be called -3. [**Call Templates**](./api/core/utcp/data/call_template.md): The communication configurations that specify how tools are accessed. Concretely this maps a tool name and provided arguments to an actual API request in a communication protocol. -4. [**UtcpClient**](./api/core/utcp/utcp_client.md): The client that calls tools using the call templates. +## Why UTCP? -## The "Manual" Approach +### The Problem with Current Approaches +Most tool integration solutions force you to: +- Build and maintain wrapper servers for every tool +- Route all traffic through a middleman protocol +- Reimplement existing authentication and security +- Accept additional latency and complexity -UTCP's fundamental philosophy is to act as a descriptive manual rather than a prescriptive middleman: +### The UTCP Solution +UTCP acts as a **"manual"** that tells agents how to call your tools directly: -:::note -A UTCP Manual tells an agent: "Here is a tool. Here is its native endpoint (HTTP, WebSocket, CLI, etc.), and here is how to call it directly." +:::tip Core Philosophy +*"If a human can call your API, an AI agent should be able to call it too - with the same security and no additional infrastructure."* ::: -This approach eliminates the need for wrapper servers and allows direct communication between agents and tools. - -## New Architecture in 1.0 - -UTCP has been refactored into a core library and a set of optional plugins: +## Quick Start (5 Minutes) -### Core Package (`utcp`) -- **Data Models**: Pydantic models for [`Tool`](./api/core/utcp/data/tool.md), [`CallTemplate`](./api/core/utcp/data/call_template.md), [`UtcpManual`](./api/core/utcp/data/utcp_manual.md), and [`Auth`](./api/core/utcp/data/auth.md) -- **Pluggable Interfaces**: [`CommunicationProtocol`](./api/core/utcp/interfaces/communication_protocol.md), [`ConcurrentToolRepository`](./api/core/utcp/interfaces/concurrent_tool_repository.md), [`ToolSearchStrategy`](./api/core/utcp/interfaces/tool_search_strategy.md), [`VariableSubstitutor`](./api/core/utcp/interfaces/variable_substitutor.md), [`ToolPostProcessor`](./api/core/utcp/interfaces/tool_post_processor.md) -- **Default Implementations**: [`UtcpClient`](./api/core/utcp/utcp_client.md), [`InMemToolRepository`](./api/core/utcp/implementations/in_mem_tool_repository.md), [`TagAndDescriptionWordMatchStrategy`](./api/core/utcp/implementations/tag_search.md) +### 1. Install UTCP -### Protocol Plugins -- `utcp-http`: Supports HTTP, SSE, and streamable HTTP, plus an OpenAPI converter -- `utcp-cli`: For wrapping local command-line tools -- `utcp-mcp`: For interoperability with the Model Context Protocol (MCP) -- `utcp-text`: For reading text files -- `utcp-socket`: TCP and UDP protocols (work in progress) -- `utcp-gql`: GraphQL (work in progress) - -## Minimal Example - -Let's see how easy it is to use UTCP with a minimal example. +```bash +# Core library + HTTP support +pip install utcp utcp-http +``` -### 1. Defining a Tool (Tool Provider) +### 2. Expose Your First Tool -Create a simple HTTP endpoint that serves a UTCP Manual (JSON): +Add a discovery endpoint to your existing API: ```python -# app.py from fastapi import FastAPI app = FastAPI() +# Your existing API endpoint (unchanged) +@app.get("/weather") +def get_weather(location: str): + return {"temperature": 22, "conditions": "Sunny"} + +# Add UTCP discovery endpoint @app.get("/utcp") -def utcp_discovery(): +def utcp_manual(): return { "manual_version": "1.0.0", "utcp_version": "1.0.1", - "tools": [ - { - "name": "get_weather", - "description": "Get current weather for a location", - "inputs": { - "type": "object", - "properties": { - "location": { - "type": "string", - "description": "City name" - } - }, - "required": ["location"] - }, - "outputs": { - "type": "object", - "properties": { - "temperature": {"type": "number"}, - "conditions": {"type": "string"} - } - }, - "tool_call_template": { - "call_template_type": "http", - "url": "https://example.com/api/weather", - "http_method": "GET" - } + "tools": [{ + "name": "get_weather", + "description": "Get current weather for a location", + "inputs": { + "type": "object", + "properties": {"location": {"type": "string"}}, + "required": ["location"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": "http://localhost:8000/weather", + "http_method": "GET", + "query_params": {"location": "${location}"} } - ] + }] } - -# Implement the actual weather API endpoint -@app.get("/api/weather") -def get_weather(location: str): - # In a real app, you'd fetch actual weather data - return {"temperature": 22.5, "conditions": "Sunny"} -``` - -Run the server: - -```bash -uvicorn app:app --reload ``` -### 2. Using the Tool (Client) +### 3. Call Your Tool ```python -# client.py import asyncio from utcp.utcp_client import UtcpClient -from utcp_http.http_call_template import HttpCallTemplate async def main(): - # Create a UTCP client with configuration client = await UtcpClient.create(config={ - "manual_call_templates": [ - { - "name": "weather_service", - "call_template_type": "http", - "http_method": "GET", - "url": "http://localhost:8000/utcp" - } - ] + "manual_call_templates": [{ + "name": "weather_api", + "call_template_type": "http", + "url": "http://localhost:8000/utcp", + "http_method": "GET" + }] }) - - # Tools are automatically registered from the manual call templates - # Call a tool by its namespaced name: {manual_name}.{tool_name} + result = await client.call_tool( - "weather_service.get_weather", + "weather_api.get_weather", tool_args={"location": "San Francisco"} ) - print(f"Weather: {result['temperature']}°C, {result['conditions']}") + print(f"Weather: {result}") -if __name__ == "__main__": - asyncio.run(main()) +asyncio.run(main()) ``` -Run the client: +**That's it!** Your tool is now discoverable and callable by any UTCP client. -```bash -python client.py +## Key Benefits + +| Benefit | Description | +|---------|-------------| +| **🚀 Zero Latency Overhead** | Direct tool calls, no proxy servers | +| **🔒 Native Security** | Use your existing authentication and authorization | +| **🌐 Protocol Flexibility** | HTTP, WebSocket, CLI, GraphQL, and more | +| **⚡ Easy Integration** | Add one endpoint, no infrastructure changes | +| **📈 Scalable** | Leverage your existing scaling and monitoring | + +## How It Works + +```mermaid +graph LR + A[AI Agent] -->|1. Discover| B[UTCP Manual] + B -->|2. Learn| C[Tool Definitions] + A -->|3. Call Directly| D[Your API] + D -->|4. Response| A ``` -## Benefits of the UTCP Approach +1. **Discovery**: Agent fetches your UTCP manual +2. **Learning**: Agent understands how to call your tools +3. **Direct Calling**: Agent calls your API directly using native protocols +4. **Response**: Your API responds normally + +## Supported Protocols + +UTCP supports multiple communication protocols through plugins: + +| Protocol | Use Case | Plugin | Status | +|----------|----------|--------|--------| +| **[HTTP](./providers/http.md)** | REST APIs, webhooks | `utcp-http` | ✅ Stable | +| **[WebSocket](./providers/websocket.md)** | Real-time communication | `utcp-websocket` | ✅ Stable | +| **[CLI](./providers/cli.md)** | Command-line tools | `utcp-cli` | ✅ Stable | +| **[Server-Sent Events](./providers/sse.md)** | Streaming data | `utcp-http` | ✅ Stable | +| **[Text Files](./providers/text.md)** | File reading | `utcp-text` | ✅ Stable | +| **[MCP](./providers/mcp.md)** | MCP interoperability | `utcp-mcp` | ✅ Stable | + +[View all protocols →](./providers/index.md) + +## Architecture Overview + +UTCP v1.0 features a modular, plugin-based architecture: + +### Core Components +- **[Manuals](./api/core/utcp/data/utcp_manual.md)**: Tool definitions and metadata +- **[Tools](./api/core/utcp/data/tool.md)**: Individual callable capabilities +- **[Call Templates](./api/core/utcp/data/call_template.md)**: Protocol-specific call instructions +- **[UTCP Client](./api/core/utcp/utcp_client.md)**: Tool discovery and execution engine + +### Plugin System +- **Protocol Plugins**: HTTP, WebSocket, CLI, etc. +- **Custom Protocols**: Extend with your own communication methods +- **Tool Repositories**: Pluggable storage for tool definitions +- **Search Strategies**: Customizable tool discovery algorithms + +[Learn more about the architecture →](./api/index.md) + +## Who Should Use UTCP? + +### 🛠️ Tool Providers +You have APIs, services, or tools that you want AI agents to use: +- **Existing API owners** - Expose your REST APIs to AI agents +- **SaaS providers** - Make your services AI-accessible +- **Enterprise teams** - Enable internal tool usage by AI systems + +[**Get started as a tool provider →**](./for-tool-providers.md) -1. **Direct Communication**: The client calls the tool's native endpoint directly -2. **No Wrapper Infrastructure**: No need to build and maintain wrapper servers -3. **Leverage Existing Systems**: Uses the tool's existing authentication, rate limiting, etc. -4. **Flexible Protocol Support**: Works with any communication protocol (HTTP, WebSockets, CLI, etc.) +### 🤖 Tool Consumers +You're building AI agents or applications that need to call external tools: +- **AI agent developers** - Give your agents access to external capabilities +- **Application builders** - Integrate third-party tools seamlessly +- **Enterprise developers** - Connect to internal and external services + +[**Get started as a tool consumer →**](./implementation.md) + +## UTCP vs Alternatives + +| Feature | UTCP | MCP | Custom Wrappers | +|---------|------|-----|-----------------| +| **Infrastructure** | None required | Wrapper servers | Custom servers | +| **Latency** | Direct calls | Double hop | Variable | +| **Security** | Native | Reimplemented | Custom | +| **Protocols** | Multiple | HTTP streaming | Single | +| **Maintenance** | Minimal | High | Very high | + +[**Detailed comparison with MCP →**](./utcp-vs-mcp.md) + +## Next Steps + +### For Tool Providers +1. **[Read the provider guide](./for-tool-providers.md)** - Learn how to expose your tools +2. **[Choose your protocol](./providers/index.md)** - Select the right communication method +3. **[Implement your manual](./implementation.md)** - Add UTCP to your existing API +4. **[Secure your tools](./security.md)** - Implement proper authentication + +### For Tool Consumers +1. **[Read the implementation guide](./implementation.md)** - Learn how to build UTCP clients +2. **[Explore protocols](./providers/index.md)** - Understand available communication options +3. **[Check examples](https://github.com/universal-tool-calling-protocol/python-utcp/tree/main/examples)** - See real-world implementations +4. **[Join the community](https://discord.gg/ZpMbQ8jRbD)** - Get help and share experiences + +### Migration from Other Systems +- **[From UTCP v0.1](./migration-v0.1-to-v1.0.md)** - Upgrade to the latest version +- **[From MCP](./providers/mcp.md)** - Migrate from Model Context Protocol +- **[From custom solutions](./implementation.md)** - Replace existing tool integrations + +## Community & Support + +- **[GitHub Organization](https://github.com/universal-tool-calling-protocol)** - Source code and issues +- **[Discord Community](https://discord.gg/ZpMbQ8jRbD)** - Real-time help and discussions +- **[Tool Registry](https://utcp.io/registry)** - Discover available tools +- **[RFC Process](/about/RFC)** - Contribute to the specification + +--- -In the following sections, we'll explore the details of UTCP's components and how to implement them in your applications. +**Ready to get started?** Choose your path: +- 🛠️ [**I want to expose my tools**](./for-tool-providers.md) +- 🤖 [**I want to call tools**](./implementation.md) +- 📚 [**I want to learn more**](./providers/index.md) diff --git a/docs/migration-v0.1-to-v1.0.md b/docs/migration-v0.1-to-v1.0.md new file mode 100644 index 0000000..b220557 --- /dev/null +++ b/docs/migration-v0.1-to-v1.0.md @@ -0,0 +1,601 @@ +--- +id: migration-v0.1-to-v1.0 +title: Migration Guide - v0.1 to v1.0 +sidebar_position: 7 +--- + +# Migration Guide: v0.1 to v1.0 + +This guide helps you migrate from UTCP v0.1 to v1.0, which introduces significant architectural improvements and new features. + +## Overview of Changes + +### Major Changes in v1.0 + +1. **Plugin Architecture**: Core functionality split into pluggable components +2. **Improved Data Models**: Enhanced Pydantic models with better validation +3. **New Protocol Support**: Additional communication protocols +4. **Better Error Handling**: More specific exception types +5. **Enhanced Authentication**: Expanded authentication options +6. **Performance Improvements**: Optimized client and protocol implementations + +### Breaking Changes + +| Component | v0.1 | v1.0 | Impact | +|-----------|------|------|--------| +| **Package Structure** | Single package | Core + plugins | High | +| **Client API** | `UtcpClient()` | `UtcpClient.create()` | Medium | +| **Configuration** | Simple dict | Structured config | Medium | +| **Protocol Registration** | Automatic | Plugin-based | High | +| **Error Types** | Generic exceptions | Specific exception types | Low | + +## Installation Changes + +### v0.1 Installation + +```bash +pip install utcp +``` + +### v1.0 Installation + +```bash +# Core package +pip install utcp + +# Protocol plugins (install as needed) +pip install utcp-http utcp-cli utcp-websocket utcp-text utcp-mcp +``` + +## Client Migration + +### v0.1 Client Code + +```python +from utcp import UtcpClient + +# Old way +client = UtcpClient(config={ + "providers": [ + { + "name": "weather_service", + "provider_type": "http", + "url": "https://weather.example.com/utcp", + "http_method": "GET" + } + ] +}) + +# Call tool +result = client.call_tool("weather_service.get_weather", {"location": "NYC"}) +``` + +### v1.0 Client Code + +```python +from utcp.utcp_client import UtcpClient + +# New way - async factory method +client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "weather_service", + "call_template_type": "http", + "url": "https://weather.example.com/utcp", + "http_method": "GET" + } + ] +}) + +# Call tool - now async +result = await client.call_tool("weather_service.get_weather", {"location": "NYC"}) +``` + +## Configuration Migration + +### v0.1 Configuration + +```yaml +providers: + - name: weather_service + provider_type: http + url: https://weather.example.com/utcp + http_method: GET + - name: file_service + provider_type: cli + command: cat + args: ["${filename}"] + +variables: + API_KEY: your_api_key +``` + +### v1.0 Configuration + +```yaml +manual_call_templates: + - name: weather_service + call_template_type: http + url: https://weather.example.com/utcp + http_method: GET + - name: file_service + call_template_type: cli + command: cat + args: ["${filename}"] + +variable_loaders: + - loader_type: env + prefix: UTCP_ + - loader_type: dotenv + file_path: .env +``` + +## Manual Format Migration + +### v0.1 Manual Format + +```json +{ + "utcp_version": "0.1.0", + "provider_info": { + "name": "weather_api", + "version": "1.0.0" + }, + "tools": [ + { + "name": "get_weather", + "description": "Get weather data", + "parameters": { + "type": "object", + "properties": { + "location": {"type": "string"} + } + }, + "provider": { + "provider_type": "http", + "url": "https://api.weather.com/current", + "method": "GET" + } + } + ] +} +``` + +### v1.0 Manual Format + +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "info": { + "title": "Weather API", + "version": "1.0.0", + "description": "Weather data and forecasting tools" + }, + "tools": [ + { + "name": "get_weather", + "description": "Get weather data", + "inputs": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"] + }, + "outputs": { + "type": "object", + "properties": { + "temperature": {"type": "number"}, + "conditions": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.weather.com/current", + "http_method": "GET", + "query_params": { + "location": "${location}" + } + } + } + ] +} +``` + +## Protocol Migration + +### HTTP Protocol Changes + +#### v0.1 HTTP Provider + +```json +{ + "provider_type": "http", + "url": "https://api.example.com/endpoint", + "method": "POST", + "headers": {"Authorization": "Bearer ${TOKEN}"}, + "body": {"data": "${input}"} +} +``` + +#### v1.0 HTTP Call Template + +```json +{ + "call_template_type": "http", + "url": "https://api.example.com/endpoint", + "http_method": "POST", + "headers": {"Authorization": "Bearer ${TOKEN}"}, + "body": {"data": "${input}"}, + "auth": { + "auth_type": "api_key", + "api_key": "${TOKEN}", + "var_name": "Authorization", + "location": "header" + } +} +``` + +### CLI Protocol Changes + +#### v0.1 CLI Provider + +```json +{ + "provider_type": "cli", + "command": "python", + "args": ["script.py", "${input}"], + "cwd": "/app" +} +``` + +#### v1.0 CLI Call Template + +```json +{ + "call_template_type": "cli", + "command": "python", + "args": ["script.py", "${input}"], + "working_directory": "/app", + "timeout": 30, + "environment": { + "PYTHONPATH": "/app/lib" + } +} +``` + +## Authentication Migration + +### v0.1 Authentication + +```json +{ + "provider": { + "provider_type": "http", + "url": "https://api.example.com/data", + "headers": { + "Authorization": "Bearer ${API_TOKEN}" + } + } +} +``` + +### v1.0 Authentication + +```json +{ + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/data", + "auth": { + "auth_type": "api_key", + "api_key": "${API_TOKEN}", + "var_name": "Authorization", + "location": "header" + } + } +} +``` + +## Error Handling Migration + +### v0.1 Error Handling + +```python +try: + result = client.call_tool("service.tool", args) +except Exception as e: + print(f"Error: {e}") +``` + +### v1.0 Error Handling + +```python +from utcp.exceptions import ( + UtcpError, + ToolNotFoundError, + ToolCallError, + AuthenticationError +) + +try: + result = await client.call_tool("service.tool", args) +except ToolNotFoundError as e: + print(f"Tool not found: {e}") +except AuthenticationError as e: + print(f"Authentication failed: {e}") +except ToolCallError as e: + print(f"Tool call failed: {e}") +except UtcpError as e: + print(f"UTCP error: {e}") +``` + +## Step-by-Step Migration + +### Step 1: Update Dependencies + +```bash +# Uninstall old version +pip uninstall utcp + +# Install new version with plugins +pip install utcp utcp-http utcp-cli utcp-websocket utcp-text +``` + +### Step 2: Update Client Code + +```python +# Before +from utcp import UtcpClient + +def main(): + client = UtcpClient(config=config) + result = client.call_tool("service.tool", args) + return result + +# After +import asyncio +from utcp.utcp_client import UtcpClient + +async def main(): + client = await UtcpClient.create(config=config) + result = await client.call_tool("service.tool", args) + return result + +if __name__ == "__main__": + asyncio.run(main()) +``` + +### Step 3: Update Configuration + +```python +# Migration helper function +def migrate_config_v0_to_v1(old_config): + new_config = { + "manual_call_templates": [], + "variable_loaders": [ + {"loader_type": "env", "prefix": "UTCP_"} + ] + } + + for provider in old_config.get("providers", []): + call_template = { + "name": provider["name"], + "call_template_type": provider["provider_type"], + } + + # Migrate HTTP providers + if provider["provider_type"] == "http": + call_template.update({ + "url": provider["url"], + "http_method": provider.get("method", "GET"), + "headers": provider.get("headers", {}), + "body": provider.get("body") + }) + + # Migrate CLI providers + elif provider["provider_type"] == "cli": + call_template.update({ + "command": provider["command"], + "args": provider.get("args", []), + "working_directory": provider.get("cwd") + }) + + new_config["manual_call_templates"].append(call_template) + + return new_config + +# Use migration helper +old_config = load_old_config() +new_config = migrate_config_v0_to_v1(old_config) +client = await UtcpClient.create(config=new_config) +``` + +### Step 4: Update Manual Format + +```python +# Migration helper for manuals +def migrate_manual_v0_to_v1(old_manual): + new_manual = { + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "info": { + "title": old_manual.get("provider_info", {}).get("name", "API"), + "version": old_manual.get("provider_info", {}).get("version", "1.0.0"), + "description": old_manual.get("provider_info", {}).get("description", "") + }, + "tools": [] + } + + for tool in old_manual.get("tools", []): + new_tool = { + "name": tool["name"], + "description": tool["description"], + "inputs": tool.get("parameters", {}), + "tool_call_template": {} + } + + # Migrate provider to call_template + provider = tool.get("provider", {}) + if provider.get("provider_type") == "http": + new_tool["tool_call_template"] = { + "call_template_type": "http", + "url": provider["url"], + "http_method": provider.get("method", "GET"), + "headers": provider.get("headers", {}), + "body": provider.get("body") + } + + new_manual["tools"].append(new_tool) + + return new_manual +``` + +### Step 5: Test Migration + +```python +import pytest +from utcp.utcp_client import UtcpClient + +@pytest.mark.asyncio +async def test_migrated_client(): + # Test with migrated configuration + client = await UtcpClient.create(config=migrated_config) + + # Test tool discovery + tools = await client.list_tools() + assert len(tools) > 0 + + # Test tool calls + result = await client.call_tool("service.tool", {"param": "value"}) + assert result is not None +``` + +## Common Migration Issues + +### Issue 1: Async/Await + +**Problem**: v1.0 client methods are async +**Solution**: Add `async`/`await` keywords + +```python +# Before +result = client.call_tool("tool", args) + +# After +result = await client.call_tool("tool", args) +``` + +### Issue 2: Configuration Format + +**Problem**: Configuration structure changed +**Solution**: Use migration helper or update manually + +```python +# Before +config = {"providers": [...]} + +# After +config = {"manual_call_templates": [...]} +``` + +### Issue 3: Plugin Dependencies + +**Problem**: Protocol implementations not found +**Solution**: Install required plugins + +```bash +pip install utcp-http utcp-cli utcp-websocket +``` + +### Issue 4: Manual Format + +**Problem**: Old manual format not recognized +**Solution**: Update manual structure + +```json +// Before +{"provider": {"provider_type": "http"}} + +// After +{"tool_call_template": {"call_template_type": "http"}} +``` + +## Validation Tools + +### Configuration Validator + +```python +from utcp.data.utcp_client_config import UtcpClientConfig + +def validate_config(config_dict): + try: + config = UtcpClientConfig(**config_dict) + print("Configuration is valid!") + return config + except Exception as e: + print(f"Configuration validation failed: {e}") + return None +``` + +### Manual Validator + +```python +from utcp.data.utcp_manual import UtcpManual + +def validate_manual(manual_dict): + try: + manual = UtcpManual(**manual_dict) + print("Manual is valid!") + return manual + except Exception as e: + print(f"Manual validation failed: {e}") + return None +``` + +## Best Practices for Migration + +1. **Gradual Migration**: Migrate one component at a time +2. **Test Thoroughly**: Test each migrated component +3. **Backup Configurations**: Keep backups of v0.1 configurations +4. **Use Validation**: Validate configurations and manuals +5. **Monitor Performance**: Check for performance regressions +6. **Update Documentation**: Update internal documentation +7. **Train Team**: Ensure team understands new patterns + +## Post-Migration Checklist + +- [ ] All dependencies updated +- [ ] Client code uses async/await +- [ ] Configuration format updated +- [ ] Manual format updated +- [ ] Error handling updated +- [ ] Tests passing +- [ ] Performance acceptable +- [ ] Documentation updated +- [ ] Team trained on changes + +## Getting Help + +If you encounter issues during migration: + +1. **Check Documentation**: Review the [Implementation Guide](./implementation.md) +2. **GitHub Issues**: Search existing issues or create new ones +3. **Discord Community**: Join the [UTCP Discord](https://discord.gg/ZpMbQ8jRbD) +4. **Examples**: Check the [examples repository](https://github.com/universal-tool-calling-protocol/python-utcp/tree/main/examples) + +## Rollback Plan + +If migration issues occur, you can rollback: + +```bash +# Rollback to v0.1 +pip uninstall utcp utcp-http utcp-cli utcp-websocket utcp-text +pip install utcp==0.1.0 + +# Restore old configuration files +cp config-v0.1-backup.yaml config.yaml +``` + +Remember to test the rollback process in a non-production environment first. diff --git a/docs/providers/cli.md b/docs/providers/cli.md new file mode 100644 index 0000000..05caf92 --- /dev/null +++ b/docs/providers/cli.md @@ -0,0 +1,323 @@ +--- +id: cli +title: CLI Protocol +sidebar_position: 4 +--- + +# CLI Protocol + +The CLI protocol plugin (`utcp-cli`) enables UTCP to execute command-line tools and scripts. This is particularly useful for wrapping existing CLI applications and making them available to AI agents. + +## Installation + +```bash +pip install utcp-cli +``` + +## Call Template Structure + +```json +{ + "call_template_type": "cli", + "command": "curl", + "args": [ + "-X", "GET", + "-H", "Authorization: Bearer ${API_TOKEN}", + "https://api.example.com/data" + ], + "working_directory": "/tmp", + "environment": { + "API_TOKEN": "${API_TOKEN}" + }, + "timeout": 30 +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"cli"` | +| `command` | string | The command to execute | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `args` | array | Command arguments | +| `working_directory` | string | Working directory for command execution | +| `environment` | object | Environment variables | +| `timeout` | number | Execution timeout in seconds (default: 30) | +| `shell` | boolean | Whether to execute through shell (default: false) | +| `capture_output` | boolean | Whether to capture stdout/stderr (default: true) | + +## Security Considerations + +:::danger Security Warning +CLI protocol executes commands on the local system. Always validate inputs and use with caution. +::: + +### Input Validation + +Always validate and sanitize inputs to prevent command injection: + +```json +{ + "name": "safe_file_read", + "description": "Safely read a file", + "inputs": { + "type": "object", + "properties": { + "filename": { + "type": "string", + "pattern": "^[a-zA-Z0-9._-]+$" + } + }, + "required": ["filename"] + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "cat", + "args": ["${filename}"], + "working_directory": "/safe/directory" + } +} +``` + +### Sandboxing + +Consider running CLI tools in sandboxed environments: + +```json +{ + "call_template_type": "cli", + "command": "docker", + "args": [ + "run", "--rm", "--read-only", + "-v", "/safe/data:/data:ro", + "alpine:latest", + "cat", "/data/${filename}" + ] +} +``` + +## Examples + +### Simple Command Execution + +```json +{ + "name": "get_system_info", + "description": "Get system information", + "inputs": { + "type": "object", + "properties": {} + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "uname", + "args": ["-a"] + } +} +``` + +### File Operations + +```json +{ + "name": "list_directory", + "description": "List files in a directory", + "inputs": { + "type": "object", + "properties": { + "path": { + "type": "string", + "description": "Directory path to list" + } + }, + "required": ["path"] + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "ls", + "args": ["-la", "${path}"], + "timeout": 10 + } +} +``` + +### Script Execution + +```json +{ + "name": "run_analysis", + "description": "Run data analysis script", + "inputs": { + "type": "object", + "properties": { + "input_file": {"type": "string"}, + "output_format": {"type": "string", "enum": ["json", "csv"]} + }, + "required": ["input_file"] + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "python", + "args": [ + "/scripts/analyze.py", + "--input", "${input_file}", + "--format", "${output_format}" + ], + "working_directory": "/workspace", + "environment": { + "PYTHONPATH": "/workspace/lib" + }, + "timeout": 300 + } +} +``` + +### Git Operations + +```json +{ + "name": "git_status", + "description": "Get git repository status", + "inputs": { + "type": "object", + "properties": { + "repo_path": {"type": "string"} + }, + "required": ["repo_path"] + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "git", + "args": ["status", "--porcelain"], + "working_directory": "${repo_path}" + } +} +``` + +## Output Handling + +The CLI protocol captures and returns: + +- **stdout**: Standard output as the primary result +- **stderr**: Standard error (included in error cases) +- **return_code**: Process exit code +- **execution_time**: Time taken to execute + +### Success Response + +```json +{ + "stdout": "file1.txt\nfile2.txt\n", + "stderr": "", + "return_code": 0, + "execution_time": 0.123 +} +``` + +### Error Response + +```json +{ + "stdout": "", + "stderr": "ls: cannot access '/invalid/path': No such file or directory\n", + "return_code": 2, + "execution_time": 0.045 +} +``` + +## Environment Variables + +Set environment variables for command execution: + +```json +{ + "call_template_type": "cli", + "command": "node", + "args": ["app.js"], + "environment": { + "NODE_ENV": "production", + "API_KEY": "${API_KEY}", + "PORT": "3000" + } +} +``` + +## Working Directory + +Specify the working directory for command execution: + +```json +{ + "call_template_type": "cli", + "command": "make", + "args": ["build"], + "working_directory": "/project/src" +} +``` + +## Best Practices + +1. **Validate Inputs**: Always validate and sanitize user inputs +2. **Use Absolute Paths**: Prefer absolute paths for commands and files +3. **Set Timeouts**: Configure appropriate timeouts to prevent hanging +4. **Limit Permissions**: Run with minimal necessary permissions +5. **Sandbox Execution**: Use containers or chroot when possible +6. **Log Execution**: Log all command executions for audit trails +7. **Handle Errors**: Properly handle and report command failures + +## Common Use Cases + +- **Development Tools**: Git, npm, pip, docker commands +- **System Administration**: File operations, process management +- **Data Processing**: Scripts for ETL, analysis, reporting +- **Build Systems**: Make, gradle, webpack execution +- **Testing**: Running test suites and validation scripts + +## Error Handling + +| Error Type | Description | Handling | +|------------|-------------|----------| +| Command Not Found | Command doesn't exist | Raise `CommandNotFoundError` | +| Permission Denied | Insufficient permissions | Raise `PermissionError` | +| Timeout | Command exceeded timeout | Raise `TimeoutError` | +| Non-zero Exit | Command failed | Include stderr in error | + +## Platform Considerations + +### Windows + +```json +{ + "call_template_type": "cli", + "command": "cmd", + "args": ["/c", "dir", "${path}"], + "shell": true +} +``` + +### Unix/Linux + +```json +{ + "call_template_type": "cli", + "command": "ls", + "args": ["-la", "${path}"] +} +``` + +### Cross-platform + +```json +{ + "call_template_type": "cli", + "command": "python", + "args": ["-c", "import os; print(os.listdir('${path}'))"] +} +``` diff --git a/docs/providers/http.md b/docs/providers/http.md new file mode 100644 index 0000000..6f67741 --- /dev/null +++ b/docs/providers/http.md @@ -0,0 +1,258 @@ +--- +id: http +title: HTTP Protocol +sidebar_position: 1 +--- + +# HTTP Protocol + +The HTTP protocol plugin (`utcp-http`) enables UTCP to call REST APIs, webhooks, and any HTTP-based services. It's the most commonly used protocol and provides comprehensive support for modern web APIs. + +## Installation + +```bash +pip install utcp-http +``` + +## Call Template Structure + +```json +{ + "call_template_type": "http", + "url": "https://api.example.com/endpoint", + "http_method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ${API_TOKEN}" + }, + "body": { + "query": "${query}", + "limit": 10 + }, + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "X-API-Key", + "location": "header" + } +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"http"` | +| `url` | string | The HTTP endpoint URL | +| `http_method` | string | HTTP method (GET, POST, PUT, DELETE, etc.) | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `headers` | object | HTTP headers to include | +| `body` | object/string | Request body for POST/PUT requests | +| `query_params` | object | URL query parameters | +| `timeout` | number | Request timeout in seconds (default: 30) | +| `follow_redirects` | boolean | Whether to follow HTTP redirects (default: true) | +| `verify_ssl` | boolean | Whether to verify SSL certificates (default: true) | + +## Authentication + +The HTTP protocol supports multiple authentication methods: + +### API Key Authentication + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "X-API-Key", + "location": "header" + } +} +``` + +**Locations:** +- `"header"`: Add as HTTP header +- `"query"`: Add as query parameter +- `"cookie"`: Add as cookie + +### Basic Authentication + +```json +{ + "auth": { + "auth_type": "basic", + "username": "${USERNAME}", + "password": "${PASSWORD}" + } +} +``` + +### OAuth2 Bearer Token + +```json +{ + "auth": { + "auth_type": "oauth2", + "client_id": "${CLIENT_ID}", + "client_secret": "${CLIENT_SECRET}", + "token_url": "https://auth.example.com/token", + "scope": "read:data" + } +} +``` + +## Variable Substitution + +Use `${VARIABLE_NAME}` syntax to substitute values at runtime: + +```json +{ + "url": "https://api.example.com/users/${user_id}", + "headers": { + "Authorization": "Bearer ${access_token}" + }, + "body": { + "name": "${user_name}", + "email": "${user_email}" + } +} +``` + +Variables can come from: +- Tool arguments +- Environment variables +- Configuration files +- Runtime context + +## Examples + +### Simple GET Request + +```json +{ + "name": "get_weather", + "description": "Get current weather for a location", + "inputs": { + "type": "object", + "properties": { + "location": {"type": "string"} + }, + "required": ["location"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.weather.com/v1/current", + "http_method": "GET", + "query_params": { + "q": "${location}", + "appid": "${WEATHER_API_KEY}" + } + } +} +``` + +### POST with JSON Body + +```json +{ + "name": "create_user", + "description": "Create a new user account", + "inputs": { + "type": "object", + "properties": { + "name": {"type": "string"}, + "email": {"type": "string"} + }, + "required": ["name", "email"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/users", + "http_method": "POST", + "headers": { + "Content-Type": "application/json", + "Authorization": "Bearer ${ACCESS_TOKEN}" + }, + "body": { + "name": "${name}", + "email": "${email}", + "created_at": "{{now}}" + } + } +} +``` + +### File Upload + +```json +{ + "name": "upload_file", + "description": "Upload a file to the server", + "inputs": { + "type": "object", + "properties": { + "file_path": {"type": "string"}, + "description": {"type": "string"} + }, + "required": ["file_path"] + }, + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/upload", + "http_method": "POST", + "headers": { + "Authorization": "Bearer ${ACCESS_TOKEN}" + }, + "body": { + "file": "@${file_path}", + "description": "${description}" + } + } +} +``` + +## Error Handling + +The HTTP protocol handles various error conditions: + +| HTTP Status | Behavior | +|-------------|----------| +| 200-299 | Success - return response body | +| 400-499 | Client error - raise `HttpClientError` | +| 500-599 | Server error - raise `HttpServerError` | +| Timeout | Raise `HttpTimeoutError` | +| Connection | Raise `HttpConnectionError` | + +## Best Practices + +1. **Use HTTPS**: Always use secure connections for production APIs +2. **Set Timeouts**: Configure appropriate timeouts for your use case +3. **Handle Rate Limits**: Implement retry logic for rate-limited APIs +4. **Validate Inputs**: Use JSON Schema to validate tool inputs +5. **Secure Credentials**: Store API keys and tokens securely +6. **Monitor Usage**: Track API usage and performance metrics + +## OpenAPI Integration + +The HTTP protocol can automatically generate UTCP manuals from OpenAPI specifications: + +```python +from utcp_http.openapi_converter import OpenApiConverter + +converter = OpenApiConverter() +manual = await converter.convert_openapi_to_manual( + "https://api.example.com/openapi.json" +) +``` + +## Related Protocols + +- [Server-Sent Events](./sse.md) - For streaming HTTP responses +- [Streamable HTTP](./streamable-http.md) - For chunked HTTP responses +- [WebSocket](./websocket.md) - For bidirectional real-time communication diff --git a/docs/providers/index.md b/docs/providers/index.md new file mode 100644 index 0000000..f1ad2ba --- /dev/null +++ b/docs/providers/index.md @@ -0,0 +1,92 @@ +--- +id: providers +title: Communication Protocols +sidebar_position: 3 +--- + +# Communication Protocols + +UTCP supports multiple communication protocols through its plugin architecture. Each protocol plugin provides the necessary implementation to call tools using that specific transport method. + +## Available Protocols + +### Core Protocols + +| Protocol | Plugin | Status | Description | +|----------|--------|--------|-------------| +| [HTTP](./http.md) | `utcp-http` | ✅ Stable | REST APIs, webhooks, and standard HTTP services | +| [Server-Sent Events](./sse.md) | `utcp-http` | ✅ Stable | Real-time streaming over HTTP | +| [WebSocket](./websocket.md) | `utcp-websocket` | ✅ Stable | Bidirectional real-time communication | +| [CLI](./cli.md) | `utcp-cli` | ✅ Stable | Command-line tools and scripts | +| [Text Files](./text.md) | `utcp-text` | ✅ Stable | Reading local and remote text files | + +### Integration Protocols + +| Protocol | Plugin | Status | Description | +|----------|--------|--------|-------------| +| [MCP](./mcp.md) | `utcp-mcp` | ✅ Stable | Model Context Protocol interoperability | + +### Experimental Protocols + +| Protocol | Plugin | Status | Description | +|----------|--------|--------|-------------| +| [GraphQL](./graphql.md) | `utcp-gql` | 🚧 Beta | GraphQL APIs and subscriptions | +| [gRPC](./grpc.md) | `utcp-grpc` | 🚧 Beta | High-performance RPC calls | +| [TCP](./tcp.md) | `utcp-socket` | 🚧 Beta | Raw TCP socket connections | +| [UDP](./udp.md) | `utcp-socket` | 🚧 Beta | UDP packet-based communication | + +## Protocol Selection Guide + +Choose the right protocol based on your tool's characteristics: + +### Use HTTP when: +- Your tool is a REST API +- You need simple request/response patterns +- You want maximum compatibility + +### Use WebSocket when: +- You need bidirectional communication +- Your tool provides real-time updates +- You want persistent connections + +### Use CLI when: +- Your tool is a command-line application +- You need to execute local scripts +- You're wrapping existing CLI tools + +### Use SSE when: +- You need server-to-client streaming +- You want real-time updates over HTTP +- You need simple event streaming + +## Plugin Architecture + +UTCP's plugin system allows you to: + +1. **Extend existing protocols** with custom authentication or data transformation +2. **Create new protocols** for specialized communication needs +3. **Combine protocols** for complex tool interactions + +### Creating Custom Protocols + +To create a custom protocol plugin, implement the [`CommunicationProtocol`](../api/core/utcp/interfaces/communication_protocol.md) interface: + +```python +from utcp.interfaces.communication_protocol import CommunicationProtocol +from utcp.data.call_template import CallTemplate + +class MyCustomProtocol(CommunicationProtocol): + def get_supported_call_template_types(self) -> List[str]: + return ["my_custom_protocol"] + + async def call_tool(self, call_template: CallTemplate, tool_args: Dict[str, Any]) -> Any: + # Implement your protocol logic here + pass +``` + +## Next Steps + +- Choose a protocol from the list above +- Read the specific protocol documentation +- Check out the [API Reference](../api/index.md) for implementation details +- See [Examples](../examples/index.md) for practical implementations diff --git a/docs/providers/mcp.md b/docs/providers/mcp.md new file mode 100644 index 0000000..f7a1088 --- /dev/null +++ b/docs/providers/mcp.md @@ -0,0 +1,336 @@ +--- +id: mcp +title: Model Context Protocol (MCP) +sidebar_position: 6 +--- + +# Model Context Protocol (MCP) + +The MCP protocol plugin (`utcp-mcp`) provides interoperability with the Model Context Protocol, allowing UTCP clients to call tools exposed through MCP servers. This enables gradual migration from MCP to UTCP or hybrid deployments. + +## Installation + +```bash +pip install utcp-mcp +``` + +## Call Template Structure + +```json +{ + "call_template_type": "mcp", + "server_config": { + "command": "node", + "args": ["/path/to/mcp-server.js"], + "env": { + "API_KEY": "${API_KEY}" + } + }, + "tool_name": "${tool_name}", + "connection_timeout": 30, + "call_timeout": 60 +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"mcp"` | +| `server_config` | object | MCP server configuration | +| `tool_name` | string | Name of the MCP tool to call | + +### Server Configuration + +| Field | Type | Description | +|-------|------|-------------| +| `command` | string | Command to start MCP server | +| `args` | array | Command arguments | +| `env` | object | Environment variables | +| `cwd` | string | Working directory | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `connection_timeout` | number | Server connection timeout (default: 30) | +| `call_timeout` | number | Tool call timeout (default: 60) | +| `server_name` | string | Friendly name for the server | +| `auto_restart` | boolean | Auto-restart server on failure (default: true) | + +## Examples + +### File System MCP Server + +```json +{ + "name": "read_file_mcp", + "description": "Read file content via MCP filesystem server", + "inputs": { + "type": "object", + "properties": { + "path": {"type": "string"} + }, + "required": ["path"] + }, + "tool_call_template": { + "call_template_type": "mcp", + "server_config": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"], + "env": {} + }, + "tool_name": "read_file" + } +} +``` + +### Database MCP Server + +```json +{ + "name": "query_database_mcp", + "description": "Query database via MCP server", + "inputs": { + "type": "object", + "properties": { + "query": {"type": "string"}, + "params": {"type": "array"} + }, + "required": ["query"] + }, + "tool_call_template": { + "call_template_type": "mcp", + "server_config": { + "command": "python", + "args": ["-m", "mcp_server_sqlite", "--db-path", "/data/app.db"], + "env": { + "DATABASE_URL": "${DATABASE_URL}" + } + }, + "tool_name": "execute_query", + "call_timeout": 120 + } +} +``` + +### Custom MCP Server + +```json +{ + "name": "custom_mcp_tool", + "description": "Call custom MCP server tool", + "inputs": { + "type": "object", + "properties": { + "input_data": {"type": "object"} + }, + "required": ["input_data"] + }, + "tool_call_template": { + "call_template_type": "mcp", + "server_config": { + "command": "node", + "args": ["./custom-mcp-server.js"], + "cwd": "/app/servers", + "env": { + "NODE_ENV": "production", + "API_KEY": "${MCP_API_KEY}" + } + }, + "tool_name": "process_data", + "server_name": "custom_processor" + } +} +``` + +## Server Management + +### Automatic Server Lifecycle + +The MCP protocol plugin automatically manages server lifecycle: + +1. **Startup**: Launches MCP server when first tool is called +2. **Connection**: Establishes JSON-RPC connection +3. **Tool Discovery**: Retrieves available tools from server +4. **Call Routing**: Routes tool calls to appropriate server +5. **Shutdown**: Gracefully shuts down server when no longer needed + +### Server Pooling + +Multiple tools can share the same MCP server instance: + +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": [ + { + "name": "list_files", + "tool_call_template": { + "call_template_type": "mcp", + "server_config": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"] + }, + "tool_name": "list_directory" + } + }, + { + "name": "read_file", + "tool_call_template": { + "call_template_type": "mcp", + "server_config": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data"] + }, + "tool_name": "read_file" + } + } + ] +} +``` + +## Error Handling + +| Error Type | Description | Handling | +|------------|-------------|----------| +| Server Start Failed | Cannot start MCP server | Raise `MCPServerStartError` | +| Connection Failed | Cannot connect to server | Raise `MCPConnectionError` | +| Tool Not Found | Tool doesn't exist on server | Raise `MCPToolNotFoundError` | +| Call Timeout | Tool call exceeded timeout | Raise `MCPTimeoutError` | +| Server Crashed | MCP server process died | Auto-restart if enabled | + +## Migration from MCP + +### Gradual Migration Strategy + +1. **Wrap Existing MCP Servers**: Use UTCP-MCP plugin to call existing servers +2. **Identify High-Value Tools**: Prioritize frequently used tools for direct migration +3. **Migrate Tool by Tool**: Convert individual tools to native UTCP protocols +4. **Deprecate MCP Servers**: Remove MCP dependency once migration is complete + +### Migration Example + +**Before (Pure MCP):** +```javascript +// MCP Client +const client = new MCPClient(); +await client.connect("filesystem-server"); +const result = await client.callTool("read_file", {path: "/data/file.txt"}); +``` + +**During Migration (UTCP with MCP):** +```python +# UTCP Client with MCP plugin +client = await UtcpClient.create() +result = await client.call_tool("filesystem.read_file", { + "path": "/data/file.txt" +}) +``` + +**After Migration (Pure UTCP):** +```python +# UTCP Client with native protocol +client = await UtcpClient.create() +result = await client.call_tool("filesystem.read_file", { + "path": "/data/file.txt" +}) +``` + +## Best Practices + +1. **Server Reuse**: Share MCP servers across multiple tools when possible +2. **Timeout Configuration**: Set appropriate timeouts for server startup and calls +3. **Error Handling**: Implement retry logic for transient server failures +4. **Resource Management**: Monitor server resource usage and lifecycle +5. **Migration Planning**: Plan gradual migration to native UTCP protocols +6. **Testing**: Test MCP server compatibility thoroughly +7. **Documentation**: Document MCP server dependencies and requirements + +## Performance Considerations + +### Overhead Comparison + +| Aspect | Native UTCP | UTCP-MCP | Pure MCP | +|--------|-------------|----------|----------| +| Latency | Low | Medium | Medium | +| Memory Usage | Low | Medium | High | +| Process Overhead | None | Medium | High | +| Network Hops | 1 | 2 | 2 | + +### Optimization Tips + +1. **Server Pooling**: Reuse servers across multiple tools +2. **Connection Caching**: Cache server connections +3. **Batch Operations**: Group related tool calls when possible +4. **Resource Limits**: Set memory and CPU limits for MCP servers +5. **Health Monitoring**: Monitor server health and performance + +## Compatibility + +### Supported MCP Versions + +- MCP 1.0.x: ✅ Full support +- MCP 0.x: ⚠️ Limited support + +### Known Limitations + +1. **Streaming**: MCP streaming not fully supported +2. **Resources**: MCP resources not mapped to UTCP +3. **Prompts**: MCP prompts not supported +4. **Sampling**: MCP sampling not supported + +## Common Use Cases + +- **Legacy Integration**: Calling existing MCP servers +- **Gradual Migration**: Transitioning from MCP to UTCP +- **Hybrid Deployments**: Using both MCP and UTCP tools +- **Third-party Tools**: Accessing MCP-only tools +- **Development**: Testing MCP compatibility + +## Troubleshooting + +### Server Won't Start + +```bash +# Check server command manually +npx -y @modelcontextprotocol/server-filesystem /path + +# Verify environment variables +echo $API_KEY + +# Check file permissions +ls -la /path/to/mcp-server.js +``` + +### Connection Issues + +```python +# Enable debug logging +import logging +logging.getLogger('utcp.mcp').setLevel(logging.DEBUG) + +# Test connection timeout +{ + "connection_timeout": 60, # Increase timeout + "auto_restart": true # Enable auto-restart +} +``` + +### Tool Not Found + +```python +# List available tools from MCP server +server_tools = await mcp_client.list_tools() +print(f"Available tools: {[tool.name for tool in server_tools]}") +``` + +## Related Documentation + +- [UTCP vs MCP Comparison](../utcp-vs-mcp.md) +- [Migration Guide](../migration/from-mcp.md) +- [HTTP Protocol](./http.md) - Alternative to MCP for REST APIs +- [CLI Protocol](./cli.md) - Alternative to MCP for command-line tools diff --git a/docs/providers/sse.md b/docs/providers/sse.md new file mode 100644 index 0000000..f39d964 --- /dev/null +++ b/docs/providers/sse.md @@ -0,0 +1,397 @@ +--- +id: sse +title: Server-Sent Events (SSE) +sidebar_position: 3 +--- + +# Server-Sent Events (SSE) + +The Server-Sent Events protocol plugin (`utcp-http`) enables UTCP to receive real-time streaming data from HTTP servers. SSE is perfect for tools that need to stream live updates, notifications, or continuous data feeds. + +## Installation + +SSE support is included with the HTTP plugin: + +```bash +pip install utcp-http +``` + +## Call Template Structure + +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/events", + "headers": { + "Authorization": "Bearer ${API_TOKEN}", + "Accept": "text/event-stream" + }, + "timeout": 60, + "max_events": 10, + "event_filter": { + "type": "data_update" + } +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"sse"` | +| `url` | string | SSE endpoint URL | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `headers` | object | HTTP headers for the request | +| `query_params` | object | URL query parameters | +| `timeout` | number | Stream timeout in seconds (default: 60) | +| `max_events` | number | Maximum events to receive (default: unlimited) | +| `event_filter` | object | Filter events by type or data | +| `reconnect` | boolean | Auto-reconnect on connection loss (default: true) | +| `reconnect_delay` | number | Delay between reconnection attempts (default: 3) | + +## Authentication + +SSE uses standard HTTP authentication methods: + +### Bearer Token + +```json +{ + "headers": { + "Authorization": "Bearer ${ACCESS_TOKEN}" + } +} +``` + +### API Key + +```json +{ + "headers": { + "X-API-Key": "${API_KEY}" + } +} +``` + +### Query Parameter Auth + +```json +{ + "query_params": { + "token": "${API_TOKEN}", + "user_id": "${USER_ID}" + } +} +``` + +## Event Handling + +### Basic Event Stream + +```json +{ + "name": "stream_notifications", + "description": "Stream real-time notifications", + "inputs": { + "type": "object", + "properties": { + "user_id": {"type": "string"} + }, + "required": ["user_id"] + }, + "tool_call_template": { + "call_template_type": "sse", + "url": "https://api.example.com/notifications/stream", + "query_params": { + "user_id": "${user_id}" + }, + "headers": { + "Authorization": "Bearer ${ACCESS_TOKEN}" + }, + "timeout": 300 + } +} +``` + +### Filtered Events + +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/events", + "event_filter": { + "type": "order_update", + "status": ["completed", "cancelled"] + }, + "max_events": 5 +} +``` + +## Examples + +### Stock Price Stream + +```json +{ + "name": "stream_stock_prices", + "description": "Stream real-time stock price updates", + "inputs": { + "type": "object", + "properties": { + "symbols": { + "type": "array", + "items": {"type": "string"} + }, + "duration": {"type": "number", "default": 60} + }, + "required": ["symbols"] + }, + "tool_call_template": { + "call_template_type": "sse", + "url": "https://api.stocks.com/stream", + "query_params": { + "symbols": "${symbols}", + "format": "json" + }, + "headers": { + "Authorization": "Bearer ${STOCK_API_KEY}" + }, + "timeout": "${duration}", + "event_filter": { + "type": "price_update" + } + } +} +``` + +### Log Monitoring + +```json +{ + "name": "monitor_logs", + "description": "Monitor application logs in real-time", + "inputs": { + "type": "object", + "properties": { + "service": {"type": "string"}, + "level": {"type": "string", "enum": ["error", "warn", "info", "debug"]} + }, + "required": ["service"] + }, + "tool_call_template": { + "call_template_type": "sse", + "url": "https://logs.example.com/stream", + "query_params": { + "service": "${service}", + "level": "${level}" + }, + "headers": { + "X-API-Key": "${LOG_API_KEY}" + }, + "timeout": 600, + "max_events": 100 + } +} +``` + +### System Metrics Stream + +```json +{ + "name": "stream_metrics", + "description": "Stream system performance metrics", + "inputs": { + "type": "object", + "properties": { + "metrics": { + "type": "array", + "items": {"type": "string"} + }, + "interval": {"type": "number", "default": 5} + }, + "required": ["metrics"] + }, + "tool_call_template": { + "call_template_type": "sse", + "url": "https://monitoring.example.com/metrics/stream", + "query_params": { + "metrics": "${metrics}", + "interval": "${interval}" + }, + "headers": { + "Authorization": "Bearer ${MONITORING_TOKEN}" + }, + "timeout": 300, + "reconnect": true, + "reconnect_delay": 5 + } +} +``` + +## Event Format + +SSE events follow the standard format: + +``` +event: message +data: {"type": "update", "value": 123} +id: event-123 +retry: 3000 + +event: heartbeat +data: {"timestamp": "2024-01-15T10:30:00Z"} + +data: {"message": "Simple data without event type"} +``` + +### Parsed Event Structure + +```json +{ + "event": "message", + "data": {"type": "update", "value": 123}, + "id": "event-123", + "retry": 3000, + "timestamp": "2024-01-15T10:30:00Z" +} +``` + +## Response Handling + +### Single Event Response + +```json +{ + "events": [ + { + "event": "data", + "data": {"result": "success"}, + "id": "1", + "timestamp": "2024-01-15T10:30:00Z" + } + ], + "total_events": 1, + "duration": 2.5 +} +``` + +### Multiple Events Response + +```json +{ + "events": [ + { + "event": "start", + "data": {"status": "processing"}, + "id": "1" + }, + { + "event": "progress", + "data": {"percent": 50}, + "id": "2" + }, + { + "event": "complete", + "data": {"result": "success"}, + "id": "3" + } + ], + "total_events": 3, + "duration": 15.2 +} +``` + +## Error Handling + +| Error Type | Description | Handling | +|------------|-------------|----------| +| Connection Failed | Cannot connect to SSE endpoint | Raise `SSEConnectionError` | +| Stream Timeout | No events received within timeout | Return partial results | +| Parse Error | Invalid SSE event format | Skip malformed events | +| Authentication Failed | Invalid credentials | Raise `SSEAuthError` | +| Server Error | HTTP 5xx response | Raise `SSEServerError` | + +## Best Practices + +1. **Set Appropriate Timeouts**: Configure timeouts based on expected data frequency +2. **Handle Reconnections**: Enable auto-reconnect for long-running streams +3. **Filter Events**: Use event filters to reduce unnecessary data processing +4. **Monitor Performance**: Track event rates and processing times +5. **Validate Data**: Validate incoming event data against expected schemas +6. **Handle Backpressure**: Implement buffering for high-frequency events +7. **Graceful Shutdown**: Properly close streams when done + +## Advanced Features + +### Custom Event Parsing + +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/events", + "event_parser": { + "format": "json", + "extract_fields": ["timestamp", "level", "message"] + } +} +``` + +### Event Aggregation + +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/metrics", + "aggregation": { + "window": 10, + "function": "average", + "field": "value" + } +} +``` + +### Conditional Termination + +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/events", + "termination_condition": { + "event_type": "complete", + "data_field": "status", + "value": "finished" + } +} +``` + +## Common Use Cases + +- **Real-time Dashboards**: Live metrics, status updates +- **Notifications**: User alerts, system notifications +- **Log Streaming**: Application logs, audit trails +- **Progress Tracking**: Long-running task progress +- **Live Data Feeds**: News, social media, sensor data +- **Chat Applications**: Message streams, typing indicators + +## Protocol Comparison + +| Feature | SSE | WebSocket | HTTP Polling | +|---------|-----|-----------|--------------| +| Server-to-Client | ✅ | ✅ | ✅ | +| Client-to-Server | ❌ | ✅ | ✅ | +| Auto-Reconnect | ✅ | Manual | Manual | +| Overhead | Low | Low | High | +| Browser Support | ✅ | ✅ | ✅ | +| Simplicity | High | Medium | High | + +## Related Protocols + +- [HTTP](./http.md) - For request/response patterns +- [WebSocket](./websocket.md) - For bidirectional communication +- [Streamable HTTP](./streamable-http.md) - For chunked HTTP responses diff --git a/docs/providers/text.md b/docs/providers/text.md new file mode 100644 index 0000000..e869124 --- /dev/null +++ b/docs/providers/text.md @@ -0,0 +1,448 @@ +--- +id: text +title: Text Protocol +sidebar_position: 5 +--- + +# Text Protocol + +The Text protocol plugin (`utcp-text`) enables UTCP to read and process text files from local filesystem or remote URLs. This is useful for tools that need to access documentation, configuration files, logs, or any text-based data. + +## Installation + +```bash +pip install utcp-text +``` + +## Call Template Structure + +```json +{ + "call_template_type": "text", + "file_path": "/path/to/file.txt", + "encoding": "utf-8", + "max_size": 1048576, + "line_range": { + "start": 1, + "end": 100 + } +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"text"` | +| `file_path` | string | Path to text file (local or URL) | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `encoding` | string | File encoding (default: "utf-8") | +| `max_size` | number | Maximum file size in bytes (default: 1MB) | +| `line_range` | object | Specific line range to read | +| `pattern` | string | Regex pattern to filter content | +| `transform` | string | Content transformation ("upper", "lower", "strip") | + +## File Sources + +### Local Files + +```json +{ + "call_template_type": "text", + "file_path": "/var/log/application.log", + "encoding": "utf-8" +} +``` + +### Remote URLs + +```json +{ + "call_template_type": "text", + "file_path": "https://example.com/config.txt", + "max_size": 512000 +} +``` + +### Variable Substitution + +```json +{ + "call_template_type": "text", + "file_path": "/data/${filename}", + "encoding": "${file_encoding}" +} +``` + +## Examples + +### Read Configuration File + +```json +{ + "name": "read_config", + "description": "Read application configuration file", + "inputs": { + "type": "object", + "properties": { + "config_name": {"type": "string"} + }, + "required": ["config_name"] + }, + "tool_call_template": { + "call_template_type": "text", + "file_path": "/etc/app/${config_name}.conf", + "encoding": "utf-8", + "max_size": 65536 + } +} +``` + +### Read Log File with Line Range + +```json +{ + "name": "read_recent_logs", + "description": "Read recent log entries", + "inputs": { + "type": "object", + "properties": { + "log_file": {"type": "string"}, + "lines": {"type": "number", "default": 100} + }, + "required": ["log_file"] + }, + "tool_call_template": { + "call_template_type": "text", + "file_path": "/var/log/${log_file}", + "line_range": { + "start": -${lines}, + "end": -1 + } + } +} +``` + +### Read Remote Documentation + +```json +{ + "name": "fetch_documentation", + "description": "Fetch documentation from remote URL", + "inputs": { + "type": "object", + "properties": { + "doc_url": {"type": "string"}, + "section": {"type": "string"} + }, + "required": ["doc_url"] + }, + "tool_call_template": { + "call_template_type": "text", + "file_path": "${doc_url}", + "pattern": "(?s)## ${section}.*?(?=## |$)", + "max_size": 2097152 + } +} +``` + +### Search in File + +```json +{ + "name": "search_in_file", + "description": "Search for pattern in text file", + "inputs": { + "type": "object", + "properties": { + "file_path": {"type": "string"}, + "search_pattern": {"type": "string"} + }, + "required": ["file_path", "search_pattern"] + }, + "tool_call_template": { + "call_template_type": "text", + "file_path": "${file_path}", + "pattern": "${search_pattern}", + "transform": "strip" + } +} +``` + +## Line Range Options + +### Absolute Line Numbers + +```json +{ + "line_range": { + "start": 10, + "end": 50 + } +} +``` + +### Relative to End (Tail) + +```json +{ + "line_range": { + "start": -100, + "end": -1 + } +} +``` + +### From Start (Head) + +```json +{ + "line_range": { + "start": 1, + "end": 100 + } +} +``` + +## Pattern Matching + +### Simple Text Search + +```json +{ + "pattern": "ERROR" +} +``` + +### Regex Pattern + +```json +{ + "pattern": "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2} ERROR.*" +} +``` + +### Multi-line Pattern + +```json +{ + "pattern": "(?s)START.*?END" +} +``` + +## Content Transformations + +### Case Transformations + +```json +{ + "transform": "upper" // Convert to uppercase +} +``` + +```json +{ + "transform": "lower" // Convert to lowercase +} +``` + +### Whitespace Handling + +```json +{ + "transform": "strip" // Remove leading/trailing whitespace +} +``` + +### Custom Transformations + +```json +{ + "transform": "normalize_whitespace" // Normalize all whitespace +} +``` + +## Response Format + +### Successful Read + +```json +{ + "content": "File content here...", + "metadata": { + "file_path": "/path/to/file.txt", + "size": 1024, + "lines": 25, + "encoding": "utf-8", + "last_modified": "2024-01-15T10:30:00Z" + } +} +``` + +### Filtered Content + +```json +{ + "content": "Matching lines...", + "metadata": { + "file_path": "/path/to/file.txt", + "total_lines": 1000, + "matched_lines": 5, + "pattern": "ERROR", + "line_range": {"start": 1, "end": 100} + } +} +``` + +## Error Handling + +| Error Type | Description | Handling | +|------------|-------------|----------| +| File Not Found | File doesn't exist | Raise `FileNotFoundError` | +| Permission Denied | No read permission | Raise `PermissionError` | +| File Too Large | Exceeds max_size limit | Raise `FileSizeError` | +| Encoding Error | Invalid file encoding | Raise `EncodingError` | +| Network Error | URL fetch failed | Raise `NetworkError` | + +## Security Considerations + +### Path Traversal Prevention + +```json +{ + "call_template_type": "text", + "file_path": "/safe/directory/${filename}", + "allowed_paths": ["/safe/directory/"] +} +``` + +### File Size Limits + +```json +{ + "max_size": 1048576 // 1MB limit +} +``` + +### URL Restrictions + +```json +{ + "allowed_domains": ["example.com", "docs.company.com"] +} +``` + +## Best Practices + +1. **Set Size Limits**: Always set appropriate max_size limits +2. **Validate Paths**: Validate file paths to prevent directory traversal +3. **Handle Encoding**: Specify encoding explicitly for non-UTF-8 files +4. **Use Line Ranges**: Use line ranges for large files to improve performance +5. **Pattern Efficiency**: Use efficient regex patterns for content filtering +6. **Cache Results**: Cache frequently accessed files +7. **Monitor Access**: Log file access for security auditing + +## Advanced Features + +### Conditional Reading + +```json +{ + "call_template_type": "text", + "file_path": "/var/log/app.log", + "condition": { + "modified_since": "2024-01-15T00:00:00Z" + } +} +``` + +### Multi-file Reading + +```json +{ + "call_template_type": "text", + "file_paths": [ + "/etc/app/config1.txt", + "/etc/app/config2.txt" + ], + "merge_strategy": "concatenate" +} +``` + +### Streaming Large Files + +```json +{ + "call_template_type": "text", + "file_path": "/var/log/huge.log", + "streaming": true, + "chunk_size": 8192 +} +``` + +## Common Use Cases + +- **Configuration Management**: Reading config files, environment files +- **Log Analysis**: Processing application logs, system logs +- **Documentation**: Accessing README files, API docs, manuals +- **Data Processing**: Reading CSV, JSON, XML text files +- **Template Processing**: Reading template files for generation +- **Code Analysis**: Reading source code files for analysis +- **Monitoring**: Reading status files, health check files + +## Performance Considerations + +| File Size | Recommended Approach | +|-----------|---------------------| +| < 1MB | Read entire file | +| 1MB - 10MB | Use line ranges | +| 10MB - 100MB | Use streaming | +| > 100MB | Use external tools | + +## Integration Examples + +### With HTTP Protocol + +```json +{ + "name": "process_uploaded_file", + "description": "Process uploaded text file", + "inputs": { + "type": "object", + "properties": { + "file_url": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "text", + "file_path": "${file_url}", + "max_size": 5242880 + } +} +``` + +### With CLI Protocol + +```json +{ + "name": "analyze_log_file", + "description": "Analyze log file with external tool", + "inputs": { + "type": "object", + "properties": { + "log_path": {"type": "string"} + } + }, + "tool_call_template": { + "call_template_type": "cli", + "command": "log-analyzer", + "args": ["--file", "${log_path}", "--format", "json"] + } +} +``` diff --git a/docs/providers/websocket.md b/docs/providers/websocket.md new file mode 100644 index 0000000..f71e3ad --- /dev/null +++ b/docs/providers/websocket.md @@ -0,0 +1,366 @@ +--- +id: websocket +title: WebSocket Protocol +sidebar_position: 2 +--- + +# WebSocket Protocol + +The WebSocket protocol plugin (`utcp-websocket`) enables UTCP to communicate with WebSocket servers for real-time, bidirectional communication. This is ideal for tools that require persistent connections or real-time updates. + +## Installation + +```bash +pip install utcp-websocket +``` + +## Call Template Structure + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": { + "type": "request", + "action": "${action}", + "data": "${data}" + }, + "connection_timeout": 10, + "response_timeout": 30, + "auth": { + "auth_type": "api_key", + "api_key": "${WS_API_KEY}", + "location": "query" + } +} +``` + +## Configuration Options + +### Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `call_template_type` | string | Must be `"websocket"` | +| `url` | string | WebSocket URL (ws:// or wss://) | +| `message` | object/string | Message to send to the WebSocket | + +### Optional Fields + +| Field | Type | Description | +|-------|------|-------------| +| `headers` | object | HTTP headers for WebSocket handshake | +| `connection_timeout` | number | Connection timeout in seconds (default: 10) | +| `response_timeout` | number | Response timeout in seconds (default: 30) | +| `close_after_response` | boolean | Close connection after receiving response (default: true) | +| `expected_responses` | number | Number of expected response messages (default: 1) | +| `ping_interval` | number | Ping interval in seconds (default: 30) | + +## Authentication + +WebSocket authentication can be handled in several ways: + +### Query Parameter Authentication + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "token", + "location": "query" + } +} +``` + +### Header Authentication + +```json +{ + "auth": { + "auth_type": "api_key", + "api_key": "${API_KEY}", + "var_name": "Authorization", + "location": "header" + } +} +``` + +### Message-based Authentication + +```json +{ + "message": { + "type": "auth", + "token": "${API_KEY}" + } +} +``` + +## Message Formats + +### JSON Messages + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": { + "id": "{{uuid}}", + "method": "getData", + "params": { + "query": "${query}", + "limit": 10 + } + } +} +``` + +### Text Messages + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": "GET_DATA:${query}" +} +``` + +### Binary Messages + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": { + "type": "binary", + "data": "${base64_data}" + } +} +``` + +## Examples + +### Real-time Data Subscription + +```json +{ + "name": "subscribe_stock_price", + "description": "Subscribe to real-time stock price updates", + "inputs": { + "type": "object", + "properties": { + "symbol": {"type": "string"}, + "duration": {"type": "number", "default": 60} + }, + "required": ["symbol"] + }, + "tool_call_template": { + "call_template_type": "websocket", + "url": "wss://api.stocks.com/ws", + "message": { + "action": "subscribe", + "symbol": "${symbol}", + "type": "price" + }, + "response_timeout": "${duration}", + "expected_responses": -1, + "close_after_response": false + } +} +``` + +### Chat Bot Integration + +```json +{ + "name": "send_chat_message", + "description": "Send a message to the chat bot", + "inputs": { + "type": "object", + "properties": { + "message": {"type": "string"}, + "user_id": {"type": "string"} + }, + "required": ["message", "user_id"] + }, + "tool_call_template": { + "call_template_type": "websocket", + "url": "wss://chat.example.com/ws", + "message": { + "type": "message", + "user_id": "${user_id}", + "content": "${message}", + "timestamp": "{{now}}" + }, + "headers": { + "Authorization": "Bearer ${CHAT_TOKEN}" + } + } +} +``` + +### IoT Device Control + +```json +{ + "name": "control_device", + "description": "Send control commands to IoT device", + "inputs": { + "type": "object", + "properties": { + "device_id": {"type": "string"}, + "command": {"type": "string"}, + "value": {"type": "number"} + }, + "required": ["device_id", "command"] + }, + "tool_call_template": { + "call_template_type": "websocket", + "url": "wss://iot.example.com/device/${device_id}", + "message": { + "command": "${command}", + "value": "${value}", + "timestamp": "{{now}}" + }, + "connection_timeout": 5, + "response_timeout": 10 + } +} +``` + +## Connection Management + +### Persistent Connections + +For tools that need to maintain persistent connections: + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": {"action": "ping"}, + "close_after_response": false, + "ping_interval": 30 +} +``` + +### Connection Pooling + +The WebSocket protocol automatically manages connection pooling for efficiency: + +- Reuses connections to the same endpoint +- Handles connection lifecycle automatically +- Implements reconnection logic for dropped connections + +## Response Handling + +### Single Response + +```json +{ + "expected_responses": 1, + "close_after_response": true +} +``` + +### Multiple Responses + +```json +{ + "expected_responses": 5, + "response_timeout": 60 +} +``` + +### Streaming Responses + +```json +{ + "expected_responses": -1, + "response_timeout": 300, + "close_after_response": false +} +``` + +## Error Handling + +| Error Type | Description | Handling | +|------------|-------------|----------| +| Connection Failed | Cannot establish WebSocket connection | Raise `WebSocketConnectionError` | +| Authentication Failed | WebSocket handshake authentication failed | Raise `WebSocketAuthError` | +| Timeout | No response within timeout period | Raise `WebSocketTimeoutError` | +| Protocol Error | Invalid WebSocket protocol usage | Raise `WebSocketProtocolError` | +| Connection Closed | Server closed connection unexpectedly | Raise `WebSocketClosedError` | + +## Best Practices + +1. **Use Secure WebSockets**: Always use `wss://` for production +2. **Handle Reconnections**: Implement retry logic for connection failures +3. **Set Appropriate Timeouts**: Configure timeouts based on expected response times +4. **Validate Messages**: Validate both outgoing and incoming messages +5. **Monitor Connections**: Track connection health and performance +6. **Implement Heartbeats**: Use ping/pong for connection health checks +7. **Handle Backpressure**: Manage message queuing for high-throughput scenarios + +## Advanced Features + +### Message Filtering + +Filter incoming messages based on criteria: + +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "message": {"subscribe": "all"}, + "message_filter": { + "type": "stock_price", + "symbol": "${symbol}" + } +} +``` + +### Custom Headers + +Include custom headers in the WebSocket handshake: + +```json +{ + "headers": { + "User-Agent": "UTCP-Client/1.0", + "X-Client-ID": "${CLIENT_ID}", + "Authorization": "Bearer ${TOKEN}" + } +} +``` + +### Compression + +Enable WebSocket compression: + +```json +{ + "compression": "deflate", + "compression_threshold": 1024 +} +``` + +## Common Use Cases + +- **Real-time Data**: Stock prices, sensor data, live metrics +- **Chat Applications**: Messaging, notifications, presence +- **Gaming**: Real-time game state, multiplayer coordination +- **IoT Control**: Device commands, status updates +- **Live Updates**: News feeds, social media streams +- **Collaborative Tools**: Document editing, shared whiteboards + +## Protocol Comparison + +| Feature | WebSocket | HTTP | SSE | +|---------|-----------|------|-----| +| Bidirectional | ✅ | ❌ | ❌ | +| Real-time | ✅ | ❌ | ✅ | +| Persistent | ✅ | ❌ | ✅ | +| Overhead | Low | High | Medium | +| Complexity | Medium | Low | Low | diff --git a/docs/security.md b/docs/security.md index b182938..de5d007 100644 --- a/docs/security.md +++ b/docs/security.md @@ -6,123 +6,647 @@ sidebar_position: 6 # Security Considerations -Security is a critical aspect of any protocol that enables tool access and execution. This section outlines key security considerations when implementing and using UTCP. +:::info Language Examples +This guide uses **Python** examples for implementation code. UTCP security principles apply to all language implementations - check the [UTCP GitHub organization](https://github.com/universal-tool-calling-protocol) for language-specific security examples. +::: + +Security is critical when enabling direct tool access through UTCP. This guide covers security considerations specific to UTCP's "manual" approach and provides practical guidance for secure implementations. + +## UTCP Security Model + +### Direct Communication Implications + +UTCP's direct communication model has unique security characteristics: + +**Advantages:** +- No middleman to compromise +- Native security controls remain intact +- Reduced attack surface (no proxy servers) +- Existing monitoring and logging continue to work + +**Considerations:** +- Clients must handle multiple authentication methods +- Manual endpoints become discovery targets +- Variable substitution introduces injection risks + +### Manual Discovery Security + +Secure your UTCP manual endpoints: + +```python +from fastapi import FastAPI, HTTPException, Depends +from fastapi.security import HTTPBearer -## Authentication +app = FastAPI() +security = HTTPBearer() -UTCP supports several authentication methods across different communication protocol types: +@app.get("/utcp") +def get_manual(token: str = Depends(security)): + # Validate discovery access + if not validate_discovery_token(token.credentials): + raise HTTPException(401, "Invalid discovery token") + + return { + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": get_authorized_tools(token.credentials) + } -### API Key Authentication +def get_authorized_tools(token: str): + # Return only tools the client is authorized to see + user_permissions = get_user_permissions(token) + return filter_tools_by_permissions(all_tools, user_permissions) +``` + +## Authentication & Authorization + +### Enhanced Authentication Examples + +#### API Key with Rotation ```json { "auth": { "auth_type": "api_key", - "api_key": "YOUR_API_KEY", + "api_key": "${API_KEY}", "var_name": "X-API-Key", "location": "header" } } ``` -The `location` field specifies where the API key is placed, and can be `header`, `query`, or `cookie`. +**Secure implementation:** +```python +import os +from datetime import datetime, timedelta + +class RotatingAPIKey: + def __init__(self): + self.current_key = os.getenv("API_KEY_CURRENT") + self.next_key = os.getenv("API_KEY_NEXT") + self.rotation_time = datetime.fromisoformat(os.getenv("KEY_ROTATION_TIME")) + + def get_valid_key(self): + if datetime.now() > self.rotation_time: + return self.next_key + return self.current_key +``` -### Basic Authentication +#### OAuth2 with Scope Validation ```json { "auth": { - "auth_type": "basic", - "username": "user", - "password": "pass" + "auth_type": "oauth2", + "client_id": "${CLIENT_ID}", + "client_secret": "${CLIENT_SECRET}", + "token_url": "https://auth.example.com/token", + "scope": "tools:read tools:execute" } } ``` -### OAuth2 Authentication +#### Per-Tool Authorization ```json { - "auth": { - "auth_type": "oauth2", - "client_id": "YOUR_CLIENT_ID", - "client_secret": "YOUR_CLIENT_SECRET", - "token_url": "https://auth.example.com/token", - "scope": "read:tools" + "name": "admin_tool", + "description": "Administrative operations", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/admin/action", + "http_method": "POST", + "auth": { + "auth_type": "api_key", + "api_key": "${ADMIN_TOKEN}", + "var_name": "Authorization", + "location": "header" + } } } ``` -The `scope` field is optional and specifies the level of access that the client is requesting. +## Protocol-Specific Security -## Tool Access Control +### HTTP/HTTPS Security -When exposing tools through UTCP, consider implementing these access controls: - -1. **Tool-Level Permissions**: Define which users/agents can access specific tools -2. **Parameter Constraints**: Restrict parameter values to prevent abuse -3. **Rate Limiting**: Implement per-user/per-tool rate limits -4. **Usage Quotas**: Set maximum usage quotas for tools -5. **Audit Logging**: Log all tool calls for security monitoring +**Required configurations:** +```json +{ + "call_template_type": "http", + "url": "https://api.example.com/endpoint", + "verify_ssl": true, + "timeout": 30, + "headers": { + "User-Agent": "UTCP-Client/1.0", + "X-Request-ID": "${request_id}" + } +} +``` -## Communication Protocol-Specific Considerations +**Security checklist:** +- ✅ Always use HTTPS in production +- ✅ Validate SSL certificates (`verify_ssl: true`) +- ✅ Set appropriate timeouts +- ✅ Include request tracking headers +- ✅ Implement retry limits -### HTTP +### WebSocket Security -- Always use HTTPS, never HTTP +**Secure WebSocket configuration:** +```json +{ + "call_template_type": "websocket", + "url": "wss://api.example.com/ws", + "headers": { + "Authorization": "Bearer ${WS_TOKEN}", + "Origin": "https://trusted-domain.com" + }, + "ping_interval": 30, + "connection_timeout": 10 +} +``` -### CLI +**Security measures:** +- ✅ Use WSS (secure WebSocket) only +- ✅ Validate Origin headers +- ✅ Implement connection timeouts +- ✅ Use heartbeat/ping for connection health +- ✅ Limit concurrent connections per client -:::important +### CLI Security -CLI poses a significant security risk as it executes commands on the local system. +:::danger High Risk Protocol +CLI execution poses significant security risks. Use with extreme caution. ::: -- Validate and sanitize all input parameters -- Run commands with the minimum necessary permissions -- Implement allow-lists for permitted commands -- Sandbox execution environments when possible +**Secure CLI implementation:** +```json +{ + "call_template_type": "cli", + "command": "/usr/local/bin/safe-script", + "args": ["--input", "${sanitized_input}"], + "working_directory": "/safe/sandbox", + "environment": { + "PATH": "/usr/local/bin:/usr/bin", + "HOME": "/tmp/sandbox" + }, + "timeout": 30, + "allowed_exit_codes": [0] +} +``` -### WebSocket +**Security requirements:** +- ✅ Use absolute paths for commands +- ✅ Sanitize all input parameters +- ✅ Run in sandboxed environments +- ✅ Limit environment variables +- ✅ Set strict timeouts +- ✅ Validate exit codes +- ✅ Use minimal user permissions + +**Input sanitization example:** +```python +import re +import shlex + +def sanitize_cli_input(input_value: str) -> str: + # Remove dangerous characters + sanitized = re.sub(r'[;&|`$(){}[\]<>]', '', input_value) + # Escape for shell safety + return shlex.quote(sanitized) +``` -- Use secure WebSocket (WSS) connections +### Server-Sent Events (SSE) Security -## Data Protection +**Secure SSE configuration:** +```json +{ + "call_template_type": "sse", + "url": "https://api.example.com/events", + "headers": { + "Authorization": "Bearer ${SSE_TOKEN}", + "Accept": "text/event-stream", + "Cache-Control": "no-cache" + }, + "timeout": 300, + "max_events": 1000 +} +``` -1. **Data in Transit**: Ensure all communications use TLS 1.2+ encryption -2. **Data at Rest**: Encrypt sensitive configuration data -3. **Sensitive Data in Logs**: Prevent logging of sensitive parameters -4. **PII Handling**: Implement proper controls for personal information +**Security considerations:** +- ✅ Authenticate SSE connections +- ✅ Set maximum event limits +- ✅ Implement connection timeouts +- ✅ Validate event data format +- ✅ Monitor for event flooding -## Secure Implementation Checklist +### Text Protocol Security -- [ ] Use HTTPS/WSS for all network communications -- [ ] Implement proper authentication for all communication protocols -- [ ] Validate all input against schemas before processing -- [ ] Sanitize inputs to prevent injection attacks -- [ ] Implement rate limiting to prevent abuse -- [ ] Set appropriate timeouts for all operations -- [ ] Log security-relevant events -- [ ] Regularly update dependencies -- [ ] Implement proper error handling that doesn't leak sensitive information +**Secure file access:** +```json +{ + "call_template_type": "text", + "file_path": "/safe/data/${filename}", + "max_size": 1048576, + "allowed_paths": ["/safe/data/"], + "encoding": "utf-8" +} +``` + +**Security measures:** +- ✅ Restrict file paths to safe directories +- ✅ Set maximum file size limits +- ✅ Validate file extensions +- ✅ Prevent directory traversal attacks +- ✅ Use safe encoding handling + +**Path validation example:** +```python +import os +from pathlib import Path + +def validate_file_path(file_path: str, allowed_dirs: list) -> bool: + try: + # Resolve to absolute path + abs_path = Path(file_path).resolve() + + # Check if path is within allowed directories + for allowed_dir in allowed_dirs: + if abs_path.is_relative_to(Path(allowed_dir).resolve()): + return True + return False + except (OSError, ValueError): + return False +``` + +### MCP Security + +**Secure MCP server configuration:** +```json +{ + "call_template_type": "mcp", + "server_config": { + "command": "/usr/local/bin/mcp-server", + "args": ["--config", "/safe/config.json"], + "env": { + "MCP_LOG_LEVEL": "INFO" + }, + "timeout": 60 + } +} +``` + +**Security considerations:** +- ✅ Use trusted MCP server implementations +- ✅ Sandbox MCP server processes +- ✅ Limit server resource usage +- ✅ Monitor server health and logs +- ✅ Implement server restart policies -## Common Vulnerabilities to Avoid +## Input Validation & Sanitization -| Vulnerability | Prevention | -|--------------|------------| -| Injection Attacks | Validate and sanitize all inputs | -| Credential Leakage | Use secure credential storage | -| Excessive Permissions | Follow the principle of least privilege | -| Man-in-the-Middle | Use certificate validation and pinning | -| Denial of Service | Implement rate limiting and timeouts | -| Information Disclosure | Ensure errors don't leak sensitive data | +### JSON Schema Validation + +**Comprehensive input validation:** +```json +{ + "inputs": { + "type": "object", + "properties": { + "email": { + "type": "string", + "format": "email", + "maxLength": 254 + }, + "age": { + "type": "integer", + "minimum": 0, + "maximum": 150 + }, + "tags": { + "type": "array", + "items": {"type": "string", "pattern": "^[a-zA-Z0-9_-]+$"}, + "maxItems": 10 + } + }, + "required": ["email"], + "additionalProperties": false + } +} +``` -## Secure Development Lifecycle +### Parameter Sanitization + +**Server-side validation:** +```python +from pydantic import BaseModel, validator, Field +import re + +class ToolInput(BaseModel): + user_id: str = Field(..., regex=r'^[a-zA-Z0-9_-]+$', max_length=50) + query: str = Field(..., max_length=1000) + + @validator('query') + def sanitize_query(cls, v): + # Remove potentially dangerous characters + return re.sub(r'[<>"\']', '', v).strip() +``` -1. **Design**: Conduct threat modeling during protocol design -2. **Implementation**: Follow secure coding practices -3. **Testing**: Perform security testing and code reviews -4. **Deployment**: Use secure deployment practices -5. **Maintenance**: Monitor for security issues and update regularly +## Secure Variable Handling + +### Environment Variable Security + +**Secure variable loading:** +```python +import os +from typing import Dict, Optional + +class SecureVariableLoader: + def __init__(self, allowed_prefixes: list = None): + self.allowed_prefixes = allowed_prefixes or ['UTCP_', 'API_'] + + def load_variable(self, var_name: str) -> Optional[str]: + # Only load variables with allowed prefixes + if not any(var_name.startswith(prefix) for prefix in self.allowed_prefixes): + raise ValueError(f"Variable {var_name} not allowed") + + return os.getenv(var_name) + + def substitute_variables(self, template: str, context: Dict[str, str]) -> str: + # Safely substitute variables + for var_name, value in context.items(): + if self.is_safe_variable(var_name, value): + template = template.replace(f"${{{var_name}}}", value) + return template + + def is_safe_variable(self, name: str, value: str) -> bool: + # Validate variable safety + if len(value) > 10000: # Prevent extremely long values + return False + if any(char in value for char in ['<', '>', '"', "'"]): # Basic XSS prevention + return False + return True +``` + +### Runtime Variable Substitution + +**Secure substitution:** +```python +import re +from typing import Dict + +def secure_substitute(template: str, variables: Dict[str, str]) -> str: + def replace_var(match): + var_name = match.group(1) + if var_name in variables: + value = variables[var_name] + # Validate and sanitize the value + if is_safe_value(value): + return value + else: + raise ValueError(f"Unsafe variable value for {var_name}") + else: + raise ValueError(f"Variable {var_name} not found") + + # Only replace variables with the expected pattern + return re.sub(r'\$\{([a-zA-Z_][a-zA-Z0-9_]*)\}', replace_var, template) + +def is_safe_value(value: str) -> bool: + # Implement your safety checks + return len(value) < 1000 and not any(c in value for c in ['<', '>', '"', "'", ';', '&']) +``` + +## Network & Transport Security + +### TLS Configuration + +**Minimum TLS requirements:** +```python +import ssl +import httpx + +# Configure secure HTTP client +client = httpx.AsyncClient( + verify=True, # Verify SSL certificates + timeout=30.0, + limits=httpx.Limits(max_connections=100, max_keepalive_connections=20) +) + +# For custom SSL context +ssl_context = ssl.create_default_context() +ssl_context.minimum_version = ssl.TLSVersion.TLSv1_2 +ssl_context.check_hostname = True +ssl_context.verify_mode = ssl.CERT_REQUIRED +``` + +### Certificate Validation + +**Enhanced certificate validation:** +```python +import ssl +import certifi + +def create_secure_context(): + context = ssl.create_default_context(cafile=certifi.where()) + context.check_hostname = True + context.verify_mode = ssl.CERT_REQUIRED + context.minimum_version = ssl.TLSVersion.TLSv1_2 + + # Disable weak ciphers + context.set_ciphers('ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:!aNULL:!MD5:!DSS') + + return context +``` + +## Monitoring & Incident Response + +### Security Logging + +**Comprehensive security logging:** +```python +import logging +import json +from datetime import datetime + +class SecurityLogger: + def __init__(self): + self.logger = logging.getLogger('utcp.security') + + def log_tool_call(self, tool_name: str, user_id: str, success: bool, **kwargs): + log_entry = { + 'timestamp': datetime.utcnow().isoformat(), + 'event_type': 'tool_call', + 'tool_name': tool_name, + 'user_id': user_id, + 'success': success, + 'metadata': kwargs + } + self.logger.info(json.dumps(log_entry)) + + def log_auth_failure(self, tool_name: str, reason: str, **kwargs): + log_entry = { + 'timestamp': datetime.utcnow().isoformat(), + 'event_type': 'auth_failure', + 'tool_name': tool_name, + 'reason': reason, + 'metadata': kwargs + } + self.logger.warning(json.dumps(log_entry)) +``` + +### Anomaly Detection + +**Basic anomaly detection:** +```python +from collections import defaultdict +from datetime import datetime, timedelta + +class AnomalyDetector: + def __init__(self): + self.call_counts = defaultdict(list) + self.rate_limits = { + 'calls_per_minute': 60, + 'calls_per_hour': 1000 + } + + def check_rate_limit(self, user_id: str) -> bool: + now = datetime.utcnow() + user_calls = self.call_counts[user_id] + + # Clean old entries + user_calls[:] = [call_time for call_time in user_calls + if now - call_time < timedelta(hours=1)] + + # Check limits + recent_calls = [call_time for call_time in user_calls + if now - call_time < timedelta(minutes=1)] + + if len(recent_calls) >= self.rate_limits['calls_per_minute']: + return False + if len(user_calls) >= self.rate_limits['calls_per_hour']: + return False + + # Record this call + user_calls.append(now) + return True +``` + +## Security Testing & Validation + +### Testing Methodologies + +**Security test examples:** +```python +import pytest +from utcp.utcp_client import UtcpClient + +@pytest.mark.asyncio +async def test_injection_prevention(): + client = await UtcpClient.create(config=test_config) + + # Test SQL injection attempt + malicious_input = "'; DROP TABLE users; --" + + with pytest.raises(ValueError, match="Invalid input"): + await client.call_tool("db.query", {"query": malicious_input}) + +@pytest.mark.asyncio +async def test_path_traversal_prevention(): + client = await UtcpClient.create(config=test_config) + + # Test directory traversal attempt + malicious_path = "../../../etc/passwd" + + with pytest.raises(ValueError, match="Path not allowed"): + await client.call_tool("file.read", {"path": malicious_path}) + +@pytest.mark.asyncio +async def test_rate_limiting(): + client = await UtcpClient.create(config=test_config) + + # Test rate limiting + for i in range(100): # Exceed rate limit + try: + await client.call_tool("api.test", {}) + except Exception as e: + assert "rate limit" in str(e).lower() + break + else: + pytest.fail("Rate limiting not enforced") +``` + +### Security Automation + +**Automated security checks:** +```python +def validate_manual_security(manual: dict) -> list: + issues = [] + + for tool in manual.get('tools', []): + call_template = tool.get('tool_call_template', {}) + + # Check for HTTP over HTTPS + if call_template.get('call_template_type') == 'http': + url = call_template.get('url', '') + if url.startswith('http://'): + issues.append(f"Tool {tool['name']}: Uses insecure HTTP") + + # Check for hardcoded credentials + template_str = str(call_template) + if any(keyword in template_str.lower() for keyword in ['password', 'secret', 'key']): + if not any(var in template_str for var in ['${', '${']): + issues.append(f"Tool {tool['name']}: May contain hardcoded credentials") + + # Check for CLI security + if call_template.get('call_template_type') == 'cli': + command = call_template.get('command', '') + if not command.startswith('/'): + issues.append(f"Tool {tool['name']}: CLI command should use absolute path") + + return issues +``` -By following these security considerations, UTCP implementations can minimize risks while enabling powerful tool integrations across various communication protocols. +## Security Checklist + +### Tool Provider Security + +- [ ] UTCP manual endpoint requires authentication +- [ ] All tool endpoints use HTTPS/WSS +- [ ] Input validation implemented for all tools +- [ ] Rate limiting configured per user/tool +- [ ] Security logging enabled +- [ ] Credentials stored securely (not hardcoded) +- [ ] SSL certificate validation enabled +- [ ] Appropriate timeouts configured +- [ ] Error messages don't leak sensitive information + +### Tool Consumer Security + +- [ ] Variable substitution is sanitized +- [ ] SSL certificate verification enabled +- [ ] Connection timeouts configured +- [ ] Rate limiting respected +- [ ] Security events logged +- [ ] Credentials rotated regularly +- [ ] Network connections monitored +- [ ] Input validation before tool calls + +### Protocol-Specific Security + +- [ ] **HTTP**: HTTPS only, certificate validation +- [ ] **WebSocket**: WSS only, origin validation +- [ ] **CLI**: Sandboxed execution, input sanitization +- [ ] **SSE**: Authenticated connections, event limits +- [ ] **Text**: Path validation, size limits +- [ ] **MCP**: Trusted servers, resource limits + +By following these security guidelines, you can safely implement UTCP while maintaining strong security posture across all communication protocols. + +For protocol-specific security details, see: +- [HTTP Security](./providers/http.md#security-considerations) +- [WebSocket Security](./providers/websocket.md#security-considerations) +- [CLI Security](./providers/cli.md#security-considerations) +- [SSE Security](./providers/sse.md#security-considerations) +- [Text Security](./providers/text.md#security-considerations) +- [MCP Security](./providers/mcp.md#security-considerations) diff --git a/docs/utcp-vs-mcp.md b/docs/utcp-vs-mcp.md index 3dec676..099f0d9 100644 --- a/docs/utcp-vs-mcp.md +++ b/docs/utcp-vs-mcp.md @@ -4,99 +4,562 @@ title: UTCP vs MCP sidebar_position: 5 --- -# UTCP vs MCP tool calling: A Comparison +# UTCP vs MCP: A Comprehensive Comparison -This page compares the Universal Tool Calling Protocol (UTCP) with the Model Context Protocol's (MCP) tool calling functionality, highlighting their different approaches to agent-tool integration. +:::info Language Examples +This comparison uses **Python** examples. Both UTCP and MCP have implementations in multiple languages - check respective GitHub organizations for language-specific examples. +::: + +This guide compares the Universal Tool Calling Protocol (UTCP) with the Model Context Protocol (MCP), helping you choose the right approach for your AI tool integration needs. ## Video Overview -## Architectural Differences - -| Aspect | MCP | UTCP | -|--------|-----|------| -| **Core Model** | Middleman | Manual | -| **Architecture** | Agents ↔ MCP Server ↔ Tool | Agent ↔ Tool (Direct) | -| **Integration Approach** | Wraps existing tools | Describes how to call existing tools | -| **Network Hops** | Double (Agent → MCP → Tool) | Single (Agent → Tool) | -| **Protocol Dependency** | Hard dependency on protocol for every call | Protocol only needed during discovery | - -## The Middleman vs Manual Philosophies +## Quick Comparison -### The MCP "Middleman" Approach +| Aspect | UTCP | MCP | +|--------|------|-----| +| **Philosophy** | Manual (describes how to call tools) | Middleman (wraps tools in protocol) | +| **Architecture** | Agent → Tool (Direct) | Agent → MCP Server → Tool | +| **Infrastructure** | None required | Wrapper servers needed | +| **Protocols** | HTTP, WebSocket, CLI, SSE, etc. | JSON-RPC over stdio/HTTP | +| **Performance** | Native tool performance | Additional proxy overhead | +| **Maintenance** | Minimal | High (server maintenance) | -MCP positions itself as the "USB-C for AI Agents" — a universal adapter that all tools must plug into. This approach: - -- Forces all traffic through a new protocol layer -- Requires writing "wrappers" for existing tools -- Needs to reinvent solutions for auth, security, and other infrastructure concerns - -This creates what we call the "wrapper tax": the additional infrastructure, development, and maintenance overhead required to adapt existing tools to work with MCP. - -### The UTCP "Manual" Approach +## Architectural Differences -UTCP takes a different approach — it's a "manual" that describes how to call tools directly: +### UTCP: The "Manual" Approach -- Provides all necessary information to call native APIs directly -- Gets out of the way after tool discovery -- Leverages existing infrastructure for auth, security, etc. +UTCP provides a standardized way to describe how to call existing tools directly: -This eliminates the wrapper tax and allows organizations to expose their existing APIs to AI agents without building and maintaining additional infrastructure. +```json +{ + "manual_version": "1.0.0", + "utcp_version": "1.0.1", + "tools": [{ + "name": "get_weather", + "description": "Get current weather", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.weather.com/current", + "http_method": "GET", + "query_params": {"location": "${location}"} + } + }] +} +``` + +**Flow:** Agent discovers manual → Agent calls tool directly + +### MCP: The "Middleman" Approach + +MCP requires building servers that wrap your tools: + +```python +# MCP Server (required infrastructure) +from mcp.server import Server +from mcp.types import Tool + +server = Server("weather-server") + +@server.list_tools() +async def list_tools(): + return [Tool(name="get_weather", description="Get weather")] + +@server.call_tool() +async def call_tool(name: str, arguments: dict): + if name == "get_weather": + # Call actual weather API + return await weather_api.get(arguments["location"]) +``` + +**Flow:** Agent → MCP Server → Tool → MCP Server → Agent + +## Technical Comparison + +### Performance Impact + +#### UTCP Performance +```python +# Direct API call - no overhead +import httpx + +async def call_weather_tool(): + async with httpx.AsyncClient() as client: + response = await client.get( + "https://api.weather.com/current", + params={"location": "San Francisco"} + ) + return response.json() + +# Latency: ~100ms (API response time only) +``` + +#### MCP Performance +```python +# Requires MCP server proxy +import mcp + +async def call_weather_tool(): + client = mcp.Client() + await client.connect("weather-server") + result = await client.call_tool("get_weather", {"location": "San Francisco"}) + return result + +# Latency: ~150ms (API + MCP server overhead) +``` + +### Infrastructure Requirements + +#### UTCP Infrastructure +```python +# Add one endpoint to existing API +@app.get("/utcp") +def get_manual(): + return utcp_manual # Static JSON + +# Total infrastructure: 0 additional servers +``` + +#### MCP Infrastructure +```python +# Requires dedicated MCP server +# Plus process management, monitoring, scaling +# Plus client connection management + +# Total infrastructure: N MCP servers (one per tool provider) +``` + +### Protocol Support Comparison + +#### UTCP Protocol Flexibility +```json +{ + "tools": [ + { + "name": "http_tool", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/data" + } + }, + { + "name": "websocket_tool", + "tool_call_template": { + "call_template_type": "websocket", + "url": "wss://api.example.com/ws" + } + }, + { + "name": "cli_tool", + "tool_call_template": { + "call_template_type": "cli", + "command": "git", + "args": ["status"] + } + } + ] +} +``` + +#### MCP Protocol Limitation +```python +# MCP only supports JSON-RPC over stdio/HTTP +# All tools must be wrapped in MCP servers +# Cannot directly call WebSocket, CLI, or other protocols +``` ## Feature Comparison -| Feature | MCP | UTCP | -|---------|-----|------| -| **Tool Discovery** | Via MCP Server | Via manual discovery endpoint | -| **Protocol Support** | HTTP Streaming | HTTP, WebSockets, gRPC, CLI, etc. | -| **Authentication** | Handled by MCP Server | Uses tool's native authentication | -| **Streaming** | Native support | Supported via appropriate communication protocol (SSE, WebSockets) | -| **Implementation Complexity** | High (requires wrapper servers) | Low (simple JSON definitions) | -| **Performance** | Additional overhead due to proxy | Direct, native performance | -| **Evolution Speed** | Slow (all participants must update) | Fast (individual communication protocols can evolve independently) | - -## When to Choose Each Protocol - -### Choose MCP When: - -- You need strict standardization across all tools -- You're building a closed ecosystem where you control all components -- You're willing to invest in building and maintaining wrapper servers +### Authentication & Security + +#### UTCP: Native Authentication +```json +{ + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.example.com/data", + "auth": { + "auth_type": "oauth2", + "client_id": "${CLIENT_ID}", + "client_secret": "${CLIENT_SECRET}", + "token_url": "https://auth.example.com/token" + } + } +} +``` + +**Benefits:** +- Uses existing authentication systems +- No credential translation needed +- Native rate limiting and monitoring +- Existing security policies apply + +#### MCP: Server-Mediated Authentication +```python +# MCP server must handle auth translation +class WeatherMCPServer: + def __init__(self, api_key): + self.api_key = api_key # Server stores credentials + + async def call_tool(self, name, args): + # Server makes authenticated call + return await weather_api.get(args["location"], auth=self.api_key) +``` + +**Challenges:** +- Credential management in MCP servers +- Additional security layer to maintain +- Auth translation complexity + +### Streaming & Real-time Data + +#### UTCP: Native Streaming Support +```json +{ + "name": "stream_logs", + "tool_call_template": { + "call_template_type": "sse", + "url": "https://api.example.com/logs/stream", + "timeout": 300 + } +} +``` + +#### MCP: Limited Streaming +```python +# MCP has basic streaming but requires server implementation +# More complex to set up and maintain +``` + +### Error Handling + +#### UTCP: Native Error Responses +```python +# Errors come directly from the tool +try: + result = await client.call_tool("api.get_data", {"id": "123"}) +except httpx.HTTPStatusError as e: + # Native HTTP error with full context + print(f"API returned {e.response.status_code}: {e.response.text}") +``` + +#### MCP: Wrapped Error Responses +```python +# Errors are wrapped by MCP server +try: + result = await mcp_client.call_tool("get_data", {"id": "123"}) +except MCPError as e: + # MCP error - original context may be lost + print(f"MCP error: {e.message}") +``` + +## Migration & Interoperability + +### Migrating from MCP to UTCP + +UTCP provides an MCP plugin for gradual migration: + +```python +# Phase 1: Use existing MCP servers via UTCP +client = await UtcpClient.create(config={ + "manual_call_templates": [{ + "name": "legacy_mcp_service", + "call_template_type": "mcp", + "server_config": { + "command": "node", + "args": ["existing-mcp-server.js"] + } + }] +}) + +# Phase 2: Migrate high-value tools to native UTCP +# Phase 3: Deprecate MCP servers +``` + +[**Complete migration guide →**](./providers/mcp.md) + +### Hybrid Approach + +You can use both protocols simultaneously: + +```python +client = await UtcpClient.create(config={ + "manual_call_templates": [ + { + "name": "native_api", + "call_template_type": "http", + "url": "https://api.example.com/utcp" + }, + { + "name": "legacy_mcp", + "call_template_type": "mcp", + "server_config": {"command": "mcp-server"} + } + ] +}) + +# Call native UTCP tool +result1 = await client.call_tool("native_api.get_data", {}) + +# Call MCP tool through UTCP +result2 = await client.call_tool("legacy_mcp.mcp_tool", {}) +``` + +## Enterprise Decision Factors + +### Total Cost of Ownership + +#### UTCP TCO +``` +Infrastructure: $0 (uses existing APIs) +Development: Low (add one endpoint) +Maintenance: Minimal (static JSON) +Scaling: Automatic (scales with existing API) +Monitoring: Existing tools work +``` + +#### MCP TCO +``` +Infrastructure: High (dedicated servers) +Development: High (build wrapper servers) +Maintenance: High (server management) +Scaling: Complex (scale MCP servers separately) +Monitoring: Additional monitoring stack needed +``` + +### Development Velocity + +#### UTCP Development Speed +```python +# Day 1: Add UTCP endpoint +@app.get("/utcp") +def get_manual(): + return {"tools": [...]} + +# Day 2: Tools are available to AI agents +# No additional infrastructure needed +``` + +#### MCP Development Speed +```python +# Week 1-2: Build MCP server +# Week 3: Deploy and configure server +# Week 4: Set up monitoring and scaling +# Week 5: Handle production issues +# Ongoing: Server maintenance +``` + +### Risk Assessment + +| Risk Factor | UTCP | MCP | +|-------------|------|-----| +| **Single Point of Failure** | None (direct calls) | MCP servers | +| **Vendor Lock-in** | Low (standard protocols) | Medium (MCP-specific) | +| **Maintenance Burden** | Low | High | +| **Security Surface** | Minimal | Expanded | +| **Performance Risk** | Low | Medium | + +## Decision Framework ### Choose UTCP When: -- You want to leverage existing APIs without building wrappers -- You need to support diverse communication protocols -- You value direct, efficient communication -- You prioritize low implementation overhead -- You want to minimize infrastructure costs +✅ **You have existing APIs** that work well +✅ **You want minimal infrastructure** overhead +✅ **You need multiple protocols** (HTTP, WebSocket, CLI, etc.) +✅ **You prioritize performance** and direct communication +✅ **You want to leverage existing** auth, monitoring, scaling +✅ **You have limited resources** for server maintenance +✅ **You need rapid deployment** of AI tool access -## Real-World Example +### Choose MCP When: + +✅ **You need strict protocol standardization** across all tools +✅ **You're building a closed ecosystem** with full control +✅ **You have resources** for building and maintaining servers +✅ **You need MCP-specific features** like resources and prompts +✅ **You're already invested** in MCP infrastructure +✅ **You prefer centralized control** over tool access + +### Hybrid Approach When: + +✅ **You're migrating from MCP** to UTCP gradually +✅ **You have mixed requirements** (some tools need MCP features) +✅ **You want to evaluate both** approaches in production +✅ **You have legacy MCP investments** to preserve + +## Real-World Examples + +### E-commerce API Integration + +#### UTCP Approach +```python +# Existing e-commerce API +@app.get("/products/{product_id}") +def get_product(product_id: str): + return {"id": product_id, "name": "Widget", "price": 29.99} + +# Add UTCP discovery +@app.get("/utcp") +def get_manual(): + return { + "tools": [{ + "name": "get_product", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.shop.com/products/${product_id}", + "http_method": "GET" + } + }] + } + +# Total additional code: ~10 lines +# Additional infrastructure: 0 servers +``` + +#### MCP Approach +```python +# Requires building MCP server +from mcp.server import Server + +server = Server("ecommerce-server") + +@server.call_tool() +async def call_tool(name: str, args: dict): + if name == "get_product": + # Call existing API + response = await httpx.get(f"https://api.shop.com/products/{args['product_id']}") + return response.json() + +# Plus: server deployment, monitoring, scaling +# Total additional code: ~50+ lines +# Additional infrastructure: 1+ servers +``` + +### Database Query Tool + +#### UTCP Approach +```json +{ + "name": "query_database", + "tool_call_template": { + "call_template_type": "http", + "url": "https://api.company.com/query", + "http_method": "POST", + "body": {"sql": "${query}"}, + "auth": { + "auth_type": "api_key", + "api_key": "${DB_API_KEY}", + "var_name": "Authorization", + "location": "header" + } + } +} +``` + +#### MCP Approach +```python +# Requires MCP server with database connection +# Plus connection pooling, query validation, etc. +# Much more complex implementation +``` + +## Performance Benchmarks + +### Latency Comparison + +| Scenario | UTCP | MCP | Difference | +|----------|------|-----|------------| +| Simple API call | 50ms | 75ms | +50% overhead | +| Complex query | 200ms | 250ms | +25% overhead | +| File operation | 10ms | 20ms | +100% overhead | +| Streaming data | Real-time | Buffered | Significant delay | + +### Resource Usage + +| Resource | UTCP | MCP | +|----------|------|-----| +| Memory | 0MB (no servers) | 50-200MB per server | +| CPU | 0% (no processing) | 5-15% per server | +| Network | Direct | Double hops | +| Storage | 0GB | Logs, state, config | + +## Migration Timeline + +### From MCP to UTCP + +**Phase 1 (Week 1): Assessment** +- Inventory existing MCP servers +- Identify high-value tools for migration +- Plan migration strategy + +**Phase 2 (Week 2-3): Hybrid Setup** +- Install UTCP with MCP plugin +- Test existing MCP tools through UTCP +- Validate functionality + +**Phase 3 (Week 4-8): Gradual Migration** +- Migrate tools one by one to native UTCP +- Add `/utcp` endpoints to existing APIs +- Update client configurations + +**Phase 4 (Week 9+): Cleanup** +- Deprecate MCP servers +- Remove MCP infrastructure +- Monitor and optimize + +[**Detailed migration guide →**](./migration-v0.1-to-v1.0.md) + +## Community & Ecosystem + +### UTCP Ecosystem +- **Multiple language implementations**: Python, TypeScript, Go, Rust +- **Growing protocol support**: HTTP, WebSocket, CLI, SSE, Text, MCP +- **Active development**: Regular releases and improvements +- **Open governance**: RFC process for changes + +### MCP Ecosystem +- **Anthropic-led development**: Centralized development +- **Growing tool library**: Community-contributed servers +- **IDE integrations**: Claude Desktop, Cline, etc. +- **Established patterns**: Well-documented server patterns -Consider an organization with an existing REST API that they want to expose to AI agents: +## Conclusion -**With MCP:** -1. Build an MCP server that wraps the REST API -2. Translate all calls between MCP format and REST format -3. Maintain and scale this additional server infrastructure -4. Handle authentication translation between MCP and the API +Both UTCP and MCP solve the tool integration problem, but with fundamentally different approaches: -**With UTCP:** -1. Create a simple JSON definition describing the REST API -2. Expose this definition via a discovery endpoint (typically `/utcp`) -3. The AI agent can now call the REST API directly +**UTCP excels when you:** +- Want to leverage existing APIs without additional infrastructure +- Need support for multiple communication protocols +- Prioritize performance and direct communication +- Have limited resources for server maintenance +- Want rapid deployment and minimal complexity -## Code comparison +**MCP excels when you:** +- Need strict protocol standardization +- Are building a controlled ecosystem +- Have resources for server infrastructure +- Need MCP-specific features beyond tool calling +- Prefer centralized tool management -You can find a full typescript example detailing the MCP and UTCP approach [here](https://github.com/universal-tool-calling-protocol/typescript-utcp/tree/main/examples/src/concrete_example). +**For most organizations**, UTCP's "manual" approach offers significant advantages in terms of simplicity, performance, and cost-effectiveness. The ability to expose existing APIs to AI agents with minimal changes and no additional infrastructure makes it an attractive choice for rapid AI tool integration. -## Conclusion +**For gradual adoption**, consider starting with UTCP's MCP plugin to use existing MCP servers while migrating high-value tools to native UTCP protocols over time. -Both MCP and UTCP aim to solve the problem of standardizing tool calling for AI agents, but they take fundamentally different approaches. +## Next Steps -MCP acts as a middleman, requiring all tools to be wrapped in its protocol. This provides standardization but at the cost of additional infrastructure and development overhead. +### To Get Started with UTCP: +1. **[Read the implementation guide](./implementation.md)** - Learn how to implement UTCP +2. **[Choose your protocols](./providers/index.md)** - Select communication methods +3. **[Check examples](https://github.com/universal-tool-calling-protocol/python-utcp/tree/main/examples)** - See real implementations -UTCP acts as a manual, describing how to call tools directly using their native interfaces. This eliminates the wrapper tax and leverages existing infrastructure, at the cost of requiring clients to handle different communication protocols. +### To Migrate from MCP: +1. **[Read the MCP integration guide](./providers/mcp.md)** - Use MCP tools via UTCP +2. **[Plan your migration](./migration-v0.1-to-v1.0.md)** - Step-by-step migration process +3. **[Join the community](https://discord.gg/ZpMbQ8jRbD)** - Get migration support -The choice between them depends on your specific requirements, existing infrastructure, and development resources. However, UTCP's "manual" approach offers significant advantages in terms of simplicity, efficiency, and leveraging existing investments in API infrastructure. +### To Learn More: +- **[UTCP Architecture](./api/index.md)** - Technical deep dive +- **[Security Considerations](./security.md)** - Security best practices +- **[Tool Provider Guide](./for-tool-providers.md)** - Expose your tools diff --git a/sidebars.ts b/sidebars.ts index 2897139..28be80c 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -13,21 +13,55 @@ import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; Create as many sidebars as you want. */ const sidebars: SidebarsConfig = { - // By default, Docusaurus generates a sidebar from the docs folder structure - tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], - - // But you can create a sidebar manually - /* + // Main documentation sidebar tutorialSidebar: [ - 'intro', - 'hello', + 'index', + 'for-tool-providers', + { + type: 'category', + label: 'Communication Protocols', + items: [ + 'providers/index', + 'providers/http', + 'providers/websocket', + 'providers/sse', + 'providers/cli', + 'providers/text', + 'providers/mcp', + ], + }, + 'implementation', + 'security', + 'utcp-vs-mcp', + 'migration-v0.1-to-v1.0', { type: 'category', - label: 'Tutorial', - items: ['tutorial-basics/create-a-document'], + label: 'API Reference', + items: [ + 'api/index', + { + type: 'category', + label: 'Core', + items: [ + { + type: 'autogenerated', + dirName: 'api/core', + }, + ], + }, + { + type: 'category', + label: 'Plugins', + items: [ + { + type: 'autogenerated', + dirName: 'api/plugins', + }, + ], + }, + ], }, ], - */ }; export default sidebars; diff --git a/src/data/contributors.json b/src/data/contributors.json index b65ebae..08a952f 100644 --- a/src/data/contributors.json +++ b/src/data/contributors.json @@ -1,14 +1,14 @@ { - "generated_at": "2025-09-03T19:07:43.751Z", - "total_contributors": 10, - "total_contributions": 505, - "total_impact_score": 359, - "total_recent_activity": 361, + "generated_at": "2025-09-05T06:38:23.414Z", + "total_contributors": 11, + "total_contributions": 521, + "total_impact_score": 375, + "total_recent_activity": 377, "scoring_method": "simplified_recent_activity", - "total_additions": 313932, - "total_deletions": 77961, - "total_changes": 391893, - "total_commits_analyzed": 505, + "total_additions": 321522, + "total_deletions": 78207, + "total_changes": 399729, + "total_commits_analyzed": 521, "contributors": [ { "id": 43811028, @@ -25,7 +25,7 @@ "followers": 22, "following": 2, "created_at": "2018-10-03T10:10:15Z", - "contributions": 169, + "contributions": 179, "repositories": [ "python-utcp", "utcp-specification", @@ -38,18 +38,18 @@ "utcp-agent" ], "repo_count": 9, - "impact_score": 169, - "total_prs": 166, - "total_merged_prs": 133, - "total_recent_commits": 169, - "total_commits": 169, + "impact_score": 179, + "total_prs": 169, + "total_merged_prs": 136, + "total_recent_commits": 179, + "total_commits": 179, "total_reviews": 9, - "last_activity": "2025-09-02T14:45:10Z", + "last_activity": "2025-09-04T12:36:01Z", "pr_success_rate": 80, - "total_additions": 210552, - "total_deletions": 54504, - "total_changes": 265056, - "commits_analyzed": 169 + "total_additions": 213252, + "total_deletions": 54522, + "total_changes": 267774, + "commits_analyzed": 179 }, { "id": 170965471, @@ -63,7 +63,7 @@ "blog": null, "hireable": true, "public_repos": 28, - "followers": 30, + "followers": 31, "following": 59, "created_at": "2024-05-27T16:22:55Z", "contributions": 244, @@ -79,9 +79,9 @@ "total_reviews": 3, "last_activity": "2025-08-29T12:15:29Z", "pr_success_rate": 74, - "total_additions": 39386, - "total_deletions": 11377, - "total_changes": 50763, + "total_additions": 39387, + "total_deletions": 11378, + "total_changes": 50765, "commits_analyzed": 244 }, { @@ -99,7 +99,7 @@ "followers": 3, "following": 3, "created_at": "2020-08-11T00:28:15Z", - "contributions": 38, + "contributions": 42, "repositories": [ "python-utcp", "utcp-specification", @@ -107,18 +107,18 @@ "utcp-mcp" ], "repo_count": 4, - "impact_score": 38, - "total_prs": 68, - "total_merged_prs": 57, - "total_recent_commits": 38, - "total_commits": 38, + "impact_score": 42, + "total_prs": 70, + "total_merged_prs": 60, + "total_recent_commits": 42, + "total_commits": 42, "total_reviews": 0, - "last_activity": "2025-08-21T12:32:23Z", - "pr_success_rate": 84, - "total_additions": 12605, - "total_deletions": 5194, - "total_changes": 17799, - "commits_analyzed": 38 + "last_activity": "2025-09-04T11:01:20Z", + "pr_success_rate": 86, + "total_additions": 17408, + "total_deletions": 5368, + "total_changes": 22776, + "commits_analyzed": 42 }, { "id": 5594869, @@ -144,13 +144,13 @@ ], "repo_count": 4, "impact_score": 24, - "total_prs": 54, + "total_prs": 55, "total_merged_prs": 44, "total_recent_commits": 24, "total_commits": 24, "total_reviews": 0, "last_activity": "2025-09-02T14:17:44Z", - "pr_success_rate": 81, + "pr_success_rate": 80, "total_additions": 12023, "total_deletions": 3946, "total_changes": 15969, @@ -212,13 +212,13 @@ ], "repo_count": 3, "impact_score": 7, - "total_prs": 51, + "total_prs": 52, "total_merged_prs": 43, "total_recent_commits": 7, "total_commits": 7, "total_reviews": 5, "last_activity": "2025-09-02T14:17:22Z", - "pr_success_rate": 84, + "pr_success_rate": 83, "total_additions": 28358, "total_deletions": 916, "total_changes": 29274, @@ -269,7 +269,7 @@ "location": null, "blog": null, "hireable": false, - "public_repos": 17, + "public_repos": 18, "followers": 2, "following": 4, "created_at": "2023-11-08T02:28:44Z", @@ -279,18 +279,51 @@ ], "repo_count": 1, "impact_score": 3, - "total_prs": 20, - "total_merged_prs": 19, + "total_prs": 22, + "total_merged_prs": 22, "total_recent_commits": 4, "total_commits": 4, "total_reviews": 0, "last_activity": "2025-08-01T15:02:36Z", - "pr_success_rate": 95, + "pr_success_rate": 100, "total_additions": 7, "total_deletions": 7, "total_changes": 14, "commits_analyzed": 4 }, + { + "id": 65916846, + "login": "actions-user", + "name": "actions-user", + "avatar_url": "https://avatars.githubusercontent.com/u/65916846?v=4", + "html_url": "https://github.com/actions-user", + "bio": null, + "company": "@actions", + "location": null, + "blog": "https://github.com/actions", + "hireable": false, + "public_repos": 0, + "followers": 3235, + "following": 0, + "created_at": "2020-05-25T17:35:50Z", + "contributions": 2, + "repositories": [ + "utcp-specification" + ], + "repo_count": 1, + "impact_score": 2, + "total_prs": 22, + "total_merged_prs": 22, + "total_recent_commits": 2, + "total_commits": 2, + "total_reviews": 0, + "last_activity": "2025-09-04T13:03:07Z", + "pr_success_rate": 100, + "total_additions": 86, + "total_deletions": 53, + "total_changes": 139, + "commits_analyzed": 2 + }, { "id": 210855846, "login": "aliraza1006", @@ -345,13 +378,13 @@ ], "repo_count": 1, "impact_score": 1, - "total_prs": 6, + "total_prs": 7, "total_merged_prs": 6, "total_recent_commits": 1, "total_commits": 1, "total_reviews": 0, "last_activity": "2025-07-24T16:29:37Z", - "pr_success_rate": 100, + "pr_success_rate": 86, "total_additions": 318, "total_deletions": 0, "total_changes": 318,