Skip to content

Commit dd2eb3c

Browse files
Thuraabtechh3xxitSalman MohammedCopilot
authored
GraphQL Plugin: UTCP 1.0 Migration (#75)
* socket protocol updated to be compatible with 1.0v utcp * cubic fixes done * pinned mcp-use to use langchain 0.3.27 * removed mcp denpendency on langchain * adding the langchain dependency for testing (temporary) * remove langchain-core pin to resolve dependency conflict * feat: Updated Graphql implementation to be compatible with UTCP 1.0v * Added gql 'how to use' guide in the README.md * updated cubic comments for GraphQl * Update comment on delimeter handling Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Razvan Radulescu <43811028+h3xxit@users.noreply.github.com> Co-authored-by: Salman Mohammed <thuraabtec@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 4432b15 commit dd2eb3c

File tree

11 files changed

+421
-105
lines changed

11 files changed

+421
-105
lines changed
Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,47 @@
1-
Find the UTCP readme at https://github.com/universal-tool-calling-protocol/python-utcp.
1+
2+
# UTCP GraphQL Communication Protocol Plugin
3+
4+
This plugin integrates GraphQL as a UTCP 1.0 communication protocol and call template. It supports discovery via schema introspection, authenticated calls, and header handling.
5+
6+
## Getting Started
7+
8+
### Installation
9+
10+
```bash
11+
pip install gql
12+
```
13+
14+
### Registration
15+
16+
```python
17+
import utcp_gql
18+
utcp_gql.register()
19+
```
20+
21+
## How To Use
22+
23+
- Ensure the plugin is imported and registered: `import utcp_gql; utcp_gql.register()`.
24+
- Add a manual in your client config:
25+
```json
26+
{
27+
"name": "my_graph",
28+
"call_template_type": "graphql",
29+
"url": "https://your.graphql/endpoint",
30+
"operation_type": "query",
31+
"headers": { "x-client": "utcp" },
32+
"header_fields": ["x-session-id"]
33+
}
34+
```
35+
- Call a tool:
36+
```python
37+
await client.call_tool("my_graph.someQuery", {"id": "123", "x-session-id": "abc"})
38+
```
39+
40+
## Notes
41+
42+
- Tool names are prefixed by the manual name (e.g., `my_graph.someQuery`).
43+
- Headers merge static `headers` plus whitelisted dynamic fields from `header_fields`.
44+
- Supported auth: API key, Basic auth, OAuth2 (client-credentials).
45+
- Security: only `https://` or `http://localhost`/`http://127.0.0.1` endpoints.
46+
47+
For UTCP core docs, see https://github.com/universal-tool-calling-protocol/python-utcp.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from utcp.plugins.discovery import register_communication_protocol, register_call_template
2+
3+
from .gql_communication_protocol import GraphQLCommunicationProtocol
4+
from .gql_call_template import GraphQLProvider, GraphQLProviderSerializer
5+
6+
7+
def register():
8+
register_communication_protocol("graphql", GraphQLCommunicationProtocol())
9+
register_call_template("graphql", GraphQLProviderSerializer())

plugins/communication_protocols/gql/src/utcp_gql/gql_call_template.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
from utcp.data.call_template import CallTemplate
2-
from utcp.data.auth import Auth
2+
from utcp.data.auth import Auth, AuthSerializer
3+
from utcp.interfaces.serializer import Serializer
4+
from utcp.exceptions import UtcpSerializerValidationError
5+
import traceback
36
from typing import Dict, List, Optional, Literal
4-
from pydantic import Field
7+
from pydantic import Field, field_serializer, field_validator
58

69
class GraphQLProvider(CallTemplate):
710
"""Provider configuration for GraphQL-based tools.
@@ -27,3 +30,31 @@ class GraphQLProvider(CallTemplate):
2730
auth: Optional[Auth] = None
2831
headers: Optional[Dict[str, str]] = None
2932
header_fields: Optional[List[str]] = Field(default=None, description="List of input fields to be sent as request headers for the initial connection.")
33+
34+
@field_serializer("auth")
35+
def serialize_auth(self, auth: Optional[Auth]):
36+
if auth is None:
37+
return None
38+
return AuthSerializer().to_dict(auth)
39+
40+
@field_validator("auth", mode="before")
41+
@classmethod
42+
def validate_auth(cls, v: Optional[Auth | dict]):
43+
if v is None:
44+
return None
45+
if isinstance(v, Auth):
46+
return v
47+
return AuthSerializer().validate_dict(v)
48+
49+
50+
class GraphQLProviderSerializer(Serializer[GraphQLProvider]):
51+
def to_dict(self, obj: GraphQLProvider) -> dict:
52+
return obj.model_dump()
53+
54+
def validate_dict(self, data: dict) -> GraphQLProvider:
55+
try:
56+
return GraphQLProvider.model_validate(data)
57+
except Exception as e:
58+
raise UtcpSerializerValidationError(
59+
f"Invalid GraphQLProvider: {e}\n{traceback.format_exc()}"
60+
)

0 commit comments

Comments
 (0)