Skip to content

Commit de8048c

Browse files
ochafikclaude
andauthored
feat(qr-server): use Python SDK meta parameter for resource CSP metadata (#303)
* update qr-server w/ python sdk fix * feat(qr-server): use Python SDK meta parameter for resource CSP metadata - Update qr-server to use the new meta parameter on @mcp.resource decorator instead of manually overriding the read_resource handler - Add ui.resourceUri to tool meta alongside legacy ui/resourceUri for forward compatibility - Simplify server.py by removing the custom _read_resource_with_meta handler - Add qr-server to grid screenshot generation tests - Update grid-cell.png screenshot This leverages the Python SDK's native support for resource metadata, eliminating the need for low-level handler manipulation. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 73ab853 commit de8048c

3 files changed

Lines changed: 15 additions & 46 deletions

File tree

examples/qr-server/grid-cell.png

-12.1 KB
Loading

examples/qr-server/server.py

Lines changed: 13 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# /// script
33
# requires-python = ">=3.10"
44
# dependencies = [
5-
# "mcp>=1.9.0",
5+
# "mcp @ git+https://github.com/modelcontextprotocol/python-sdk@main",
66
# "qrcode[pil]>=8.0",
77
# "uvicorn>=0.34.0",
88
# "starlette>=0.46.0",
@@ -30,7 +30,10 @@
3030
mcp = FastMCP("QR Code Server", port=PORT, stateless_http=True)
3131

3232

33-
@mcp.tool(meta={"ui/resourceUri": WIDGET_URI})
33+
@mcp.tool(meta={
34+
"ui":{"resourceUri": WIDGET_URI},
35+
"ui/resourceUri": WIDGET_URI, # legacy support
36+
})
3437
def generate_qr(
3538
text: str = "https://modelcontextprotocol.io",
3639
box_size: int = 10,
@@ -72,53 +75,17 @@ def generate_qr(
7275
return [types.ImageContent(type="image", data=b64, mimeType="image/png")]
7376

7477

75-
# Register widget resource using FastMCP decorator (returns HTML string)
76-
@mcp.resource(WIDGET_URI, mime_type="text/html;profile=mcp-app")
78+
# IMPORTANT: all the external domains used by app must be listed
79+
# in the meta.ui.csp.resourceDomains - otherwise they will be blocked by CSP policy
80+
@mcp.resource(
81+
WIDGET_URI,
82+
mime_type="text/html;profile=mcp-app",
83+
meta={"ui": {"csp": {"resourceDomains": ["https://unpkg.com"]}}},
84+
)
7785
def widget() -> str:
86+
"""Widget HTML resource with CSP metadata for external dependencies."""
7887
return Path(__file__).parent.joinpath("widget.html").read_text()
7988

80-
81-
# Override the read_resource handler to inject _meta into the response
82-
# This is needed because FastMCP doesn't support custom _meta on resources
83-
_low_level_server = mcp._mcp_server
84-
85-
86-
async def _read_resource_with_meta(req: types.ReadResourceRequest):
87-
"""Custom handler that injects CSP metadata for the widget resource."""
88-
uri = str(req.params.uri)
89-
html = Path(__file__).parent.joinpath("widget.html").read_text()
90-
91-
if uri == WIDGET_URI:
92-
# NOTE: Must use model_validate with '_meta' key (not 'meta') due to Pydantic alias behavior
93-
content = types.TextResourceContents.model_validate({
94-
"uri": WIDGET_URI,
95-
"mimeType": "text/html;profile=mcp-app",
96-
"text": html,
97-
# IMPORTANT: all the external domains used by app must be listed
98-
# in the _meta.ui.csp.resourceDomains - otherwise they will be blocked by CSP policy
99-
"_meta": {"ui": {"csp": {"resourceDomains": ["https://unpkg.com"]}}}
100-
})
101-
return types.ServerResult(
102-
types.ReadResourceResult(contents=[content])
103-
)
104-
105-
# Fallback for other resources (shouldn't happen for this server)
106-
return types.ServerResult(
107-
types.ReadResourceResult(
108-
contents=[
109-
types.TextResourceContents(
110-
uri=uri,
111-
mimeType="text/plain",
112-
text="Resource not found"
113-
)
114-
]
115-
)
116-
)
117-
118-
119-
# Replace the handler after FastMCP has registered its own
120-
_low_level_server.request_handlers[types.ReadResourceRequest] = _read_resource_with_meta
121-
12289
if __name__ == "__main__":
12390
if "--stdio" in sys.argv:
12491
# Claude Desktop mode

tests/e2e/generate-grid-screenshots.spec.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ const SERVERS = [
5454
},
5555
{ key: "map-server", name: "Map Server", dir: "map-server" },
5656
{ key: "pdf-server", name: "PDF Server", dir: "pdf-server" },
57+
{ key: "qr-server", name: "QR Code Server", dir: "qr-server" },
58+
{ key: "say-server", name: "Say Demo", dir: "say-server" },
5759
{
5860
key: "scenario-modeler",
5961
name: "SaaS Scenario Modeler",

0 commit comments

Comments
 (0)