Jeremiah Lowin commited on
Commit
5d452fd
·
1 Parent(s): dcfc46e

add _mcp methods

Browse files
docs/clients/client.mdx CHANGED
@@ -149,6 +149,34 @@ The `Client` provides methods corresponding to standard MCP requests:
149
  * **`list_prompts()`**: Retrieves available prompt templates.
150
  * **`get_prompt(name: str, arguments: dict[str, Any] | None = None)`**: Retrieves a rendered prompt message list.
151
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  ### Advanced Features
153
 
154
  MCP allows servers to interact with clients in order to provide additional capabilities. The `Client` constructor accepts additional configuration to handle these server requests.
@@ -268,5 +296,5 @@ async def safe_call_tool():
268
  Other errors, like connection failures, will raise standard Python exceptions (e.g., `ConnectionError`, `TimeoutError`).
269
 
270
  <Tip>
271
- The client transport often has its own error-handling mechanisms, so you can not always trap errors like those raised by `call_tool` outside of the `async with` block. Instead, you can call `call_tool(..., _return_raw_result=True)` to get the raw `mcp.types.CallToolResult` object and handle errors yourself by checking its `isError` attribute.
272
  </Tip>
 
149
  * **`list_prompts()`**: Retrieves available prompt templates.
150
  * **`get_prompt(name: str, arguments: dict[str, Any] | None = None)`**: Retrieves a rendered prompt message list.
151
 
152
+ ### Raw MCP Protocol Objects
153
+
154
+ The FastMCP client attempts to provide a "friendly" interface to the MCP protocol, but sometimes you may need access to the raw MCP protocol objects. Each of the main client methods that returns data has a corresponding `*_mcp` method that returns the raw MCP protocol objects directly.
155
+
156
+ ```python
157
+ # Standard method - returns just the list of tools
158
+ tools = await client.list_tools()
159
+ # tools -> list[mcp.types.Tool]
160
+
161
+ # Raw MCP method - returns the full protocol object
162
+ result = await client.list_tools_mcp()
163
+ # result -> mcp.types.ListToolsResult
164
+ tools = result.tools
165
+ ```
166
+
167
+ Available raw MCP methods:
168
+
169
+ * **`list_tools_mcp()`**: Returns `mcp.types.ListToolsResult`
170
+ * **`call_tool_mcp(name, arguments)`**: Returns `mcp.types.CallToolResult`
171
+ * **`list_resources_mcp()`**: Returns `mcp.types.ListResourcesResult`
172
+ * **`list_resource_templates_mcp()`**: Returns `mcp.types.ListResourceTemplatesResult`
173
+ * **`read_resource_mcp(uri)`**: Returns `mcp.types.ReadResourceResult`
174
+ * **`list_prompts_mcp()`**: Returns `mcp.types.ListPromptsResult`
175
+ * **`get_prompt_mcp(name, arguments)`**: Returns `mcp.types.GetPromptResult`
176
+ * **`complete_mcp(ref, argument)`**: Returns `mcp.types.CompleteResult`
177
+
178
+ These methods are especially useful for debugging or when you need to access metadata or fields that aren't exposed by the simplified methods.
179
+
180
  ### Advanced Features
181
 
182
  MCP allows servers to interact with clients in order to provide additional capabilities. The `Client` constructor accepts additional configuration to handle these server requests.
 
296
  Other errors, like connection failures, will raise standard Python exceptions (e.g., `ConnectionError`, `TimeoutError`).
297
 
298
  <Tip>
299
+ The client transport often has its own error-handling mechanisms, so you can not always trap errors like those raised by `call_tool` outside of the `async with` block. Instead, you can use `call_tool_mcp()` to get the raw `mcp.types.CallToolResult` object and handle errors yourself by checking its `isError` attribute.
300
  </Tip>
src/fastmcp/client/client.py CHANGED
@@ -1,7 +1,7 @@
1
  import datetime
