fastmcp 2.11.1__py3-none-any.whl → 2.11.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. fastmcp/cli/cli.py +5 -5
  2. fastmcp/cli/install/claude_code.py +2 -2
  3. fastmcp/cli/install/claude_desktop.py +2 -2
  4. fastmcp/cli/install/cursor.py +2 -2
  5. fastmcp/cli/install/mcp_json.py +2 -2
  6. fastmcp/cli/install/shared.py +2 -2
  7. fastmcp/cli/run.py +74 -24
  8. fastmcp/client/logging.py +25 -1
  9. fastmcp/client/transports.py +4 -3
  10. fastmcp/experimental/server/openapi/routing.py +1 -1
  11. fastmcp/experimental/server/openapi/server.py +10 -23
  12. fastmcp/experimental/utilities/openapi/__init__.py +2 -2
  13. fastmcp/experimental/utilities/openapi/formatters.py +34 -0
  14. fastmcp/experimental/utilities/openapi/models.py +5 -2
  15. fastmcp/experimental/utilities/openapi/parser.py +248 -70
  16. fastmcp/experimental/utilities/openapi/schemas.py +135 -106
  17. fastmcp/prompts/prompt_manager.py +2 -2
  18. fastmcp/resources/resource_manager.py +12 -6
  19. fastmcp/server/auth/__init__.py +9 -1
  20. fastmcp/server/auth/auth.py +17 -1
  21. fastmcp/server/auth/providers/jwt.py +3 -4
  22. fastmcp/server/auth/registry.py +1 -1
  23. fastmcp/server/dependencies.py +32 -2
  24. fastmcp/server/http.py +41 -34
  25. fastmcp/server/proxy.py +33 -15
  26. fastmcp/server/server.py +18 -11
  27. fastmcp/settings.py +6 -9
  28. fastmcp/tools/tool.py +7 -7
  29. fastmcp/tools/tool_manager.py +3 -1
  30. fastmcp/tools/tool_transform.py +41 -27
  31. fastmcp/utilities/components.py +19 -4
  32. fastmcp/utilities/inspect.py +12 -17
  33. fastmcp/utilities/openapi.py +4 -4
  34. {fastmcp-2.11.1.dist-info → fastmcp-2.11.3.dist-info}/METADATA +2 -2
  35. {fastmcp-2.11.1.dist-info → fastmcp-2.11.3.dist-info}/RECORD +38 -38
  36. {fastmcp-2.11.1.dist-info → fastmcp-2.11.3.dist-info}/WHEEL +0 -0
  37. {fastmcp-2.11.1.dist-info → fastmcp-2.11.3.dist-info}/entry_points.txt +0 -0
  38. {fastmcp-2.11.1.dist-info → fastmcp-2.11.3.dist-info}/licenses/LICENSE +0 -0
