Jeremiah Lowin commited on
Commit
d4fceae
·
1 Parent(s): 0c34dfa

Update test typing

Browse files
tests/client/test_client.py CHANGED
@@ -16,7 +16,6 @@ from fastmcp.client.transports import (
16
  infer_transport,
17
  )
18
  from fastmcp.exceptions import ResourceError, ToolError
19
- from fastmcp.prompts.prompt import TextContent
20
  from fastmcp.server.server import FastMCP
21
 
22
 
@@ -201,8 +200,7 @@ async def test_get_prompt(fastmcp_server):
201
  result = await client.get_prompt("welcome", {"name": "Developer"})
202
 
203
  # The result should contain our welcome message
204
- assert isinstance(result.messages[0].content, TextContent)
205
- assert result.messages[0].content.text == "Welcome to FastMCP, Developer!"
206
  assert result.description == "Example greeting prompt."
207
 
208
 
@@ -214,8 +212,7 @@ async def test_get_prompt_mcp(fastmcp_server):
214
  result = await client.get_prompt_mcp("welcome", {"name": "Developer"})
215
 
216
  # The result should contain our welcome message
217
- assert isinstance(result.messages[0].content, TextContent)
218
- assert result.messages[0].content.text == "Welcome to FastMCP, Developer!"
219
  assert result.description == "Example greeting prompt."
220
 
221
 
@@ -522,9 +519,8 @@ class TestErrorHandling:
522
  async with client:
523
  result = await client.call_tool_mcp("error_tool", {})
524
  assert result.isError
525
- assert isinstance(result.content[0], TextContent)
526
- assert "test error" in result.content[0].text
527
- assert "abc" in result.content[0].text
528
 
529
  async def test_general_tool_exceptions_are_masked_when_enabled(self):
530
  mcp = FastMCP("TestServer", mask_error_details=True)
@@ -538,9 +534,8 @@ class TestErrorHandling:
538
  async with client:
539
  result = await client.call_tool_mcp("error_tool", {})
540
  assert result.isError
541
- assert isinstance(result.content[0], TextContent)
542
- assert "test error" not in result.content[0].text
543
- assert "abc" not in result.content[0].text
544
 
545
  async def test_specific_tool_errors_are_sent_to_client(self):
546
  mcp = FastMCP("TestServer")
@@ -554,9 +549,8 @@ class TestErrorHandling:
554
  async with client:
555
  result = await client.call_tool_mcp("custom_error_tool", {})
556
  assert result.isError
557
- assert isinstance(result.content[0], TextContent)
558
- assert "test error" in result.content[0].text
559
- assert "abc" in result.content[0].text
560
 
561
  async def test_general_resource_exceptions_are_not_masked_by_default(self):
562
  mcp = FastMCP("TestServer")
 
16
  infer_transport,
17
  )
18
  from fastmcp.exceptions import ResourceError, ToolError
 
19
  from fastmcp.server.server import FastMCP
20
 
21
 
 
200
  result = await client.get_prompt("welcome", {"name": "Developer"})
201
 
202
  # The result should contain our welcome message
203
+ assert result.messages[0].content.text == "Welcome to FastMCP, Developer!" # type: ignore[attr-defined]
 
204
  assert result.description == "Example greeting prompt."
205
 
206
 
 
212
  result = await client.get_prompt_mcp("welcome", {"name": "Developer"})
213
 
214
  # The result should contain our welcome message
215
+ assert result.messages[0].content.text == "Welcome to FastMCP, Developer!" # type: ignore[attr-defined]
 
216
  assert result.description == "Example greeting prompt."
217
 
218
 
 
519
  async with client:
520
  result = await client.call_tool_mcp("error_tool", {})
521
  assert result.isError
522
+ assert "test error" in result.content[0].text # type: ignore[attr-defined]
523
+ assert "abc" in result.content[0].text # type: ignore[attr-defined]
 
524
 
525
  async def test_general_tool_exceptions_are_masked_when_enabled(self):
526
  mcp = FastMCP("TestServer", mask_error_details=True)
 
534
  async with client:
535
  result = await client.call_tool_mcp("error_tool", {})
536
  assert result.isError
537
+ assert "test error" not in result.content[0].text # type: ignore[attr-defined]
538
+ assert "abc" not in result.content[0].text # type: ignore[attr-defined]
 
539
 
540
  async def test_specific_tool_errors_are_sent_to_client(self):
541
  mcp = FastMCP("TestServer")
 
549
  async with client:
550
  result = await client.call_tool_mcp("custom_error_tool", {})
551
  assert result.isError
552
+ assert "test error" in result.content[0].text # type: ignore[attr-defined]
553
+ assert "abc" in result.content[0].text # type: ignore[attr-defined]
 
554
 
555
  async def test_general_resource_exceptions_are_not_masked_by_default(self):
556
  mcp = FastMCP("TestServer")
tests/client/test_openapi.py CHANGED
@@ -5,7 +5,6 @@ from collections.abc import Generator
5
  import pytest
6
  import uvicorn
7
  from fastapi import FastAPI, Request
8
- from mcp.types import TextContent, TextResourceContents
9
 
10
  from fastmcp import Client, FastMCP
11
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
@@ -111,8 +110,7 @@ class TestClientHeaders:
111
  transport=SSETransport(sse_server, headers={"X-TEST": "test-123"})
112
  ) as client:
113
  result = await client.read_resource("resource://get_headers_headers_get")
114
- assert isinstance(result[0], TextResourceContents)
115
- headers = json.loads(result[0].text)
116
  assert headers["x-test"] == "test-123"
117
 
118
  async def test_client_headers_shttp_resource(self, shttp_server: str):
@@ -122,8 +120,7 @@ class TestClientHeaders:
122
  )
123
  ) as client:
124
  result = await client.read_resource("resource://get_headers_headers_get")
125
- assert isinstance(result[0], TextResourceContents)
126
- headers = json.loads(result[0].text)
127
  assert headers["x-test"] == "test-123"
128
 
129
  async def test_client_headers_sse_resource_template(self, sse_server: str):
@@ -133,8 +130,7 @@ class TestClientHeaders:
133
  result = await client.read_resource(
134
  "resource://get_header_by_name_headers/x-test"
135
  )
136
- assert isinstance(result[0], TextResourceContents)
137
- header = json.loads(result[0].text)
138
  assert header == "test-123"
139
 
140
  async def test_client_headers_shttp_resource_template(self, shttp_server: str):
@@ -146,8 +142,7 @@ class TestClientHeaders:
146
  result = await client.read_resource(
147
  "resource://get_header_by_name_headers/x-test"
148
  )
149
- assert isinstance(result[0], TextResourceContents)
150
- header = json.loads(result[0].text)
151
  assert header == "test-123"
152
 
153
  async def test_client_headers_sse_tool(self, sse_server: str):
@@ -155,8 +150,7 @@ class TestClientHeaders:
155
  transport=SSETransport(sse_server, headers={"X-TEST": "test-123"})
156
  ) as client:
157
  result = await client.call_tool("post_headers_headers_post")
158
- assert isinstance(result[0], TextContent)
159
- headers = json.loads(result[0].text)
160
  assert headers["x-test"] == "test-123"
161
 
162
  async def test_client_headers_shttp_tool(self, shttp_server: str):
@@ -166,8 +160,7 @@ class TestClientHeaders:
166
  )
167
  ) as client:
168
  result = await client.call_tool("post_headers_headers_post")
169
- assert isinstance(result[0], TextContent)
170
- headers = json.loads(result[0].text)
171
  assert headers["x-test"] == "test-123"
172
 
173
  async def test_client_overrides_server_headers(self, shttp_server: str):
@@ -177,8 +170,7 @@ class TestClientHeaders:
177
  )
178
  ) as client:
179
  result = await client.read_resource("resource://get_headers_headers_get")
180
- assert isinstance(result[0], TextResourceContents)
181
- headers = json.loads(result[0].text)
182
  assert headers["x-server-header"] == "test-client"
183
 
184
  async def test_client_with_excluded_header_is_ignored(self, sse_server: str):
@@ -193,8 +185,7 @@ class TestClientHeaders:
193
  )
194
  ) as client:
195
  result = await client.read_resource("resource://get_headers_headers_get")
196
- assert isinstance(result[0], TextResourceContents)
197
- headers = json.loads(result[0].text)
198
  assert headers["not-host"] == "1.2.3.4"
199
  assert headers["host"] == "fastapi"
200
 
@@ -204,6 +195,5 @@ class TestClientHeaders:
204
  """
205
  async with Client(transport=StreamableHttpTransport(proxy_server)) as client:
206
  result = await client.read_resource("resource://get_headers_headers_get")
207
- assert isinstance(result[0], TextResourceContents)
208
- headers = json.loads(result[0].text)
209
  assert headers["x-server-header"] == "test-abc"
 
5
  import pytest
6
  import uvicorn
7
  from fastapi import FastAPI, Request
 
8
 
9
  from fastmcp import Client, FastMCP
10
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
 
110
  transport=SSETransport(sse_server, headers={"X-TEST": "test-123"})
111
  ) as client:
112
  result = await client.read_resource("resource://get_headers_headers_get")
113
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
114
  assert headers["x-test"] == "test-123"
115
 
116
  async def test_client_headers_shttp_resource(self, shttp_server: str):
 
120
  )
121
  ) as client:
122
  result = await client.read_resource("resource://get_headers_headers_get")
123
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
124
  assert headers["x-test"] == "test-123"
125
 
126
  async def test_client_headers_sse_resource_template(self, sse_server: str):
 
130
  result = await client.read_resource(
131
  "resource://get_header_by_name_headers/x-test"
132
  )
133
+ header = json.loads(result[0].text) # type: ignore[attr-defined]
 
134
  assert header == "test-123"
135
 
136
  async def test_client_headers_shttp_resource_template(self, shttp_server: str):
 
142
  result = await client.read_resource(
143
  "resource://get_header_by_name_headers/x-test"
144
  )
145
+ header = json.loads(result[0].text) # type: ignore[attr-defined]
 
146
  assert header == "test-123"
147
 
148
  async def test_client_headers_sse_tool(self, sse_server: str):
 
150
  transport=SSETransport(sse_server, headers={"X-TEST": "test-123"})
151
  ) as client:
152
  result = await client.call_tool("post_headers_headers_post")
153
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
154
  assert headers["x-test"] == "test-123"
155
 
156
  async def test_client_headers_shttp_tool(self, shttp_server: str):
 
160
  )
161
  ) as client:
162
  result = await client.call_tool("post_headers_headers_post")
163
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
164
  assert headers["x-test"] == "test-123"
165
 
166
  async def test_client_overrides_server_headers(self, shttp_server: str):
 
170
  )
171
  ) as client:
172
  result = await client.read_resource("resource://get_headers_headers_get")
173
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
174
  assert headers["x-server-header"] == "test-client"
175
 
176
  async def test_client_with_excluded_header_is_ignored(self, sse_server: str):
 
185
  )
186
  ) as client:
187
  result = await client.read_resource("resource://get_headers_headers_get")
188
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
189
  assert headers["not-host"] == "1.2.3.4"
190
  assert headers["host"] == "fastapi"
191
 
 
195
  """
196
  async with Client(transport=StreamableHttpTransport(proxy_server)) as client:
197
  result = await client.read_resource("resource://get_headers_headers_get")
198
+ headers = json.loads(result[0].text) # type: ignore[attr-defined]
 
199
  assert headers["x-server-header"] == "test-abc"
tests/client/test_roots.py CHANGED
@@ -1,7 +1,6 @@
1
  import json
2
 
3
  import pytest
4
- from mcp.types import TextContent
5
 
6
  from fastmcp import Client, Context, FastMCP
7
 
@@ -41,8 +40,7 @@ class TestClientRoots:
41
  async def test_valid_roots(self, fastmcp_server: FastMCP, roots: list[str]):
42
  async with Client(fastmcp_server, roots=roots) as client:
43
  result = await client.call_tool("list_roots", {})
44
- assert isinstance(result[0], TextContent)
45
- assert json.loads(result[0].text) == [
46
  "file://x/y/z",
47
  "file://x/y/z",
48
  ]
 
1
  import json
2
 
3
  import pytest
 
4
 
5
  from fastmcp import Client, Context, FastMCP
6
 
 
40
  async def test_valid_roots(self, fastmcp_server: FastMCP, roots: list[str]):
41
  async with Client(fastmcp_server, roots=roots) as client:
42
  result = await client.call_tool("list_roots", {})
43
+ assert json.loads(result[0].text) == [ # type: ignore[attr-defined]
 
44
  "file://x/y/z",
45
  "file://x/y/z",
46
  ]
tests/client/test_sse.py CHANGED
@@ -6,7 +6,6 @@ from collections.abc import Generator
6
  import pytest
7
  import uvicorn
8
  from mcp import McpError
9
- from mcp.types import TextResourceContents
10
  from starlette.applications import Starlette
11
  from starlette.routing import Mount
12
 
@@ -96,8 +95,7 @@ async def test_http_headers(sse_server: str):
96
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
97
  ) as client:
98
  raw_result = await client.read_resource("request://headers")
99
- assert isinstance(raw_result[0], TextResourceContents)
100
- json_result = json.loads(raw_result[0].text)
101
  assert "x-demo-header" in json_result
102
  assert json_result["x-demo-header"] == "ABC"
103
 
 
6
  import pytest
7
  import uvicorn
8
  from mcp import McpError
 
9
  from starlette.applications import Starlette
10
  from starlette.routing import Mount
11
 
 
95
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
96
  ) as client:
97
  raw_result = await client.read_resource("request://headers")
98
+ json_result = json.loads(raw_result[0].text) # type: ignore[attr-defined]
 
99
  assert "x-demo-header" in json_result
100
  assert json_result["x-demo-header"] == "ABC"
101
 
tests/client/test_stdio.py CHANGED
@@ -1,7 +1,6 @@
1
  import inspect
2
 
3
  import pytest
4
- from mcp.types import TextContent
5
 
6
  from fastmcp import Client
7
  from fastmcp.client.transports import PythonStdioTransport, StdioTransport
@@ -49,13 +48,11 @@ class TestKeepAlive:
49
 
50
  async with client:
51
  result1 = await client.call_tool("pid")
52
- assert isinstance(result1[0], TextContent)
53
- pid1 = int(result1[0].text)
54
 
55
  async with client:
56
  result2 = await client.call_tool("pid")
57
- assert isinstance(result2[0], TextContent)
58
- pid2 = int(result2[0].text)
59
 
60
  assert pid1 == pid2
61
 
@@ -69,13 +66,11 @@ class TestKeepAlive:
69
 
70
  async with client:
71
  result1 = await client.call_tool("pid")
72
- assert isinstance(result1[0], TextContent)
73
- pid1 = int(result1[0].text)
74
 
75
  async with client:
76
  result2 = await client.call_tool("pid")
77
- assert isinstance(result2[0], TextContent)
78
- pid2 = int(result2[0].text)
79
 
80
  assert pid1 != pid2
81
 
@@ -85,15 +80,13 @@ class TestKeepAlive:
85
 
86
  async with client:
87
  result1 = await client.call_tool("pid")
88
- assert isinstance(result1[0], TextContent)
89
- pid1 = int(result1[0].text)
90
 
91
  await client.close()
92
 
93
  async with client:
94
  result2 = await client.call_tool("pid")
95
- assert isinstance(result2[0], TextContent)
96
- pid2 = int(result2[0].text)
97
 
98
  assert pid1 != pid2
99
 
@@ -103,17 +96,14 @@ class TestKeepAlive:
103
 
104
  async with client:
105
  result1 = await client.call_tool("pid")
106
- assert isinstance(result1[0], TextContent)
107
- pid1 = int(result1[0].text)
108
 
109
  async with client:
110
  result2 = await client.call_tool("pid")
111
- assert isinstance(result2[0], TextContent)
112
- pid2 = int(result2[0].text)
113
 
114
  result3 = await client.call_tool("pid")
115
- assert isinstance(result3[0], TextContent)
116
- pid3 = int(result3[0].text)
117
 
118
  assert pid1 == pid2 == pid3
119
 
 
1
  import inspect
2
 
3
  import pytest
 
4
 
5
  from fastmcp import Client
6
  from fastmcp.client.transports import PythonStdioTransport, StdioTransport
 
48
 
49
  async with client:
50
  result1 = await client.call_tool("pid")
51
+ pid1 = int(result1[0].text) # type: ignore[attr-defined]
 
52
 
53
  async with client:
54
  result2 = await client.call_tool("pid")
55
+ pid2 = int(result2[0].text) # type: ignore[attr-defined]
 
56
 
57
  assert pid1 == pid2
58
 
 
66
 
67
  async with client:
68
  result1 = await client.call_tool("pid")
69
+ pid1 = int(result1[0].text) # type: ignore[attr-defined]
 
70
 
71
  async with client:
72
  result2 = await client.call_tool("pid")
73
+ pid2 = int(result2[0].text) # type: ignore[attr-defined]
 
74
 
75
  assert pid1 != pid2
76
 
 
80
 
81
  async with client:
82
  result1 = await client.call_tool("pid")
83
+ pid1 = int(result1[0].text) # type: ignore[attr-defined]
 
84
 
85
  await client.close()
86
 
87
  async with client:
88
  result2 = await client.call_tool("pid")
89
+ pid2 = int(result2[0].text) # type: ignore[attr-defined]
 
90
 
91
  assert pid1 != pid2
92
 
 
96
 
97
  async with client:
98
  result1 = await client.call_tool("pid")
99
+ pid1 = int(result1[0].text) # type: ignore[attr-defined]
 
100
 
101
  async with client:
102
  result2 = await client.call_tool("pid")
103
+ pid2 = int(result2[0].text) # type: ignore[attr-defined]
 
104
 
105
  result3 = await client.call_tool("pid")
106
+ pid3 = int(result3[0].text) # type: ignore[attr-defined]
 
107
 
108
  assert pid1 == pid2 == pid3
109
 
tests/client/test_streamable_http.py CHANGED
@@ -6,7 +6,6 @@ from collections.abc import Generator
6
  import pytest
7
  import uvicorn
8
  from mcp import McpError
9
- from mcp.types import TextResourceContents
10
  from starlette.applications import Starlette
11
  from starlette.routing import Mount
12
 
@@ -106,8 +105,7 @@ async def test_http_headers(streamable_http_server: str):
106
  )
107
  ) as client:
108
  raw_result = await client.read_resource("request://headers")
109
- assert isinstance(raw_result[0], TextResourceContents)
110
- json_result = json.loads(raw_result[0].text)
111
  assert "x-demo-header" in json_result
112
  assert json_result["x-demo-header"] == "ABC"
113
 
 
6
  import pytest
7
  import uvicorn
8
  from mcp import McpError
 
9
  from starlette.applications import Starlette
10
  from starlette.routing import Mount
11
 
 
105
  )
106
  ) as client:
107
  raw_result = await client.read_resource("request://headers")
108
+ json_result = json.loads(raw_result[0].text) # type: ignore[attr-defined]
 
109
  assert "x-demo-header" in json_result
110
  assert json_result["x-demo-header"] == "ABC"
111
 
tests/prompts/test_prompt_manager.py CHANGED
@@ -393,8 +393,7 @@ class TestContextHandling:
393
  messages = await prompt.render(arguments={"x": 42})
394
 
395
  assert len(messages) == 1
396
- assert isinstance(messages[0].content, TextContent)
397
- assert messages[0].content.text == "42"
398
 
399
  async def test_context_optional(self):
400
  """Test that context is optional when rendering prompts."""
@@ -416,8 +415,7 @@ class TestContextHandling:
416
  )
417
 
418
  assert len(messages) == 1
419
- assert isinstance(messages[0].content, TextContent)
420
- assert messages[0].content.text == "42"
421
 
422
  async def test_annotated_context_parameter_detection(self):
423
  """Test that annotated context parameters are properly detected in
 
393
  messages = await prompt.render(arguments={"x": 42})
394
 
395
  assert len(messages) == 1
396
+ assert messages[0].content.text == "42" # type: ignore[attr-defined]
 
397
 
398
  async def test_context_optional(self):
399
  """Test that context is optional when rendering prompts."""
 
415
  )
416
 
417
  assert len(messages) == 1
418
+ assert messages[0].content.text == "42" # type: ignore[attr-defined]
 
419
 
420
  async def test_annotated_context_parameter_detection(self):
421
  """Test that annotated context parameters are properly detected in
tests/resources/test_file_resources.py CHANGED
@@ -74,7 +74,6 @@ class TestFileResource:
74
  is_binary=True,
75
  )
76
  content = await resource.read()
77
- assert isinstance(content, bytes)
78
  assert content == b"test content"
79
 
80
  def test_relative_path_error(self):
 
74
  is_binary=True,
75
  )
76
  content = await resource.read()
 
77
  assert content == b"test content"
78
 
79
  def test_relative_path_error(self):
tests/server/http/test_http_dependencies.py CHANGED
@@ -4,7 +4,6 @@ from collections.abc import Generator
4
 
5
  import pytest
6
  import uvicorn
7
- from mcp.types import TextContent, TextResourceContents
8
 
9
  from fastmcp.client import Client
10
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
@@ -99,8 +98,7 @@ async def test_http_headers_resource_shttp(shttp_server: str):
99
  )
100
  ) as client:
101
  raw_result = await client.read_resource("request://headers")
102
- assert isinstance(raw_result[0], TextResourceContents)
103
- json_result = json.loads(raw_result[0].text)
104
  assert "x-demo-header" in json_result
105
  assert json_result["x-demo-header"] == "ABC"
106
 
@@ -111,8 +109,7 @@ async def test_http_headers_resource_sse(sse_server: str):
111
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
112
  ) as client:
113
  raw_result = await client.read_resource("request://headers")
114
- assert isinstance(raw_result[0], TextResourceContents)
115
- json_result = json.loads(raw_result[0].text)
116
  assert "x-demo-header" in json_result
117
  assert json_result["x-demo-header"] == "ABC"
118
 
@@ -125,8 +122,7 @@ async def test_http_headers_tool_shttp(shttp_server: str):
125
  )
126
  ) as client:
127
  result = await client.call_tool("get_headers_tool")
128
- assert isinstance(result[0], TextContent)
129
- json_result = json.loads(result[0].text)
130
  assert "x-demo-header" in json_result
131
  assert json_result["x-demo-header"] == "ABC"
132
 
@@ -136,8 +132,7 @@ async def test_http_headers_tool_sse(sse_server: str):
136
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
137
  ) as client:
138
  result = await client.call_tool("get_headers_tool")
139
- assert isinstance(result[0], TextContent)
140
- json_result = json.loads(result[0].text)
141
  assert "x-demo-header" in json_result
142
  assert json_result["x-demo-header"] == "ABC"
143
 
@@ -150,8 +145,7 @@ async def test_http_headers_prompt_shttp(shttp_server: str):
150
  )
151
  ) as client:
152
  result = await client.get_prompt("get_headers_prompt")
153
- assert isinstance(result.messages[0].content, TextContent)
154
- json_result = json.loads(result.messages[0].content.text)
155
  assert "x-demo-header" in json_result
156
  assert json_result["x-demo-header"] == "ABC"
157
 
@@ -162,7 +156,6 @@ async def test_http_headers_prompt_sse(sse_server: str):
162
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
163
  ) as client:
164
  result = await client.get_prompt("get_headers_prompt")
165
- assert isinstance(result.messages[0].content, TextContent)
166
- json_result = json.loads(result.messages[0].content.text)
167
  assert "x-demo-header" in json_result
168
  assert json_result["x-demo-header"] == "ABC"
 
4
 
5
  import pytest
6
  import uvicorn
 
7
 
8
  from fastmcp.client import Client
9
  from fastmcp.client.transports import SSETransport, StreamableHttpTransport
 
98
  )
99
  ) as client:
100
  raw_result = await client.read_resource("request://headers")
101
+ json_result = json.loads(raw_result[0].text) # type: ignore[attr-defined]
 
102
  assert "x-demo-header" in json_result
103
  assert json_result["x-demo-header"] == "ABC"
104
 
 
109
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
110
  ) as client:
111
  raw_result = await client.read_resource("request://headers")
112
+ json_result = json.loads(raw_result[0].text) # type: ignore[attr-defined]
 
113
  assert "x-demo-header" in json_result
114
  assert json_result["x-demo-header"] == "ABC"
115
 
 
122
  )
123
  ) as client:
124
  result = await client.call_tool("get_headers_tool")
125
+ json_result = json.loads(result[0].text) # type: ignore[attr-defined]
 
126
  assert "x-demo-header" in json_result
127
  assert json_result["x-demo-header"] == "ABC"
128
 
 
132
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
133
  ) as client:
134
  result = await client.call_tool("get_headers_tool")
135
+ json_result = json.loads(result[0].text) # type: ignore[attr-defined]
 
136
  assert "x-demo-header" in json_result
137
  assert json_result["x-demo-header"] == "ABC"
138
 
 
145
  )
146
  ) as client:
147
  result = await client.get_prompt("get_headers_prompt")
148
+ json_result = json.loads(result.messages[0].content.text) # type: ignore[attr-defined]
 
149
  assert "x-demo-header" in json_result
150
  assert json_result["x-demo-header"] == "ABC"
151
 
 
156
  transport=SSETransport(sse_server, headers={"X-DEMO-HEADER": "ABC"})
157
  ) as client:
158
  result = await client.get_prompt("get_headers_prompt")
159
+ json_result = json.loads(result.messages[0].content.text) # type: ignore[attr-defined]
 
160
  assert "x-demo-header" in json_result
161
  assert json_result["x-demo-header"] == "ABC"
tests/server/openapi/test_openapi.py CHANGED
@@ -9,7 +9,7 @@ from dirty_equals import IsStr
9
  from fastapi import FastAPI, HTTPException, Response
10
  from fastapi.responses import PlainTextResponse
11
  from httpx import ASGITransport, AsyncClient
12
- from mcp.types import BlobResourceContents, TextContent, TextResourceContents
13
  from pydantic import BaseModel, TypeAdapter
14
  from pydantic.networks import AnyUrl
15
 
@@ -234,11 +234,7 @@ class TestTools:
234
  "create_user_users_post", {"name": "David", "active": False}
235
  )
236
 
237
- # Convert TextContent to dict for comparison
238
- assert isinstance(tool_response, list) and len(tool_response) == 1
239
- assert isinstance(tool_response[0], TextContent)
240
-
241
- response_data = json.loads(tool_response[0].text)
242
  expected_user = User(id=4, name="David", active=False).model_dump()
243
  assert response_data == expected_user
244
 
@@ -249,8 +245,7 @@ class TestTools:
249
  # Check that the user was created via MCP
250
  async with Client(fastmcp_openapi_server) as client:
251
  user_response = await client.read_resource("resource://get_user_users/4")
252
- assert isinstance(user_response[0], TextResourceContents)
253
- response_text = user_response[0].text
254
  user = json.loads(response_text)
255
  assert user == expected_user
256
 
@@ -266,11 +261,7 @@ class TestTools:
266
  {"user_id": 1, "name": "XYZ"},
267
  )
268
 
269
- # Convert TextContent to dict for comparison
270
- assert isinstance(tool_response, list) and len(tool_response) == 1
271
- assert isinstance(tool_response[0], TextContent)
272
-
273
- response_data = json.loads(tool_response[0].text)
274
  expected_data = dict(id=1, name="XYZ", active=True)
275
  assert response_data == expected_data
276
 
@@ -281,8 +272,7 @@ class TestTools:
281
  # Check that the user was updated via MCP
282
  async with Client(fastmcp_openapi_server) as client:
283
  user_response = await client.read_resource("resource://get_user_users/1")
284
- assert isinstance(user_response[0], TextResourceContents)
285
- response_text = user_response[0].text
286
  user = json.loads(response_text)
287
  assert user == expected_data
288
 
@@ -305,9 +295,7 @@ class TestTools:
305
  )
306
  async with Client(mcp_server) as client:
307
  tool_response = await client.call_tool("get_users_users_get", {})
308
- assert isinstance(tool_response, list)
309
- assert isinstance(tool_response[0], TextContent)
310
- assert json.loads(tool_response[0].text) == [
311
  user.model_dump()
312
  for user in sorted(users_db.values(), key=lambda x: x.id)
313
  ]
@@ -341,8 +329,7 @@ class TestResources:
341
  resource_response = await client.read_resource(
342
  "resource://get_users_users_get"
343
  )
344
- assert isinstance(resource_response[0], TextResourceContents)
345
- response_text = resource_response[0].text
346
  resource = json.loads(response_text)
347
  assert resource == json_users
348
  response = await api_client.get("/users")
@@ -369,8 +356,7 @@ class TestResources:
369
  """Test reading a resource that returns a string."""
370
  async with Client(fastmcp_openapi_server) as client:
371
  resource_response = await client.read_resource("resource://ping_ping_get")
372
- assert isinstance(resource_response[0], TextResourceContents)
373
- assert resource_response[0].text == "pong"
374
 
375
 
376
  class TestResourceTemplates:
@@ -407,8 +393,7 @@ class TestResourceTemplates:
407
  resource_response = await client.read_resource(
408
  f"resource://get_user_users/{user_id}"
409
  )
410
- assert isinstance(resource_response[0], TextResourceContents)
411
- response_text = resource_response[0].text
412
  resource = json.loads(response_text)
413
 
414
  assert resource == users_db[user_id].model_dump()
@@ -430,8 +415,7 @@ class TestResourceTemplates:
430
  resource_response = await client.read_resource(
431
  f"resource://get_user_active_state_users/{is_active}/{user_id}"
432
  )
433
- assert isinstance(resource_response[0], TextResourceContents)
434
- response_text = resource_response[0].text
435
  resource = json.loads(response_text)
436
 
437
  assert resource == users_db[user_id].model_dump()
@@ -681,8 +665,7 @@ class TestOpenAPI30Compatibility:
681
  """Test reading a resource from an OpenAPI 3.0 server."""
682
  async with Client(openapi_30_server) as client:
683
  resource_response = await client.read_resource("resource://listProducts")
684
- assert isinstance(resource_response[0], TextResourceContents)
685
- response_text = resource_response[0].text
686
  content = json.loads(response_text)
687
  assert len(content) == 2
688
  assert content[0]["name"] == "Product 1"
@@ -692,8 +675,7 @@ class TestOpenAPI30Compatibility:
692
  """Test reading a resource from template from an OpenAPI 3.0 server."""
693
  async with Client(openapi_30_server) as client:
694
  resource_response = await client.read_resource("resource://getProduct/p1")
695
- assert isinstance(resource_response[0], TextResourceContents)
696
- response_text = resource_response[0].text
697
  content = json.loads(response_text)
698
  assert content["id"] == "p1"
699
  assert content["name"] == "Product 1"
@@ -707,8 +689,7 @@ class TestOpenAPI30Compatibility:
707
  )
708
  # Result should be a text content
709
  assert len(result) == 1
710
- assert isinstance(result[0], TextContent)
711
- product = json.loads(result[0].text)
712
  assert product["id"] == "p3"
713
  assert product["name"] == "New Product"
714
  assert product["price"] == 39.99
@@ -857,8 +838,7 @@ class TestOpenAPI31Compatibility:
857
  """Test reading a resource from an OpenAPI 3.1 server."""
858
  async with Client(openapi_31_server) as client:
859
  resource_response = await client.read_resource("resource://listOrders")
860
- assert isinstance(resource_response[0], TextResourceContents)
861
- response_text = resource_response[0].text
862
  content = json.loads(response_text)
863
  assert len(content) == 2
864
  assert content[0]["customer"] == "Alice"
@@ -868,8 +848,7 @@ class TestOpenAPI31Compatibility:
868
  """Test reading a resource from template from an OpenAPI 3.1 server."""
869
  async with Client(openapi_31_server) as client:
870
  resource_response = await client.read_resource("resource://getOrder/o1")
871
- assert isinstance(resource_response[0], TextResourceContents)
872
- response_text = resource_response[0].text
873
  content = json.loads(response_text)
874
  assert content["id"] == "o1"
875
  assert content["customer"] == "Alice"
@@ -883,8 +862,7 @@ class TestOpenAPI31Compatibility:
883
  )
884
  # Result should be a text content
885
  assert len(result) == 1
886
- assert isinstance(result[0], TextContent)
887
- order = json.loads(result[0].text)
888
  assert order["id"] == "o3"
889
  assert order["customer"] == "Charlie"
890
  assert order["items"] == ["item4", "item5"]
 
9
  from fastapi import FastAPI, HTTPException, Response
10
  from fastapi.responses import PlainTextResponse
11
  from httpx import ASGITransport, AsyncClient
12
+ from mcp.types import BlobResourceContents
13
  from pydantic import BaseModel, TypeAdapter
14
  from pydantic.networks import AnyUrl
15
 
 
234
  "create_user_users_post", {"name": "David", "active": False}
235
  )
236
 
237
+ response_data = json.loads(tool_response[0].text) # type: ignore[attr-defined]
 
 
 
 
238
  expected_user = User(id=4, name="David", active=False).model_dump()
239
  assert response_data == expected_user
240
 
 
245
  # Check that the user was created via MCP
246
  async with Client(fastmcp_openapi_server) as client:
247
  user_response = await client.read_resource("resource://get_user_users/4")
248
+ response_text = user_response[0].text # type: ignore[attr-defined]
 
249
  user = json.loads(response_text)
250
  assert user == expected_user
251
 
 
261
  {"user_id": 1, "name": "XYZ"},
262
  )
263
 
264
+ response_data = json.loads(tool_response[0].text) # type: ignore[attr-defined]
 
 
 
 
265
  expected_data = dict(id=1, name="XYZ", active=True)
266
  assert response_data == expected_data
267
 
 
272
  # Check that the user was updated via MCP
273
  async with Client(fastmcp_openapi_server) as client:
274
  user_response = await client.read_resource("resource://get_user_users/1")
275
+ response_text = user_response[0].text # type: ignore[attr-defined]
 
276
  user = json.loads(response_text)
277
  assert user == expected_data
278
 
 
295
  )
296
  async with Client(mcp_server) as client:
297
  tool_response = await client.call_tool("get_users_users_get", {})
298
+ assert json.loads(tool_response[0].text) == [ # type: ignore[attr-defined]
 
 
299
  user.model_dump()
300
  for user in sorted(users_db.values(), key=lambda x: x.id)
301
  ]
 
329
  resource_response = await client.read_resource(
330
  "resource://get_users_users_get"
331
  )
332
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
333
  resource = json.loads(response_text)
334
  assert resource == json_users
335
  response = await api_client.get("/users")
 
356
  """Test reading a resource that returns a string."""
357
  async with Client(fastmcp_openapi_server) as client:
358
  resource_response = await client.read_resource("resource://ping_ping_get")
359
+ assert resource_response[0].text == "pong" # type: ignore[attr-defined]
 
360
 
361
 
362
  class TestResourceTemplates:
 
393
  resource_response = await client.read_resource(
394
  f"resource://get_user_users/{user_id}"
395
  )
396
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
397
  resource = json.loads(response_text)
398
 
399
  assert resource == users_db[user_id].model_dump()
 
415
  resource_response = await client.read_resource(
416
  f"resource://get_user_active_state_users/{is_active}/{user_id}"
417
  )
418
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
419
  resource = json.loads(response_text)
420
 
421
  assert resource == users_db[user_id].model_dump()
 
665
  """Test reading a resource from an OpenAPI 3.0 server."""
666
  async with Client(openapi_30_server) as client:
667
  resource_response = await client.read_resource("resource://listProducts")
668
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
669
  content = json.loads(response_text)
670
  assert len(content) == 2
671
  assert content[0]["name"] == "Product 1"
 
675
  """Test reading a resource from template from an OpenAPI 3.0 server."""
676
  async with Client(openapi_30_server) as client:
677
  resource_response = await client.read_resource("resource://getProduct/p1")
678
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
679
  content = json.loads(response_text)
680
  assert content["id"] == "p1"
681
  assert content["name"] == "Product 1"
 
689
  )
690
  # Result should be a text content
691
  assert len(result) == 1
692
+ product = json.loads(result[0].text) # type: ignore[attr-defined]
 
693
  assert product["id"] == "p3"
694
  assert product["name"] == "New Product"
695
  assert product["price"] == 39.99
 
838
  """Test reading a resource from an OpenAPI 3.1 server."""
839
  async with Client(openapi_31_server) as client:
840
  resource_response = await client.read_resource("resource://listOrders")
841
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
842
  content = json.loads(response_text)
843
  assert len(content) == 2
844
  assert content[0]["customer"] == "Alice"
 
848
  """Test reading a resource from template from an OpenAPI 3.1 server."""
849
  async with Client(openapi_31_server) as client:
850
  resource_response = await client.read_resource("resource://getOrder/o1")
851
+ response_text = resource_response[0].text # type: ignore[attr-defined]
 
852
  content = json.loads(response_text)
853
  assert content["id"] == "o1"
854
  assert content["customer"] == "Alice"
 
862
  )
863
  # Result should be a text content
864
  assert len(result) == 1
865
+ order = json.loads(result[0].text) # type: ignore[attr-dict]
 
866
  assert order["id"] == "o3"
867
  assert order["customer"] == "Charlie"
868
  assert order["items"] == ["item4", "item5"]
tests/server/test_import_server.py CHANGED
@@ -1,8 +1,6 @@
1
  import json
2
  from urllib.parse import quote
3
 
4
- from mcp.types import TextContent, TextResourceContents
5
-
6
  from fastmcp.client.client import Client
7
  from fastmcp.server.server import FastMCP