2
  from contextlib import AbstractAsyncContextManager
3
  from pathlib import Path
4
- from typing import Any, Literal, cast, overload
5
 
6
  import mcp.types
7
  from mcp import ClientSession
@@ -107,6 +107,7 @@ class Client:
107
  self._session = None
108
 
109
  # --- MCP Client Methods ---
 
110
  async def ping(self) -> None:
111
  """Send a ping request."""
112
  await self.session.send_ping()
@@ -128,23 +129,100 @@ class Client:
128
  """Send a roots/list_changed notification."""
129
  await self.session.send_roots_list_changed()
130
 
131
- async def list_resources(self) -> list[mcp.types.Resource]:
132
- """Send a resources/list request."""
 
 
 
 
 
 
 
 
 
 
133
  result = await self.session.list_resources()
 
 
 
 
 
 
 
 
 
 
 
 
134
  return result.resources
135
 
136
- async def list_resource_templates(self) -> list[mcp.types.ResourceTemplate]:
137
- """Send a resources/listResourceTemplates request."""
 
 
 
 
 
 
 
 
 
 
138
  result = await self.session.list_resource_templates()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  return result.resourceTemplates
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  async def read_resource(
142
  self, uri: AnyUrl | str
143
  ) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]:
144
- """Send a resources/read request."""
 
 
 
 
 
 
 
 
 
 
 
145
  if isinstance(uri, str):
146
  uri = AnyUrl(uri) # Ensure AnyUrl
147
- result = await self.session.read_resource(uri)
148
  return result.contents
149
 
150
  # async def subscribe_resource(self, uri: AnyUrl | str) -> None:
@@ -159,66 +237,193 @@ class Client:
159
  # uri = AnyUrl(uri)
160
  # await self.session.unsubscribe_resource(uri)
161
 
162
- async def list_prompts(self) -> list[mcp.types.Prompt]:
163
- """Send a prompts/list request."""
 
 
 
 
 
 
 
 
 
 
164
  result = await self.session.list_prompts()
 
 
 
 
 
 
 
 
 
 
 
 
165
  return result.prompts
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  async def get_prompt(
168
  self, name: str, arguments: dict[str, str] | None = None
169
  ) -> list[mcp.types.PromptMessage]:
170
- """Send a prompts/get request."""
171
- result = await self.session.get_prompt(name, arguments)
 
 
 
 
 
 
 
 
 
 
 
172
  return result.messages
173
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  async def complete(
175
  self,
176
  ref: mcp.types.ResourceReference | mcp.types.PromptReference,
177
  argument: dict[str, str],
178
  ) -> mcp.types.Completion:
179
- """Send a completion request."""
180
- result = await self.session.complete(ref, argument)
 
 
 
 
 
 
 
 
 
 
 
181
  return result.completion
182
 
183
- async def list_tools(self) -> list[mcp.types.Tool]:
184
- """Send a tools/list request."""
 
 
 
 
 
 
 
 
 
 
185
  result = await self.session.list_tools()
 
 
 
 
 
 
 
 
 
 
 
 
186
  return result.tools
187
 
188
- @overload
189
- async def call_tool(
190
- self,
191
- name: str,
192
- arguments: dict[str, Any] | None = None,
193
- _return_raw_result: Literal[False] = False,
194
- ) -> list[
195
- mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource
196
- ]: ...
197
 
198
- @overload
199
- async def call_tool(
200
- self,
201
- name: str,
202
- arguments: dict[str, Any] | None = None,
203
- _return_raw_result: Literal[True] = True,
204
- ) -> mcp.types.CallToolResult: ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  async def call_tool(
207
  self,
208
  name: str,
209
  arguments: dict[str, Any] | None = None,
210
- _return_raw_result: bool = False,
211
  ) -> (
212
  list[
213
  mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource
214
  ]
215
  | mcp.types.CallToolResult
216
  ):
