fix: address rpc and mcp review followups
This commit is contained in:
@@ -61,7 +61,8 @@ def mcp_auth_headers(auth: AuthRecord | None) -> dict[str, str]:
|
||||
else {}
|
||||
)
|
||||
token = auth.payload.get("token")
|
||||
if isinstance(token, str) and "Authorization" not in headers:
|
||||
has_authorization = any(key.lower() == "authorization" for key in headers)
|
||||
if isinstance(token, str) and not has_authorization:
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
return headers
|
||||
|
||||
|
||||
@@ -69,11 +69,7 @@ def _root_exception(exc: BaseException) -> BaseException:
|
||||
"""Unwrap the first nested exception from MCP task-group ExceptionGroups."""
|
||||
current: BaseException = exc
|
||||
while isinstance(current, ExceptionGroup) and current.exceptions:
|
||||
nested = current.exceptions[0]
|
||||
if isinstance(nested, BaseException):
|
||||
current = nested
|
||||
continue
|
||||
break
|
||||
current = current.exceptions[0]
|
||||
return current
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import asdict, dataclass, field
|
||||
@@ -30,6 +31,8 @@ def connection_runtime_fingerprint(
|
||||
command, URL, account, or auth payload must create a fresh session.
|
||||
"""
|
||||
|
||||
# Expected payloads are dataclasses/primitive mappings today. `default=str`
|
||||
# is only a fallback for SDK URL/path-like leaf values in auth/transport data.
|
||||
return json.dumps(
|
||||
{
|
||||
"connection": asdict(connection),
|
||||
@@ -53,6 +56,7 @@ class McpRuntimePool:
|
||||
|
||||
session_factory: SessionFactory
|
||||
_sessions: dict[str, tuple[str, PersistentMcpSession]] = field(default_factory=dict)
|
||||
_session_locks: dict[str, asyncio.Lock] = field(default_factory=dict)
|
||||
|
||||
async def get_session(
|
||||
self,
|
||||
@@ -63,16 +67,22 @@ class McpRuntimePool:
|
||||
current = self._sessions.get(connection.id)
|
||||
if current is not None and current[0] == fingerprint:
|
||||
return current[1]
|
||||
if current is not None:
|
||||
await current[1].close()
|
||||
|
||||
created = self.session_factory(connection, auth)
|
||||
if isawaitable(created):
|
||||
session = await created
|
||||
else:
|
||||
session = cast(PersistentMcpSession, created)
|
||||
self._sessions[connection.id] = (fingerprint, session)
|
||||
return session
|
||||
lock = self._session_locks.setdefault(connection.id, asyncio.Lock())
|
||||
async with lock:
|
||||
current = self._sessions.get(connection.id)
|
||||
if current is not None and current[0] == fingerprint:
|
||||
return current[1]
|
||||
if current is not None:
|
||||
await current[1].close()
|
||||
|
||||
created = self.session_factory(connection, auth)
|
||||
if isawaitable(created):
|
||||
session = await created
|
||||
else:
|
||||
session = cast(PersistentMcpSession, created)
|
||||
self._sessions[connection.id] = (fingerprint, session)
|
||||
return session
|
||||
|
||||
async def call_tool(
|
||||
self,
|
||||
@@ -157,6 +167,7 @@ class McpRuntimePool:
|
||||
|
||||
async def close_connection(self, connection_id: str) -> None:
|
||||
current = self._sessions.pop(connection_id, None)
|
||||
self._session_locks.pop(connection_id, None)
|
||||
if current is not None:
|
||||
await current[1].close()
|
||||
|
||||
@@ -164,5 +175,6 @@ class McpRuntimePool:
|
||||
"""Close all live runtimes; useful for server shutdown and tests."""
|
||||
sessions = list(self._sessions.values())
|
||||
self._sessions.clear()
|
||||
self._session_locks.clear()
|
||||
for _fingerprint, session in sessions:
|
||||
await session.close()
|
||||
|
||||
@@ -38,7 +38,7 @@ def _python_type_from_schema(schema: object) -> object:
|
||||
|
||||
if schema_type == "array":
|
||||
item_type = _python_type_from_schema(schema.get("items", {}))
|
||||
return list[item_type] if isinstance(item_type, type) else list[Any]
|
||||
return list[item_type]
|
||||
|
||||
if not isinstance(schema_type, str):
|
||||
return Any
|
||||
@@ -52,7 +52,7 @@ def _optional_type(annotation: object) -> object:
|
||||
origin = get_origin(annotation)
|
||||
if origin in {Union, UnionType} and NoneType in get_args(annotation):
|
||||
return annotation
|
||||
return annotation | None if isinstance(annotation, type) else Any
|
||||
return cast(Any, annotation) | None
|
||||
|
||||
|
||||
def _field_default(
|
||||
|
||||
@@ -10,7 +10,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from pathlib import Path
|
||||
from typing import Literal, Protocol, cast
|
||||
from typing import Any, Literal, Protocol, cast
|
||||
|
||||
from pydantic import Field, field_validator, model_validator
|
||||
|
||||
@@ -60,7 +60,7 @@ class LegacyConnectionConfigLike(Protocol):
|
||||
def enabled(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def metadata(self) -> Mapping[str, object]: ...
|
||||
def metadata(self) -> Mapping[str, Any]: ...
|
||||
|
||||
|
||||
class McpSourceRegistryEntry(SourceRegistryBaseModel):
|
||||
|
||||
Reference in New Issue
Block a user