8
 
@@ -223,8 +221,7 @@ async def test_call_imported_custom_named_tool():
223
 
224
  async with Client(main_app) as client:
225
  result = await client.call_tool("api_get_data", {"query": "test"})
226
- assert isinstance(result[0], TextContent)
227
- assert result[0].text == "Data for query: test"
228
 
229
 
230
  async def test_first_level_importing_with_custom_name():
@@ -278,8 +275,7 @@ async def test_call_nested_imported_tool():
278
  result = await main_app._tool_manager.call_tool(
279
  "service_provider_compute", {"input": 21}
280
  )
281
- assert isinstance(result[0], TextContent)
282
- assert result[0].text == "42"
283
 
284
 
285
  async def test_import_with_proxy_tools():
@@ -302,8 +298,7 @@ async def test_import_with_proxy_tools():
302
  await main_app.import_server("api", proxy_app)
303
 
304
  result = await main_app._mcp_call_tool("api_get_data", {"query": "test"})
305
- assert isinstance(result[0], TextContent)
306
- assert result[0].text == "Data for query: test"
307
 
308
 
309
  async def test_import_with_proxy_prompts():
@@ -326,7 +321,6 @@ async def test_import_with_proxy_prompts():
326
  await main_app.import_server("api", proxy_app)
327
 
328
  result = await main_app._mcp_get_prompt("api_greeting", {"name": "World"})
329
- assert isinstance(result.messages[0].content, TextContent)
330
  assert result.messages[0].content.text == "Hello, World from API!"
331
  assert result.description == "Example greeting prompt."
332
 
@@ -356,8 +350,7 @@ async def test_import_with_proxy_resources():
356
  # Access the resource through the main app with the prefixed key
357
  async with Client(main_app) as client:
358
  result = await client.read_resource("config://api/settings")
359
- assert isinstance(result[0], TextResourceContents)
360
- content = json.loads(result[0].text)
361
  assert content["api_key"] == "12345"
362
  assert content["base_url"] == "https://api.example.com"
363
 
@@ -387,8 +380,7 @@ async def test_import_with_proxy_resource_templates():
387
  quoted_email = quote("john@example.com", safe="")
388
  async with Client(main_app) as client:
389
  result = await client.read_resource(f"user://api/{quoted_name}/{quoted_email}")
390
- assert isinstance(result[0], TextResourceContents)
391
- content = json.loads(result[0].text)
392
  assert content["name"] == "John Doe"
393
  assert content["email"] == "john@example.com"
394
 
 
1
  import json
2
  from urllib.parse import quote
3
 
 
 
4
  from fastmcp.client.client import Client
5
  from fastmcp.server.server import FastMCP
6
 
 
221
 
222
  async with Client(main_app) as client:
223
  result = await client.call_tool("api_get_data", {"query": "test"})
224
+ assert result[0].text == "Data for query: test" # type: ignore[attr-defined]
 
225
 
226
 
227
  async def test_first_level_importing_with_custom_name():
 
275
  result = await main_app._tool_manager.call_tool(
276
  "service_provider_compute", {"input": 21}
277
  )
278
+ assert result[0].text == "42" # type: ignore[attr-defined]
 
279
 
280
 
281
  async def test_import_with_proxy_tools():
 
298
  await main_app.import_server("api", proxy_app)
299
 
300
  result = await main_app._mcp_call_tool("api_get_data", {"query": "test"})
301
+ assert result[0].text == "Data for query: test" # type: ignore[attr-defined]
 
302
 
303
 
304
  async def test_import_with_proxy_prompts():
 
321
  await main_app.import_server("api", proxy_app)
322
 
323
  result = await main_app._mcp_get_prompt("api_greeting", {"name": "World"})
 
324
  assert result.messages[0].content.text == "Hello, World from API!"
325
  assert result.description == "Example greeting prompt."
326
 
 
350
  # Access the resource through the main app with the prefixed key
351
  async with Client(main_app) as client:
352
  result = await client.read_resource("config://api/settings")
353
+ content = json.loads(result[0].text) # type: ignore[attr-defined]
 
354
  assert content["api_key"] == "12345"
355
  assert content["base_url"] == "https://api.example.com"
356
 
 
380
  quoted_email = quote("john@example.com", safe="")
381
  async with Client(main_app) as client:
382
  result = await client.read_resource(f"user://api/{quoted_name}/{quoted_email}")
383
+ content = json.loads(result[0].text) # type: ignore[attr-defined]
 
384
  assert content["name"] == "John Doe"
385
  assert content["email"] == "john@example.com"
386
 
tests/server/test_mount.py CHANGED
@@ -3,8 +3,6 @@ import sys
3
  from contextlib import asynccontextmanager
4
 
5
  import pytest
6
- from mcp.server.lowlevel.helper_types import ReadResourceContents
7
- from mcp.types import TextContent, TextResourceContents
8
 
9
  from fastmcp import FastMCP
10
  from fastmcp.client import Client
@@ -36,8 +34,7 @@ class TestBasicMount:
36
 
37
  async with Client(main_app) as client:
38
  result = await client.call_tool("sub_sub_tool", {})
39
- assert isinstance(result[0], TextContent)
40
- assert result[0].text == "This is from the sub app"
41
 
42
  async def test_mount_with_custom_separator(self):
43
  """Test mounting with a custom tool separator (deprecated but still supported)."""
@@ -57,8 +54,7 @@ class TestBasicMount:
57
 
58
  # Call the tool
59
  result = await main_app._mcp_call_tool("sub_greet", {"name": "World"})
60
- assert isinstance(result[0], TextContent)
61
- assert result[0].text == "Hello, World!"
62
 
63
  async def test_mount_invalid_resource_prefix(self):
64
  main_app = FastMCP("MainApp")
@@ -147,12 +143,10 @@ class TestMultipleServerMount:
147
 
148
  # Call tools from both mounted servers
149
  result1 = await main_app._mcp_call_tool("weather_get_forecast", {})
150
- assert isinstance(result1[0], TextContent)
151
- assert result1[0].text == "Weather forecast"
152
 
153
  result2 = await main_app._mcp_call_tool("news_get_headlines", {})
154
- assert isinstance(result2[0], TextContent)
155
- assert result2[0].text == "News headlines"
156
 
157
  async def test_mount_same_prefix(self):
158
  """Test that mounting with the same prefix replaces the previous mount."""
@@ -227,8 +221,7 @@ class TestMultipleServerMount:
227
 
228
  # Test calling a tool
229
  result = await client.call_tool("working_working_tool", {})
230
- assert isinstance(result[0], TextContent)
231
- assert result[0].text == "Working tool"
232
 
233
  # Test resources
234
  resources = await client.list_resources()
@@ -284,8 +277,7 @@ class TestDynamicChanges:
284
 
285
  # Call the dynamically added tool
286
  result = await main_app._mcp_call_tool("sub_dynamic_tool", {})
287
- assert isinstance(result[0], TextContent)
288
- assert result[0].text == "Added after mounting"
289
 
290
  async def test_removing_tool_after_mounting(self):
291
  """Test that tools removed from mounted servers are no longer accessible."""
@@ -335,8 +327,7 @@ class TestResourcesAndTemplates:
335
  # Check that resource can be accessed
336
  async with Client(main_app) as client:
337
  result = await client.read_resource("data://data/users")
338
- assert isinstance(result[0], TextResourceContents)
339
- assert json.loads(result[0].text) == ["user1", "user2"]
340
 
341
  async def test_mount_with_resource_templates(self):
342
  """Test mounting a server with resource templates."""
@@ -357,8 +348,7 @@ class TestResourcesAndTemplates:
357
  # Check template instantiation
358
  async with Client(main_app) as client:
359
  result = await client.read_resource("users://api/123/profile")
360
- assert isinstance(result[0], TextResourceContents)
361
- profile = json.loads(result[0].text)
362
  assert profile["id"] == "123"
363
  assert profile["name"] == "User 123"
364
 
@@ -382,8 +372,7 @@ class TestResourcesAndTemplates:
382
  # Check access to the resource
383
  async with Client(main_app) as client:
384
  result = await client.read_resource("data://data/config")
385
- assert isinstance(result[0], TextResourceContents)
386
- config = json.loads(result[0].text)
387
  assert config["version"] == "1.0"
388
 
389
 
@@ -461,8 +450,7 @@ class TestProxyServer:
461
 
462
  # Call the tool
463
  result = await main_app._mcp_call_tool("proxy_get_data", {"query": "test"})
464
- assert isinstance(result[0], TextContent)
465
- assert result[0].text == "Data for test"
466
 
467
  async def test_dynamically_adding_to_proxied_server(self):
468
  """Test that changes to the original server are reflected in the mounted proxy."""
@@ -489,8 +477,7 @@ class TestProxyServer:
489
 
490
  # Call the tool
491
  result = await main_app._mcp_call_tool("proxy_dynamic_data", {})
492
- assert isinstance(result[0], TextContent)
493
- assert result[0].text == "Dynamic data"
494
 
495
  async def test_proxy_server_with_resources(self):
496
  """Test mounting a proxy server with resources."""
@@ -512,8 +499,7 @@ class TestProxyServer:
512
 
513
  # Resource should be accessible through main app
514
  result = await main_app._mcp_read_resource("config://proxy/settings")
515
- assert isinstance(result[0], ReadResourceContents)
516
- config = json.loads(result[0].content)
517
  assert config["api_key"] == "12345"
518
 
519
  async def test_proxy_server_with_prompts(self):
 
3
  from contextlib import asynccontextmanager
4
 
5
  import pytest
 
 
6
 
7
  from fastmcp import FastMCP
8
  from fastmcp.client import Client
 
34
 
35
  async with Client(main_app) as client:
36
  result = await client.call_tool("sub_sub_tool", {})
37
+ assert result[0].text == "This is from the sub app" # type: ignore[attr-defined]
 
38
 
39
  async def test_mount_with_custom_separator(self):
40
  """Test mounting with a custom tool separator (deprecated but still supported)."""
 
54
 
55
  # Call the tool
56
  result = await main_app._mcp_call_tool("sub_greet", {"name": "World"})
57
+ assert result[0].text == "Hello, World!" # type: ignore[attr-defined]
 
58
 
59
  async def test_mount_invalid_resource_prefix(self):
60
  main_app = FastMCP("MainApp")
 
143
 
144
  # Call tools from both mounted servers
145
  result1 = await main_app._mcp_call_tool("weather_get_forecast", {})
146
+ assert result1[0].text == "Weather forecast" # type: ignore[attr-defined]
 
147
 
148
  result2 = await main_app._mcp_call_tool("news_get_headlines", {})
149
+ assert result2[0].text == "News headlines" # type: ignore[attr-defined]
 
150
 
151
  async def test_mount_same_prefix(self):
152
  """Test that mounting with the same prefix replaces the previous mount."""
 
221
 
222
  # Test calling a tool
223
  result = await client.call_tool("working_working_tool", {})
224
+ assert result[0].text == "Working tool" # type: ignore[attr-defined]
 
225
 
226
  # Test resources
227
  resources = await client.list_resources()
 
277
 
278
  # Call the dynamically added tool
279
  result = await main_app._mcp_call_tool("sub_dynamic_tool", {})
280
+ assert result[0].text == "Added after mounting" # type: ignore[attr-defined]
 
281
 
282
  async def test_removing_tool_after_mounting(self):
283
  """Test that tools removed from mounted servers are no longer accessible."""
 
327
  # Check that resource can be accessed
328
  async with Client(main_app) as client:
329
  result = await client.read_resource("data://data/users")
330
+ assert json.loads(result[0].text) == ["user1", "user2"] # type: ignore[attr-defined]
 
331
 
332
  async def test_mount_with_resource_templates(self):
333
  """Test mounting a server with resource templates."""
 
348
  # Check template instantiation
349
  async with Client(main_app) as client:
350
  result = await client.read_resource("users://api/123/profile")
351
+ profile = json.loads(result[0].text) # type: ignore
 
352
  assert profile["id"] == "123"
353
  assert profile["name"] == "User 123"
354
 
 
372
  # Check access to the resource
373
  async with Client(main_app) as client:
374
  result = await client.read_resource("data://data/config")
375
+ config = json.loads(result[0].text) # type: ignore[attr-defined]
 
376
  assert config["version"] == "1.0"
377
 
378
 
 
450
 
451
  # Call the tool
452
  result = await main_app._mcp_call_tool("proxy_get_data", {"query": "test"})
453
+ assert result[0].text == "Data for test" # type: ignore[attr-defined]
 
454
 
455
  async def test_dynamically_adding_to_proxied_server(self):
456
  """Test that changes to the original server are reflected in the mounted proxy."""
 
477
 
478
  # Call the tool
479
  result = await main_app._mcp_call_tool("proxy_dynamic_data", {})
480
+ assert result[0].text == "Dynamic data" # type: ignore[attr-defined]
 
481
 
482
  async def test_proxy_server_with_resources(self):
483
  """Test mounting a proxy server with resources."""
 
499
 
500
  # Resource should be accessible through main app
501
  result = await main_app._mcp_read_resource("config://proxy/settings")
502
+ config = json.loads(result[0].content) # type: ignore[attr-defined]
 
503
  assert config["api_key"] == "12345"
504
 
505
  async def test_proxy_server_with_prompts(self):
tests/server/test_proxy.py CHANGED
@@ -1,7 +1,6 @@
1
  import json
2
  from typing import Any
3
 
4
- import mcp.types
5
  import pytest
6
  from anyio import create_task_group
7
  from dirty_equals import Contains
@@ -90,16 +89,14 @@ async def test_as_proxy_with_server(fastmcp_server):
90
  """FastMCP.as_proxy should accept a FastMCP instance."""
91
  proxy = FastMCP.as_proxy(fastmcp_server)
92
  result = await proxy._mcp_call_tool("greet", {"name": "Test"})
93
- assert isinstance(result[0], mcp.types.TextContent)
94
- assert result[0].text == "Hello, Test!"
95
 
96
 
97
  async def test_as_proxy_with_transport(fastmcp_server):
98
  """FastMCP.as_proxy should accept a ClientTransport."""
99
  proxy = FastMCP.as_proxy(FastMCPTransport(fastmcp_server))
100
  result = await proxy._mcp_call_tool("greet", {"name": "Test"})
101
- assert isinstance(result[0], mcp.types.TextContent)
102
- assert result[0].text == "Hello, Test!"
103
 
104
 
105
  def test_as_proxy_with_url():
@@ -138,9 +135,7 @@ class TestTools:
138
  async def test_call_tool_calls_tool(self, proxy_server):
139
  async with Client(proxy_server) as client:
140
  proxy_result = await client.call_tool("add", {"a": 1, "b": 2})
141
-
142
- assert isinstance(proxy_result[0], mcp.types.TextContent)
143
- assert proxy_result[0].text == "3"
144
 
145
  async def test_error_tool_raises_error(self, proxy_server):
146
  with pytest.raises(ToolError, match=""):
@@ -164,8 +159,7 @@ class TestResources:
164
  async def test_read_resource(self, proxy_server: FastMCPProxy):
165
  async with Client(proxy_server) as client:
166
  result = await client.read_resource("resource://wave")
167
- assert isinstance(result[0], mcp.types.TextResourceContents)
168
- assert result[0].text == "👋"
169
 
170
  async def test_read_resource_same_as_original(self, fastmcp_server, proxy_server):
171
  async with Client(fastmcp_server) as client:
@@ -177,8 +171,7 @@ class TestResources:
177
  async def test_read_json_resource(self, proxy_server: FastMCPProxy):
178
  async with Client(proxy_server) as client:
179
  result = await client.read_resource("data://users")
180
- assert isinstance(result[0], mcp.types.TextResourceContents)
181
- assert json.loads(result[0].text) == USERS
182
 
183
  async def test_read_resource_returns_none_if_not_found(self, proxy_server):
184
  with pytest.raises(McpError, match="Unknown resource: resource://nonexistent"):
@@ -202,8 +195,7 @@ class TestResourceTemplates:
202
  async def test_read_resource_template(self, proxy_server: FastMCPProxy, id: int):
203
  async with Client(proxy_server) as client:
204
  result = await client.read_resource(f"data://user/{id}")
205
- assert isinstance(result[0], mcp.types.TextResourceContents)
206
- assert json.loads(result[0].text) == USERS[id - 1]
207
 
