Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ jobs:
pip install -e plugins/communication_protocols/http[dev]
pip install -e plugins/communication_protocols/mcp[dev]
pip install -e plugins/communication_protocols/text[dev]
pip install -e plugins/communication_protocols/socket[dev]

- name: Run tests with pytest
run: |
pytest core/tests/ plugins/communication_protocols/cli/tests/ plugins/communication_protocols/http/tests/ plugins/communication_protocols/mcp/tests/ plugins/communication_protocols/text/tests/ --doctest-modules --junitxml=junit/test-results.xml --cov=core/src/utcp --cov-report=xml --cov-report=html
pytest core/tests/ plugins/communication_protocols/cli/tests/ plugins/communication_protocols/http/tests/ plugins/communication_protocols/mcp/tests/ plugins/communication_protocols/text/tests/ plugins/communication_protocols/socket/tests/ --doctest-modules --junitxml=junit/test-results.xml --cov=core/src/utcp --cov-report=xml --cov-report=html

- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
Expand Down
48 changes: 47 additions & 1 deletion plugins/communication_protocols/gql/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,47 @@
Find the UTCP readme at https://github.com/universal-tool-calling-protocol/python-utcp.

UTCP GraphQL Communication Protocol Plugin
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README is missing a title marker (#) at the top. Line 2 "UTCP GraphQL Communication Protocol Plugin" should be formatted as # UTCP GraphQL Communication Protocol Plugin to render properly as a heading in Markdown.

Suggested change
UTCP GraphQL Communication Protocol Plugin
# UTCP GraphQL Communication Protocol Plugin

Copilot uses AI. Check for mistakes.

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.

Getting Started

Installation

```bash
pip install gql
```

Registration

```python
import utcp_gql
utcp_gql.register()
```

How To Use

- Ensure the plugin is imported and registered: `import utcp_gql; utcp_gql.register()`.
- Add a manual in your client config:
```json
{
"name": "my_graph",
"call_template_type": "graphql",
"url": "https://your.graphql/endpoint",
"operation_type": "query",
"headers": { "x-client": "utcp" },
"header_fields": ["x-session-id"]
}
```
- Call a tool:
```python
await client.call_tool("my_graph.someQuery", {"id": "123", "x-session-id": "abc"})
```

Notes
Comment on lines +6 to +40
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Section headers "Getting Started", "Installation", "Registration", "How To Use", and "Notes" are missing heading markers. They should be formatted with ## to render properly as section headers in Markdown (e.g., ## Getting Started, ## Installation, etc.).

Copilot uses AI. Check for mistakes.

- Tool names are prefixed by the manual name (e.g., `my_graph.someQuery`).
- Headers merge static `headers` plus whitelisted dynamic fields from `header_fields`.
- Supported auth: API key, Basic auth, OAuth2 (client-credentials).
- Security: only `https://` or `http://localhost`/`http://127.0.0.1` endpoints.

For UTCP core docs, see https://github.com/universal-tool-calling-protocol/python-utcp.
9 changes: 9 additions & 0 deletions plugins/communication_protocols/gql/src/utcp_gql/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from utcp.plugins.discovery import register_communication_protocol, register_call_template

from .gql_communication_protocol import GraphQLCommunicationProtocol
from .gql_call_template import GraphQLProvider, GraphQLProviderSerializer


def register():
register_communication_protocol("graphql", GraphQLCommunicationProtocol())
register_call_template("graphql", GraphQLProviderSerializer())
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from utcp.data.call_template import CallTemplate
from utcp.data.auth import Auth
from utcp.data.call_template import CallTemplate, CallTemplateSerializer
Copy link

Copilot AI Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'CallTemplateSerializer' is not used.

Suggested change
from utcp.data.call_template import CallTemplate, CallTemplateSerializer
from utcp.data.call_template import CallTemplate

Copilot uses AI. Check for mistakes.
from utcp.data.auth import Auth, AuthSerializer
from utcp.interfaces.serializer import Serializer
from utcp.exceptions import UtcpSerializerValidationError
import traceback
from typing import Dict, List, Optional, Literal
from pydantic import Field
from pydantic import Field, field_serializer, field_validator

class GraphQLProvider(CallTemplate):
"""Provider configuration for GraphQL-based tools.
Expand All @@ -27,3 +30,31 @@ class GraphQLProvider(CallTemplate):
auth: Optional[Auth] = None
headers: Optional[Dict[str, str]] = None
header_fields: Optional[List[str]] = Field(default=None, description="List of input fields to be sent as request headers for the initial connection.")

@field_serializer("auth")
def serialize_auth(self, auth: Optional[Auth]):
if auth is None:
return None
return AuthSerializer().to_dict(auth)

@field_validator("auth", mode="before")
@classmethod
def validate_auth(cls, v: Optional[Auth | dict]):
if v is None:
return None
if isinstance(v, Auth):
return v
return AuthSerializer().validate_dict(v)


class GraphQLProviderSerializer(Serializer[GraphQLProvider]):
def to_dict(self, obj: GraphQLProvider) -> dict:
return obj.model_dump()

def validate_dict(self, data: dict) -> GraphQLProvider:
try:
return GraphQLProvider.model_validate(data)
except Exception as e:
raise UtcpSerializerValidationError(
f"Invalid GraphQLProvider: {e}\n{traceback.format_exc()}"
)
Loading
Loading