This commit is contained in:
Classic298 2026-09-30 15:19:08 +00:00 • committed by GitHub
commit ce61570b09
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -2,7 +2,7 @@ import asyncio
import logging import logging
from contextlib import AsyncExitStack from contextlib import AsyncExitStack
from datetime import timedelta from datetime import timedelta
from typing import Optional from typing import Any, Optional
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -17,6 +17,7 @@ from open_webui.env import (
AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER, AIOHTTP_CLIENT_TIMEOUT_TOOL_SERVER,
MCP_INITIALIZE_TIMEOUT, MCP_INITIALIZE_TIMEOUT,
) )
from open_webui.utils.json_codec import JSONCodec
def _build_httpx_client(headers=None, timeout=None, auth=None, verify=True): def _build_httpx_client(headers=None, timeout=None, auth=None, verify=True):
@ -57,6 +58,24 @@ def create_insecure_httpx_client(headers=None, timeout=None, auth=None):
return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=False) return _build_httpx_client(headers=headers, timeout=timeout, auth=auth, verify=False)
def _parse_json_text(text: str) -> Any:
try:
return JSONCodec.loads(text)
except JSONCodec.JSONDecodeError:
return text
def _content_includes_structured(content: list, structured_content: dict) -> bool:
texts = [item.get('text') for item in content if item.get('type') == 'text']
values = [_parse_json_text(text) for text in texts]
payload = structured_content
# The Python SDK wraps non-object returns as {'result': ...}
if structured_content.keys() == {'result'}:
payload = structured_content['result']
candidates = (texts, values, *texts, *values)
return payload in candidates or structured_content in candidates
class MCPClient: class MCPClient:
def __init__(self): def __init__(self):
self.session: Optional[ClientSession] = None self.session: Optional[ClientSession] = None
@ -127,10 +146,13 @@ class MCPClient:
result_dict = result.model_dump(mode='json') result_dict = result.model_dump(mode='json')
result_content = result_dict.get('content', {}) result_content = result_dict.get('content', {})
structured_content = result_dict.get('structuredContent')
if result.isError: if result.isError:
raise Exception(result_content) raise Exception(result_content)
else: else:
if structured_content and not _content_includes_structured(result_content, structured_content):
result_content.append({'type': 'text', 'text': JSONCodec.dumps(structured_content)})
return result_content return result_content
async def list_resources(self, cursor: Optional[str] = None) -> Optional[dict]: async def list_resources(self, cursor: Optional[str] = None) -> Optional[dict]: