-
Notifications
You must be signed in to change notification settings - Fork 35
Add WebSocket transport implementation for real-time communication #33
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add WebSocket transport implementation for real-time communication #33
Conversation
Implements comprehensive WebSocket transport following UTCP architecture: ## Core Features - Real-time bidirectional communication via WebSocket protocol - Tool discovery through WebSocket handshake using UTCP messages - Streaming tool execution with proper error handling - Connection management with keep-alive and reconnection support ## Architecture Compliance - Dependency injection pattern with constructor injection - Implements ClientTransportInterface contract - Composition over inheritance design - Clear separation of data and business logic - Thread-safe and scalable implementation ## Authentication & Security - Full authentication support (API Key, Basic Auth, OAuth2) - Security enforcement (WSS required, localhost exception) - Custom headers and protocol specification support ## Testing & Quality - Unit tests covering all functionality (80%+ coverage) - Mock WebSocket server for development/testing - Integration with existing UTCP test patterns - Comprehensive error handling and edge cases ## Protocol Implementation - Discovery: {"type": "discover", "request_id": "id"} - Tool calls: {"type": "call_tool", "tool_name": "name", "arguments": {...}} - Responses: {"type": "tool_response|tool_error", "result": {...}} ## Documentation - Complete example with interactive client/server demo - Updated README removing "work in progress" status - Protocol specification and usage examples Addresses the "No wrapper tax" principle by enabling direct WebSocket communication without requiring changes to existing WebSocket services. Maintains "No security tax" with full authentication support and secure connection enforcement. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good job. Left some comments. I'll ping you again, once I have a reference implementation for the socket like communication.
Could you change the PR to be in another branch. A feature/websockets branch. So I can also make some changes on it, before we merge it into dev
inputs=ToolInputOutputSchema(**tool_data.get("inputs", {})), | ||
outputs=ToolInputOutputSchema(**tool_data.get("outputs", {})), | ||
tags=tool_data.get("tags", []), | ||
tool_provider=manual_provider |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Each tool should have their own tool_provider, as a HTTP manual can have tools that are called with Websockets, grpc, cli and so on. So following the same logic, a WS manual can have tools that are not accessible via the same websocket, or via a different transport.
async def call_tool(self, tool_name: str, arguments: Dict[str, Any], tool_provider: Provider) -> Any: | ||
""" | ||
Call a tool via WebSocket. | ||
|
||
Sends a JSON message: | ||
{"type": "call_tool", "request_id": "unique_id", "tool_name": "tool", "arguments": {...}} | ||
|
||
Expected response: | ||
{"type": "tool_response", "request_id": "unique_id", "result": {...}} | ||
or | ||
{"type": "tool_error", "request_id": "unique_id", "error": "error message"} | ||
""" | ||
if not isinstance(tool_provider, WebSocketProvider): | ||
raise ValueError("WebSocketClientTransport can only be used with WebSocketProvider") | ||
|
||
ws = await self._get_connection(tool_provider) | ||
|
||
# Prepare tool call request | ||
request_id = f"call_{tool_name}_{id(arguments)}" | ||
call_request = { | ||
"type": "call_tool", | ||
"request_id": request_id, | ||
"tool_name": tool_name, | ||
"arguments": arguments | ||
} | ||
|
||
# Add any header fields to the request | ||
if tool_provider.header_fields and arguments: | ||
headers = {} | ||
for field in tool_provider.header_fields: | ||
if field in arguments: | ||
headers[field] = arguments[field] | ||
if headers: | ||
call_request["headers"] = headers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This enforces a strict format for the data transmitted via the socket. Unfortunately websockets don't enforce a well-known standard for the information format (unlike REST where the format is always sent as part of the headers).
Because the goal of UTCP is to enable even agents to transmit information to existing endpoints, just like humans would, without needing change in the endpoints, we need a more flexible format for the sent data. I will be working today on the UDP implementation, and once that's done I can ping you to adapt it accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closing this PR in favor of a new one using the Changes made based on review feedback:
New PR: #36 Thanks for the feedback @h3xxit! 🙏 |
Implements comprehensive WebSocket transport following UTCP architecture:
Core Features
Architecture Compliance
Authentication & Security
Testing & Quality
Protocol Implementation
Documentation
Addresses the "No wrapper tax" principle by enabling direct WebSocket communication without requiring changes to existing WebSocket services. Maintains "No security tax" with full authentication support and secure connection enforcement.
🤖 Generated with Claude Code