208
  async def test_read_resource_template_same_as_original(
209
  self, fastmcp_server, proxy_server
@@ -239,10 +231,8 @@ class TestPrompts:
239
  async def test_render_prompt_calls_prompt(self, proxy_server):
240
  async with Client(proxy_server) as client:
241
  result = await client.get_prompt("welcome", {"name": "Alice"})
242
- assert isinstance(result.messages[0], mcp.types.PromptMessage)
243
  assert result.messages[0].role == "user"
244
- assert isinstance(result.messages[0].content, mcp.types.TextContent)
245
- assert result.messages[0].content.text == "Welcome to FastMCP, Alice!"
246
 
247
 
248
  async def test_proxy_handles_multiple_concurrent_tasks_correctly(
 
1
  import json
2
  from typing import Any
3
 
 
4
  import pytest
5
  from anyio import create_task_group
6
  from dirty_equals import Contains
 
89
  """FastMCP.as_proxy should accept a FastMCP instance."""
90
  proxy = FastMCP.as_proxy(fastmcp_server)
91
  result = await proxy._mcp_call_tool("greet", {"name": "Test"})
92
+ assert result[0].text == "Hello, Test!" # type: ignore[attr-defined]
 
93
 
94
 
95
  async def test_as_proxy_with_transport(fastmcp_server):
96
  """FastMCP.as_proxy should accept a ClientTransport."""
97
  proxy = FastMCP.as_proxy(FastMCPTransport(fastmcp_server))
98
  result = await proxy._mcp_call_tool("greet", {"name": "Test"})
99
+ assert result[0].text == "Hello, Test!" # type: ignore[attr-defined]
 
100
 
101
 
102
  def test_as_proxy_with_url():
 
135
  async def test_call_tool_calls_tool(self, proxy_server):
136
  async with Client(proxy_server) as client:
137
  proxy_result = await client.call_tool("add", {"a": 1, "b": 2})
138
+ assert proxy_result[0].text == "3" # type: ignore[attr-defined]
 
 
139
 
140
  async def test_error_tool_raises_error(self, proxy_server):
141
  with pytest.raises(ToolError, match=""):
 
159
  async def test_read_resource(self, proxy_server: FastMCPProxy):
160
  async with Client(proxy_server) as client:
161
  result = await client.read_resource("resource://wave")
162
+ assert result[0].text == "👋" # type: ignore[attr-defined]
 
163
 
164
  async def test_read_resource_same_as_original(self, fastmcp_server, proxy_server):
165
  async with Client(fastmcp_server) as client:
 
171
  async def test_read_json_resource(self, proxy_server: FastMCPProxy):
172
  async with Client(proxy_server) as client:
173
  result = await client.read_resource("data://users")
174
+ assert json.loads(result[0].text) == USERS # type: ignore[attr-defined]
 
175
 
176
  async def test_read_resource_returns_none_if_not_found(self, proxy_server):
177
  with pytest.raises(McpError, match="Unknown resource: resource://nonexistent"):
 
195
  async def test_read_resource_template(self, proxy_server: FastMCPProxy, id: int):
196
  async with Client(proxy_server) as client:
197
  result = await client.read_resource(f"data://user/{id}")
198
+ assert json.loads(result[0].text) == USERS[id - 1] # type: ignore[attr-defined]
 
199
 
200
  async def test_read_resource_template_same_as_original(
201
  self, fastmcp_server, proxy_server
 
231
  async def test_render_prompt_calls_prompt(self, proxy_server):
232
  async with Client(proxy_server) as client:
233
  result = await client.get_prompt("welcome", {"name": "Alice"})
 
234
  assert result.messages[0].role == "user"
235
+ assert result.messages[0].content.text == "Welcome to FastMCP, Alice!" # type: ignore[attr-defined]
 
236
 
237
 
238
  async def test_proxy_handles_multiple_concurrent_tasks_correctly(
tests/server/test_server.py CHANGED
@@ -2,10 +2,6 @@ from typing import Annotated
2
 
3
  import pytest
4
  from mcp import McpError
5
- from mcp.types import (
6
- TextContent,
7
- TextResourceContents,
8
- )
9
  from pydantic import Field
10
 
11
  from fastmcp import Client, FastMCP
@@ -48,8 +44,7 @@ class TestCreateServer:
48
  result = await client.call_tool("hello_world", {})
49
  assert len(result) == 1
50
  content = result[0]
51
- assert isinstance(content, TextContent)
52
- assert "¡Hola, 世界! 👋" == content.text
53
 
54
 
55
  class TestTools:
@@ -114,8 +109,7 @@ class TestToolDecorator:
114
  return x + y
115
 
116
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
117
- assert isinstance(result[0], TextContent)
118
- assert result[0].text == "3"
119
 
120
  async def test_tool_decorator_incorrect_usage(self):
121
  mcp = FastMCP()
@@ -134,8 +128,7 @@ class TestToolDecorator:
134
  return x + y
135
 
136
  result = await mcp._mcp_call_tool("custom-add", {"x": 1, "y": 2})
137
- assert isinstance(result[0], TextContent)
138
- assert result[0].text == "3"
139
 
140
  async def test_tool_decorator_with_description(self):
141
  mcp = FastMCP()
@@ -163,8 +156,7 @@ class TestToolDecorator:
163
  obj = MyClass(10)
164
  mcp.add_tool(obj.add)
165
  result = await mcp._mcp_call_tool("add", {"y": 2})
166
- assert isinstance(result[0], TextContent)
167
- assert result[0].text == "12"
168
 
169
  async def test_tool_decorator_classmethod(self):
170
  mcp = FastMCP()
@@ -178,8 +170,7 @@ class TestToolDecorator:
178
 
179
  mcp.add_tool(MyClass.add)
180
  result = await mcp._mcp_call_tool("add", {"y": 2})
181
- assert isinstance(result[0], TextContent)
182
- assert result[0].text == "12"
183
 
184
  async def test_tool_decorator_staticmethod(self):
185
  mcp = FastMCP()
@@ -191,8 +182,7 @@ class TestToolDecorator:
191
  return x + y
192
 
193
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
194
- assert isinstance(result[0], TextContent)
195
- assert result[0].text == "3"
196
 
197
  async def test_tool_decorator_async_function(self):
198
  mcp = FastMCP()
@@ -202,8 +192,7 @@ class TestToolDecorator:
202
  return x + y
203
 
204
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
205
- assert isinstance(result[0], TextContent)
206
- assert result[0].text == "3"
207
 
208
  async def test_tool_decorator_classmethod_async_function(self):
209
  mcp = FastMCP()
@@ -217,8 +206,7 @@ class TestToolDecorator:
217
 
218
  mcp.add_tool(MyClass.add)
219
  result = await mcp._mcp_call_tool("add", {"y": 2})
220
- assert isinstance(result[0], TextContent)
221
- assert result[0].text == "12"
222
 
223
  async def test_tool_decorator_staticmethod_async_function(self):
224
  mcp = FastMCP()
@@ -230,8 +218,7 @@ class TestToolDecorator:
230
 
231
  mcp.add_tool(MyClass.add)
232
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
233
- assert isinstance(result[0], TextContent)
234
- assert result[0].text == "3"
235
 
236
  async def test_tool_decorator_with_tags(self):
237
  """Test that the tool decorator properly sets tags."""
@@ -262,8 +249,7 @@ class TestToolDecorator:
262
 
263
  # Call the tool by its custom name
264
  result = await mcp._mcp_call_tool("custom_multiply", {"a": 5, "b": 3})
265
- assert isinstance(result[0], TextContent)
266
- assert result[0].text == "15"
267
 
268
  # Original name should not be registered
269
  assert "multiply" not in tools
@@ -316,8 +302,7 @@ class TestResourceDecorator:
316
 
317
  async with Client(mcp) as client:
318
  result = await client.read_resource("resource://data")
319
- assert isinstance(result[0], TextResourceContents)
320
- assert result[0].text == "Hello, world!"
321
 
322
  async def test_resource_decorator_incorrect_usage(self):
323
  mcp = FastMCP()
@@ -344,8 +329,7 @@ class TestResourceDecorator:
344
 
345
  async with Client(mcp) as client:
346
  result = await client.read_resource("resource://data")
347
- assert isinstance(result[0], TextResourceContents)
348
- assert result[0].text == "Hello, world!"
349
 
350
  async def test_resource_decorator_with_description(self):
351
  mcp = FastMCP()
@@ -389,8 +373,7 @@ class TestResourceDecorator:
389
 
390
  async with Client(mcp) as client:
391
  result = await client.read_resource("resource://data")
392
- assert isinstance(result[0], TextResourceContents)
393
- assert result[0].text == "My prefix: Hello, world!"
394
 
395
  async def test_resource_decorator_classmethod(self):
396
  mcp = FastMCP()
@@ -408,8 +391,7 @@ class TestResourceDecorator:
408
 
409
  async with Client(mcp) as client:
410
  result = await client.read_resource("resource://data")
411
- assert isinstance(result[0], TextResourceContents)
412
- assert result[0].text == "Class prefix: Hello, world!"
413
 
414
  async def test_resource_decorator_staticmethod(self):
415
  mcp = FastMCP()
@@ -422,8 +404,7 @@ class TestResourceDecorator:
422
 
423
  async with Client(mcp) as client:
424
  result = await client.read_resource("resource://data")
425
- assert isinstance(result[0], TextResourceContents)
426
- assert result[0].text == "Static Hello, world!"
427
 
428
  async def test_resource_decorator_async_function(self):
429
  mcp = FastMCP()
@@ -434,8 +415,7 @@ class TestResourceDecorator:
434
 
435
  async with Client(mcp) as client:
436
  result = await client.read_resource("resource://data")
437
- assert isinstance(result[0], TextResourceContents)
438
- assert result[0].text == "Async Hello, world!"
439
 
440
 
441
  class TestTemplateDecorator:
@@ -454,8 +434,7 @@ class TestTemplateDecorator:
454
 
455
  async with Client(mcp) as client:
456
  result = await client.read_resource("resource://test/data")
457
- assert isinstance(result[0], TextResourceContents)
458
- assert result[0].text == "Data for test"
459
 
460
  async def test_template_decorator_incorrect_usage(self):
461
  mcp = FastMCP()
@@ -482,8 +461,7 @@ class TestTemplateDecorator:
482
 
483
  async with Client(mcp) as client:
484
  result = await client.read_resource("resource://test/data")
485
- assert isinstance(result[0], TextResourceContents)
486
- assert result[0].text == "Data for test"
487
 
488
  async def test_template_decorator_with_description(self):
489
  mcp = FastMCP()
@@ -514,8 +492,7 @@ class TestTemplateDecorator:
514
 
515
  async with Client(mcp) as client:
516
  result = await client.read_resource("resource://test/data")
517
- assert isinstance(result[0], TextResourceContents)
518
- assert result[0].text == "My prefix: Data for test"
519
 
520
  async def test_template_decorator_classmethod(self):
521
  mcp = FastMCP()
@@ -535,8 +512,7 @@ class TestTemplateDecorator:
535
 
536
  async with Client(mcp) as client:
537
  result = await client.read_resource("resource://test/data")
538
- assert isinstance(result[0], TextResourceContents)
539
- assert result[0].text == "Class prefix: Data for test"
540
 
541
  async def test_template_decorator_staticmethod(self):
542
  mcp = FastMCP()
@@ -549,8 +525,7 @@ class TestTemplateDecorator:
549
 
550
  async with Client(mcp) as client:
551
  result = await client.read_resource("resource://test/data")
552
- assert isinstance(result[0], TextResourceContents)
553
- assert result[0].text == "Static Data for test"
554
 
555
  async def test_template_decorator_async_function(self):
556
  mcp = FastMCP()
@@ -561,8 +536,7 @@ class TestTemplateDecorator:
561
 
562
  async with Client(mcp) as client:
563
  result = await client.read_resource("resource://test/data")
564
- assert isinstance(result[0], TextResourceContents)
565
- assert result[0].text == "Async Data for test"
566
 
567
  async def test_template_decorator_with_tags(self):
568
  """Test that the template decorator properly sets tags."""
@@ -603,8 +577,7 @@ class TestPromptDecorator:
603
  assert prompt.name == "fn"
604
  # Don't compare functions directly since validate_call wraps them
605
  content = await prompt.render()
606
- assert isinstance(content[0].content, TextContent)
607
- assert content[0].content.text == "Hello, world!"
608
 
609
  async def test_prompt_decorator_incorrect_usage(self):
610
  mcp = FastMCP()
@@ -629,8 +602,7 @@ class TestPromptDecorator:
629
  prompt = prompts_dict["custom_name"]
630
  assert prompt.name == "custom_name"
631
  content = await prompt.render()
632
- assert isinstance(content[0].content, TextContent)
633
- assert content[0].content.text == "Hello, world!"
634
 
635
  async def test_prompt_decorator_with_description(self):
636
  mcp = FastMCP()
@@ -644,8 +616,7 @@ class TestPromptDecorator:
644
  prompt = prompts_dict["fn"]
645
  assert prompt.description == "A custom description"
646
  content = await prompt.render()
647
- assert isinstance(content[0].content, TextContent)
648
- assert content[0].content.text == "Hello, world!"
649
 
650
  async def test_prompt_decorator_with_parameters(self):
651
  mcp = FastMCP()
@@ -668,16 +639,14 @@ class TestPromptDecorator:
668
  result = await client.get_prompt("test_prompt", {"name": "World"})
669
  assert len(result.messages) == 1
670
  message = result.messages[0]
671
- assert isinstance(message.content, TextContent)
672
- assert message.content.text == "Hello, World!"
673
 
674
  result = await client.get_prompt(
675
  "test_prompt", {"name": "World", "greeting": "Hi"}
676
  )
677
  assert len(result.messages) == 1
678
  message = result.messages[0]
679
- assert isinstance(message.content, TextContent)
680
- assert message.content.text == "Hi, World!"
681
 
682
  async def test_prompt_decorator_instance_method(self):
683
  mcp = FastMCP()
@@ -696,8 +665,7 @@ class TestPromptDecorator:
696
  result = await client.get_prompt("test_prompt")
697
  assert len(result.messages) == 1
698
  message = result.messages[0]
699
- assert isinstance(message.content, TextContent)
700
- assert message.content.text == "My prefix: Hello, world!"
701
 
702
  async def test_prompt_decorator_classmethod(self):
703
  mcp = FastMCP()
@@ -715,8 +683,7 @@ class TestPromptDecorator:
715
  result = await client.get_prompt("test_prompt")
716
  assert len(result.messages) == 1
717
  message = result.messages[0]
718
- assert isinstance(message.content, TextContent)
719
- assert message.content.text == "Class prefix: Hello, world!"
720
 
721
  async def test_prompt_decorator_staticmethod(self):
722
  mcp = FastMCP()
@@ -731,8 +698,7 @@ class TestPromptDecorator:
731
  result = await client.get_prompt("test_prompt")
732
  assert len(result.messages) == 1
733
  message = result.messages[0]
734
- assert isinstance(message.content, TextContent)
735
- assert message.content.text == "Static Hello, world!"
736
 
737
  async def test_prompt_decorator_async_function(self):
738
  mcp = FastMCP()
@@ -745,8 +711,7 @@ class TestPromptDecorator:
745
  result = await client.get_prompt("test_prompt")
746
  assert len(result.messages) == 1
747
  message = result.messages[0]
748
- assert isinstance(message.content, TextContent)
749
- assert message.content.text == "Async Hello, world!"
750
 
751
  async def test_prompt_decorator_with_tags(self):
752
  """Test that the prompt decorator properly sets tags."""
@@ -943,20 +908,17 @@ class TestResourcePrefixMounting:
943
  async with Client(main_server) as client:
944
  # Regular resource
945
  result = await client.read_resource("resource://prefix/test-resource")
946
- assert isinstance(result[0], TextResourceContents)
947
- assert result[0].text == "Resource content"
948
 
949
  # Absolute path resource
950
  result = await client.read_resource("resource://prefix//absolute/path")
951
- assert isinstance(result[0], TextResourceContents)
952
- assert result[0].text == "Absolute resource content"
953
 
954
  # Template resource
955
  result = await client.read_resource(
956
  "resource://prefix/param-value/template"
957
  )
958
- assert isinstance(result[0], TextResourceContents)
959
- assert result[0].text == "Template resource with param-value"
960
 
961
  @pytest.mark.parametrize(
962
  "uri,prefix,expected_match,expected_strip",
@@ -1032,15 +994,12 @@ class TestResourcePrefixMounting:
1032
  # Verify we can access the resources
1033
  async with Client(target_server) as client:
1034
  result = await client.read_resource("resource://imported/test-resource")
1035
- assert isinstance(result[0], TextResourceContents)
1036
- assert result[0].text == "Resource content"
1037
 
1038
  result = await client.read_resource("resource://imported//absolute/path")
1039
- assert isinstance(result[0], TextResourceContents)
1040
- assert result[0].text == "Absolute resource content"
1041
 
1042
  result = await client.read_resource(
1043
  "resource://imported/param-value/template"
1044
  )
1045
- assert isinstance(result[0], TextResourceContents)
1046
- assert result[0].text == "Template resource with param-value"
 
2
 
3
  import pytest
4
  from mcp import McpError
 
 
 
 
5
  from pydantic import Field
6
 
7
  from fastmcp import Client, FastMCP
 
44
  result = await client.call_tool("hello_world", {})
45
  assert len(result) == 1
46
  content = result[0]
47
+ assert content.text == "¡Hola, 世界! 👋" # type: ignore[attr-defined]
 
48
 
49
 
50
  class TestTools:
 
109
  return x + y
110
 
111
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
112
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
113
 
114
  async def test_tool_decorator_incorrect_usage(self):
115
  mcp = FastMCP()
 
128
  return x + y
129
 
130
  result = await mcp._mcp_call_tool("custom-add", {"x": 1, "y": 2})
131
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
132
 
133
  async def test_tool_decorator_with_description(self):
134
  mcp = FastMCP()
 
156
  obj = MyClass(10)
157
  mcp.add_tool(obj.add)
158
  result = await mcp._mcp_call_tool("add", {"y": 2})
159
+ assert result[0].text == "12" # type: ignore[attr-defined]
 
160
 
161
  async def test_tool_decorator_classmethod(self):
162
  mcp = FastMCP()
 
170
 
171
  mcp.add_tool(MyClass.add)
172
  result = await mcp._mcp_call_tool("add", {"y": 2})
173
+ assert result[0].text == "12" # type: ignore[attr-defined]
 
174
 
175
  async def test_tool_decorator_staticmethod(self):
176
  mcp = FastMCP()
 
182
  return x + y
183
 
184
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
185
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
186
 
187
  async def test_tool_decorator_async_function(self):
188
  mcp = FastMCP()
 
192
  return x + y
193
 
194
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
195
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
196
 
197
  async def test_tool_decorator_classmethod_async_function(self):
198
  mcp = FastMCP()
 
206
 
207
  mcp.add_tool(MyClass.add)
208
  result = await mcp._mcp_call_tool("add", {"y": 2})
209
+ assert result[0].text == "12" # type: ignore[attr-defined]
 
210
 
211
  async def test_tool_decorator_staticmethod_async_function(self):
212
  mcp = FastMCP()
 
218
 
219
  mcp.add_tool(MyClass.add)
220
  result = await mcp._mcp_call_tool("add", {"x": 1, "y": 2})
221
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
222
 
223
  async def test_tool_decorator_with_tags(self):
224
  """Test that the tool decorator properly sets tags."""
 
249
 
250
  # Call the tool by its custom name
251
  result = await mcp._mcp_call_tool("custom_multiply", {"a": 5, "b": 3})
252
+ assert result[0].text == "15" # type: ignore[attr-defined]
 
253
 
254
  # Original name should not be registered
255
  assert "multiply" not in tools
 
302
 
303
  async with Client(mcp) as client:
304
  result = await client.read_resource("resource://data")
305
+ assert result[0].text == "Hello, world!" # type: ignore[attr-defined]
 
306
 
307
  async def test_resource_decorator_incorrect_usage(self):
308
  mcp = FastMCP()
 
329
 
330
  async with Client(mcp) as client:
331
  result = await client.read_resource("resource://data")
332
+ assert result[0].text == "Hello, world!" # type: ignore[attr-defined]
 
333
 
334
  async def test_resource_decorator_with_description(self):
335
  mcp = FastMCP()
 
373
 
374
  async with Client(mcp) as client:
375
  result = await client.read_resource("resource://data")
376
+ assert result[0].text == "My prefix: Hello, world!" # type: ignore[attr-defined]
 
377
 
378
  async def test_resource_decorator_classmethod(self):
379
  mcp = FastMCP()
 
391
 
392
  async with Client(mcp) as client:
393
  result = await client.read_resource("resource://data")
394
+ assert result[0].text == "Class prefix: Hello, world!" # type: ignore[attr-defined]
 
395
 
396
  async def test_resource_decorator_staticmethod(self):
397
  mcp = FastMCP()
 
404
 
405
  async with Client(mcp) as client:
406
  result = await client.read_resource("resource://data")
407
+ assert result[0].text == "Static Hello, world!" # type: ignore[attr-defined]
 
408
 
409
  async def test_resource_decorator_async_function(self):
410
  mcp = FastMCP()
 
415
 
416
  async with Client(mcp) as client:
417
  result = await client.read_resource("resource://data")
418
+ assert result[0].text == "Async Hello, world!" # type: ignore[attr-defined]
 
419
 
420
 
421
  class TestTemplateDecorator:
 
434
 
435
  async with Client(mcp) as client:
436
  result = await client.read_resource("resource://test/data")
437
+ assert result[0].text == "Data for test" # type: ignore[attr-defined]
 
438
 
439
  async def test_template_decorator_incorrect_usage(self):
440
  mcp = FastMCP()
 
461
 
462
  async with Client(mcp) as client:
463
  result = await client.read_resource("resource://test/data")
464
+ assert result[0].text == "Data for test" # type: ignore[attr-defined]
 
465
 
466
  async def test_template_decorator_with_description(self):
467
  mcp = FastMCP()
 
492
 
493
  async with Client(mcp) as client:
494
  result = await client.read_resource("resource://test/data")
495
+ assert result[0].text == "My prefix: Data for test" # type: ignore[attr-defined]
 
496
 
497
  async def test_template_decorator_classmethod(self):
498
  mcp = FastMCP()
 
512
 
513
  async with Client(mcp) as client:
514
  result = await client.read_resource("resource://test/data")
515
+ assert result[0].text == "Class prefix: Data for test" # type: ignore[attr-defined]
 
516
 
517
  async def test_template_decorator_staticmethod(self):
518
  mcp = FastMCP()
 
525
 
526
  async with Client(mcp) as client:
527
  result = await client.read_resource("resource://test/data")
528
+ assert result[0].text == "Static Data for test" # type: ignore[attr-defined]
 
529
 
530
  async def test_template_decorator_async_function(self):
531
  mcp = FastMCP()
 
536
 
537
  async with Client(mcp) as client:
538
  result = await client.read_resource("resource://test/data")
539
+ assert result[0].text == "Async Data for test" # type: ignore[attr-defined]
 
540
 
541
  async def test_template_decorator_with_tags(self):
542
  """Test that the template decorator properly sets tags."""
 
577
  assert prompt.name == "fn"
578
  # Don't compare functions directly since validate_call wraps them
579
  content = await prompt.render()
580
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
581
 
582
  async def test_prompt_decorator_incorrect_usage(self):
583
  mcp = FastMCP()
 
602
  prompt = prompts_dict["custom_name"]
603
  assert prompt.name == "custom_name"
604
  content = await prompt.render()
605
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
606
 
607
  async def test_prompt_decorator_with_description(self):
608
  mcp = FastMCP()
 
616
  prompt = prompts_dict["fn"]
617
  assert prompt.description == "A custom description"
618
  content = await prompt.render()
619
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
620
 
621
  async def test_prompt_decorator_with_parameters(self):
622
  mcp = FastMCP()
 
639
  result = await client.get_prompt("test_prompt", {"name": "World"})
640
  assert len(result.messages) == 1
641
  message = result.messages[0]
642
+ assert message.content.text == "Hello, World!" # type: ignore[attr-defined]
 
643
 
644
  result = await client.get_prompt(
645
  "test_prompt", {"name": "World", "greeting": "Hi"}
646
  )
647
  assert len(result.messages) == 1
648
  message = result.messages[0]
649
+ assert message.content.text == "Hi, World!" # type: ignore[attr-defined]
 
650
 
651
  async def test_prompt_decorator_instance_method(self):
652
  mcp = FastMCP()
 
665
  result = await client.get_prompt("test_prompt")
666
  assert len(result.messages) == 1
667
  message = result.messages[0]
668
+ assert message.content.text == "My prefix: Hello, world!" # type: ignore[attr-defined]
 
669
 
670
  async def test_prompt_decorator_classmethod(self):
671
  mcp = FastMCP()
 
683
  result = await client.get_prompt("test_prompt")
684
  assert len(result.messages) == 1
685
  message = result.messages[0]
686
+ assert message.content.text == "Class prefix: Hello, world!" # type: ignore[attr-defined]
 
687
 
688
  async def test_prompt_decorator_staticmethod(self):
689
  mcp = FastMCP()
 
698
  result = await client.get_prompt("test_prompt")
699
  assert len(result.messages) == 1
700
  message = result.messages[0]
701
+ assert message.content.text == "Static Hello, world!" # type: ignore[attr-defined]
 
702
 
703
  async def test_prompt_decorator_async_function(self):
704
  mcp = FastMCP()
 
711
  result = await client.get_prompt("test_prompt")
712
  assert len(result.messages) == 1
713
  message = result.messages[0]
714
+ assert message.content.text == "Async Hello, world!" # type: ignore[attr-defined]
 
715
 
716
  async def test_prompt_decorator_with_tags(self):
717
  """Test that the prompt decorator properly sets tags."""
 
908
  async with Client(main_server) as client:
909
  # Regular resource
910
  result = await client.read_resource("resource://prefix/test-resource")
911
+ assert result[0].text == "Resource content" # type: ignore[attr-defined]
 
912
 
913
  # Absolute path resource
914
  result = await client.read_resource("resource://prefix//absolute/path")
915
+ assert result[0].text == "Absolute resource content" # type: ignore[attr-defined]
 
916
 
917
  # Template resource
918
  result = await client.read_resource(
919
  "resource://prefix/param-value/template"
920
  )
921
+ assert result[0].text == "Template resource with param-value" # type: ignore[attr-defined]
 
922
 
923
  @pytest.mark.parametrize(
924
  "uri,prefix,expected_match,expected_strip",
 
994
  # Verify we can access the resources
995
  async with Client(target_server) as client:
996
  result = await client.read_resource("resource://imported/test-resource")
997
+ assert result[0].text == "Resource content" # type: ignore[attr-defined]
 
998
 
999
  result = await client.read_resource("resource://imported//absolute/path")
1000
+ assert result[0].text == "Absolute resource content" # type: ignore[attr-defined]
 
1001
 
1002
  result = await client.read_resource(
1003
  "resource://imported/param-value/template"
1004
  )
1005
+ assert result[0].text == "Template resource with param-value" # type: ignore[attr-defined]
 
tests/server/test_server_interactions.py CHANGED
@@ -10,7 +10,6 @@ import pydantic_core
10
  import pytest
11
  from mcp import McpError
12
  from mcp.types import (
13
- BlobResourceContents,
14
  ImageContent,
15
  TextContent,
16
  TextResourceContents,
@@ -77,14 +76,12 @@ class TestTools:
77
  async def test_call_tool(self, tool_server: FastMCP):
78
  async with Client(tool_server) as client:
79
  result = await client.call_tool("add", {"x": 1, "y": 2})
80
- assert isinstance(result[0], TextContent)
81
- assert result[0].text == "3"
82
 
83
  async def test_call_tool_as_client(self, tool_server: FastMCP):
84
  async with Client(tool_server) as client:
85
  result = await client.call_tool("add", {"x": 1, "y": 2})
86
- assert isinstance(result[0], TextContent)
87
- assert result[0].text == "3"
88
 
89
  async def test_call_tool_error(self, tool_server: FastMCP):
90
  async with Client(tool_server) as client:
@@ -113,8 +110,7 @@ class TestTools:
113
  async def test_tool_returns_list(self, tool_server: FastMCP):
114
  async with Client(tool_server) as client:
115
  result = await client.call_tool("list_tool", {})
116
- assert isinstance(result[0], TextContent)
117
- assert result[0].text == '[\n "x",\n 2\n]'
118
 
119
 
120
  class TestToolReturnTypes:
@@ -127,8 +123,7 @@ class TestToolReturnTypes:
127
 
128
  async with Client(mcp) as client:
129
  result = await client.call_tool("string_tool", {})
130
- assert isinstance(result[0], TextContent)
131
- assert result[0].text == "Hello, world!"
132
 
133
  async def test_bytes(self, tmp_path: Path):
134
  mcp = FastMCP()
@@ -139,8 +134,7 @@ class TestToolReturnTypes:
139
 
140
  async with Client(mcp) as client:
141
  result = await client.call_tool("bytes_tool", {})
142
- assert isinstance(result[0], TextContent)
143
- assert result[0].text == '"Hello, world!"'
144
 
145
  async def test_uuid(self):
146
  mcp = FastMCP()
@@ -153,8 +147,7 @@ class TestToolReturnTypes:
153
 
154
  async with Client(mcp) as client:
155
  result = await client.call_tool("uuid_tool", {})
156
- assert isinstance(result[0], TextContent)
157
- assert result[0].text == pydantic_core.to_json(test_uuid).decode()
158
 
159
  async def test_path(self):
160
  mcp = FastMCP()
@@ -167,8 +160,7 @@ class TestToolReturnTypes:
167
 
168
  async with Client(mcp) as client:
169
  result = await client.call_tool("path_tool", {})
170
- assert isinstance(result[0], TextContent)
171
- assert result[0].text == pydantic_core.to_json(test_path).decode()
172
 
173
  async def test_datetime(self):
174
  mcp = FastMCP()
@@ -181,8 +173,7 @@ class TestToolReturnTypes:
181
 
182
  async with Client(mcp) as client:
183
  result = await client.call_tool("datetime_tool", {})
184
- assert isinstance(result[0], TextContent)
185
- assert result[0].text == pydantic_core.to_json(dt).decode()
186
 
187
  async def test_image(self, tmp_path: Path):
188
  mcp = FastMCP()
@@ -337,8 +328,7 @@ class TestToolParameters:
337
  async with Client(mcp) as client:
338
  # String with integer value should be coerced to int
339
  result = await client.call_tool("add_one", {"x": "42"})
340
- assert isinstance(result[0], TextContent)
341
- assert result[0].text == "43"
342
 
343
  async def test_tool_bool_coercion(self):
344
  """Test string-to-bool type coercion."""
@@ -351,12 +341,10 @@ class TestToolParameters:
351
  async with Client(mcp) as client:
352
  # String with boolean value should be coerced to bool
353
  result = await client.call_tool("toggle", {"flag": "true"})
354
- assert isinstance(result[0], TextContent)
355
- assert result[0].text == "false"
356
 
357
  result = await client.call_tool("toggle", {"flag": "false"})
358
- assert isinstance(result[0], TextContent)
359
- assert result[0].text == "true"
360
 
361
  async def test_annotated_field_validation(self):
362
  mcp = FastMCP()
@@ -411,8 +399,7 @@ class TestToolParameters:
411
 
412
  async with Client(mcp) as client:
413
  result = await client.call_tool("analyze", {"x": "a"})
414
- assert isinstance(result[0], TextContent)
415
- assert result[0].text == "a"
416
 
417
  async def test_enum_type_validation_error(self):
418
  mcp = FastMCP()
@@ -444,8 +431,7 @@ class TestToolParameters:
444
 
445
  async with Client(mcp) as client:
446
  result = await client.call_tool("analyze", {"x": "red"})
447
- assert isinstance(result[0], TextContent)
448
- assert result[0].text == "red"
449
 
450
  async def test_union_type_validation(self):
451
  mcp = FastMCP()
@@ -456,12 +442,10 @@ class TestToolParameters:
456
 
457
  async with Client(mcp) as client:
458
  result = await client.call_tool("analyze", {"x": 1})
459
- assert isinstance(result[0], TextContent)
460
- assert result[0].text == "1"
461
 
462
  result = await client.call_tool("analyze", {"x": 1.0})
463
- assert isinstance(result[0], TextContent)
464
- assert result[0].text == "1.0"
465
 
466
  with pytest.raises(ToolError, match="Error calling tool 'analyze'"):
467
  await client.call_tool("analyze", {"x": "not a number"})
@@ -479,8 +463,7 @@ class TestToolParameters:
479
 
480
  async with Client(mcp) as client:
481
  result = await client.call_tool("send_path", {"path": str(test_path)})
482
- assert isinstance(result[0], TextContent)
483
- assert result[0].text == str(test_path)
484
 
485
  async def test_path_type_error(self):
486
  mcp = FastMCP()
@@ -505,8 +488,7 @@ class TestToolParameters:
505
 
506
  async with Client(mcp) as client:
507
  result = await client.call_tool("send_uuid", {"x": test_uuid})
508
- assert isinstance(result[0], TextContent)
509
- assert result[0].text == str(test_uuid)
510
 
511
  async def test_uuid_type_error(self):
512
  mcp = FastMCP()
@@ -530,8 +512,7 @@ class TestToolParameters:
530
 
531
  async with Client(mcp) as client:
532
  result = await client.call_tool("send_datetime", {"x": dt})
533
- assert isinstance(result[0], TextContent)
534
- assert result[0].text == dt.isoformat()
535
 
536
  async def test_datetime_type_parse_string(self):
537
  mcp = FastMCP()
@@ -544,8 +525,7 @@ class TestToolParameters:
544
  result = await client.call_tool(
545
  "send_datetime", {"x": "2021-01-01T00:00:00"}
546
  )
547
- assert isinstance(result[0], TextContent)
548
- assert result[0].text == "2021-01-01T00:00:00"
549
 
550
  async def test_datetime_type_error(self):
551
  mcp = FastMCP()
@@ -567,8 +547,7 @@ class TestToolParameters:
567
 
568
  async with Client(mcp) as client:
569
  result = await client.call_tool("send_date", {"x": datetime.date.today()})
570
- assert isinstance(result[0], TextContent)
571
- assert result[0].text == datetime.date.today().isoformat()
572
 
573
  async def test_date_type_parse_string(self):
574
  mcp = FastMCP()
@@ -579,8 +558,7 @@ class TestToolParameters:
579
 
580
  async with Client(mcp) as client:
581
  result = await client.call_tool("send_date", {"x": "2021-01-01"})
582
- assert isinstance(result[0], TextContent)
583
- assert result[0].text == "2021-01-01"
584
 
585
  async def test_timedelta_type(self):
586
  mcp = FastMCP()
@@ -593,8 +571,7 @@ class TestToolParameters:
593
  result = await client.call_tool(
594
  "send_timedelta", {"x": datetime.timedelta(days=1)}
595
  )
596
- assert isinstance(result[0], TextContent)
597
- assert result[0].text == "1 day, 0:00:00"
598
 
599
  async def test_timedelta_type_parse_int(self):
600
  mcp = FastMCP()
@@ -605,8 +582,7 @@ class TestToolParameters:
605
 
606
  async with Client(mcp) as client:
607
  result = await client.call_tool("send_timedelta", {"x": 1000})
608
- assert isinstance(result[0], TextContent)
609
- assert result[0].text == "0:16:40"
610
 
611
 
612
  class TestToolContextInjection:
@@ -639,7 +615,7 @@ class TestToolContextInjection:
639
  result = await client.call_tool("tool_with_context", {"x": 42})
640
  assert len(result) == 1
641
  content = result[0]
642
- assert isinstance(content, TextContent)
643
 
644
  async def test_async_context(self):
645
  """Test that context works in async functions."""
@@ -654,8 +630,7 @@ class TestToolContextInjection:
654
  result = await client.call_tool("async_tool", {"x": 42})
655
  assert len(result) == 1
656
  content = result[0]
657
- assert isinstance(content, TextContent)
658
- assert content.text == "Async request 2: 42"
659
 
660
  async def test_optional_context(self):
661
  """Test that context is optional."""
@@ -669,8 +644,7 @@ class TestToolContextInjection:
669
  result = await client.call_tool("no_context", {"x": 21})
670
  assert len(result) == 1
671
  content = result[0]
672
- assert isinstance(content, TextContent)
673
- assert content.text == "42"
674
 
675
  async def test_context_resource_access(self):
676
  """Test that context can access resources."""
@@ -692,8 +666,7 @@ class TestToolContextInjection:
692
  result = await client.call_tool("tool_with_resource", {})
693
  assert len(result) == 1
694
  content = result[0]
695
- assert isinstance(content, TextContent)
696
- assert "Read resource: resource data" in content.text
697
 
698
  async def test_tool_decorator_with_tags(self):
699
  """Test that the tool decorator properly sets tags."""
@@ -721,8 +694,7 @@ class TestToolContextInjection:
721
 
722
  async with Client(mcp) as client:
723
  result = await client.call_tool("MyTool", {"x": 2})
724
- assert isinstance(result[0], TextContent)
725
- assert result[0].text == "4"
726
 
727
 
728
  class TestResource:
@@ -739,8 +711,7 @@ class TestResource:
739
 
740
  async with Client(mcp) as client:
741
  result = await client.read_resource(AnyUrl("resource://test"))
742
- assert isinstance(result[0], TextResourceContents)
743
- assert result[0].text == "Hello, world!"
744
 
745
  async def test_binary_resource(self):
746
  mcp = FastMCP()
@@ -758,8 +729,7 @@ class TestResource:
758
 
759
  async with Client(mcp) as client:
760
  result = await client.read_resource(AnyUrl("resource://binary"))
761
- assert isinstance(result[0], BlobResourceContents)
762
- assert result[0].blob == base64.b64encode(b"Binary data").decode()
763
 
764
  async def test_file_resource_text(self, tmp_path: Path):
765
  mcp = FastMCP()
@@ -775,8 +745,7 @@ class TestResource:
775
 
776
  async with Client(mcp) as client:
777
  result = await client.read_resource(AnyUrl("file://test.txt"))
778
- assert isinstance(result[0], TextResourceContents)
779
- assert result[0].text == "Hello from file!"
780
 
781
  async def test_file_resource_binary(self, tmp_path: Path):
782
  mcp = FastMCP()
@@ -795,8 +764,7 @@ class TestResource:
795
 
796
  async with Client(mcp) as client:
797
  result = await client.read_resource(AnyUrl("file://test.bin"))
798
- assert isinstance(result[0], BlobResourceContents)
799
- assert result[0].blob == base64.b64encode(b"Binary file data").decode()
800
 
801
 
802
  class TestResourceContext:
@@ -810,8 +778,7 @@ class TestResourceContext:
810
 
811
  async with Client(mcp) as client:
812
  result = await client.read_resource(AnyUrl("resource://test"))
813
- assert isinstance(result[0], TextResourceContents)
814
- assert result[0].text == "2"
815
 
816
 
817
  class TestResourceTemplates:
@@ -860,8 +827,7 @@ class TestResourceTemplates:
860
 
861
  async with Client(mcp) as client:
862
  result = await client.read_resource(AnyUrl("resource://test/data"))
863
- assert isinstance(result[0], TextResourceContents)
864
- assert result[0].text == "Data for test"
865
 
866
  async def test_resource_mismatched_params(self):
867
  """Test that mismatched parameters raise an error"""
@@ -888,8 +854,7 @@ class TestResourceTemplates:
888
  result = await client.read_resource(
889
  AnyUrl("resource://cursor/fastmcp/data")
890
  )
891
- assert isinstance(result[0], TextResourceContents)
892
- assert result[0].text == "Data for cursor/fastmcp"
893
 
894
  async def test_resource_multiple_mismatched_params(self):
895
  """Test that mismatched parameters raise an error"""
@@ -913,8 +878,7 @@ class TestResourceTemplates:
913
 
914
  async with Client(mcp) as client:
915
  result = await client.read_resource(AnyUrl("resource://static"))
916
- assert isinstance(result[0], TextResourceContents)
917
- assert result[0].text == "Static data"
918
 
919
  async def test_template_with_varkwargs(self):
920
  """Test that a template can have **kwargs."""
@@ -926,8 +890,7 @@ class TestResourceTemplates:
926
 
927
  async with Client(mcp) as client:
928
  result = await client.read_resource(AnyUrl("test://1/2/3"))
929
- assert isinstance(result[0], TextResourceContents)
930
- assert result[0].text == "6"
931
 
932
  async def test_template_with_default_params(self):
933
  """Test that a template can have default parameters."""
@@ -946,13 +909,11 @@ class TestResourceTemplates:
946
  # Call the template and verify it uses the default value
947
  async with Client(mcp) as client:
948
  result = await client.read_resource(AnyUrl("math://add/5"))
949
- assert isinstance(result[0], TextResourceContents)
950
- assert result[0].text == "15" # 5 + default 10
951
 
952
  # Can also call with explicit params
953
  result2 = await client.read_resource(AnyUrl("math://add/7"))
954
- assert isinstance(result2[0], TextResourceContents)
955
- assert result2[0].text == "17" # 7 + default 10
956
 
957
  async def test_template_to_resource_conversion(self):
958
  """Test that a template can be converted to a resource."""
@@ -971,8 +932,7 @@ class TestResourceTemplates:
971
  # When accessed, should create a concrete resource
972
  async with Client(mcp) as client:
973
  result = await client.read_resource(AnyUrl("resource://test/data"))
974
- assert isinstance(result[0], TextResourceContents)
975
- assert result[0].text == "Data for test"
976
 
977
  async def test_stacked_resource_template_decorators(self):
978
  """Test that resource template decorators can be stacked."""
@@ -1011,15 +971,15 @@ class TestResourceTemplates:
1011
  email_result = await client.read_resource(
1012
  AnyUrl("users://email/user@example.com")
1013
  )
1014
- assert isinstance(email_result[0], TextResourceContents)
1015
- email_data = json.loads(email_result[0].text)
1016
  assert email_data["lookup"] == "email"
1017
  assert email_data["email"] == "user@example.com"
1018
 
1019
  # Test lookup by name
1020
  name_result = await client.read_resource(AnyUrl("users://name/John"))
1021
- assert isinstance(name_result[0], TextResourceContents)
1022
- name_data = json.loads(name_result[0].text)
1023
  assert name_data["lookup"] == "name"
1024
  assert name_data["name"] == "John"
1025
  assert name_data["email"] == "dummy@example.com"
@@ -1044,8 +1004,7 @@ class TestResourceTemplates:
1044
 
1045
  async with Client(mcp) as client:
1046
  result = await client.read_resource(AnyUrl("resource://test/data"))
1047
- assert isinstance(result[0], TextResourceContents)
1048
- assert result[0].text == "Template resource: test/data"
1049
 
1050
  async def test_templates_match_in_order_of_definition(self):
1051
  """
@@ -1065,12 +1024,10 @@ class TestResourceTemplates:
1065
 
1066
  async with Client(mcp) as client:
1067
  result = await client.read_resource(AnyUrl("resource://a/b/c"))
1068
- assert isinstance(result[0], TextResourceContents)
1069
- assert result[0].text == "Template resource 1: a/b/c"
1070
 
1071
  result = await client.read_resource(AnyUrl("resource://a/b"))
1072
- assert isinstance(result[0], TextResourceContents)
1073
- assert result[0].text == "Template resource 1: a/b"
1074
 
1075
  async def test_templates_shadow_each_other_reorder(self):
1076
  """
@@ -1089,12 +1046,10 @@ class TestResourceTemplates:
1089
 
1090
  async with Client(mcp) as client:
1091
  result = await client.read_resource(AnyUrl("resource://a/b/c"))
1092
- assert isinstance(result[0], TextResourceContents)
1093
- assert result[0].text == "Template resource 2: a/b/c"
1094
 
1095
  result = await client.read_resource(AnyUrl("resource://a/b"))
1096
- assert isinstance(result[0], TextResourceContents)
1097
- assert result[0].text == "Template resource 1: a/b"
1098
 
1099
 
1100
  class TestResourceTemplateContext:
@@ -1108,8 +1063,7 @@ class TestResourceTemplateContext:
1108
 
1109
  async with Client(mcp) as client:
1110
  result = await client.read_resource(AnyUrl("resource://test"))
1111
- assert isinstance(result[0], TextResourceContents)
1112
- assert result[0].text.startswith("Resource template: test 2")
1113
 
1114
  async def test_resource_template_context_with_callable_object(self):
1115
  mcp = FastMCP()
@@ -1122,8 +1076,7 @@ class TestResourceTemplateContext:
1122
 
1123
  async with Client(mcp) as client:
1124
  result = await client.read_resource(AnyUrl("resource://test"))
1125
- assert isinstance(result[0], TextResourceContents)
1126
- assert result[0].text.startswith("Resource template: test 2")
1127
 
1128
 
1129
  class TestPrompts:
@@ -1143,8 +1096,7 @@ class TestPrompts:
1143
  assert prompt.name == "fn"
1144
  # Don't compare functions directly since validate_call wraps them
1145
  content = await prompt.render()
1146
- assert isinstance(content[0].content, TextContent)
1147
- assert content[0].content.text == "Hello, world!"
1148
 
1149
  async def test_prompt_decorator_with_name(self):
1150
  """Test prompt decorator with custom name."""
@@ -1159,8 +1111,7 @@ class TestPrompts:
1159
  prompt = prompts_dict["custom_name"]
1160
  assert prompt.name == "custom_name"
1161
  content = await prompt.render()
1162
- assert isinstance(content[0].content, TextContent)
1163
- assert content[0].content.text == "Hello, world!"
1164
 
1165
  async def test_prompt_decorator_with_description(self):
1166
  """Test prompt decorator with custom description."""
@@ -1175,8 +1126,7 @@ class TestPrompts:
1175
  prompt = prompts_dict["fn"]
1176
  assert prompt.description == "A custom description"
1177
  content = await prompt.render()
1178
- assert isinstance(content[0].content, TextContent)
1179
- assert content[0].content.text == "Hello, world!"
1180
 
1181
  def test_prompt_decorator_error(self):
1182
  """Test error when decorator is used incorrectly."""
@@ -1224,8 +1174,7 @@ class TestPrompts:
1224
  message = result.messages[0]
1225
  assert message.role == "user"
1226
  content = message.content
1227
- assert isinstance(content, TextContent)
1228
- assert content.text == "Hello, World!"
1229
 
1230
  async def test_get_prompt_with_resource(self):
1231
  """Test getting a prompt that returns resource content."""
@@ -1249,10 +1198,10 @@ class TestPrompts:
1249
  result = await client.get_prompt("fn")
1250
  assert result.messages[0].role == "user"
1251
  content = result.messages[0].content
1252
- assert isinstance(content, EmbeddedResource)
1253
  resource = content.resource
1254
- assert isinstance(resource, TextResourceContents)
1255
- assert resource.text == "File contents"
1256
  assert resource.mimeType == "text/plain"
1257
 
1258
  async def test_get_unknown_prompt(self):
@@ -1342,5 +1291,4 @@ class TestPromptContext:
1342
  assert len(result.messages) == 1
1343
  message = result.messages[0]
1344
  assert message.role == "user"
1345
- assert isinstance(message.content, TextContent)
1346
- assert message.content.text == "Hello, World! 2"
 
10
  import pytest
11
  from mcp import McpError
12
  from mcp.types import (
 
13
  ImageContent,
14
  TextContent,
15
  TextResourceContents,
 
76
  async def test_call_tool(self, tool_server: FastMCP):
77
  async with Client(tool_server) as client:
78
  result = await client.call_tool("add", {"x": 1, "y": 2})
79
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
80
 
81
  async def test_call_tool_as_client(self, tool_server: FastMCP):
82
  async with Client(tool_server) as client:
83
  result = await client.call_tool("add", {"x": 1, "y": 2})
84
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
85
 
86
  async def test_call_tool_error(self, tool_server: FastMCP):
87
  async with Client(tool_server) as client:
 
110
  async def test_tool_returns_list(self, tool_server: FastMCP):
111
  async with Client(tool_server) as client:
112
  result = await client.call_tool("list_tool", {})
113
+ assert result[0].text == '[\n "x",\n 2\n]' # type: ignore[attr-defined]
 
114
 
115
 
116
  class TestToolReturnTypes:
 
123
 
124
  async with Client(mcp) as client:
125
  result = await client.call_tool("string_tool", {})
126
+ assert result[0].text == "Hello, world!" # type: ignore[attr-defined]
 
127
 
128
  async def test_bytes(self, tmp_path: Path):
129
  mcp = FastMCP()
 
134
 
135
  async with Client(mcp) as client:
136
  result = await client.call_tool("bytes_tool", {})
137
+ assert result[0].text == '"Hello, world!"' # type: ignore[attr-defined]
 
138
 
139
  async def test_uuid(self):
140
  mcp = FastMCP()
 
147
 
148
  async with Client(mcp) as client:
149
  result = await client.call_tool("uuid_tool", {})
150
+ assert result[0].text == pydantic_core.to_json(test_uuid).decode() # type: ignore[attr-defined]
 
151
 
152
  async def test_path(self):
153
  mcp = FastMCP()
 
160
 
161
  async with Client(mcp) as client:
162
  result = await client.call_tool("path_tool", {})
163
+ assert result[0].text == pydantic_core.to_json(test_path).decode() # type: ignore[attr-defined]
 
164
 
165
  async def test_datetime(self):
166
  mcp = FastMCP()
 
173
 
174
  async with Client(mcp) as client:
175
  result = await client.call_tool("datetime_tool", {})
176
+ assert result[0].text == pydantic_core.to_json(dt).decode() # type: ignore[attr-defined]
 
177
 
178
  async def test_image(self, tmp_path: Path):
179
  mcp = FastMCP()
 
328
  async with Client(mcp) as client:
329
  # String with integer value should be coerced to int
330
  result = await client.call_tool("add_one", {"x": "42"})
331
+ assert result[0].text == "43" # type: ignore[attr-defined]
 
332
 
333
  async def test_tool_bool_coercion(self):
334
  """Test string-to-bool type coercion."""
 
341
  async with Client(mcp) as client:
342
  # String with boolean value should be coerced to bool
343
  result = await client.call_tool("toggle", {"flag": "true"})
344
+ assert result[0].text == "false" # type: ignore[attr-defined]
 
345
 
346
  result = await client.call_tool("toggle", {"flag": "false"})
347
+ assert result[0].text == "true" # type: ignore[attr-defined]
 
348
 
349
  async def test_annotated_field_validation(self):
350
  mcp = FastMCP()
 
399
 
400
  async with Client(mcp) as client:
401
  result = await client.call_tool("analyze", {"x": "a"})
402
+ assert result[0].text == "a" # type: ignore[attr-defined]
 
403
 
404
  async def test_enum_type_validation_error(self):
405
  mcp = FastMCP()
 
431
 
432
  async with Client(mcp) as client:
433
  result = await client.call_tool("analyze", {"x": "red"})
434
+ assert result[0].text == "red" # type: ignore[attr-defined]
 
435
 
436
  async def test_union_type_validation(self):
437
  mcp = FastMCP()
 
442
 
443
  async with Client(mcp) as client:
444
  result = await client.call_tool("analyze", {"x": 1})
445
+ assert result[0].text == "1" # type: ignore[attr-defined]
 
446
 
447
  result = await client.call_tool("analyze", {"x": 1.0})
448
+ assert result[0].text == "1.0" # type: ignore[attr-defined]
 
449
 
450
  with pytest.raises(ToolError, match="Error calling tool 'analyze'"):
451
  await client.call_tool("analyze", {"x": "not a number"})
 
463
 
464
  async with Client(mcp) as client:
465
  result = await client.call_tool("send_path", {"path": str(test_path)})
466
+ assert result[0].text == str(test_path) # type: ignore[attr-defined]
 
467
 
468
  async def test_path_type_error(self):
469
  mcp = FastMCP()
 
488
 
489
  async with Client(mcp) as client:
490
  result = await client.call_tool("send_uuid", {"x": test_uuid})
491
+ assert result[0].text == str(test_uuid) # type: ignore[attr-defined]
 
492
 
493
  async def test_uuid_type_error(self):
494
  mcp = FastMCP()
 
512
 
513
  async with Client(mcp) as client:
514
  result = await client.call_tool("send_datetime", {"x": dt})
515
+ assert result[0].text == dt.isoformat() # type: ignore[attr-defined]
 
516
 
517
  async def test_datetime_type_parse_string(self):
518
  mcp = FastMCP()
 
525
  result = await client.call_tool(
526
  "send_datetime", {"x": "2021-01-01T00:00:00"}
527
  )
528
+ assert result[0].text == "2021-01-01T00:00:00" # type: ignore[attr-defined]
 
529
 
530
  async def test_datetime_type_error(self):
531
  mcp = FastMCP()
 
547
 
548
  async with Client(mcp) as client:
549
  result = await client.call_tool("send_date", {"x": datetime.date.today()})
550
+ assert result[0].text == datetime.date.today().isoformat() # type: ignore[attr-defined]
 
551
 
552
  async def test_date_type_parse_string(self):
553
  mcp = FastMCP()
 
558
 
559
  async with Client(mcp) as client:
560
  result = await client.call_tool("send_date", {"x": "2021-01-01"})
561
+ assert result[0].text == "2021-01-01" # type: ignore[attr-defined]
 
562
 
563
  async def test_timedelta_type(self):
564
  mcp = FastMCP()
 
571
  result = await client.call_tool(
572
  "send_timedelta", {"x": datetime.timedelta(days=1)}
573
  )
574
+ assert result[0].text == "1 day, 0:00:00" # type: ignore[attr-defined]
 
575
 
576
  async def test_timedelta_type_parse_int(self):
577
  mcp = FastMCP()
 
582
 
583
  async with Client(mcp) as client:
584
  result = await client.call_tool("send_timedelta", {"x": 1000})
585
+ assert result[0].text == "0:16:40" # type: ignore[attr-defined]
 
586
 
587
 
588
  class TestToolContextInjection:
 
615
  result = await client.call_tool("tool_with_context", {"x": 42})
616
  assert len(result) == 1
617
  content = result[0]
618
+ assert content.text == "2" # type: ignore[attr-defined]
619
 
620
  async def test_async_context(self):
621
  """Test that context works in async functions."""
 
630
  result = await client.call_tool("async_tool", {"x": 42})
631
  assert len(result) == 1
632
  content = result[0]
633
+ assert content.text == "Async request 2: 42" # type: ignore[attr-defined]
 
634
 
635
  async def test_optional_context(self):
636
  """Test that context is optional."""
 
644
  result = await client.call_tool("no_context", {"x": 21})
645
  assert len(result) == 1
646
  content = result[0]
647
+ assert content.text == "42" # type: ignore[attr-defined]
 
648
 
649
  async def test_context_resource_access(self):
650
  """Test that context can access resources."""
 
666
  result = await client.call_tool("tool_with_resource", {})
667
  assert len(result) == 1
668
  content = result[0]
669
+ assert "Read resource: resource data" in content.text # type: ignore[attr-defined]
 
670
 
671
  async def test_tool_decorator_with_tags(self):
672
  """Test that the tool decorator properly sets tags."""
 
694
 
695
  async with Client(mcp) as client:
696
  result = await client.call_tool("MyTool", {"x": 2})
697
+ assert result[0].text == "4" # type: ignore[attr-defined]
 
698
 
699
 
700
  class TestResource:
 
711
 
712
  async with Client(mcp) as client:
713
  result = await client.read_resource(AnyUrl("resource://test"))
714
+ assert result[0].text == "Hello, world!" # type: ignore[attr-defined]
 
715
 
716
  async def test_binary_resource(self):
717
  mcp = FastMCP()
 
729
 
730
  async with Client(mcp) as client:
731
  result = await client.read_resource(AnyUrl("resource://binary"))
732
+ assert result[0].blob == base64.b64encode(b"Binary data").decode() # type: ignore[attr-defined]
 
733
 
734
  async def test_file_resource_text(self, tmp_path: Path):
735
  mcp = FastMCP()
 
745
 
746
  async with Client(mcp) as client:
747
  result = await client.read_resource(AnyUrl("file://test.txt"))
748
+ assert result[0].text == "Hello from file!" # type: ignore[attr-defined]
 
749
 
750
  async def test_file_resource_binary(self, tmp_path: Path):
751
  mcp = FastMCP()
 
764
 
765
  async with Client(mcp) as client:
766
  result = await client.read_resource(AnyUrl("file://test.bin"))
767
+ assert result[0].blob == base64.b64encode(b"Binary file data").decode() # type: ignore[attr-defined]
 
768
 
769
 
770
  class TestResourceContext:
 
778
 
779
  async with Client(mcp) as client:
780
  result = await client.read_resource(AnyUrl("resource://test"))
781
+ assert result[0].text == "2" # type: ignore[attr-defined]
 
782
 
783
 
784
  class TestResourceTemplates:
 
827
 
828
  async with Client(mcp) as client:
829
  result = await client.read_resource(AnyUrl("resource://test/data"))
830
+ assert result[0].text == "Data for test" # type: ignore[attr-defined]
 
831
 
832
  async def test_resource_mismatched_params(self):
833
  """Test that mismatched parameters raise an error"""
 
854
  result = await client.read_resource(
855
  AnyUrl("resource://cursor/fastmcp/data")
856
  )
857
+ assert result[0].text == "Data for cursor/fastmcp" # type: ignore[attr-defined]
 
858
 
859
  async def test_resource_multiple_mismatched_params(self):
860
  """Test that mismatched parameters raise an error"""
 
878
 
879
  async with Client(mcp) as client:
880
  result = await client.read_resource(AnyUrl("resource://static"))
881
+ assert result[0].text == "Static data" # type: ignore[attr-defined]
 
882
 
883
  async def test_template_with_varkwargs(self):
884
  """Test that a template can have **kwargs."""
 
890
 
891
  async with Client(mcp) as client:
892
  result = await client.read_resource(AnyUrl("test://1/2/3"))
893
+ assert result[0].text == "6" # type: ignore[attr-defined]
 
894
 
895
  async def test_template_with_default_params(self):
896
  """Test that a template can have default parameters."""
 
909
  # Call the template and verify it uses the default value
910
  async with Client(mcp) as client:
911
  result = await client.read_resource(AnyUrl("math://add/5"))
912
+ assert result[0].text == "15" # type: ignore[attr-defined]
 
913
 
914
  # Can also call with explicit params
915
  result2 = await client.read_resource(AnyUrl("math://add/7"))
916
+ assert result2[0].text == "17" # type: ignore[attr-defined]
 
917
 
918
  async def test_template_to_resource_conversion(self):
919
  """Test that a template can be converted to a resource."""
 
932
  # When accessed, should create a concrete resource
933
  async with Client(mcp) as client:
934
  result = await client.read_resource(AnyUrl("resource://test/data"))
935
+ assert result[0].text == "Data for test" # type: ignore[attr-defined]
 
936
 
937
  async def test_stacked_resource_template_decorators(self):
938
  """Test that resource template decorators can be stacked."""
 
971
  email_result = await client.read_resource(
972
  AnyUrl("users://email/user@example.com")
973
  )
974
+ assert email_result[0].text # type: ignore[attr-defined]
975
+ email_data = json.loads(email_result[0].text) # type: ignore[attr-defined]
976
  assert email_data["lookup"] == "email"
977
  assert email_data["email"] == "user@example.com"
978
 
979
  # Test lookup by name
980
  name_result = await client.read_resource(AnyUrl("users://name/John"))
981
+ assert name_result[0].text # type: ignore[attr-defined]
982
+ name_
983
  assert name_data["lookup"] == "name"
984
  assert name_data["name"] == "John"
985
  assert name_data["email"] == "dummy@example.com"
 
1004
 
1005
  async with Client(mcp) as client:
1006
  result = await client.read_resource(AnyUrl("resource://test/data"))
1007
+ assert result[0].text == "Template resource: test/data" # type: ignore[attr-defined]
 
1008
 
1009
  async def test_templates_match_in_order_of_definition(self):
1010
  """
 
1024
 
1025
  async with Client(mcp) as client:
1026
  result = await client.read_resource(AnyUrl("resource://a/b/c"))
1027
+ assert result[0].text == "Template resource 1: a/b/c" # type: ignore[attr-defined]
 
1028
 
1029
  result = await client.read_resource(AnyUrl("resource://a/b"))
1030
+ assert result[0].text == "Template resource 1: a/b" # type: ignore[attr-defined]
 
1031
 
1032
  async def test_templates_shadow_each_other_reorder(self):
1033
  """
 
1046
 
1047
  async with Client(mcp) as client:
1048
  result = await client.read_resource(AnyUrl("resource://a/b/c"))
1049
+ assert result[0].text == "Template resource 2: a/b/c" # type: ignore[attr-defined]
 
1050
 
1051
  result = await client.read_resource(AnyUrl("resource://a/b"))
1052
+ assert result[0].text == "Template resource 1: a/b" # type: ignore[attr-defined]
 
1053
 
1054
 
1055
  class TestResourceTemplateContext:
 
1063
 
1064
  async with Client(mcp) as client:
1065
  result = await client.read_resource(AnyUrl("resource://test"))
1066
+ assert result[0].text.startswith("Resource template: test 2") # type: ignore[attr-defined]
 
1067
 
1068
  async def test_resource_template_context_with_callable_object(self):
1069
  mcp = FastMCP()
 
1076
 
1077
  async with Client(mcp) as client:
1078
  result = await client.read_resource(AnyUrl("resource://test"))
1079
+ assert result[0].text.startswith("Resource template: test 2") # type: ignore[attr-defined]
 
1080
 
1081
 
1082
  class TestPrompts:
 
1096
  assert prompt.name == "fn"
1097
  # Don't compare functions directly since validate_call wraps them
1098
  content = await prompt.render()
1099
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
1100
 
1101
  async def test_prompt_decorator_with_name(self):
1102
  """Test prompt decorator with custom name."""
 
1111
  prompt = prompts_dict["custom_name"]
1112
  assert prompt.name == "custom_name"
1113
  content = await prompt.render()
1114
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
1115
 
1116
  async def test_prompt_decorator_with_description(self):
1117
  """Test prompt decorator with custom description."""
 
1126
  prompt = prompts_dict["fn"]
1127
  assert prompt.description == "A custom description"
1128
  content = await prompt.render()
1129
+ assert content[0].content.text == "Hello, world!" # type: ignore[attr-defined]
 
1130
 
1131
  def test_prompt_decorator_error(self):
1132
  """Test error when decorator is used incorrectly."""
 
1174
  message = result.messages[0]
1175
  assert message.role == "user"
1176
  content = message.content
1177
+ assert content.text == "Hello, World!" # type: ignore[attr-defined]
 
1178
 
1179
  async def test_get_prompt_with_resource(self):
1180
  """Test getting a prompt that returns resource content."""
 
1198
  result = await client.get_prompt("fn")
1199
  assert result.messages[0].role == "user"
1200
  content = result.messages[0].content
1201
+ assert isinstance(content, EmbeddedResource) # type: ignore[attr-defined]
1202
  resource = content.resource
1203
+ assert isinstance(resource, TextResourceContents) # type: ignore[attr-defined]
1204
+ assert resource.text == "File contents" # type: ignore[attr-defined]
1205
  assert resource.mimeType == "text/plain"
1206
 
1207
  async def test_get_unknown_prompt(self):
 
1291
  assert len(result.messages) == 1
1292
  message = result.messages[0]
1293
  assert message.role == "user"
1294
+ assert message.content.text == "Hello, World! 2" # type: ignore[attr-defined]
 
tests/server/test_tool_annotations.py CHANGED
@@ -1,6 +1,6 @@
1
  from typing import Any
2
 
3
- from mcp.types import TextContent, ToolAnnotations
4
 
5
  from fastmcp import Client, FastMCP
6
 
@@ -212,8 +212,7 @@ async def test_tool_functionality_with_annotations():
212
  "create_item", {"name": "test_item", "value": 42}
213
  )
214
  assert len(result) == 1
215
- assert isinstance(result[0], TextContent)
216
 
217
  # The result should contain the expected JSON
218
- assert '"name": "test_item"' in result[0].text
219
- assert '"value": 42' in result[0].text
 
1
  from typing import Any
2
 
3
+ from mcp.types import ToolAnnotations
4
 
5
  from fastmcp import Client, FastMCP
6
 
 
212
  "create_item", {"name": "test_item", "value": 42}
213
  )
214
  assert len(result) == 1
 
215
 
216
  # The result should contain the expected JSON
217
+ assert '"name": "test_item"' in result[0].text # type: ignore[attr-defined]
218
+ assert '"value": 42' in result[0].text # type: ignore[attr-defined]
tests/test_examples.py CHANGED
@@ -1,10 +1,5 @@
1
  """Tests for example servers"""
2
 
3
- from mcp.types import (
4
- PromptMessage,
5
- TextContent,
6
- TextResourceContents,
7
- )
8
  from pydantic import AnyUrl
9
 
10
  from fastmcp import Client
@@ -17,8 +12,7 @@ async def test_simple_echo():
17
  async with Client(mcp) as client:
18
  result = await client.call_tool("echo", {"text": "hello"})
19
  assert len(result) == 1
20
- assert isinstance(result[0], TextContent)
21
- assert result[0].text == "hello"
22
 
23
 
24
  async def test_complex_inputs():
@@ -31,8 +25,7 @@ async def test_complex_inputs():
31
  "name_shrimp", {"tank": tank, "extra_names": ["charlie"]}
32
  )
33
  assert len(result) == 1
34
- assert isinstance(result[0], TextContent)
35
- assert result[0].text == '[\n "bob",\n "alice",\n "charlie"\n]'
36
 
37
 
38
  async def test_desktop(monkeypatch):
@@ -43,15 +36,12 @@ async def test_desktop(monkeypatch):
43
  # Test the add function
44
  result = await client.call_tool("add", {"a": 1, "b": 2})
45
  assert len(result) == 1
46
- assert isinstance(result[0], TextContent)
47
- assert result[0].text == "3"
48
 
49
  async with Client(mcp) as client:
50
  result = await client.read_resource(AnyUrl("greeting://rooter12"))
51
  assert len(result) == 1
52
- assert isinstance(result[0], TextResourceContents)
53
- assert isinstance(result[0].text, str)
54
- assert result[0].text == "Hello, rooter12!"
55
 
56
 
57
  async def test_echo():
@@ -61,27 +51,19 @@ async def test_echo():
61
  async with Client(mcp) as client:
62
  result = await client.call_tool("echo_tool", {"text": "hello"})
63
  assert len(result) == 1
64
- assert isinstance(result[0], TextContent)
65
- assert result[0].text == "hello"
66
 
67
  async with Client(mcp) as client:
68
  result = await client.read_resource(AnyUrl("echo://static"))
69
  assert len(result) == 1
70
- assert isinstance(result[0], TextResourceContents)
71
- assert isinstance(result[0].text, str)
72
- assert result[0].text == "Echo!"
73
 
74
  async with Client(mcp) as client:
75
  result = await client.read_resource(AnyUrl("echo://server42"))
76
  assert len(result) == 1
77
- assert isinstance(result[0], TextResourceContents)
78
- assert isinstance(result[0].text, str)
79
- assert result[0].text == "Echo: server42"
80
 
81
  async with Client(mcp) as client:
82
  result = await client.get_prompt("echo", {"text": "hello"})
83
  assert len(result.messages) == 1
84
- assert isinstance(result.messages[0], PromptMessage)
85
- assert isinstance(result.messages[0].content, TextContent)
86
- assert isinstance(result.messages[0].content.text, str)
87
- assert result.messages[0].content.text == "hello"
 
1
  """Tests for example servers"""
2
 
 
 
 
 
 
3
  from pydantic import AnyUrl
4
 
5
  from fastmcp import Client
 
12
  async with Client(mcp) as client:
13
  result = await client.call_tool("echo", {"text": "hello"})
14
  assert len(result) == 1
15
+ assert result[0].text == "hello" # type: ignore[attr-defined]
 
16
 
17
 
18
  async def test_complex_inputs():
 
25
  "name_shrimp", {"tank": tank, "extra_names": ["charlie"]}
26
  )
27
  assert len(result) == 1
28
+ assert result[0].text == '[\n "bob",\n "alice",\n "charlie"\n]' # type: ignore[attr-defined]
 
29
 
30
 
31
  async def test_desktop(monkeypatch):
 
36
  # Test the add function
37
  result = await client.call_tool("add", {"a": 1, "b": 2})
38
  assert len(result) == 1
39
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
40
 
41
  async with Client(mcp) as client:
42
  result = await client.read_resource(AnyUrl("greeting://rooter12"))
43
  assert len(result) == 1
44
+ assert result[0].text == "Hello, rooter12!" # type: ignore[attr-defined]
 
 
45
 
46
 
47
  async def test_echo():
 
51
  async with Client(mcp) as client:
52
  result = await client.call_tool("echo_tool", {"text": "hello"})
53
  assert len(result) == 1
54
+ assert result[0].text == "hello" # type: ignore[attr-defined]
 
55
 
56
  async with Client(mcp) as client:
57
  result = await client.read_resource(AnyUrl("echo://static"))
58
  assert len(result) == 1
59
+ assert result[0].text == "Echo!" # type: ignore[attr-defined]
 
 
60
 
61
  async with Client(mcp) as client:
62
  result = await client.read_resource(AnyUrl("echo://server42"))
63
  assert len(result) == 1
64
+ assert result[0].text == "Echo: server42" # type: ignore[attr-defined]
 
 
65
 
66
  async with Client(mcp) as client:
67
  result = await client.get_prompt("echo", {"text": "hello"})
68
  assert len(result.messages) == 1
69
+ assert result.messages[0].content.text == "hello" # type: ignore[attr-defined]
 
 
 
tests/tools/test_tool.py CHANGED
@@ -1,5 +1,5 @@
1
  import pytest
2
- from mcp.types import ImageContent, TextContent
3
  from pydantic import BaseModel
4
 
5
  from fastmcp import FastMCP, Image
@@ -209,9 +209,7 @@ class TestLegacyToolJsonParsing:
209
 
210
  # Run the tool which will do JSON parsing
211
  result = await tool.run(json_args)
212
- assert len(result) == 1
213
- assert isinstance(result[0], TextContent)
214
- assert result[0].text == "1-a,b,c"
215
 
216
  async def test_str_vs_list_str(self):
217
  """Test handling of string vs list[str] type annotations."""
@@ -223,23 +221,17 @@ class TestLegacyToolJsonParsing:
223
 
224
  # Test regular string input (should remain a string)
225
  result = await tool.run({"str_or_list": "hello"})
226
- assert len(result) == 1
227
- assert isinstance(result[0], TextContent)
228
- assert result[0].text == "hello"
229
 
230
  # Test JSON string input (should be parsed as a string)
231
  result = await tool.run({"str_or_list": '"hello"'})
232
- assert len(result) == 1
233
- assert isinstance(result[0], TextContent)
234
- assert result[0].text == "hello"
235
 
236
  # Test JSON list input (should be parsed as a list)
237
  result = await tool.run({"str_or_list": '["hello", "world"]'})
238
- assert len(result) == 1
239
- assert isinstance(result[0], TextContent)
240
 
241
  # The exact formatting might vary, so we just check that it contains the key elements
242
- text_without_whitespace = result[0].text.replace(" ", "").replace("\n", "")
243
  assert "hello" in text_without_whitespace
244
  assert "world" in text_without_whitespace
245
  assert "[" in text_without_whitespace
@@ -256,9 +248,7 @@ class TestLegacyToolJsonParsing:
256
  # Invalid JSON should remain a string
257
  invalid_json = "{'nice to meet you': 'hello', 'goodbye': 5}"
258
  result = await tool.run({"string": invalid_json})
259
- assert len(result) == 1
260
- assert isinstance(result[0], TextContent)
261
- assert result[0].text == invalid_json
262
 
263
  async def test_keep_str_union_as_str(self):
264
  """Test that string arguments are kept as strings when parsing would create an invalid value"""
@@ -273,9 +263,7 @@ class TestLegacyToolJsonParsing:
273
  # Invalid JSON for the union type should remain a string
274
  invalid_json = "{'nice to meet you': 'hello', 'goodbye': 5}"
275
  result = await tool.run({"string": invalid_json})
276
- assert len(result) == 1
277
- assert isinstance(result[0], TextContent)
278
- assert result[0].text == invalid_json
279
 
280
  async def test_complex_type_validation(self):
281
  """Test that parsed JSON is validated against complex types"""
@@ -292,11 +280,9 @@ class TestLegacyToolJsonParsing:
292
  # Valid JSON for the model
293
  valid_json = '{"x": 1, "y": {"1": "hello"}}'
294
  result = await tool.run({"data": valid_json})
295
- assert len(result) == 1
296
- assert isinstance(result[0], TextContent)
297
- assert '"x": 1' in result[0].text
298
- assert '"y": {' in result[0].text
299
- assert '"1": "hello"' in result[0].text
300
 
301
  # Invalid JSON for the model (y has string keys, not int keys)
302
  # Should throw a validation error
@@ -317,8 +303,7 @@ class TestLegacyToolJsonParsing:
317
  result = await client.call_tool(
318
  "process_list", {"items": "[1, 2, 3, 4, 5]"}
319
  )
320
- assert isinstance(result[0], TextContent)
321
- assert result[0].text == "15"
322
 
323
  async def test_tool_list_coercion_error(self):
324
  """Test that a list coercion error is raised if the input is not a valid list."""
@@ -348,8 +333,7 @@ class TestLegacyToolJsonParsing:
348
  result = await client.call_tool(
349
  "process_dict", {"data": '{"a": 1, "b": "2", "c": 3}'}
350
  )
351
- assert isinstance(result[0], TextContent)
352
- assert result[0].text == "6"
353
 
354
  async def test_tool_set_coercion(self):
355
  """Test JSON string to set type coercion."""
@@ -362,8 +346,7 @@ class TestLegacyToolJsonParsing:
362
 
363
  async with Client(mcp) as client:
364
  result = await client.call_tool("process_set", {"items": "[1, 2, 3, 4, 5]"})
365
- assert isinstance(result[0], TextContent)
366
- assert result[0].text == "15"
367
 
368
  async def test_tool_tuple_coercion(self):
369
  """Test JSON string to tuple type coercion."""
@@ -376,5 +359,4 @@ class TestLegacyToolJsonParsing:
376
 
377
  async with Client(mcp) as client:
378
  result = await client.call_tool("process_tuple", {"items": '["1", "two"]'})
379
- assert isinstance(result[0], TextContent)
380
- assert result[0].text == "4"
 
1
  import pytest
2
+ from mcp.types import ImageContent
3
  from pydantic import BaseModel
4
 
5
  from fastmcp import FastMCP, Image
 
209
 
210
  # Run the tool which will do JSON parsing
211
  result = await tool.run(json_args)
212
+ assert result[0].text == "1-a,b,c" # type: ignore[attr-dict]
 
 
213
 
214
  async def test_str_vs_list_str(self):
215
  """Test handling of string vs list[str] type annotations."""
 
221
 
222
  # Test regular string input (should remain a string)
223
  result = await tool.run({"str_or_list": "hello"})
224
+ assert result[0].text == "hello" # type: ignore[attr-dict]
 
 
225
 
226
  # Test JSON string input (should be parsed as a string)
227
  result = await tool.run({"str_or_list": '"hello"'})
228
+ assert result[0].text == "hello" # type: ignore[attr-dict]
 
 
229
 
230
  # Test JSON list input (should be parsed as a list)
231
  result = await tool.run({"str_or_list": '["hello", "world"]'})
 
 
232
 
233
  # The exact formatting might vary, so we just check that it contains the key elements
234
+ text_without_whitespace = result[0].text.replace(" ", "").replace("\n", "") # type: ignore[attr-dict]
235
  assert "hello" in text_without_whitespace
236
  assert "world" in text_without_whitespace
237
  assert "[" in text_without_whitespace
 
248
  # Invalid JSON should remain a string
249
  invalid_json = "{'nice to meet you': 'hello', 'goodbye': 5}"
250
  result = await tool.run({"string": invalid_json})
251
+ assert result[0].text == invalid_json # type: ignore[attr-dict]
 
 
252
 
253
  async def test_keep_str_union_as_str(self):
254
  """Test that string arguments are kept as strings when parsing would create an invalid value"""
 
263
  # Invalid JSON for the union type should remain a string
264
  invalid_json = "{'nice to meet you': 'hello', 'goodbye': 5}"
265
  result = await tool.run({"string": invalid_json})
266
+ assert result[0].text == invalid_json # type: ignore[attr-dict]
 
 
267
 
268
  async def test_complex_type_validation(self):
269
  """Test that parsed JSON is validated against complex types"""
 
280
  # Valid JSON for the model
281
  valid_json = '{"x": 1, "y": {"1": "hello"}}'
282
  result = await tool.run({"data": valid_json})
283
+ assert '"x": 1' in result[0].text # type: ignore[attr-dict]
284
+ assert '"y": {' in result[0].text # type: ignore[attr-dict]
285
+ assert '"1": "hello"' in result[0].text # type: ignore[attr-dict]
 
 
286
 
287
  # Invalid JSON for the model (y has string keys, not int keys)
288
  # Should throw a validation error
 
303
  result = await client.call_tool(
304
  "process_list", {"items": "[1, 2, 3, 4, 5]"}
305
  )
306
+ assert result[0].text == "15" # type: ignore[attr-dict]
 
307
 
308
  async def test_tool_list_coercion_error(self):
309
  """Test that a list coercion error is raised if the input is not a valid list."""
 
333
  result = await client.call_tool(
334
  "process_dict", {"data": '{"a": 1, "b": "2", "c": 3}'}
335
  )
336
+ assert result[0].text == "6" # type: ignore[attr-dict]
 
337
 
338
  async def test_tool_set_coercion(self):
339
  """Test JSON string to set type coercion."""
 
346
 
347
  async with Client(mcp) as client:
348
  result = await client.call_tool("process_set", {"items": "[1, 2, 3, 4, 5]"})
349
+ assert result[0].text == "15" # type: ignore[attr-dict]
 
350
 
351
  async def test_tool_tuple_coercion(self):
352
  """Test JSON string to tuple type coercion."""
 
359
 
360
  async with Client(mcp) as client:
361
  result = await client.call_tool("process_tuple", {"items": '["1", "two"]'})
362
+ assert result[0].text == "4" # type: ignore[attr-dict]
 
tests/tools/test_tool_manager.py CHANGED
@@ -5,7 +5,7 @@ from typing import Annotated, Any
5
 
6
  import pydantic_core
7
  import pytest
8
- from mcp.types import ImageContent, TextContent
9
  from pydantic import BaseModel
10
 
11
  from fastmcp import Context, FastMCP, Image
@@ -318,13 +318,8 @@ class TestCallTools:
318
  manager = ToolManager()
319
  manager.add_tool_from_fn(add)
320
  result = await manager.call_tool("add", {"a": 1, "b": 2})
321
- assert isinstance(result, list)
322
- assert len(result) == 1
323
- from mcp.types import TextContent
324
 
325
- assert isinstance(result[0], TextContent)
326
- assert result[0].text == "3"
327
- assert json.loads(result[0].text) == 3
328
 
329
  async def test_call_async_tool(self):
330
  async def double(n: int) -> int:
@@ -334,12 +329,7 @@ class TestCallTools:
334
  manager = ToolManager()
335
  manager.add_tool_from_fn(double)
336
  result = await manager.call_tool("double", {"n": 5})
337
- assert isinstance(result, list)
338
- assert len(result) == 1
339
-
340
- assert isinstance(result[0], TextContent)
341
- assert result[0].text == "10"
342
- assert json.loads(result[0].text) == 10
343
 
344
  async def test_call_tool_callable_object(self):
345
  class Adder:
@@ -352,11 +342,7 @@ class TestCallTools:
352
  manager = ToolManager()
353
  manager.add_tool_from_fn(Adder())
354
  result = await manager.call_tool("Adder", {"x": 1, "y": 2})
355
- assert isinstance(result, list)
356
- assert len(result) == 1
357
- assert isinstance(result[0], TextContent)
358
- assert result[0].text == "3"
359
- assert json.loads(result[0].text) == 3
360
 
361
  async def test_call_tool_callable_object_async(self):
362
  class Adder:
@@ -369,11 +355,7 @@ class TestCallTools:
369
  manager = ToolManager()
370
  manager.add_tool_from_fn(Adder())
371
  result = await manager.call_tool("Adder", {"x": 1, "y": 2})
372
- assert isinstance(result, list)
373
- assert len(result) == 1
374
- assert isinstance(result[0], TextContent)
375
- assert result[0].text == "3"
376
- assert json.loads(result[0].text) == 3
377
 
378
  async def test_call_tool_with_default_args(self):
379
  def add(a: int, b: int = 1) -> int:
@@ -383,12 +365,8 @@ class TestCallTools:
383
  manager = ToolManager()
384
  manager.add_tool_from_fn(add)
385
  result = await manager.call_tool("add", {"a": 1})
386
- assert isinstance(result, list)
387
- assert len(result) == 1
388
 
389
- assert isinstance(result[0], TextContent)
390
- assert result[0].text == "2"
391
- assert json.loads(result[0].text) == 2
392
 
393
  async def test_call_tool_with_missing_args(self):
394
  def add(a: int, b: int) -> int:
@@ -413,11 +391,7 @@ class TestCallTools:
413
  manager.add_tool_from_fn(sum_vals)
414
 
415
  result = await manager.call_tool("sum_vals", {"vals": [1, 2, 3]})
416
- assert isinstance(result, list)
417
- assert len(result) == 1
418
- assert isinstance(result[0], TextContent)
419
- assert result[0].text == "6"
420
- assert json.loads(result[0].text) == 6
421
 
422
  async def test_call_tool_with_list_int_input_legacy_behavior(self):
423
  """Legacy behavior -- parse a stringified JSON object"""
@@ -431,11 +405,7 @@ class TestCallTools:
431
 
432
  with temporary_settings(tool_attempt_parse_json_args=True):
433
  result = await manager.call_tool("sum_vals", {"vals": "[1, 2, 3]"})
434
- assert isinstance(result, list)
435
- assert len(result) == 1
436
- assert isinstance(result[0], TextContent)
437
- assert result[0].text == "6"
438
- assert json.loads(result[0].text) == 6
439
 
440
  async def test_call_tool_with_list_str_or_str_input(self):
441
  def concat_strs(vals: list[str] | str) -> str:
@@ -446,16 +416,10 @@ class TestCallTools:
446
 
447
  # Try both with plain python object and with JSON list
448
  result = await manager.call_tool("concat_strs", {"vals": ["a", "b", "c"]})
449
- assert isinstance(result, list)
450
- assert len(result) == 1
451
- assert isinstance(result[0], TextContent)
452
- assert result[0].text == "abc"
453
 
454
  result = await manager.call_tool("concat_strs", {"vals": "a"})
455
- assert isinstance(result, list)
456
- assert len(result) == 1
457
- assert isinstance(result[0], TextContent)
458
- assert result[0].text == "a"
459
 
460
  async def test_call_tool_with_list_str_or_str_input_legacy_behavior(self):
461
  """Legacy behavior -- parse a stringified JSON object"""
@@ -468,16 +432,10 @@ class TestCallTools:
468
 
469
  with temporary_settings(tool_attempt_parse_json_args=True):
470
  result = await manager.call_tool("concat_strs", {"vals": '["a", "b", "c"]'})
471
- assert isinstance(result, list)
472
- assert len(result) == 1
473
- assert isinstance(result[0], TextContent)
474
- assert result[0].text == "abc"
475
 
476
  result = await manager.call_tool("concat_strs", {"vals": '"a"'})
477
- assert isinstance(result, list)
478
- assert len(result) == 1
479
- assert isinstance(result[0], TextContent)
480
- assert result[0].text == "a"
481
 
482
  async def test_call_tool_with_complex_model(self):
483
  class MyShrimpTank(BaseModel):
@@ -507,10 +465,7 @@ class TestCallTools:
507
  },
508
  )
509
 
510
- assert isinstance(result, list)
511
- assert len(result) == 1
512
- assert isinstance(result[0], TextContent)
513
- assert result[0].text == '[\n "rex",\n "gertrude"\n]'
514
 
515
  async def test_call_tool_with_custom_serializer(self):
516
  """Test that a custom serializer provided to FastMCP is used by tools."""
@@ -530,10 +485,7 @@ class TestCallTools:
530
  manager.add_tool_from_fn(get_data)
531
 
532
  result = await manager.call_tool("get_data", {})
533
- assert isinstance(result, list)
534
- assert len(result) == 1
535
- assert isinstance(result[0], TextContent)
536
- assert result[0].text == 'CUSTOM:{"key": "value", "number": 123}'
537
 
538
  async def test_call_tool_with_list_result_custom_serializer(self):
539
  """Test that a custom serializer provided to FastMCP is used by tools that return lists."""
@@ -555,12 +507,9 @@ class TestCallTools:
555
  manager.add_tool_from_fn(get_data)
556
 
557
  result = await manager.call_tool("get_data", {})
558
- assert isinstance(result, list)
559
- assert len(result) == 1
560
- assert isinstance(result[0], TextContent)
561
  assert (
562
- result[0].text
563
- == 'CUSTOM:[{"key": "value", "number": 123}, {"key": "value2", "number": 456}]'
564
  )
565
 
566
  async def test_custom_serializer_fallback_on_error(self):
@@ -580,10 +529,7 @@ class TestCallTools:
580
  manager.add_tool_from_fn(get_data)
581
 
582
  result = await manager.call_tool("get_data", {})
583
- assert isinstance(result, list)
584
- assert len(result) == 1
585
- assert isinstance(result[0], TextContent)
586
- assert result[0].text == pydantic_core.to_json(uuid_result).decode()
587
 
588
 
589
  class TestToolSchema:
@@ -648,10 +594,7 @@ class TestContextHandling:
648
 
649
  with context:
650
  result = await manager.call_tool("tool_with_context", {"x": 42})
651
- assert isinstance(result, list)
652
- assert len(result) == 1
653
- assert isinstance(result[0], TextContent)
654
- assert result[0].text == "42"
655
 
656
  async def test_context_injection_async(self):
657
  """Test that context is properly injected in async tools."""
@@ -668,14 +611,10 @@ class TestContextHandling:
668
 
669
  with context:
670
  result = await manager.call_tool("async_tool", {"x": 42})
671
- assert isinstance(result, list)
672
- assert len(result) == 1
673
- assert isinstance(result[0], TextContent)
674
- assert result[0].text == "42"
675
 
676
  async def test_context_optional(self):
677
  """Test that context is optional when calling tools."""
678
- from mcp.types import TextContent
679
 
680
  def tool_with_context(x: int, ctx: Context | None) -> int:
681
  return x
@@ -689,10 +628,7 @@ class TestContextHandling:
689
 
690
  with context:
691
  result = await manager.call_tool("tool_with_context", {"x": 42})
692
- assert isinstance(result, list)
693
- assert len(result) == 1
694
- assert isinstance(result[0], TextContent)
695
- assert result[0].text == "42"
696
 
697
  def test_parameterized_context_parameter_detection(self):
698
  """Test that context parameters are properly detected in
@@ -782,7 +718,6 @@ class TestCustomToolNames:
782
 
783
  async def test_call_tool_with_custom_name(self):
784
  """Test calling a tool added with a custom name."""
785
- from mcp.types import TextContent
786
 
787
  def multiply(a: int, b: int) -> int:
788
  """Multiply two numbers."""
@@ -793,11 +728,7 @@ class TestCustomToolNames:
793
 
794
  # Tool should be callable by its custom name
795
  result = await manager.call_tool("custom_multiply", {"a": 5, "b": 3})
796
- assert isinstance(result, list)
797
- assert len(result) == 1
798
- assert isinstance(result[0], TextContent)
799
- assert result[0].text == "15"
800
- assert json.loads(result[0].text) == 15
801
 
802
  # Original name should not be registered
803
  with pytest.raises(NotFoundError, match="Unknown tool: multiply"):
 
5
 
6
  import pydantic_core
7
  import pytest
8
+ from mcp.types import ImageContent
9
  from pydantic import BaseModel
10
 
11
  from fastmcp import Context, FastMCP, Image
 
318
  manager = ToolManager()
319
  manager.add_tool_from_fn(add)
320
  result = await manager.call_tool("add", {"a": 1, "b": 2})
 
 
 
321
 
322
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
 
323
 
324
  async def test_call_async_tool(self):
325
  async def double(n: int) -> int:
 
329
  manager = ToolManager()
330
  manager.add_tool_from_fn(double)
331
  result = await manager.call_tool("double", {"n": 5})
332
+ assert result[0].text == "10" # type: ignore[attr-defined]
 
 
 
 
 
333
 
334
  async def test_call_tool_callable_object(self):
335
  class Adder:
 
342
  manager = ToolManager()
343
  manager.add_tool_from_fn(Adder())
344
  result = await manager.call_tool("Adder", {"x": 1, "y": 2})
345
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
 
 
 
346
 
347
  async def test_call_tool_callable_object_async(self):
348
  class Adder:
 
355
  manager = ToolManager()
356
  manager.add_tool_from_fn(Adder())
357
  result = await manager.call_tool("Adder", {"x": 1, "y": 2})
358
+ assert result[0].text == "3" # type: ignore[attr-defined]
 
 
 
 
359
 
360
  async def test_call_tool_with_default_args(self):
361
  def add(a: int, b: int = 1) -> int:
 
365
  manager = ToolManager()
366
  manager.add_tool_from_fn(add)
367
  result = await manager.call_tool("add", {"a": 1})
 
 
368
 
369
+ assert result[0].text == "2" # type: ignore[attr-defined]
 
 
370
 
371
  async def test_call_tool_with_missing_args(self):
372
  def add(a: int, b: int) -> int:
 
391
  manager.add_tool_from_fn(sum_vals)
392
 
393
  result = await manager.call_tool("sum_vals", {"vals": [1, 2, 3]})
394
+ assert result[0].text == "6" # type: ignore[attr-defined]
 
 
 
 
395
 
396
  async def test_call_tool_with_list_int_input_legacy_behavior(self):
397
  """Legacy behavior -- parse a stringified JSON object"""
 
405
 
406
  with temporary_settings(tool_attempt_parse_json_args=True):
407
  result = await manager.call_tool("sum_vals", {"vals": "[1, 2, 3]"})
408
+ assert result[0].text == "6" # type: ignore[attr-defined]
 
 
 
 
409
 
410
  async def test_call_tool_with_list_str_or_str_input(self):
411
  def concat_strs(vals: list[str] | str) -> str:
 
416
 
417
  # Try both with plain python object and with JSON list
418
  result = await manager.call_tool("concat_strs", {"vals": ["a", "b", "c"]})
419
+ assert result[0].text == "abc" # type: ignore[attr-defined]
 
 
 
420
 
421
  result = await manager.call_tool("concat_strs", {"vals": "a"})
422
+ assert result[0].text == "a" # type: ignore[attr-defined]
 
 
 
423
 
424
  async def test_call_tool_with_list_str_or_str_input_legacy_behavior(self):
425
  """Legacy behavior -- parse a stringified JSON object"""
 
432
 
433
  with temporary_settings(tool_attempt_parse_json_args=True):
434
  result = await manager.call_tool("concat_strs", {"vals": '["a", "b", "c"]'})
435
+ assert result[0].text == "abc" # type: ignore[attr-defined]
 
 
 
436
 
437
  result = await manager.call_tool("concat_strs", {"vals": '"a"'})
438
+ assert result[0].text == "a" # type: ignore[attr-defined]
 
 
 
439
 
440
  async def test_call_tool_with_complex_model(self):
441
  class MyShrimpTank(BaseModel):
 
465
  },
466
  )
467
 
468
+ assert result[0].text == '[\n "rex",\n "gertrude"\n]' # type: ignore[attr-defined]
 
 
 
469
 
470
  async def test_call_tool_with_custom_serializer(self):
471
  """Test that a custom serializer provided to FastMCP is used by tools."""
 
485
  manager.add_tool_from_fn(get_data)
486
 
487
  result = await manager.call_tool("get_data", {})
488
+ assert result[0].text == 'CUSTOM:{"key": "value", "number": 123}' # type: ignore[attr-defined]
 
 
 
489
 
490
  async def test_call_tool_with_list_result_custom_serializer(self):
491
  """Test that a custom serializer provided to FastMCP is used by tools that return lists."""
 
507
  manager.add_tool_from_fn(get_data)
508
 
509
  result = await manager.call_tool("get_data", {})
 
 
 
510
  assert (
511
+ result[0].text # type: ignore[attr-defined]
512
+ == 'CUSTOM:[{"key": "value", "number": 123}, {"key": "value2", "number": 456}]' # type: ignore[attr-defined]
513
  )
514
 
515
  async def test_custom_serializer_fallback_on_error(self):
 
529
  manager.add_tool_from_fn(get_data)
530
 
531
  result = await manager.call_tool("get_data", {})
532
+ assert result[0].text == pydantic_core.to_json(uuid_result).decode() # type: ignore[attr-defined]
 
 
 
533
 
534
 
535
  class TestToolSchema:
 
594
 
595
  with context:
596
  result = await manager.call_tool("tool_with_context", {"x": 42})
597
+ assert result[0].text == "42" # type: ignore[attr-defined]
 
 
 
598
 
599
  async def test_context_injection_async(self):
600
  """Test that context is properly injected in async tools."""
 
611
 
612
  with context:
613
  result = await manager.call_tool("async_tool", {"x": 42})
614
+ assert result[0].text == "42" # type: ignore[attr-defined]
 
 
 
615
 
616
  async def test_context_optional(self):
617
  """Test that context is optional when calling tools."""
 
618
 
619
  def tool_with_context(x: int, ctx: Context | None) -> int:
620
  return x
 
628
 
629
  with context:
630
  result = await manager.call_tool("tool_with_context", {"x": 42})
631
+ assert result[0].text == "42" # type: ignore[attr-defined]
 
 
 
632
 
633
  def test_parameterized_context_parameter_detection(self):
634
  """Test that context parameters are properly detected in
 
718
 
719
  async def test_call_tool_with_custom_name(self):
720
  """Test calling a tool added with a custom name."""
 
721
 
722
  def multiply(a: int, b: int) -> int:
723
  """Multiply two numbers."""
 
728
 
729
  # Tool should be callable by its custom name
730
  result = await manager.call_tool("custom_multiply", {"a": 5, "b": 3})
731
+ assert result[0].text == "15" # type: ignore[attr-defined]
 
 
 
 
732
 
733
  # Original name should not be registered
734
  with pytest.raises(NotFoundError, match="Unknown tool: multiply"):
tests/utilities/test_mcp_config.py CHANGED
@@ -1,8 +1,6 @@
1
  import inspect
2
  from pathlib import Path
3
 
4
- from mcp.types import TextContent
5
-
6
  from fastmcp.client.client import Client
7
  from fastmcp.client.transports import (
8
  SSETransport,
@@ -136,7 +134,5 @@ async def test_multi_client(tmp_path: Path):
136
 
137
  result_1 = await client.call_tool("test_1_add", {"a": 1, "b": 2})
138
  result_2 = await client.call_tool("test_2_add", {"a": 1, "b": 2})
139
- assert isinstance(result_1[0], TextContent)
140
- assert result_1[0].text == "3"
141
- assert isinstance(result_2[0], TextContent)
142
- assert result_2[0].text == "3"
 
1
  import inspect
2
  from pathlib import Path
3
 
 
 
4
  from fastmcp.client.client import Client
5
  from fastmcp.client.transports import (
6
  SSETransport,
 
134
 
135
  result_1 = await client.call_tool("test_1_add", {"a": 1, "b": 2})
136
  result_2 = await client.call_tool("test_2_add", {"a": 1, "b": 2})
137
+ assert result_1[0].text == "3" # type: ignore[attr-dict]
138
+ assert result_2[0].text == "3" # type: ignore[attr-dict]