217
- """Send a tools/call request."""
218
- result = await self.session.call_tool(name, arguments)
219
- if _return_raw_result:
220
- return result
221
- elif result.isError:
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  msg = cast(mcp.types.TextContent, result.content[0]).text
223
  raise ClientError(msg)
224
  return result.content
 
1
  import datetime
2
  from contextlib import AbstractAsyncContextManager
3
  from pathlib import Path
4
+ from typing import Any, cast
5
 
6
  import mcp.types
7
  from mcp import ClientSession
 
107
  self._session = None
108
 
109
  # --- MCP Client Methods ---
110
+
111
  async def ping(self) -> None:
112
  """Send a ping request."""
113
  await self.session.send_ping()
 
129
  """Send a roots/list_changed notification."""
130
  await self.session.send_roots_list_changed()
131
 
132
+ # --- Resources ---
133
+
134
+ async def list_resources_mcp(self) -> mcp.types.ListResourcesResult:
135
+ """Send a resources/list request and return the complete MCP protocol result.
136
+
137
+ Returns:
138
+ mcp.types.ListResourcesResult: The complete response object from the protocol,
139
+ containing the list of resources and any additional metadata.
140
+
141
+ Raises:
142
+ RuntimeError: If called while the client is not connected.
143
+ """
144
  result = await self.session.list_resources()
145
+ return result
146
+
147
+ async def list_resources(self) -> list[mcp.types.Resource]:
148
+ """Retrieve a list of resources available on the server.
149
+
150
+ Returns:
151
+ list[mcp.types.Resource]: A list of Resource objects.
152
+
153
+ Raises:
154
+ RuntimeError: If called while the client is not connected.
155
+ """
156
+ result = await self.list_resources_mcp()
157
  return result.resources
158
 
159
+ async def list_resource_templates_mcp(
160
+ self,
161
+ ) -> mcp.types.ListResourceTemplatesResult:
162
+ """Send a resources/listResourceTemplates request and return the complete MCP protocol result.
163
+
164
+ Returns:
165
+ mcp.types.ListResourceTemplatesResult: The complete response object from the protocol,
166
+ containing the list of resource templates and any additional metadata.
167
+
168
+ Raises:
169
+ RuntimeError: If called while the client is not connected.
170
+ """
171
  result = await self.session.list_resource_templates()
172
+ return result
173
+
174
+ async def list_resource_templates(
175
+ self,
176
+ ) -> list[mcp.types.ResourceTemplate]:
177
+ """Retrieve a list of resource templates available on the server.
178
+
179
+ Returns:
180
+ list[mcp.types.ResourceTemplate]: A list of ResourceTemplate objects.
181
+
182
+ Raises:
183
+ RuntimeError: If called while the client is not connected.
184
+ """
185
+ result = await self.list_resource_templates_mcp()
186
  return result.resourceTemplates
187
 
188
+ async def read_resource_mcp(
189
+ self, uri: AnyUrl | str
190
+ ) -> mcp.types.ReadResourceResult:
191
+ """Send a resources/read request and return the complete MCP protocol result.
192
+
193
+ Args:
194
+ uri (AnyUrl | str): The URI of the resource to read. Can be a string or an AnyUrl object.
195
+
196
+ Returns:
197
+ mcp.types.ReadResourceResult: The complete response object from the protocol,
198
+ containing the resource contents and any additional metadata.
199
+
200
+ Raises:
201
+ RuntimeError: If called while the client is not connected.
202
+ """
203
+ if isinstance(uri, str):
204
+ uri = AnyUrl(uri) # Ensure AnyUrl
205
+ result = await self.session.read_resource(uri)
206
+ return result
207
+
208
  async def read_resource(
209
  self, uri: AnyUrl | str
210
  ) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]:
