Skip to content

Commit bd26046

Browse files
fix: Resolve lint issues and formatting
- Move ReadResourceContents import to top of file in lowlevel example - Use union syntax (X | Y) instead of tuple in isinstance call - Fix line length issue by breaking long function signature - Add proper type annotations for consistency 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 109f881 commit bd26046

File tree

5 files changed

+50
-47
lines changed

5 files changed

+50
-47
lines changed

examples/snippets/servers/lowlevel/resource_contents_direct.py

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Example showing how to return ResourceContents objects directly from
2+
Example showing how to return ResourceContents objects directly from
33
low-level server resources.
44
55
The main benefit is the ability to include metadata (_meta field) with
@@ -15,7 +15,7 @@
1515
import mcp.server.stdio as stdio
1616
import mcp.types as types
1717
from mcp.server import NotificationOptions, Server
18-
18+
from mcp.server.lowlevel.server import ReadResourceContents
1919

2020
# Create a server instance
2121
server = Server(
@@ -29,7 +29,7 @@
2929
async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | types.BlobResourceContents]:
3030
"""Handle resource reading with direct ResourceContents return."""
3131
uri_str = str(uri)
32-
32+
3333
if uri_str == "text://readme":
3434
# Return TextResourceContents with document metadata
3535
return [
@@ -44,10 +44,10 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
4444
"version": "2.1.0",
4545
"language": "en",
4646
"license": "MIT",
47-
}
47+
},
4848
)
4949
]
50-
50+
5151
elif uri_str == "data://config.json":
5252
# Return JSON data with schema and validation metadata
5353
return [
@@ -61,13 +61,14 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
6161
"environment": "production",
6262
"lastValidated": "2024-01-15T14:00:00Z",
6363
"checksum": "sha256:abc123...",
64-
}
64+
},
6565
)
6666
]
67-
67+
6868
elif uri_str == "image://icon.png":
6969
# Return binary data with comprehensive image metadata
7070
import base64
71+
7172
# This is a 1x1 transparent PNG
7273
png_data = base64.b64decode(
7374
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
@@ -89,10 +90,10 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
8990
"hasAlpha": True,
9091
"generated": "2024-01-15T12:00:00Z",
9192
"generator": "Example MCP Server",
92-
}
93+
},
9394
)
9495
]
95-
96+
9697
elif uri_str == "multi://content":
9798
# Return multiple ResourceContents objects with part metadata
9899
return [
@@ -105,7 +106,7 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
105106
"title": "Introduction",
106107
"order": 1,
107108
"required": True,
108-
}
109+
},
109110
),
110111
types.TextResourceContents(
111112
uri=uri,
@@ -117,7 +118,7 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
117118
"order": 2,
118119
"wordCount": 8,
119120
"headingLevel": 2,
120-
}
121+
},
121122
),
122123
types.BlobResourceContents(
123124
uri=uri,
@@ -129,19 +130,19 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
129130
"order": 3,
130131
"encoding": "base64",
131132
"originalSize": 19,
132-
}
133+
},
133134
),
134135
]
135-
136+
136137
elif uri_str.startswith("code://"):
137138
# Extract language from URI for syntax highlighting
138139
language = uri_str.split("://")[1].split("/")[0]
139140
code_samples = {
140141
"python": ('def hello():\n print("Hello, World!")', "text/x-python"),
141142
"javascript": ('console.log("Hello, World!");', "text/javascript"),
142-
"html": ('<h1>Hello, World!</h1>', "text/html"),
143+
"html": ("<h1>Hello, World!</h1>", "text/html"),
143144
}
144-
145+
145146
if language in code_samples:
146147
code, mime_type = code_samples[language]
147148
return [
@@ -155,10 +156,10 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
155156
"lineNumbers": True,
156157
"executable": language in ["python", "javascript"],
157158
"documentation": f"https://docs.example.com/languages/{language}",
158-
}
159+
},
159160
)
160161
]
161-
162+
162163
# Default case - resource not found
163164
return [
164165
types.TextResourceContents(
@@ -228,14 +229,13 @@ async def list_legacy_resources() -> list[types.Resource]:
228229

229230

230231
# Mix old and new styles to show compatibility
231-
from mcp.server.lowlevel.server import ReadResourceContents
232-
233-
234232
@server.read_resource()
235-
async def read_legacy_resource(uri: AnyUrl) -> Iterable[ReadResourceContents | types.TextResourceContents]:
233+
async def read_legacy_resource(
234+
uri: AnyUrl,
235+
) -> Iterable[ReadResourceContents | types.TextResourceContents | types.BlobResourceContents]:
236236
"""Handle legacy resources alongside new ResourceContents."""
237237
uri_str = str(uri)
238-
238+
239239
if uri_str == "legacy://text":
240240
# Old style - return ReadResourceContents
241241
return [
@@ -244,7 +244,7 @@ async def read_legacy_resource(uri: AnyUrl) -> Iterable[ReadResourceContents | t
244244
mime_type="text/plain",
245245
)
246246
]
247-
247+
248248
# Delegate to the new handler for other resources
249249
return await read_resource(uri)
250250

@@ -264,4 +264,4 @@ async def main():
264264

265265
if __name__ == "__main__":
266266
# Run with: python resource_contents_direct.py
267-
asyncio.run(main())
267+
asyncio.run(main())

examples/snippets/servers/resource_contents_direct.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
This metadata helps clients better understand and work with the resource content.
1818
"""
1919

20-
from mcp.server.fastmcp import FastMCP
21-
from mcp.types import TextResourceContents, BlobResourceContents
22-
from pydantic import AnyUrl
2320
import base64
2421

22+
from pydantic import AnyUrl
23+
24+
from mcp.server.fastmcp import FastMCP
25+
from mcp.types import BlobResourceContents, TextResourceContents
26+
2527
mcp = FastMCP(name="Direct ResourceContents Example")
2628

2729

@@ -40,7 +42,7 @@ def get_report() -> TextResourceContents:
4042
"version": "1.2.0",
4143
"tags": ["monthly", "finance", "q1-2024"],
4244
"confidentiality": "internal",
43-
}
45+
},
4446
)
4547

4648

@@ -50,7 +52,7 @@ def get_logo() -> BlobResourceContents:
5052
"""Return a logo image with metadata about dimensions and format."""
5153
# In a real app, you might read this from a file
5254
image_bytes = b"\x89PNG\r\n\x1a\n..." # PNG header
53-
55+
5456
return BlobResourceContents(
5557
uri=AnyUrl("image://logo"),
5658
blob=base64.b64encode(image_bytes).decode(),
@@ -64,7 +66,7 @@ def get_logo() -> BlobResourceContents:
6466
"hasAlpha": True,
6567
"fileSize": 24576,
6668
"lastModified": "2024-01-10T08:00:00Z",
67-
}
69+
},
6870
)
6971

7072

@@ -73,13 +75,14 @@ def get_logo() -> BlobResourceContents:
7375
async def get_metrics(metric_type: str) -> TextResourceContents:
7476
"""Return metrics data with metadata about collection time and source."""
7577
import datetime
76-
78+
7779
# Simulate collecting metrics
7880
metrics = {"cpu": 45.2, "memory": 78.5, "disk": 62.1}
7981
timestamp = datetime.datetime.now(datetime.UTC).isoformat()
80-
82+
8183
if metric_type == "json":
8284
import json
85+
8386
return TextResourceContents(
8487
uri=AnyUrl(f"data://metrics/{metric_type}"),
8588
text=json.dumps(metrics, indent=2),
@@ -90,7 +93,7 @@ async def get_metrics(metric_type: str) -> TextResourceContents:
9093
"interval": "5s",
9194
"aggregation": "average",
9295
"host": "prod-server-01",
93-
}
96+
},
9497
)
9598
elif metric_type == "csv":
9699
csv_text = "metric,value\n" + "\n".join(f"{k},{v}" for k, v in metrics.items())
@@ -102,7 +105,7 @@ async def get_metrics(metric_type: str) -> TextResourceContents:
102105
"timestamp": timestamp,
103106
"columns": ["metric", "value"],
104107
"row_count": len(metrics),
105-
}
108+
},
106109
)
107110
else:
108111
text = "\n".join(f"{k.upper()}: {v}%" for k, v in metrics.items())
@@ -113,7 +116,7 @@ async def get_metrics(metric_type: str) -> TextResourceContents:
113116
_meta={
114117
"timestamp": timestamp,
115118
"format": "human-readable",
116-
}
119+
},
117120
)
118121

119122

@@ -122,7 +125,7 @@ async def get_metrics(metric_type: str) -> TextResourceContents:
122125
def get_config() -> TextResourceContents:
123126
"""Return application config with version and environment metadata."""
124127
import json
125-
128+
126129
config = {
127130
"version": "1.0.0",
128131
"features": {
@@ -133,9 +136,9 @@ def get_config() -> TextResourceContents:
133136
"limits": {
134137
"max_file_size": 10485760, # 10MB
135138
"max_connections": 100,
136-
}
139+
},
137140
}
138-
141+
139142
return TextResourceContents(
140143
uri=AnyUrl("config://app"),
141144
text=json.dumps(config, indent=2),
@@ -146,7 +149,7 @@ def get_config() -> TextResourceContents:
146149
"environment": "production",
147150
"schema": "https://example.com/schemas/config/v1.0",
148151
"editable": False,
149-
}
152+
},
150153
)
151154

152155

@@ -156,7 +159,7 @@ async def get_users() -> TextResourceContents:
156159
"""Return query results with execution time and row count."""
157160
import json
158161
import time
159-
162+
160163
# Simulate database query
161164
start_time = time.time()
162165
users = [
@@ -165,7 +168,7 @@ async def get_users() -> TextResourceContents:
165168
{"id": 3, "name": "Charlie", "role": "user"},
166169
]
167170
execution_time = time.time() - start_time
168-
171+
169172
return TextResourceContents(
170173
uri=AnyUrl("db://query/users"),
171174
text=json.dumps(users, indent=2),
@@ -177,10 +180,10 @@ async def get_users() -> TextResourceContents:
177180
"database": "main",
178181
"cached": False,
179182
"timestamp": "2024-01-15T16:00:00Z",
180-
}
183+
},
181184
)
182185

183186

184187
if __name__ == "__main__":
185188
# Run with: python resource_contents_direct.py
186-
mcp.run()
189+
mcp.run()

src/mcp/server/fastmcp/resources/types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pydantic import AnyUrl, Field, ValidationInfo, validate_call
1515

1616
from mcp.server.fastmcp.resources.base import Resource
17-
from mcp.types import BlobResourceContents, Icon, ResourceContents, TextResourceContents
17+
from mcp.types import BlobResourceContents, Icon, TextResourceContents
1818

1919

2020
class TextResource(Resource):
@@ -63,7 +63,7 @@ async def read(self) -> str | bytes | TextResourceContents | BlobResourceContent
6363

6464
if isinstance(result, Resource):
6565
return await result.read()
66-
elif isinstance(result, (TextResourceContents, BlobResourceContents)):
66+
elif isinstance(result, TextResourceContents | BlobResourceContents):
6767
return result
6868
elif isinstance(result, bytes):
6969
return result

tests/server/fastmcp/resources/test_resource_contents_direct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,4 @@ async def get_item_contents(category: str, item: str) -> TextResourceContents:
187187
assert isinstance(content, TextResourceContents)
188188
assert content.text == "Content for python in books"
189189
assert content.mimeType == "text/plain"
190-
assert str(content.uri) == "resource://books/python"
190+
assert str(content.uri) == "resource://books/python"

tests/server/test_read_resource_direct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,4 @@ async def read_resource(uri: AnyUrl) -> Iterable[types.TextResourceContents | ty
188188
content3 = result.root.contents[2]
189189
assert isinstance(content3, types.TextResourceContents)
190190
assert content3.text == "Third text content"
191-
assert content3.mimeType == "text/markdown"
191+
assert content3.mimeType == "text/markdown"

0 commit comments

Comments
 (0)