fastmcp/cli/cli.py CHANGED
@@ -138,7 +138,7 @@ def version(
138
138
 
139
139
 
140
140
  @app.command
141
- def dev(
141
+ async def dev(
142
142
  server_spec: str,
143
143
  *,
144
144
  with_editable: Annotated[
@@ -220,7 +220,7 @@ def dev(
220
220
 
221
221
  try:
222
222
  # Import server to get dependencies
223
- server: FastMCP = run_module.import_server(file, server_object)
223
+ server: FastMCP = await run_module.import_server(file, server_object)
224
224
  if server.dependencies is not None:
225
225
  with_packages = list(set(with_packages + server.dependencies))
226
226
 
@@ -283,7 +283,7 @@ def dev(
283
283
 
284
284
 
285
285
  @app.command
286
- def run(
286
+ async def run(
287
287
  server_spec: str,
288
288
  *server_args: str,
289
289
  transport: Annotated[
@@ -414,7 +414,7 @@ def run(
414
414
  else:
415
415
  # Use direct import for backwards compatibility
416
416
  try:
417
- run_module.run_command(
417
+ await run_module.run_command(
418
418
  server_spec=server_spec,
419
419
  transport=transport,
420
420
  host=host,
@@ -476,7 +476,7 @@ async def inspect(
476
476
 
477
477
  try:
478
478
  # Import the server
479
- server = run_module.import_server(file, server_object)
479
+ server = await run_module.import_server(file, server_object)
480
480
 
481
481
  # Get server information - using native async support
482
482
  info = await inspect_fastmcp(server)
@@ -167,7 +167,7 @@ def install_claude_code(
167
167
  return False
168
168
 
169
169
 
170
- def claude_code_command(
170
+ async def claude_code_command(
171
171
  server_spec: str,
172
172
  *,
173
173
  server_name: Annotated[
@@ -234,7 +234,7 @@ def claude_code_command(
234
234
  Args:
235
235
  server_spec: Python file to install, optionally with :object suffix
236
236
  """
237
- file, server_object, name, packages, env_dict = process_common_args(
237
+ file, server_object, name, packages, env_dict = await process_common_args(
238
238
  server_spec, server_name, with_packages, env_vars, env_file
239
239
  )
240
240
 
@@ -140,7 +140,7 @@ def install_claude_desktop(
140
140
  return False
141
141
 
142
142
 
143
- def claude_desktop_command(
143
+ async def claude_desktop_command(
144
144
  server_spec: str,
145
145
  *,
146
146
  server_name: Annotated[
@@ -207,7 +207,7 @@ def claude_desktop_command(
207
207
  Args:
208
208
  server_spec: Python file to install, optionally with :object suffix
209
209
  """
210
- file, server_object, name, with_packages, env_dict = process_common_args(
210
+ file, server_object, name, with_packages, env_dict = await process_common_args(
211
211
  server_spec, server_name, with_packages, env_vars, env_file
212
212
  )
213
213
 
@@ -150,7 +150,7 @@ def install_cursor(
150
150
  return False
151
151
 
152
152
 
153
- def cursor_command(
153
+ async def cursor_command(
154
154
  server_spec: str,
155
155
  *,
156
156
  server_name: Annotated[
@@ -217,7 +217,7 @@ def cursor_command(
217
217
  Args:
218
218
  server_spec: Python file to install, optionally with :object suffix
219
219
  """
220
- file, server_object, name, with_packages, env_dict = process_common_args(
220
+ file, server_object, name, with_packages, env_dict = await process_common_args(
221
221
  server_spec, server_name, with_packages, env_vars, env_file
222
222
  )
223
223
 
@@ -113,7 +113,7 @@ def install_mcp_json(
113
113
  return False
114
114
 
115
115
 
116
- def mcp_json_command(
116
+ async def mcp_json_command(
117
117
  server_spec: str,
118
118
  *,
119
119
  server_name: Annotated[
@@ -188,7 +188,7 @@ def mcp_json_command(
188
188
  Args:
189
189
  server_spec: Python file to install, optionally with :object suffix
190
190
  """
191
- file, server_object, name, packages, env_dict = process_common_args(
191
+ file, server_object, name, packages, env_dict = await process_common_args(
192
192
  server_spec, server_name, with_packages, env_vars, env_file
193
193
  )
194
194
 
@@ -23,7 +23,7 @@ def parse_env_var(env_var: str) -> tuple[str, str]:
23
23
  return key.strip(), value.strip()
24
24
 
25
25
 
26
- def process_common_args(
26
+ async def process_common_args(
27
27
  server_spec: str,
28
28
  server_name: str | None,
29
29
  with_packages: list[str],
@@ -49,7 +49,7 @@ def process_common_args(
49
49
  server = None
50
50
  if not name:
51
51
  try:
52
- server = import_server(file, server_object)
52
+ server = await import_server(file, server_object)
53
53
  name = server.name
54
54
  except (ImportError, ModuleNotFoundError) as e:
55
55
  logger.debug(
fastmcp/cli/run.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """FastMCP run command implementation with enhanced type hints."""
2
2
 
3
3
  import importlib.util
4
+ import inspect
4
5
  import json
5
6
  import re
6
7
  import subprocess
@@ -58,15 +59,15 @@ def parse_file_path(server_spec: str) -> tuple[Path, str | None]:
58
59
  return file_path, server_object
59
60
 
60
61
 
61
- def import_server(file: Path, server_object: str | None = None) -> Any:
62
+ async def import_server(file: Path, server_or_factory: str | None = None) -> Any:
62
63
  """Import a MCP server from a file.
63
64
 
64
65
  Args:
65
66
  file: Path to the file
66
- server_object: Optional object name in format "module:object" or just "object"
67
+ server_or_factory: Optional object name in format "module:object" or just "object"
67
68
 
68
69
  Returns:
69
- The server object
70
+ The server object (or result of calling a factory function)
70
71
  """
71
72
  # Add parent directory to Python path so imports can be resolved
72
73
  file_dir = str(file.parent)
@@ -86,11 +87,12 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
86
87
  spec.loader.exec_module(module)
87
88
 
88
89
  # If no object specified, try common server names
89
- if not server_object:
90
- # Look for the most common server object names
90
+ if not server_or_factory:
91
+ # Look for common server instance names
91
92
  for name in ["mcp", "server", "app"]:
92
93
  if hasattr(module, name):
93
- return getattr(module, name)
94
+ obj = getattr(module, name)
95
+ return await _resolve_server_or_factory(obj, file, name)
94
96
 
95
97
  logger.error(
96
98
  f"No server object found in {file}. Please either:\n"
@@ -100,14 +102,14 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
100
102
  )
101
103
  sys.exit(1)
102
104
 
103
- assert server_object is not None
105
+ assert server_or_factory is not None
104
106
 
105
107
  # Handle module:object syntax
106
- if ":" in server_object:
107
- module_name, object_name = server_object.split(":", 1)
108
+ if ":" in server_or_factory:
109
+ module_name, object_name = server_or_factory.split(":", 1)
108
110
  try:
109
111
  server_module = importlib.import_module(module_name)
110
- server = getattr(server_module, object_name, None)
112
+ obj = getattr(server_module, object_name, None)
111
113
  except ImportError:
112
114
  logger.error(
113
115
  f"Could not import module '{module_name}'",
@@ -116,16 +118,62 @@ def import_server(file: Path, server_object: str | None = None) -> Any:
116
118
  sys.exit(1)
117
119
  else:
118
120
  # Just object name
119
- server = getattr(module, server_object, None)
121
+ obj = getattr(module, server_or_factory, None)
120
122
 
121
- if server is None:
123
+ if obj is None:
122
124
  logger.error(
123
- f"Server object '{server_object}' not found",
125
+ f"Server object '{server_or_factory}' not found",
124
126
  extra={"file": str(file)},
125
127
  )
126
128
  sys.exit(1)
127
129
 
128
- return server
130
+ return await _resolve_server_or_factory(obj, file, server_or_factory)
131
+
132
+
133
+ async def _resolve_server_or_factory(obj: Any, file: Path, name: str) -> Any:
134
+ """Resolve a server object or factory function to a server instance.
135
+
136
+ Args:
137
+ obj: The object that might be a server or factory function
138
+ file: Path to the file for error messages
139
+ name: Name of the object for error messages
140
+
141
+ Returns:
142
+ A server instance
143
+ """
144
+ # Check if it's a function or coroutine function
145
+ if inspect.isfunction(obj) or inspect.iscoroutinefunction(obj):
146
+ logger.debug(f"Found factory function '{name}' in {file}")
147
+
148
+ try:
149
+ if inspect.iscoroutinefunction(obj):
150
+ # Async factory function
151
+ server = await obj()
152
+ else:
153
+ # Sync factory function
154
+ server = obj()
155
+
156
+ # Validate the result is a FastMCP server
157
+ if not isinstance(server, FastMCP | FastMCP1x):
158
+ logger.error(
159
+ f"Factory function '{name}' must return a FastMCP server instance, "
160
+ f"got {type(server).__name__}",
161
+ extra={"file": str(file)},
162
+ )
163
+ sys.exit(1)
164
+
165
+ logger.debug(f"Factory function '{name}' created server: {server.name}")
166
+ return server
167
+
168
+ except Exception as e:
169
+ logger.error(
170
+ f"Failed to call factory function '{name}': {e}",
171
+ extra={"file": str(file)},
172
+ )
173
+ sys.exit(1)
174
+
175
+ # Not a function, return as-is (should be a server instance)
176
+ return obj
129
177
 
130
178
 
131
179
  def run_with_uv(
@@ -219,7 +267,7 @@ def create_client_server(url: str) -> Any:
219
267
  import fastmcp
220
268
 
221
269
  client = fastmcp.Client(url)
222
- server = fastmcp.FastMCP.from_client(client)
270
+ server = fastmcp.FastMCP.as_proxy(client)
223
271
  return server
224
272
  except Exception as e:
225
273
  logger.error(f"Failed to create client for URL {url}: {e}")
@@ -237,14 +285,16 @@ def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]:
237
285
  return server
238
286
 
239
287
 
240
- def import_server_with_args(
241
- file: Path, server_object: str | None = None, server_args: list[str] | None = None
288
+ async def import_server_with_args(
289
+ file: Path,
290
+ server_or_factory: str | None = None,
291
+ server_args: list[str] | None = None,
242
292
  ) -> Any:
243
293
  """Import a server with optional command line arguments.
244
294
 
245
295
  Args:
246
296
  file: Path to the server file
247
- server_object: Optional server object name
297
+ server_or_factory: Optional server object or factory function name
248
298
  server_args: Optional command line arguments to inject
249
299
 
250
300
  Returns:
@@ -254,14 +304,14 @@ def import_server_with_args(
254
304
  original_argv = sys.argv[:]
255
305
  try:
256
306
  sys.argv = [str(file)] + server_args
257
- return import_server(file, server_object)
307
+ return await import_server(file, server_or_factory)
258
308
  finally:
259
309
  sys.argv = original_argv
260
310
  else:
261
- return import_server(file, server_object)
311
+ return await import_server(file, server_or_factory)
262
312
 
263
313
 
264
- def run_command(
314
+ async def run_command(
265
315
  server_spec: str,
266
316
  transport: TransportType | None = None,
267
317
  host: str | None = None,
@@ -293,8 +343,8 @@ def run_command(
293
343
  server = create_mcp_config_server(Path(server_spec))
294
344
  else:
295
345
  # Handle file case
296
- file, server_object = parse_file_path(server_spec)
297
- server = import_server_with_args(file, server_object, server_args)
346
+ file, server_or_factory = parse_file_path(server_spec)
347
+ server = await import_server_with_args(file, server_or_factory, server_args)
298
348
  logger.debug(f'Found server "{server.name}" in {file}')
299
349
 
300
350
  # Run the server
@@ -320,7 +370,7 @@ def run_command(
320
370
  kwargs["show_banner"] = False
321
371
 
322
372
  try:
323
- server.run(**kwargs)
373
+ await server.run_async(**kwargs)
324
374
  except Exception as e:
325
375
  logger.error(f"Failed to run server: {e}")
326
376
  sys.exit(1)
fastmcp/client/logging.py CHANGED
@@ -13,7 +13,31 @@ LogHandler: TypeAlias = Callable[[LogMessage], Awaitable[None]]
13
13
 
14
14
 
15
15
  async def default_log_handler(message: LogMessage) -> None:
16
- logger.debug(f"Log received: {message}")
16
+ """Default handler that properly routes server log messages to appropriate log levels."""
17
+ msg = message.data.get("msg", str(message))
18
+ extra = message.data.get("extra", {})
19
+
20
+ # Map MCP log levels to Python logging levels
21
+ level_map = {
22
+ "debug": logger.debug,
23
+ "info": logger.info,
24
+ "notice": logger.info, # Python doesn't have 'notice', map to info
25
+ "warning": logger.warning,
26
+ "error": logger.error,
27
+ "critical": logger.critical,
28
+ "alert": logger.critical, # Map alert to critical
29
+ "emergency": logger.critical, # Map emergency to critical
30
+ }
31
+
32
+ # Get the appropriate logging function based on the message level
33
+ log_fn = level_map.get(message.level.lower(), logger.info)
34
+
35
+ # Include logger name if available
36
+ if message.logger:
37
+ msg = f"[{message.logger}] {msg}"
38
+
39
+ # Log with appropriate level and extra data
40
+ log_fn(f"Server log: {msg}", extra=extra)
17
41
 
18
42
 
19
43
  def create_log_callback(handler: LogHandler | None = None) -> LoggingFnT:
@@ -6,7 +6,7 @@ import os
6
6
  import shutil
7
7
  import sys
8
8
  import warnings
9
- from collections.abc import AsyncIterator, Callable
9
+ from collections.abc import AsyncIterator
10
10
  from pathlib import Path
11
11
  from typing import Any, Literal, TypeVar, cast, overload
12
12
 
@@ -22,6 +22,7 @@ from mcp.client.session import (
22
22
  SamplingFnT,
23
23
  )
24
24
  from mcp.server.fastmcp import FastMCP as FastMCP1Server
25
+ from mcp.shared._httpx_utils import McpHttpClientFactory
25
26
  from mcp.shared.memory import create_client_server_memory_streams
26
27
  from pydantic import AnyUrl
27
28
  from typing_extensions import TypedDict, Unpack
@@ -161,7 +162,7 @@ class SSETransport(ClientTransport):
161
162
  headers: dict[str, str] | None = None,
162
163
  auth: httpx.Auth | Literal["oauth"] | str | None = None,
163
164
  sse_read_timeout: datetime.timedelta | float | int | None = None,
164
- httpx_client_factory: Callable[[], httpx.AsyncClient] | None = None,
165
+ httpx_client_factory: McpHttpClientFactory | None = None,
165
166
  ):
166
167
  if isinstance(url, AnyUrl):
167
168
  url = str(url)
@@ -233,7 +234,7 @@ class StreamableHttpTransport(ClientTransport):
233
234
  headers: dict[str, str] | None = None,
234
235
  auth: httpx.Auth | Literal["oauth"] | str | None = None,
235
236
  sse_read_timeout: datetime.timedelta | float | int | None = None,
236
- httpx_client_factory: Callable[[], httpx.AsyncClient] | None = None,
237
+ httpx_client_factory: McpHttpClientFactory | None = None,
237
238
  ):
238
239
  if isinstance(url, AnyUrl):
239
240
  url = str(url)
@@ -113,7 +113,7 @@ def _determine_route_type(
113
113
  # We know mcp_type is not None here due to post_init validation
114
114
  assert route_map.mcp_type is not None
115
115
  logger.debug(
116
- f"Route {route.method} {route.path} matched mapping to {route_map.mcp_type.name}"
116
+ f"Route {route.method} {route.path} mapped to {route_map.mcp_type.name}"
117
117
  )
118
118
  return route_map
119
119
 
@@ -11,7 +11,7 @@ from jsonschema_path import SchemaPath
11
11
  from fastmcp.experimental.utilities.openapi import (
12
12
  HTTPRoute,
13
13
  extract_output_schema_from_responses,
14
- format_description_with_responses,
14
+ format_simple_description,
15
15
  parse_openapi_to_http_routes,
16
16
  )
17
17
  from fastmcp.experimental.utilities.openapi.director import RequestDirector
@@ -151,9 +151,6 @@ class FastMCPOpenAPI(FastMCP):
151
151
  try:
152
152
  self._spec = SchemaPath.from_dict(openapi_spec) # type: ignore[arg-type]
153
153
  self._director = RequestDirector(self._spec)
154
- logger.debug(
155
- "Initialized OpenAPI RequestDirector for stateless request building"
156
- )
157
154
  except Exception as e:
158
155
  logger.error(f"Failed to initialize RequestDirector: {e}")
159
156
  raise ValueError(f"Invalid OpenAPI specification: {e}") from e
@@ -270,7 +267,9 @@ class FastMCPOpenAPI(FastMCP):
270
267
 
271
268
  # Extract output schema from OpenAPI responses
272
269
  output_schema = extract_output_schema_from_responses(
273
- route.responses, route.schema_definitions, route.openapi_version
270
+ route.responses,
271
+ route.response_schemas,
272
+ route.openapi_version,
274
273
  )
275
274
 
276
275
  # Get a unique tool name
@@ -282,10 +281,9 @@ class FastMCPOpenAPI(FastMCP):
282
281
  or f"Executes {route.method} {route.path}"
283
282
  )
284
283
 
285
- # Format enhanced description with parameters and request body
286
- enhanced_description = format_description_with_responses(
284
+ # Use simplified description formatter for tools
285
+ enhanced_description = format_simple_description(
287
286
  base_description=base_description,
288
- responses=route.responses,
289
287
  parameters=route.parameters,
290
288
  request_body=route.request_body,
291
289
  )
@@ -318,9 +316,6 @@ class FastMCPOpenAPI(FastMCP):
318
316
 
319
317
  # Register the tool by directly assigning to the tools dictionary
320
318
  self._tool_manager._tools[final_tool_name] = tool
321
- logger.debug(
322
- f"Registered TOOL: {final_tool_name} ({route.method} {route.path}) with tags: {route.tags}"
323
- )
324
319
 
325
320
  def _create_openapi_resource(
326
321
  self,
@@ -337,10 +332,9 @@ class FastMCPOpenAPI(FastMCP):
337
332
  route.description or route.summary or f"Represents {route.path}"
338
333
  )
339
334
 
340
- # Format enhanced description with parameters and request body
341
- enhanced_description = format_description_with_responses(
335
+ # Use simplified description for resources
336
+ enhanced_description = format_simple_description(
342
337
  base_description=base_description,
343
- responses=route.responses,
344
338
  parameters=route.parameters,
345
339
  request_body=route.request_body,
346
340
  )
@@ -372,9 +366,6 @@ class FastMCPOpenAPI(FastMCP):
372
366
 
373
367
  # Register the resource by directly assigning to the resources dictionary
374
368
  self._resource_manager._resources[final_resource_uri] = resource
375
- logger.debug(
376
- f"Registered RESOURCE: {final_resource_uri} ({route.method} {route.path}) with tags: {route.tags}"
377
- )
378
369
 
379
370
  def _create_openapi_template(
380
371
  self,
@@ -397,10 +388,9 @@ class FastMCPOpenAPI(FastMCP):
397
388
  route.description or route.summary or f"Template for {route.path}"
398
389
  )
399
390
 
400
- # Format enhanced description with parameters and request body
401
- enhanced_description = format_description_with_responses(
391
+ # Use simplified description for resource templates
392
+ enhanced_description = format_simple_description(
402
393
  base_description=base_description,
403
- responses=route.responses,
404
394
  parameters=route.parameters,
405
395
  request_body=route.request_body,
406
396
  )
@@ -455,9 +445,6 @@ class FastMCPOpenAPI(FastMCP):
455
445
 
456
446
  # Register the template by directly assigning to the templates dictionary
457
447
  self._resource_manager._templates[final_template_uri] = template
458
- logger.debug(
459
- f"Registered TEMPLATE: {final_template_uri} ({route.method} {route.path}) with tags: {route.tags}"
460
- )
461
448
 
462
449
 
463
450
  # Export public symbols
@@ -20,6 +20,7 @@ from .formatters import (
20
20
  format_deep_object_parameter,
21
21
  format_description_with_responses,
22
22
  format_json_for_description,
23
+ format_simple_description,
23
24
  generate_example_from_schema,
24
25
  )
25
26
 
@@ -28,7 +29,6 @@ from .schemas import (
28
29
  _combine_schemas,
29
30
  extract_output_schema_from_responses,
30
31
  clean_schema_for_display,
31
- _replace_ref_with_defs,
32
32
  _make_optional_parameter_nullable,
33
33
  )
34
34
 
@@ -55,12 +55,12 @@ __all__ = [
55
55
  "format_deep_object_parameter",
56
56
  "format_description_with_responses",
57
57
  "format_json_for_description",
58
+ "format_simple_description",
58
59
  "generate_example_from_schema",
59
60
  # Schemas
60
61
  "_combine_schemas",
61
62
  "extract_output_schema_from_responses",
62
63
  "clean_schema_for_display",
63
- "_replace_ref_with_defs",
64
64
  "_make_optional_parameter_nullable",
65
65
  # JSON Schema Converter
66
66
  "convert_openapi_schema_to_json_schema",
@@ -189,6 +189,39 @@ def format_json_for_description(data: Any, indent: int = 2) -> str:
189
189
  return f"```\nCould not serialize to JSON: {data}\n```"
190
190
 
191
191
 
192
+ def format_simple_description(
193
+ base_description: str,
194
+ parameters: list[ParameterInfo] | None = None,
195
+ request_body: RequestBodyInfo | None = None,
196
+ ) -> str:
197
+ """
198
+ Formats a simple description for MCP objects (tools, resources, prompts).
199
+ Excludes response details, examples, and verbose status codes.
200
+
201
+ Args:
202
+ base_description (str): The initial description to be formatted.
203
+ parameters (list[ParameterInfo] | None, optional): A list of parameter information.
204
+ request_body (RequestBodyInfo | None, optional): Information about the request body.
205
+
206
+ Returns:
207
+ str: The formatted description string with minimal details.
208
+ """
209
+ desc_parts = [base_description]
210
+
211
+ # Only add critical parameter information if they have descriptions
212
+ if parameters:
213
+ path_params = [p for p in parameters if p.location == "path" and p.description]
214
+ if path_params:
215
+ desc_parts.append("\n\n**Path Parameters:**")
216
+ for param in path_params:
217
+ desc_parts.append(f"\n- **{param.name}**: {param.description}")
218
+
219
+ # Skip query parameters, request body details, and all response information
220
+ # These are already captured in the inputSchema
221
+
222
+ return "\n".join(desc_parts)
223
+
224
+
192
225
  def format_description_with_responses(
193
226
  base_description: str,
194
227
  responses: dict[
@@ -351,5 +384,6 @@ __all__ = [
351
384
  "format_deep_object_parameter",
352
385
  "format_description_with_responses",
353
386
  "format_json_for_description",
387
+ "format_simple_description",
354
388
  "generate_example_from_schema",
355
389
  ]
@@ -58,9 +58,12 @@ class HTTPRoute(FastMCPBaseModel):
58
58
  responses: dict[str, ResponseInfo] = Field(
59
59
  default_factory=dict
60
60
  ) # Key: status code str
61
- schema_definitions: dict[str, JsonSchema] = Field(
61
+ request_schemas: dict[str, JsonSchema] = Field(
62
62
  default_factory=dict
63
- ) # Store component schemas
63
+ ) # Store schemas needed for input (parameters/request body)
64
+ response_schemas: dict[str, JsonSchema] = Field(
65
+ default_factory=dict
66
+ ) # Store schemas needed for output (responses)
64
67
  extensions: dict[str, Any] = Field(default_factory=dict)
65
68
  openapi_version: str | None = None
66
69