211
+ """Read the contents of a resource or resolved template.
212
+
213
+ Args:
214
+ uri (AnyUrl | str): The URI of the resource to read. Can be a string or an AnyUrl object.
215
+
216
+ Returns:
217
+ list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents]: A list of content
218
+ objects, typically containing either text or binary data.
219
+
220
+ Raises:
221
+ RuntimeError: If called while the client is not connected.
222
+ """
223
  if isinstance(uri, str):
224
  uri = AnyUrl(uri) # Ensure AnyUrl
225
+ result = await self.read_resource_mcp(uri)
226
  return result.contents
227
 
228
  # async def subscribe_resource(self, uri: AnyUrl | str) -> None:
 
237
  # uri = AnyUrl(uri)
238
  # await self.session.unsubscribe_resource(uri)
239
 
240
+ # --- Prompts ---
241
+
242
+ async def list_prompts_mcp(self) -> mcp.types.ListPromptsResult:
243
+ """Send a prompts/list request and return the complete MCP protocol result.
244
+
245
+ Returns:
246
+ mcp.types.ListPromptsResult: The complete response object from the protocol,
247
+ containing the list of prompts and any additional metadata.
248
+
249
+ Raises:
250
+ RuntimeError: If called while the client is not connected.
251
+ """
252
  result = await self.session.list_prompts()
253
+ return result
254
+
255
+ async def list_prompts(self) -> list[mcp.types.Prompt]:
256
+ """Retrieve a list of prompts available on the server.
257
+
258
+ Returns:
259
+ list[mcp.types.Prompt]: A list of Prompt objects.
260
+
261
+ Raises:
262
+ RuntimeError: If called while the client is not connected.
263
+ """
264
+ result = await self.list_prompts_mcp()
265
  return result.prompts
266
 
267
+ # --- Prompt ---
268
+ async def get_prompt_mcp(
269
+ self, name: str, arguments: dict[str, str] | None = None
270
+ ) -> mcp.types.GetPromptResult:
271
+ """Send a prompts/get request and return the complete MCP protocol result.
272
+
273
+ Args:
274
+ name (str): The name of the prompt to retrieve.
275
+ arguments (dict[str, str] | None, optional): Arguments to pass to the prompt. Defaults to None.
276
+
277
+ Returns:
278
+ mcp.types.GetPromptResult: The complete response object from the protocol,
279
+ containing the prompt messages and any additional metadata.
280
+
281
+ Raises:
282
+ RuntimeError: If called while the client is not connected.
283
+ """
284
+ result = await self.session.get_prompt(name=name, arguments=arguments)
285
+ return result
286
+
287
  async def get_prompt(
288
  self, name: str, arguments: dict[str, str] | None = None
289
  ) -> list[mcp.types.PromptMessage]:
290
+ """Retrieve a rendered prompt message list from the server.
291
+
292
+ Args:
293
+ name (str): The name of the prompt to retrieve.
294
+ arguments (dict[str, str] | None, optional): Arguments to pass to the prompt. Defaults to None.
295
+
296
+ Returns:
297
+ list[mcp.types.PromptMessage]: A list of prompt messages.
298
+
299
+ Raises:
300
+ RuntimeError: If called while the client is not connected.
301
+ """
302
+ result = await self.get_prompt_mcp(name=name, arguments=arguments)
303
  return result.messages
304
 
305
+ # --- Completion ---
306
+
307
+ async def complete_mcp(
308
+ self,
309
+ ref: mcp.types.ResourceReference | mcp.types.PromptReference,
310
+ argument: dict[str, str],
311
+ ) -> mcp.types.CompleteResult:
312
+ """Send a completion request and return the complete MCP protocol result.
313
+
314
+ Args:
315
+ ref (mcp.types.ResourceReference | mcp.types.PromptReference): The reference to complete.
316
+ argument (dict[str, str]): Arguments to pass to the completion request.
317
+
318
+ Returns:
319
+ mcp.types.CompleteResult: The complete response object from the protocol,
320
+ containing the completion and any additional metadata.
321
+
322
+ Raises:
323
+ RuntimeError: If called while the client is not connected.
324
+ """
325
+ result = await self.session.complete(ref=ref, argument=argument)
326
+ return result
327
+
328
  async def complete(
329
  self,
330
  ref: mcp.types.ResourceReference | mcp.types.PromptReference,
331
  argument: dict[str, str],
332
  ) -> mcp.types.Completion:
