refactor: move mcp catalog dtos to wf_sources_mcp

This commit is contained in:
lda
2026-06-07 00:48:23 +07:00 Verified
parent 83a3793534
commit 9bef0b0f63
25 changed files with 1117 additions and 146 deletions
+22
View File
@@ -0,0 +1,22 @@
from __future__ import annotations
from .entries import (
CatalogNodeEntry,
CatalogPromptEntry,
CatalogResourceEntry,
DiscoveredPrompt,
DiscoveredResource,
DiscoveredTool,
)
from .models import CatalogSnapshot, dump_catalog_snapshot
__all__ = [
"CatalogNodeEntry",
"CatalogPromptEntry",
"CatalogResourceEntry",
"CatalogSnapshot",
"DiscoveredPrompt",
"DiscoveredResource",
"DiscoveredTool",
"dump_catalog_snapshot",
]
+91
View File
@@ -0,0 +1,91 @@
from __future__ import annotations
from dataclasses import dataclass, field
from typing import Any
@dataclass(slots=True)
class DiscoveredTool:
"""Tool snapshot after converting from an upstream MCP SDK tool."""
name: str
title: str | None
description: str | None
input_schema: dict[str, Any]
output_schema: dict[str, Any]
outcomes: tuple[str, ...] = ("ok",)
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(slots=True)
class DiscoveredResource:
"""Resource snapshot after converting from an upstream MCP SDK resource."""
uri: str
name: str
title: str | None
description: str | None
mime_type: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(slots=True)
class DiscoveredPrompt:
"""Prompt snapshot after converting from an upstream MCP SDK prompt."""
name: str
title: str | None
description: str | None
arguments: list[dict[str, Any]] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(slots=True)
class CatalogNodeEntry:
"""Namespaced tool entry stored in an MCP upstream catalog snapshot."""
qualified_name: str
connection_id: str
local_name: str
title: str | None
description: str | None
outcomes: tuple[str, ...]
input_schema: dict[str, Any]
output_schema: dict[str, Any]
@dataclass(slots=True)
class CatalogResourceEntry:
"""Namespaced resource entry stored in an MCP upstream catalog snapshot."""
qualified_name: str
connection_id: str
local_name: str
title: str | None
uri: str
description: str | None
mime_type: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass(slots=True)
class CatalogPromptEntry:
"""Namespaced prompt entry stored in an MCP upstream catalog snapshot."""
qualified_name: str
connection_id: str
local_name: str
title: str | None
description: str | None
arguments: list[dict[str, Any]] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
__all__ = [
"CatalogNodeEntry",
"CatalogPromptEntry",
"CatalogResourceEntry",
"DiscoveredPrompt",
"DiscoveredResource",
"DiscoveredTool",
]
+41
View File
@@ -0,0 +1,41 @@
from __future__ import annotations
from dataclasses import asdict, dataclass, field
from typing import Any
from .entries import CatalogNodeEntry, CatalogPromptEntry, CatalogResourceEntry
@dataclass(slots=True)
class CatalogSnapshot:
"""Stored upstream MCP catalog snapshot for one source connection."""
connection_id: str
fetched_at_epoch_ms: int
max_age_seconds: int
nodes: list[CatalogNodeEntry] = field(default_factory=list)
resources: list[CatalogResourceEntry] = field(default_factory=list)
prompts: list[CatalogPromptEntry] = field(default_factory=list)
metadata: dict[str, Any] = field(default_factory=dict)
def is_stale(self, now_epoch_ms: int) -> bool:
age_ms = now_epoch_ms - self.fetched_at_epoch_ms
return age_ms > self.max_age_seconds * 1000
def dump_catalog_snapshot(snapshot: CatalogSnapshot) -> dict[str, Any]:
return {
"connection_id": snapshot.connection_id,
"fetched_at_epoch_ms": snapshot.fetched_at_epoch_ms,
"max_age_seconds": snapshot.max_age_seconds,
"nodes": [asdict(node) for node in snapshot.nodes],
"resources": [asdict(resource) for resource in snapshot.resources],
"prompts": [asdict(prompt) for prompt in snapshot.prompts],
"metadata": snapshot.metadata,
}
__all__ = [
"CatalogSnapshot",
"dump_catalog_snapshot",
]
+7 -6
View File
@@ -1,8 +1,7 @@
"""MCP upstream-source auth and catalog file stores.
These stores preserve the current MCP compatibility JSON shapes. Catalog entry
types still come from `wf_mcp` until catalog DTOs finish moving to a neutral or
source-provider package.
types come from `wf_sources_mcp.catalog`.
"""
from __future__ import annotations
@@ -16,7 +15,7 @@ from wf_api.auth import validate_auth_id
from wf_sources_mcp.auth import AuthRecord, mcp_auth_from_neutral, neutral_auth_from_mcp
if TYPE_CHECKING:
from wf_mcp.catalog.models import CatalogSnapshot
from wf_sources_mcp.catalog.models import CatalogSnapshot
class AuthStore:
@@ -142,7 +141,7 @@ class FileCatalogStore(CatalogStore):
return path
def save_catalog(self, snapshot: CatalogSnapshot) -> None:
from wf_mcp.catalog.models import dump_catalog_snapshot
from wf_sources_mcp.catalog.models import dump_catalog_snapshot
self._catalog_path(snapshot.connection_id).write_text(
json.dumps(dump_catalog_snapshot(snapshot), indent=2),
@@ -150,12 +149,14 @@ class FileCatalogStore(CatalogStore):
)
def load_catalog(self, connection_id: str) -> CatalogSnapshot | None:
from wf_mcp.capabilities import (
from wf_sources_mcp.catalog import (
CatalogNodeEntry,
CatalogPromptEntry,
CatalogResourceEntry,
)
from wf_mcp.catalog.models import CatalogSnapshot as CatalogSnapshotType
from wf_sources_mcp.catalog import (
CatalogSnapshot as CatalogSnapshotType,
)
path = self._catalog_path(connection_id)
if not path.exists():