Skip to content

Commit fc90442

Browse files
committed
tests: add integ tests for mcp resources
1 parent 5f29723 commit fc90442

File tree

2 files changed

+148
-0
lines changed

2 files changed

+148
-0
lines changed

tests_integ/mcp/echo_server.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
"""
1717

1818
import base64
19+
import json
1920
from typing import Literal
2021

2122
from mcp.server import FastMCP
2223
from mcp.types import BlobResourceContents, CallToolResult, EmbeddedResource, TextContent, TextResourceContents
2324
from pydantic import BaseModel
2425

26+
TEST_IMAGE_BASE64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="
27+
2528

2629
class EchoResponse(BaseModel):
2730
"""Response model for echo with structured content."""
@@ -102,6 +105,22 @@ def get_weather(location: Literal["New York", "London", "Tokyo"] = "New York"):
102105
)
103106
]
104107

108+
# Resources
109+
@mcp.resource("test://static-text")
110+
def static_text_resource() -> str:
111+
"""A static text resource for testing"""
112+
return "This is the content of the static text resource."
113+
114+
@mcp.resource("test://static-binary")
115+
def static_binary_resource() -> bytes:
116+
"""A static binary resource (image) for testing"""
117+
return base64.b64decode(TEST_IMAGE_BASE64)
118+
119+
@mcp.resource("test://template/{id}/data")
120+
def template_resource(id: str) -> str:
121+
"""A resource template with parameter substitution"""
122+
return json.dumps({"id": id, "templateTest": True, "data": f"Data for ID: {id}"})
123+
105124
mcp.run(transport="stdio")
106125

107126

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
"""
2+
Integration tests for MCP client resource functionality.
3+
4+
This module tests the resource-related methods in MCPClient:
5+
- list_resources_sync()
6+
- read_resource_sync()
7+
- list_resource_templates_sync()
8+
9+
The tests use the echo server which has been extended with resource functionality.
10+
"""
11+
12+
import base64
13+
import json
14+
15+
import pytest
16+
from mcp import StdioServerParameters, stdio_client
17+
from mcp.types import BlobResourceContents, TextResourceContents
18+
from pydantic import AnyUrl
19+
20+
from strands.tools.mcp.mcp_client import MCPClient
21+
22+
23+
def test_mcp_resources_list_and_read():
24+
"""Test listing and reading various types of resources."""
25+
mcp_client = MCPClient(
26+
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
27+
)
28+
29+
with mcp_client:
30+
# Test list_resources_sync
31+
resources_result = mcp_client.list_resources_sync()
32+
assert len(resources_result.resources) >= 2 # At least our 2 static resources
33+
34+
# Verify resource URIs exist (only static resources, not templates)
35+
resource_uris = [str(r.uri) for r in resources_result.resources]
36+
assert "test://static-text" in resource_uris
37+
assert "test://static-binary" in resource_uris
38+
# Template resources are not listed in static resources
39+
40+
# Test reading text resource
41+
text_resource = mcp_client.read_resource_sync("test://static-text")
42+
assert len(text_resource.contents) == 1
43+
content = text_resource.contents[0]
44+
assert isinstance(content, TextResourceContents)
45+
assert "This is the content of the static text resource." in content.text
46+
47+
# Test reading binary resource
48+
binary_resource = mcp_client.read_resource_sync("test://static-binary")
49+
assert len(binary_resource.contents) == 1
50+
binary_content = binary_resource.contents[0]
51+
assert isinstance(binary_content, BlobResourceContents)
52+
# Verify it's valid base64 encoded data
53+
decoded_data = base64.b64decode(binary_content.blob)
54+
assert len(decoded_data) > 0
55+
56+
57+
def test_mcp_resources_templates():
58+
"""Test listing resource templates and reading from template resources."""
59+
mcp_client = MCPClient(
60+
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
61+
)
62+
63+
with mcp_client:
64+
# Test list_resource_templates_sync
65+
templates_result = mcp_client.list_resource_templates_sync()
66+
assert len(templates_result.resourceTemplates) >= 1
67+
68+
# Verify template URIs exist
69+
template_uris = [t.uriTemplate for t in templates_result.resourceTemplates]
70+
assert "test://template/{id}/data" in template_uris
71+
72+
# Test reading from template resource
73+
template_resource = mcp_client.read_resource_sync("test://template/123/data")
74+
assert len(template_resource.contents) == 1
75+
template_content = template_resource.contents[0]
76+
assert isinstance(template_content, TextResourceContents)
77+
78+
# Parse the JSON response
79+
parsed_json = json.loads(template_content.text)
80+
assert parsed_json["id"] == "123"
81+
assert parsed_json["templateTest"] is True
82+
assert "Data for ID: 123" in parsed_json["data"]
83+
84+
85+
def test_mcp_resources_pagination():
86+
"""Test pagination support for resources."""
87+
mcp_client = MCPClient(
88+
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
89+
)
90+
91+
with mcp_client:
92+
# Test with pagination token (should work even if server doesn't implement pagination)
93+
resources_result = mcp_client.list_resources_sync(pagination_token=None)
94+
assert len(resources_result.resources) >= 0
95+
96+
# Test resource templates pagination
97+
templates_result = mcp_client.list_resource_templates_sync(pagination_token=None)
98+
assert len(templates_result.resourceTemplates) >= 0
99+
100+
101+
def test_mcp_resources_error_handling():
102+
"""Test error handling for resource operations."""
103+
mcp_client = MCPClient(
104+
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
105+
)
106+
107+
with mcp_client:
108+
# Test reading non-existent resource
109+
with pytest.raises(Exception): # Should raise an exception for non-existent resource
110+
mcp_client.read_resource_sync("test://nonexistent")
111+
112+
113+
def test_mcp_resources_uri_types():
114+
"""Test that both string and AnyUrl types work for read_resource_sync."""
115+
mcp_client = MCPClient(
116+
lambda: stdio_client(StdioServerParameters(command="python", args=["tests_integ/mcp/echo_server.py"]))
117+
)
118+
119+
with mcp_client:
120+
# Test with string URI
121+
text_resource_str = mcp_client.read_resource_sync("test://static-text")
122+
assert len(text_resource_str.contents) == 1
123+
124+
# Test with AnyUrl URI
125+
text_resource_url = mcp_client.read_resource_sync(AnyUrl("test://static-text"))
126+
assert len(text_resource_url.contents) == 1
127+
128+
# Both should return the same content
129+
assert text_resource_str.contents[0].text == text_resource_url.contents[0].text

0 commit comments

Comments
 (0)