333
+ """Send a completion request to the server.
334
+
335
+ Args:
336
+ ref (mcp.types.ResourceReference | mcp.types.PromptReference): The reference to complete.
337
+ argument (dict[str, str]): Arguments to pass to the completion request.
338
+
339
+ Returns:
340
+ mcp.types.Completion: The completion object.
341
+
342
+ Raises:
343
+ RuntimeError: If called while the client is not connected.
344
+ """
345
+ result = await self.complete_mcp(ref=ref, argument=argument)
346
  return result.completion
347
 
348
+ # --- Tools ---
349
+
350
+ async def list_tools_mcp(self) -> mcp.types.ListToolsResult:
351
+ """Send a tools/list request and return the complete MCP protocol result.
352
+
353
+ Returns:
354
+ mcp.types.ListToolsResult: The complete response object from the protocol,
355
+ containing the list of tools and any additional metadata.
356
+
357
+ Raises:
358
+ RuntimeError: If called while the client is not connected.
359
+ """
360
  result = await self.session.list_tools()
361
+ return result
362
+
363
+ async def list_tools(self) -> list[mcp.types.Tool]:
364
+ """Retrieve a list of tools available on the server.
365
+
366
+ Returns:
367
+ list[mcp.types.Tool]: A list of Tool objects.
368
+
369
+ Raises:
370
+ RuntimeError: If called while the client is not connected.
371
+ """
372
+ result = await self.list_tools_mcp()
373
  return result.tools
374
 
375
+ # --- Call Tool ---
 
 
 
 
 
 
 
 
376
 
377
+ async def call_tool_mcp(
378
+ self, name: str, arguments: dict[str, Any]
379
+ ) -> mcp.types.CallToolResult:
380
+ """Send a tools/call request and return the complete MCP protocol result.
381
+
382
+ This method returns the raw CallToolResult object, which includes an isError flag
383
+ and other metadata. It does not raise an exception if the tool call results in an error.
384
+
385
+ Args:
386
+ name (str): The name of the tool to call.
387
+ arguments (dict[str, Any]): Arguments to pass to the tool.
388
+
389
+ Returns:
390
+ mcp.types.CallToolResult: The complete response object from the protocol,
391
+ containing the tool result and any additional metadata.
392
+
393
+ Raises:
394
+ RuntimeError: If called while the client is not connected.
395
+ """
396
+ result = await self.session.call_tool(name=name, arguments=arguments)
397
+ return result
398
 
399
  async def call_tool(
400
  self,
401
  name: str,
402
  arguments: dict[str, Any] | None = None,
 
403
  ) -> (
404
  list[
405
  mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource
406
  ]
407
  | mcp.types.CallToolResult
408
  ):
409
+ """Call a tool on the server.
410
+
411
+ Unlike call_tool_mcp, this method raises a ClientError if the tool call results in an error.
412
+
413
+ Args:
414
+ name (str): The name of the tool to call.
415
+ arguments (dict[str, Any] | None, optional): Arguments to pass to the tool. Defaults to None.
416
+
417
+ Returns:
418
+ list[mcp.types.TextContent | mcp.types.ImageContent | mcp.types.EmbeddedResource]:
419
+ The content returned by the tool.
420
+
421
+ Raises:
422
+ ClientError: If the tool call results in an error.
423
+ RuntimeError: If called while the client is not connected.
424
+ """
425
+ result = await self.call_tool_mcp(name=name, arguments=arguments or {})
426
+ if result.isError:
427
  msg = cast(mcp.types.TextContent, result.content[0]).text
428
  raise ClientError(msg)
429
  return result.content