-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathbase.py
More file actions
64 lines (53 loc) · 2.31 KB
/
base.py
File metadata and controls
64 lines (53 loc) · 2.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""Base classes and interfaces for MCPServer resources."""
import abc
from typing import Any
from pydantic import (
BaseModel,
ConfigDict,
Field,
ValidationInfo,
field_validator,
)
from mcp.types import Annotations, Icon
class Resource(BaseModel, abc.ABC):
"""Base class for all resources."""
model_config = ConfigDict(validate_default=True)
uri: str = Field(default=..., description="URI of the resource")
name: str | None = Field(description="Name of the resource", default=None)
title: str | None = Field(description="Human-readable title of the resource", default=None)
description: str | None = Field(description="Description of the resource", default=None)
mime_type: str = Field(
default="text/plain",
description="MIME type of the resource content",
)
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource")
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource")
meta: dict[str, Any] | None = Field(default=None, description="Optional metadata for this resource")
@field_validator("mime_type")
@classmethod
def validate_mime_type(cls, value: str) -> str:
"""Validate that mime_type has a basic type/subtype structure.
The MCP spec defines mimeType as an optional string with no format
constraints. This validator only checks for the minimal type/subtype
structure to catch obvious mistakes, without restricting valid MIME
types per RFC 2045.
"""
if "/" not in value:
raise ValueError(
f"Invalid MIME type '{value}': must contain a '/' separating type and subtype "
f"(e.g. 'text/plain', 'application/json')"
)
return value
@field_validator("name", mode="before")
@classmethod
def set_default_name(cls, name: str | None, info: ValidationInfo) -> str:
"""Set default name from URI if not provided."""
if name:
return name
if uri := info.data.get("uri"):
return str(uri)
raise ValueError("Either name or uri must be provided")
@abc.abstractmethod
async def read(self) -> str | bytes:
"""Read the resource content."""
pass # pragma: no cover