Jeremiah Lowin commited on
Commit
d2069f2
·
1 Parent(s): b5d856c

Ensure openapi resources return valid responses

Browse files
src/fastmcp/server/openapi.py CHANGED
@@ -257,7 +257,7 @@ class OpenAPIResource(Resource):
257
  self._client = client
258
  self._route = route
259
 
260
- async def read(self) -> str:
261
  """Fetch the resource data by making an HTTP request."""
262
  try:
263
  # Extract path parameters from the URI if present
@@ -297,15 +297,15 @@ class OpenAPIResource(Resource):
297
  # Raise for 4xx/5xx responses
298
  response.raise_for_status()
299
 
300
- # Return response content based on mime type
301
- if self.mime_type == "application/json":
302
- try:
303
- return response.json()
304
- except (json.JSONDecodeError, ValueError):
305
- # Fallback to returning the text
306
- return response.text
307
- else:
308
  return response.text
 
 
309
 
310
  except httpx.HTTPStatusError as e:
311
  # Handle HTTP errors (4xx, 5xx)
@@ -367,18 +367,15 @@ class OpenAPIResourceTemplate(ResourceTemplate):
367
  # Raise for 4xx/5xx responses
368
  response.raise_for_status()
369
 
370
- # Determine the mime type from the response
371
- content_type = response.headers.get("content-type", "application/json")
372
- mime_type = content_type.split(";")[0].strip()
373
 
374
- # Return the appropriate data
375
- if mime_type == "application/json":
376
- try:
377
- return response.json()
378
- except (json.JSONDecodeError, ValueError):
379
- return response.text
380
- else:
381
  return response.text
 
 
382
 
383
  except httpx.HTTPStatusError as e:
384
  error_message = (
 
257
  self._client = client
258
  self._route = route
259
 
260
+ async def read(self) -> str | bytes:
261
  """Fetch the resource data by making an HTTP request."""
262
  try:
263
  # Extract path parameters from the URI if present
 
297
  # Raise for 4xx/5xx responses
298
  response.raise_for_status()
299
 
300
+ # Determine content type and return appropriate format
301
+ content_type = response.headers.get("content-type", "").lower()
302
+
303
+ if "application/json" in content_type:
304
+ return str(response.json())
305
+ elif any(ct in content_type for ct in ["text/", "application/xml"]):
 
 
306
  return response.text
307
+ else:
308
+ return response.content
309
 
310
  except httpx.HTTPStatusError as e:
311
  # Handle HTTP errors (4xx, 5xx)
 
367
  # Raise for 4xx/5xx responses
368
  response.raise_for_status()
369
 
370
+ # Determine content type and return appropriate format
371
+ content_type = response.headers.get("content-type", "").lower()
 
372
 
373
+ if "application/json" in content_type:
374
+ return str(response.json())
375
+ elif any(ct in content_type for ct in ["text/", "application/xml"]):
 
 
 
 
376
  return response.text
377
+ else:
378
+ return response.content
379
 
380
  except httpx.HTTPStatusError as e:
381
  error_message = (
tests/server/test_openapi.py CHANGED
@@ -1,12 +1,13 @@
 
1
  import json
2
  import re
3
 
4
  import httpx
5
  import pytest
6
  from dirty_equals import IsStr
7
- from fastapi import FastAPI, HTTPException
8
  from httpx import ASGITransport, AsyncClient
9
- from mcp.types import TextContent
10
  from pydantic import BaseModel, TypeAdapter
11
  from pydantic.networks import AnyUrl
12
 
@@ -66,6 +67,17 @@ def fastapi_app(users_db: dict[int, User]) -> FastAPI:
66
  user.name = name
67
  return user
68
 
 
 
 
 
 
 
 
 
 
 
 
69
  return app
70
 
71
 
@@ -120,7 +132,8 @@ class TestTools:
120
  """
121
  By default, tools exclude GET methods
122
  """
123
- tools = await fastmcp_openapi_server._mcp_list_tools()
 
124
  assert len(tools) == 2
125
 
126
  assert tools[0].model_dump() == dict(
@@ -156,9 +169,10 @@ class TestTools:
156
  """
157
  The tool created by the OpenAPI server should be the same as the original
158
  """
159
- tool_response = await fastmcp_openapi_server._mcp_call_tool(
160
- "create_user_users_post", {"name": "David", "active": False}
161
- )
 
162
 
163
  # Convert TextContent to dict for comparison
164
  assert isinstance(tool_response, list) and len(tool_response) == 1
@@ -173,10 +187,13 @@ class TestTools:
173
  assert len(response.json()) == 4
174
 
175
  # Check that the user was created via MCP
176
- user_response = await fastmcp_openapi_server._mcp_read_resource(
177
- "resource://openapi/get_user_users__user_id__get/4"
178
- )
179
- user = user_response[0].content
 
 
 
180
  assert user == expected_user
181
 
182
  async def test_call_update_user_name_tool(
@@ -185,9 +202,11 @@ class TestTools:
185
  """
186
  The tool created by the OpenAPI server should be the same as the original
187
  """
188
- tool_response = await fastmcp_openapi_server._mcp_call_tool(
189
- "update_user_name_users__user_id__name_patch", {"user_id": 1, "name": "XYZ"}
190
- )
 
 
191
 
192
  # Convert TextContent to dict for comparison
193
  assert isinstance(tool_response, list) and len(tool_response) == 1
@@ -202,10 +221,13 @@ class TestTools:
202
  assert expected_data in response.json()
203
 
204
  # Check that the user was updated via MCP
205
- user_response = await fastmcp_openapi_server._mcp_read_resource(
206
- "resource://openapi/get_user_users__user_id__get/1"
207
- )
208
- user = user_response[0].content
 
 
 
209
  assert user == expected_data
210
 
211
 
@@ -214,8 +236,9 @@ class TestResources:
214
  """
215
  By default, resources exclude GET methods without parameters
216
  """
217
- resources = await fastmcp_openapi_server._mcp_list_resources()
218
- assert len(resources) == 1
 
219
  assert resources[0].uri == AnyUrl("resource://openapi/get_users_users_get")
220
  assert resources[0].name == "get_users_users_get"
221
 
@@ -228,17 +251,47 @@ class TestResources:
228
  """
229
  The resource created by the OpenAPI server should be the same as the original
230
  """
 
231
  json_users = TypeAdapter(list[User]).dump_python(
232
  sorted(users_db.values(), key=lambda x: x.id)
233
  )
234
- resource_response = await fastmcp_openapi_server._mcp_read_resource(
235
- "resource://openapi/get_users_users_get"
236
- )
237
- resource = resource_response[0].content
 
 
 
238
  assert resource == json_users
239
  response = await api_client.get("/users")
240
  assert response.json() == json_users
241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  class TestResourceTemplates:
244
  async def test_list_resource_templates(
@@ -247,7 +300,8 @@ class TestResourceTemplates:
247
  """
248
  By default, resource templates exclude GET methods without parameters
249
  """
250
- resource_templates = await fastmcp_openapi_server._mcp_list_resource_templates()
 
251
  assert len(resource_templates) == 1
252
  assert resource_templates[0].name == "get_user_users__user_id__get"
253
  assert (
@@ -265,11 +319,14 @@ class TestResourceTemplates:
265
  The resource template created by the OpenAPI server should be the same as the original
266
  """
267
  user_id = 2
268
- resource_response = await fastmcp_openapi_server._mcp_read_resource(
269
- f"resource://openapi/get_user_users__user_id__get/{user_id}"
270
- )
 
 
 
 
271
 
272
- resource = resource_response[0].content
273
  assert resource == users_db[user_id].model_dump()
274
  response = await api_client.get(f"/users/{user_id}")
275
  assert resource == response.json()
@@ -280,7 +337,8 @@ class TestPrompts:
280
  """
281
  By default, there are no prompts.
282
  """
283
- prompts = await fastmcp_openapi_server._mcp_list_prompts()
 
284
  assert len(prompts) == 0
285
 
286
 
@@ -494,20 +552,23 @@ class TestOpenAPI30Compatibility:
494
 
495
  async def test_resource_discovery(self, openapi_30_server):
496
  """Test that resources are correctly discovered from an OpenAPI 3.0 spec."""
497
- resources = await openapi_30_server._mcp_list_resources()
 
498
  assert len(resources) == 1
499
  assert resources[0].uri == AnyUrl("resource://openapi/listProducts")
500
 
501
  async def test_resource_template_discovery(self, openapi_30_server):
502
  """Test that resource templates are correctly discovered from an OpenAPI 3.0 spec."""
503
- templates = await openapi_30_server._mcp_list_resource_templates()
 
504
  assert len(templates) == 1
505
  assert templates[0].name == "getProduct"
506
  assert templates[0].uriTemplate == r"resource://openapi/getProduct/{product_id}"
507
 
508
  async def test_tool_discovery(self, openapi_30_server):
509
  """Test that tools are correctly discovered from an OpenAPI 3.0 spec."""
510
- tools = await openapi_30_server._mcp_list_tools()
 
511
  assert len(tools) == 1
512
  assert tools[0].name == "createProduct"
513
  assert "name" in tools[0].inputSchema["properties"]
@@ -515,20 +576,26 @@ class TestOpenAPI30Compatibility:
515
 
516
  async def test_resource_access(self, openapi_30_server):
517
  """Test reading a resource from an OpenAPI 3.0 server."""
518
- resource_response = await openapi_30_server._mcp_read_resource(
519
- "resource://openapi/listProducts"
520
- )
521
- content = resource_response[0].content
 
 
 
522
  assert len(content) == 2
523
  assert content[0]["name"] == "Product 1"
524
  assert content[1]["name"] == "Product 2"
525
 
526
  async def test_resource_template_access(self, openapi_30_server):
527
  """Test reading a resource from template from an OpenAPI 3.0 server."""
528
- resource_response = await openapi_30_server._mcp_read_resource(
529
- "resource://openapi/getProduct/p1"
530
- )
531
- content = resource_response[0].content
 
 
 
532
  assert content["id"] == "p1"
533
  assert content["name"] == "Product 1"
534
  assert content["price"] == 19.99
@@ -665,20 +732,23 @@ class TestOpenAPI31Compatibility:
665
 
666
  async def test_resource_discovery(self, openapi_31_server):
667
  """Test that resources are correctly discovered from an OpenAPI 3.1 spec."""
668
- resources = await openapi_31_server._mcp_list_resources()
 
669
  assert len(resources) == 1
670
  assert resources[0].uri == AnyUrl("resource://openapi/listOrders")
671
 
672
  async def test_resource_template_discovery(self, openapi_31_server):
673
  """Test that resource templates are correctly discovered from an OpenAPI 3.1 spec."""
674
- templates = await openapi_31_server._mcp_list_resource_templates()
 
675
  assert len(templates) == 1
676
  assert templates[0].name == "getOrder"
677
  assert templates[0].uriTemplate == r"resource://openapi/getOrder/{order_id}"
678
 
679
  async def test_tool_discovery(self, openapi_31_server):
680
  """Test that tools are correctly discovered from an OpenAPI 3.1 spec."""
681
- tools = await openapi_31_server._mcp_list_tools()
 
682
  assert len(tools) == 1
683
  assert tools[0].name == "createOrder"
684
  assert "customer" in tools[0].inputSchema["properties"]
@@ -686,20 +756,26 @@ class TestOpenAPI31Compatibility:
686
 
687
  async def test_resource_access(self, openapi_31_server):
688
  """Test reading a resource from an OpenAPI 3.1 server."""
689
- resource_response = await openapi_31_server._mcp_read_resource(
690
- "resource://openapi/listOrders"
691
- )
692
- content = resource_response[0].content
 
 
 
693
  assert len(content) == 2
694
  assert content[0]["customer"] == "Alice"
695
  assert content[1]["customer"] == "Bob"
696
 
697
  async def test_resource_template_access(self, openapi_31_server):
698
  """Test reading a resource from template from an OpenAPI 3.1 server."""
699
- resource_response = await openapi_31_server._mcp_read_resource(
700
- "resource://openapi/getOrder/o1"
701
- )
702
- content = resource_response[0].content
 
 
 
703
  assert content["id"] == "o1"
704
  assert content["customer"] == "Alice"
705
  assert content["items"] == ["item1", "item2"]
@@ -729,8 +805,9 @@ class TestMountFastMCP:
729
  await mcp.import_server("fastapi", fastmcp_openapi_server)
730
 
731
  # Check that resources are available with prefixed URIs
732
- resources = await mcp._mcp_list_resources()
733
- assert len(resources) == 1
 
734
  # We're checking the key used by mcp to store the resource
735
  # The prefixed URI is used as the key, but the resource's original uri is preserved
736
  prefixed_uri = "fastapi+resource://openapi/get_users_users_get"
@@ -738,7 +815,8 @@ class TestMountFastMCP:
738
  assert resource is not None
739
 
740
  # Check that templates are available with prefixed URIs
741
- templates = await mcp._mcp_list_resource_templates()
 
742
  assert len(templates) == 1
743
  assert templates[0].name == "get_user_users__user_id__get"
744
  prefixed_template_uri = (
@@ -748,10 +826,12 @@ class TestMountFastMCP:
748
  assert template is not None
749
 
750
  # Check that tools are available with prefixed names
751
- tools = await mcp._mcp_list_tools()
 
752
  assert len(tools) == 2
753
  assert tools[0].name == "fastapi_create_user_users_post"
754
  assert tools[1].name == "fastapi_update_user_name_users__user_id__name_patch"
755
 
756
- prompts = await mcp._mcp_list_prompts()
 
757
  assert len(prompts) == 0
 
1
+ import base64
2
  import json
3
  import re
4
 
5
  import httpx
6
  import pytest
7
  from dirty_equals import IsStr
8
+ from fastapi import FastAPI, HTTPException, Response
9
  from httpx import ASGITransport, AsyncClient
10
+ from mcp.types import BlobResourceContents, TextContent, TextResourceContents
11
  from pydantic import BaseModel, TypeAdapter
12
  from pydantic.networks import AnyUrl
13
 
 
67
  user.name = name
68
  return user
69
 
70
+ @app.get("/ping")
71
+ async def ping() -> str:
72
+ """Ping the server."""
73
+ return "pong"
74
+
75
+ @app.get("/ping-bytes")
76
+ async def ping_bytes() -> Response:
77
+ """Ping the server and get a bytes response."""
78
+
79
+ return Response(content=b"pong")
80
+
81
  return app
82
 
83
 
 
132
  """
133
  By default, tools exclude GET methods
134
  """
135
+ async with Client(fastmcp_openapi_server) as client:
136
+ tools = await client.list_tools()
137
  assert len(tools) == 2
138
 
139
  assert tools[0].model_dump() == dict(
 
169
  """
170
  The tool created by the OpenAPI server should be the same as the original
171
  """
172
+ async with Client(fastmcp_openapi_server) as client:
173
+ tool_response = await client.call_tool(
174
+ "create_user_users_post", {"name": "David", "active": False}
175
+ )
176
 
177
  # Convert TextContent to dict for comparison
178
  assert isinstance(tool_response, list) and len(tool_response) == 1
 
187
  assert len(response.json()) == 4
188
 
189
  # Check that the user was created via MCP
190
+ async with Client(fastmcp_openapi_server) as client:
191
+ user_response = await client.read_resource(
192
+ "resource://openapi/get_user_users__user_id__get/4"
193
+ )
194
+ assert isinstance(user_response[0], TextResourceContents)
195
+ response_text = user_response[0].text
196
+ user = json.loads(response_text)
197
  assert user == expected_user
198
 
199
  async def test_call_update_user_name_tool(
 
202
  """
203
  The tool created by the OpenAPI server should be the same as the original
204
  """
205
+ async with Client(fastmcp_openapi_server) as client:
206
+ tool_response = await client.call_tool(
207
+ "update_user_name_users__user_id__name_patch",
208
+ {"user_id": 1, "name": "XYZ"},
209
+ )
210
 
211
  # Convert TextContent to dict for comparison
212
  assert isinstance(tool_response, list) and len(tool_response) == 1
 
221
  assert expected_data in response.json()
222
 
223
  # Check that the user was updated via MCP
224
+ async with Client(fastmcp_openapi_server) as client:
225
+ user_response = await client.read_resource(
226
+ "resource://openapi/get_user_users__user_id__get/1"
227
+ )
228
+ assert isinstance(user_response[0], TextResourceContents)
229
+ response_text = user_response[0].text
230
+ user = json.loads(response_text)
231
  assert user == expected_data
232
 
233
 
 
236
  """
237
  By default, resources exclude GET methods without parameters
238
  """
239
+ async with Client(fastmcp_openapi_server) as client:
240
+ resources = await client.list_resources()
241
+ assert len(resources) == 3
242
  assert resources[0].uri == AnyUrl("resource://openapi/get_users_users_get")
243
  assert resources[0].name == "get_users_users_get"
244
 
 
251
  """
252
  The resource created by the OpenAPI server should be the same as the original
253
  """
254
+
255
  json_users = TypeAdapter(list[User]).dump_python(
256
  sorted(users_db.values(), key=lambda x: x.id)
257
  )
258
+ async with Client(fastmcp_openapi_server) as client:
259
+ resource_response = await client.read_resource(
260
+ "resource://openapi/get_users_users_get"
261
+ )
262
+ assert isinstance(resource_response[0], TextResourceContents)
263
+ response_text = resource_response[0].text
264
+ resource = json.loads(response_text)
265
  assert resource == json_users
266
  response = await api_client.get("/users")
267
  assert response.json() == json_users
268
 
269
+ async def test_get_bytes_resource(
270
+ self,
271
+ fastmcp_openapi_server: FastMCPOpenAPI,
272
+ api_client,
273
+ ):
274
+ """Test reading a resource that returns bytes."""
275
+ async with Client(fastmcp_openapi_server) as client:
276
+ resource_response = await client.read_resource(
277
+ "resource://openapi/ping_bytes_ping_bytes_get"
278
+ )
279
+ assert isinstance(resource_response[0], BlobResourceContents)
280
+ assert base64.b64decode(resource_response[0].blob) == b"pong"
281
+
282
+ async def test_get_str_resource(
283
+ self,
284
+ fastmcp_openapi_server: FastMCPOpenAPI,
285
+ api_client,
286
+ ):
287
+ """Test reading a resource that returns a string."""
288
+ async with Client(fastmcp_openapi_server) as client:
289
+ resource_response = await client.read_resource(
290
+ "resource://openapi/ping_ping_get"
291
+ )
292
+ assert isinstance(resource_response[0], TextResourceContents)
293
+ assert resource_response[0].text == "pong"
294
+
295
 
296
  class TestResourceTemplates:
297
  async def test_list_resource_templates(
 
300
  """
301
  By default, resource templates exclude GET methods without parameters
302
  """
303
+ async with Client(fastmcp_openapi_server) as client:
304
+ resource_templates = await client.list_resource_templates()
305
  assert len(resource_templates) == 1
306
  assert resource_templates[0].name == "get_user_users__user_id__get"
307
  assert (
 
319
  The resource template created by the OpenAPI server should be the same as the original
320
  """
321
  user_id = 2
322
+ async with Client(fastmcp_openapi_server) as client:
323
+ resource_response = await client.read_resource(
324
+ f"resource://openapi/get_user_users__user_id__get/{user_id}"
325
+ )
326
+ assert isinstance(resource_response[0], TextResourceContents)
327
+ response_text = resource_response[0].text
328
+ resource = json.loads(response_text)
329
 
 
330
  assert resource == users_db[user_id].model_dump()
331
  response = await api_client.get(f"/users/{user_id}")
332
  assert resource == response.json()
 
337
  """
338
  By default, there are no prompts.
339
  """
340
+ async with Client(fastmcp_openapi_server) as client:
341
+ prompts = await client.list_prompts()
342
  assert len(prompts) == 0
343
 
344
 
 
552
 
553
  async def test_resource_discovery(self, openapi_30_server):
554
  """Test that resources are correctly discovered from an OpenAPI 3.0 spec."""
555
+ async with Client(openapi_30_server) as client:
556
+ resources = await client.list_resources()
557
  assert len(resources) == 1
558
  assert resources[0].uri == AnyUrl("resource://openapi/listProducts")
559
 
560
  async def test_resource_template_discovery(self, openapi_30_server):
561
  """Test that resource templates are correctly discovered from an OpenAPI 3.0 spec."""
562
+ async with Client(openapi_30_server) as client:
563
+ templates = await client.list_resource_templates()
564
  assert len(templates) == 1
565
  assert templates[0].name == "getProduct"
566
  assert templates[0].uriTemplate == r"resource://openapi/getProduct/{product_id}"
567
 
568
  async def test_tool_discovery(self, openapi_30_server):
569
  """Test that tools are correctly discovered from an OpenAPI 3.0 spec."""
570
+ async with Client(openapi_30_server) as client:
571
+ tools = await client.list_tools()
572
  assert len(tools) == 1
573
  assert tools[0].name == "createProduct"
574
  assert "name" in tools[0].inputSchema["properties"]
 
576
 
577
  async def test_resource_access(self, openapi_30_server):
578
  """Test reading a resource from an OpenAPI 3.0 server."""
579
+ async with Client(openapi_30_server) as client:
580
+ resource_response = await client.read_resource(
581
+ "resource://openapi/listProducts"
582
+ )
583
+ assert isinstance(resource_response[0], TextResourceContents)
584
+ response_text = resource_response[0].text
585
+ content = json.loads(response_text)
586
  assert len(content) == 2
587
  assert content[0]["name"] == "Product 1"
588
  assert content[1]["name"] == "Product 2"
589
 
590
  async def test_resource_template_access(self, openapi_30_server):
591
  """Test reading a resource from template from an OpenAPI 3.0 server."""
592
+ async with Client(openapi_30_server) as client:
593
+ resource_response = await client.read_resource(
594
+ "resource://openapi/getProduct/p1"
595
+ )
596
+ assert isinstance(resource_response[0], TextResourceContents)
597
+ response_text = resource_response[0].text
598
+ content = json.loads(response_text)
599
  assert content["id"] == "p1"
600
  assert content["name"] == "Product 1"
601
  assert content["price"] == 19.99
 
732
 
733
  async def test_resource_discovery(self, openapi_31_server):
734
  """Test that resources are correctly discovered from an OpenAPI 3.1 spec."""
735
+ async with Client(openapi_31_server) as client:
736
+ resources = await client.list_resources()
737
  assert len(resources) == 1
738
  assert resources[0].uri == AnyUrl("resource://openapi/listOrders")
739
 
740
  async def test_resource_template_discovery(self, openapi_31_server):
741
  """Test that resource templates are correctly discovered from an OpenAPI 3.1 spec."""
742
+ async with Client(openapi_31_server) as client:
743
+ templates = await client.list_resource_templates()
744
  assert len(templates) == 1
745
  assert templates[0].name == "getOrder"
746
  assert templates[0].uriTemplate == r"resource://openapi/getOrder/{order_id}"
747
 
748
  async def test_tool_discovery(self, openapi_31_server):
749
  """Test that tools are correctly discovered from an OpenAPI 3.1 spec."""
750
+ async with Client(openapi_31_server) as client:
751
+ tools = await client.list_tools()
752
  assert len(tools) == 1
753
  assert tools[0].name == "createOrder"
754
  assert "customer" in tools[0].inputSchema["properties"]
 
756
 
757
  async def test_resource_access(self, openapi_31_server):
758
  """Test reading a resource from an OpenAPI 3.1 server."""
759
+ async with Client(openapi_31_server) as client:
760
+ resource_response = await client.read_resource(
761
+ "resource://openapi/listOrders"
762
+ )
763
+ assert isinstance(resource_response[0], TextResourceContents)
764
+ response_text = resource_response[0].text
765
+ content = json.loads(response_text)
766
  assert len(content) == 2
767
  assert content[0]["customer"] == "Alice"
768
  assert content[1]["customer"] == "Bob"
769
 
770
  async def test_resource_template_access(self, openapi_31_server):
771
  """Test reading a resource from template from an OpenAPI 3.1 server."""
772
+ async with Client(openapi_31_server) as client:
773
+ resource_response = await client.read_resource(
774
+ "resource://openapi/getOrder/o1"
775
+ )
776
+ assert isinstance(resource_response[0], TextResourceContents)
777
+ response_text = resource_response[0].text
778
+ content = json.loads(response_text)
779
  assert content["id"] == "o1"
780
  assert content["customer"] == "Alice"
781
  assert content["items"] == ["item1", "item2"]
 
805
  await mcp.import_server("fastapi", fastmcp_openapi_server)
806
 
807
  # Check that resources are available with prefixed URIs
808
+ async with Client(mcp) as client:
809
+ resources = await client.list_resources()
810
+ assert len(resources) == 3
811
  # We're checking the key used by mcp to store the resource
812
  # The prefixed URI is used as the key, but the resource's original uri is preserved
813
  prefixed_uri = "fastapi+resource://openapi/get_users_users_get"
 
815
  assert resource is not None
816
 
817
  # Check that templates are available with prefixed URIs
818
+ async with Client(mcp) as client:
819
+ templates = await client.list_resource_templates()
820
  assert len(templates) == 1
821
  assert templates[0].name == "get_user_users__user_id__get"
822
  prefixed_template_uri = (
 
826
  assert template is not None
827
 
828
  # Check that tools are available with prefixed names
829
+ async with Client(mcp) as client:
830
+ tools = await client.list_tools()
831
  assert len(tools) == 2
832
  assert tools[0].name == "fastapi_create_user_users_post"
833
  assert tools[1].name == "fastapi_update_user_name_users__user_id__name_patch"
834
 
835
+ async with Client(mcp) as client:
836
+ prompts = await client.list_prompts()
837
  assert len(prompts) == 0