fastmcp 2.10.6__py3-none-any.whl → 2.11.0__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 (61) hide show
  1. fastmcp/cli/cli.py +128 -33
  2. fastmcp/cli/install/claude_code.py +42 -1
  3. fastmcp/cli/install/claude_desktop.py +42 -1
  4. fastmcp/cli/install/cursor.py +42 -1
  5. fastmcp/cli/install/mcp_json.py +41 -0
  6. fastmcp/cli/run.py +127 -1
  7. fastmcp/client/__init__.py +2 -0
  8. fastmcp/client/auth/oauth.py +68 -99
  9. fastmcp/client/oauth_callback.py +18 -0
  10. fastmcp/client/transports.py +69 -15
  11. fastmcp/contrib/component_manager/example.py +2 -2
  12. fastmcp/experimental/server/openapi/README.md +266 -0
  13. fastmcp/experimental/server/openapi/__init__.py +38 -0
  14. fastmcp/experimental/server/openapi/components.py +348 -0
  15. fastmcp/experimental/server/openapi/routing.py +132 -0
  16. fastmcp/experimental/server/openapi/server.py +466 -0
  17. fastmcp/experimental/utilities/openapi/README.md +239 -0
  18. fastmcp/experimental/utilities/openapi/__init__.py +68 -0
  19. fastmcp/experimental/utilities/openapi/director.py +208 -0
  20. fastmcp/experimental/utilities/openapi/formatters.py +355 -0
  21. fastmcp/experimental/utilities/openapi/json_schema_converter.py +340 -0
  22. fastmcp/experimental/utilities/openapi/models.py +85 -0
  23. fastmcp/experimental/utilities/openapi/parser.py +618 -0
  24. fastmcp/experimental/utilities/openapi/schemas.py +538 -0
  25. fastmcp/mcp_config.py +125 -88
  26. fastmcp/prompts/prompt.py +11 -1
  27. fastmcp/resources/resource.py +21 -1
  28. fastmcp/resources/template.py +20 -1
  29. fastmcp/server/auth/__init__.py +17 -2
  30. fastmcp/server/auth/auth.py +144 -7
  31. fastmcp/server/auth/providers/bearer.py +25 -473
  32. fastmcp/server/auth/providers/in_memory.py +4 -2
  33. fastmcp/server/auth/providers/jwt.py +538 -0
  34. fastmcp/server/auth/providers/workos.py +170 -0
  35. fastmcp/server/auth/registry.py +52 -0
  36. fastmcp/server/context.py +107 -26
  37. fastmcp/server/dependencies.py +9 -2
  38. fastmcp/server/http.py +62 -30
  39. fastmcp/server/middleware/middleware.py +3 -23
  40. fastmcp/server/openapi.py +1 -1
  41. fastmcp/server/proxy.py +50 -11
  42. fastmcp/server/server.py +168 -59
  43. fastmcp/settings.py +73 -6
  44. fastmcp/tools/tool.py +36 -3
  45. fastmcp/tools/tool_manager.py +38 -2
  46. fastmcp/tools/tool_transform.py +112 -3
  47. fastmcp/utilities/components.py +35 -2
  48. fastmcp/utilities/json_schema.py +136 -98
  49. fastmcp/utilities/json_schema_type.py +1 -3
  50. fastmcp/utilities/mcp_config.py +28 -0
  51. fastmcp/utilities/openapi.py +240 -50
  52. fastmcp/utilities/tests.py +54 -6
  53. fastmcp/utilities/types.py +89 -11
  54. {fastmcp-2.10.6.dist-info → fastmcp-2.11.0.dist-info}/METADATA +4 -3
  55. fastmcp-2.11.0.dist-info/RECORD +108 -0
  56. fastmcp/server/auth/providers/bearer_env.py +0 -63
  57. fastmcp/utilities/cache.py +0 -26
  58. fastmcp-2.10.6.dist-info/RECORD +0 -93
  59. {fastmcp-2.10.6.dist-info → fastmcp-2.11.0.dist-info}/WHEEL +0 -0
  60. {fastmcp-2.10.6.dist-info → fastmcp-2.11.0.dist-info}/entry_points.txt +0 -0
  61. {fastmcp-2.10.6.dist-info → fastmcp-2.11.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,239 @@
1
+ # OpenAPI Utilities (New Implementation)
2
+
3
+ This directory contains the next-generation OpenAPI integration utilities for FastMCP, designed to replace the legacy `openapi.py` implementation.
4
+
5
+ ## Architecture Overview
6
+
7
+ The new implementation follows a **stateless request building strategy** using `openapi-core` for high-performance, per-request HTTP request construction, eliminating startup latency while maintaining robust OpenAPI compliance.
8
+
9
+ ### Core Components
10
+
11
+ 1. **`director.py`** - `RequestDirector` for stateless HTTP request building
12
+ 2. **`parser.py`** - OpenAPI spec parsing and route extraction with pre-calculated schemas
13
+ 3. **`schemas.py`** - Schema processing with parameter mapping for collision handling
14
+ 4. **`models.py`** - Enhanced data models with pre-calculated fields for performance
15
+ 5. **`formatters.py`** - Response formatting and processing utilities
16
+
17
+ ### Key Architecture Principles
18
+
19
+ #### 1. Stateless Request Building
20
+ - Uses `openapi-core` library for robust OpenAPI parameter serialization
21
+ - Builds HTTP requests on-demand with zero startup latency
22
+ - Offloads OpenAPI compliance to a well-tested library without code generation overhead
23
+
24
+ #### 2. Pre-calculated Optimization
25
+ - **Schema Pre-calculation**: Combined schemas calculated once during parsing
26
+ - **Parameter Mapping**: Collision resolution mapping calculated upfront
27
+ - **Zero Runtime Overhead**: All complex processing done during initialization
28
+
29
+ #### 3. Performance-First Design
30
+ - **No Code Generation**: Eliminates 100-200ms startup latency
31
+ - **Serverless Friendly**: Ideal for cold-start environments
32
+ - **Minimal Dependencies**: Uses lightweight `openapi-core` instead of full client generation
33
+
34
+ ## Data Flow
35
+
36
+ ### Initialization Process
37
+
38
+ ```
39
+ OpenAPI Spec → Parser → HTTPRoute with Pre-calculated Fields → RequestDirector + SchemaPath
40
+ ```
41
+
42
+ 1. **Input**: Raw OpenAPI specification (dict)
43
+ 2. **Parsing**: Extract operations to `HTTPRoute` models
44
+ 3. **Pre-calculation**: Generate combined schemas and parameter maps during parsing
45
+ 4. **Director Setup**: Create `RequestDirector` with `SchemaPath` for request building
46
+
47
+ ### Request Processing
48
+
49
+ ```
50
+ MCP Tool Call → RequestDirector.build() → httpx.Request → HTTP Response → Structured Output
51
+ ```
52
+
53
+ 1. **Tool Invocation**: FastMCP receives tool call with parameters
54
+ 2. **Request Building**: RequestDirector builds HTTP request using parameter map
55
+ 3. **Parameter Handling**: openapi-core handles all OpenAPI serialization rules
56
+ 4. **Response Processing**: Parse response into structured format with proper error handling
57
+
58
+ ## Key Features
59
+
60
+ ### 1. High-Performance Request Building
61
+ - Zero startup latency - no code generation required
62
+ - Stateless request building scales infinitely
63
+ - Uses proven `openapi-core` library for OpenAPI compliance
64
+ - Perfect for serverless and cold-start environments
65
+
66
+ ### 2. Comprehensive Parameter Support
67
+ - **Parameter Collisions**: Intelligent collision resolution with suffixing
68
+ - **DeepObject Style**: Full support for deepObject parameters with explode=true/false
69
+ - **Complex Schemas**: Handles nested objects, arrays, and all OpenAPI types
70
+ - **Pre-calculated Mapping**: Parameter location mapping done upfront for performance
71
+
72
+ ### 3. Enhanced Error Handling
73
+ - HTTP status code mapping to MCP errors
74
+ - Structured error responses with detailed information
75
+ - Graceful handling of network timeouts and connection errors
76
+ - Proper error context preservation
77
+
78
+ ### 4. Advanced Schema Processing
79
+ - **Pre-calculated Schemas**: Combined parameter and body schemas calculated once
80
+ - **Collision-aware**: Automatically handles parameter name collisions
81
+ - **Type Safety**: Full Pydantic model validation
82
+ - **Performance**: Zero runtime schema processing overhead
83
+
84
+ ## Component Integration
85
+
86
+ ### Server Components (`/server/openapi_new/`)
87
+
88
+ 1. **`OpenAPITool`** - Simplified tool implementation using RequestDirector
89
+ 2. **`OpenAPIResource`** - Resource implementation with RequestDirector
90
+ 3. **`OpenAPIResourceTemplate`** - Resource template with RequestDirector support
91
+ 4. **`FastMCPOpenAPI`** - Main server class with stateless request building
92
+
93
+ ### RequestDirector Integration
94
+
95
+ All components use the same RequestDirector approach:
96
+ - Consistent parameter handling across all component types
97
+ - Uniform error handling and response processing
98
+ - Simplified architecture without fallback complexity
99
+ - High performance for all operation types
100
+
101
+ ## Usage Examples
102
+
103
+ ### Basic Server Setup
104
+
105
+ ```python
106
+ import httpx
107
+ from fastmcp.server.openapi_new import FastMCPOpenAPI
108
+
109
+ # OpenAPI spec (can be loaded from file/URL)
110
+ openapi_spec = {...}
111
+
112
+ # Create HTTP client
113
+ async with httpx.AsyncClient() as client:
114
+ # Create server with stateless request building
115
+ server = FastMCPOpenAPI(
116
+ openapi_spec=openapi_spec,
117
+ client=client,
118
+ name="My API Server"
119
+ )
120
+
121
+ # Server automatically creates RequestDirector and pre-calculates schemas
122
+ ```
123
+
124
+ ### Direct RequestDirector Usage
125
+
126
+ ```python
127
+ from fastmcp.experimental.utilities.openapi.director import RequestDirector
128
+ from jsonschema_path import SchemaPath
129
+
130
+ # Create RequestDirector manually
131
+ spec = SchemaPath.from_dict(openapi_spec)
132
+ director = RequestDirector(spec)
133
+
134
+ # Build HTTP request
135
+ request = director.build(route, flat_arguments, base_url)
136
+
137
+ # Execute with httpx
138
+ async with httpx.AsyncClient() as client:
139
+ response = await client.send(request)
140
+ ```
141
+
142
+ ## Testing Strategy
143
+
144
+ Tests are located in `/tests/server/openapi_new/`:
145
+
146
+ ### Test Categories
147
+
148
+ 1. **Core Functionality**
149
+ - `test_server.py` - Server initialization and RequestDirector integration
150
+
151
+ 2. **OpenAPI Features**
152
+ - `test_parameter_collisions.py` - Parameter name collision handling
153
+ - `test_deepobject_style.py` - DeepObject parameter style support
154
+ - `test_openapi_features.py` - General OpenAPI feature compliance
155
+
156
+ ### Testing Philosophy
157
+
158
+ - **Real Objects**: Use real HTTPRoute models and OpenAPI specifications
159
+ - **Minimal Mocking**: Only mock external HTTP endpoints
160
+ - **Performance Focus**: Test that initialization is fast and stateless
161
+ - **Behavioral Testing**: Verify OpenAPI compliance without implementation details
162
+
163
+ ## Migration Guide
164
+
165
+ ### From Legacy Implementation
166
+
167
+ 1. **Import Changes**:
168
+ ```python
169
+ # Old
170
+ from fastmcp.server.openapi import FastMCPOpenAPI
171
+
172
+ # New
173
+ from fastmcp.server.openapi_new import FastMCPOpenAPI
174
+ ```
175
+
176
+ 2. **Constructor**: Same interface, no changes needed
177
+
178
+ 3. **Automatic Benefits**:
179
+ - Eliminates startup latency (100-200ms improvement)
180
+ - Better OpenAPI compliance via openapi-core
181
+ - Serverless-friendly performance characteristics
182
+ - Simplified architecture without fallback complexity
183
+
184
+ ### Performance Improvements
185
+
186
+ - **Cold Start**: Zero latency penalty for serverless deployments
187
+ - **Memory Usage**: Lower memory footprint without generated client code
188
+ - **Reliability**: No dynamic code generation failures
189
+ - **Maintainability**: Simpler architecture with fewer moving parts
190
+
191
+ ## Future Enhancements
192
+
193
+ ### Planned Features
194
+
195
+ 1. **Response Streaming**: Handle streaming API responses
196
+ 2. **Enhanced Authentication**: More auth provider integrations
197
+ 3. **Advanced Metrics**: Detailed request/response monitoring
198
+ 4. **Schema Validation**: Enhanced input/output validation
199
+ 5. **Batch Operations**: Optimized multi-operation requests
200
+
201
+ ### Performance Improvements
202
+
203
+ 1. **Schema Caching**: More aggressive schema pre-calculation
204
+ 2. **Memory Optimization**: Further reduce memory footprint
205
+ 3. **Request Batching**: Smart batching for bulk operations
206
+ 4. **Connection Optimization**: Enhanced connection pooling strategies
207
+
208
+ ## Troubleshooting
209
+
210
+ ### Common Issues
211
+
212
+ 1. **RequestDirector Initialization Fails**
213
+ - Check OpenAPI spec validity with `jsonschema-path`
214
+ - Verify spec format is correct JSON/YAML
215
+ - Ensure all required OpenAPI fields are present
216
+
217
+ 2. **Parameter Mapping Issues**
218
+ - Check parameter collision resolution in debug logs
219
+ - Verify parameter names match OpenAPI spec exactly
220
+ - Review pre-calculated parameter map in HTTPRoute
221
+
222
+ 3. **Request Building Errors**
223
+ - Check network connectivity to target API
224
+ - Verify base URL configuration
225
+ - Review parameter validation and type mismatches
226
+
227
+ ### Debugging
228
+
229
+ - Enable debug logging: `logger.setLevel(logging.DEBUG)`
230
+ - Check RequestDirector initialization logs
231
+ - Review parameter mapping in HTTPRoute models
232
+ - Monitor request building and API response patterns
233
+
234
+ ## Dependencies
235
+
236
+ - `openapi-core` - OpenAPI specification processing and validation
237
+ - `httpx` - HTTP client library
238
+ - `pydantic` - Data validation and serialization
239
+ - `urllib.parse` - URL building and manipulation
@@ -0,0 +1,68 @@
1
+ """OpenAPI utilities for FastMCP - refactored for better maintainability."""
2
+
3
+ # Import from models
4
+ from .models import (
5
+ HTTPRoute,
6
+ HttpMethod,
7
+ JsonSchema,
8
+ ParameterInfo,
9
+ ParameterLocation,
10
+ RequestBodyInfo,
11
+ ResponseInfo,
12
+ )
13
+
14
+ # Import from parser
15
+ from .parser import parse_openapi_to_http_routes
16
+
17
+ # Import from formatters
18
+ from .formatters import (
19
+ format_array_parameter,
20
+ format_deep_object_parameter,
21
+ format_description_with_responses,
22
+ format_json_for_description,
23
+ generate_example_from_schema,
24
+ )
25
+
26
+ # Import from schemas
27
+ from .schemas import (
28
+ _combine_schemas,
29
+ extract_output_schema_from_responses,
30
+ clean_schema_for_display,
31
+ _replace_ref_with_defs,
32
+ _make_optional_parameter_nullable,
33
+ )
34
+
35
+ # Import from json_schema_converter
36
+ from .json_schema_converter import (
37
+ convert_openapi_schema_to_json_schema,
38
+ convert_schema_definitions,
39
+ )
40
+
41
+ # Export public symbols - maintaining backward compatibility
42
+ __all__ = [
43
+ # Models
44
+ "HTTPRoute",
45
+ "ParameterInfo",
46
+ "RequestBodyInfo",
47
+ "ResponseInfo",
48
+ "HttpMethod",
49
+ "ParameterLocation",
50
+ "JsonSchema",
51
+ # Parser
52
+ "parse_openapi_to_http_routes",
53
+ # Formatters
54
+ "format_array_parameter",
55
+ "format_deep_object_parameter",
56
+ "format_description_with_responses",
57
+ "format_json_for_description",
58
+ "generate_example_from_schema",
59
+ # Schemas
60
+ "_combine_schemas",
61
+ "extract_output_schema_from_responses",
62
+ "clean_schema_for_display",
63
+ "_replace_ref_with_defs",
64
+ "_make_optional_parameter_nullable",
65
+ # JSON Schema Converter
66
+ "convert_openapi_schema_to_json_schema",
67
+ "convert_schema_definitions",
68
+ ]
@@ -0,0 +1,208 @@
1
+ """Request director using openapi-core for stateless HTTP request building."""
2
+
3
+ from typing import Any
4
+ from urllib.parse import urljoin
5
+
6
+ import httpx
7
+ from jsonschema_path import SchemaPath
8
+
9
+ from fastmcp.utilities.logging import get_logger
10
+
11
+ from .models import HTTPRoute
12
+
13
+ logger = get_logger(__name__)
14
+
15
+
16
+ class RequestDirector:
17
+ """Builds httpx.Request objects from HTTPRoute and arguments using openapi-core."""
18
+
19
+ def __init__(self, spec: SchemaPath):
20
+ """Initialize with a parsed SchemaPath object."""
21
+ self._spec = spec
22
+
23
+ def build(
24
+ self,
25
+ route: HTTPRoute,
26
+ flat_args: dict[str, Any],
27
+ base_url: str = "http://localhost",
28
+ ) -> httpx.Request:
29
+ """
30
+ Constructs a final httpx.Request object, handling all OpenAPI serialization.
31
+
32
+ Args:
33
+ route: HTTPRoute containing OpenAPI operation details
34
+ flat_args: Flattened arguments from LLM (may include suffixed parameters)
35
+ base_url: Base URL for the request
36
+
37
+ Returns:
38
+ httpx.Request: Properly formatted HTTP request
39
+ """
40
+ logger.debug(
41
+ f"Building request for {route.method} {route.path} with args: {flat_args}"
42
+ )
43
+
44
+ # Step 1: Un-flatten arguments into path, query, body, etc. using parameter map
45
+ path_params, query_params, header_params, body = self._unflatten_arguments(
46
+ route, flat_args
47
+ )
48
+
49
+ logger.debug(
50
+ f"Unflattened - path: {path_params}, query: {query_params}, headers: {header_params}, body: {body}"
51
+ )
52
+
53
+ # Step 2: Build base URL with path parameters
54
+ url = self._build_url(route.path, path_params, base_url)
55
+
56
+ # Step 3: Prepare request data
57
+ request_data = {
58
+ "method": route.method.upper(),
59
+ "url": url,
60
+ "params": query_params if query_params else None,
61
+ "headers": header_params if header_params else None,
62
+ }
63
+
64
+ # Step 4: Handle request body
65
+ if body is not None:
66
+ if isinstance(body, dict) or isinstance(body, list):
67
+ request_data["json"] = body
68
+ else:
69
+ request_data["content"] = body
70
+
71
+ # Step 5: Create httpx.Request
72
+ return httpx.Request(**{k: v for k, v in request_data.items() if v is not None})
73
+
74
+ def _unflatten_arguments(
75
+ self, route: HTTPRoute, flat_args: dict[str, Any]
76
+ ) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any], Any]:
77
+ """
78
+ Maps flat arguments back to their OpenAPI locations using the parameter map.
79
+
80
+ Args:
81
+ route: HTTPRoute with parameter_map containing location mappings
82
+ flat_args: Flat arguments from LLM call
83
+
84
+ Returns:
85
+ Tuple of (path_params, query_params, header_params, body)
86
+ """
87
+ path_params = {}
88
+ query_params = {}
89
+ header_params = {}
90
+ body_props = {}
91
+
92
+ # Use parameter map to route arguments to correct locations
93
+ if hasattr(route, "parameter_map") and route.parameter_map:
94
+ for arg_name, value in flat_args.items():
95
+ if value is None:
96
+ continue # Skip None values for optional parameters
97
+
98
+ if arg_name not in route.parameter_map:
99
+ logger.warning(
100
+ f"Argument '{arg_name}' not found in parameter map for {route.operation_id}"
101
+ )
102
+ continue
103
+
104
+ mapping = route.parameter_map[arg_name]
105
+ location = mapping["location"]
106
+ openapi_name = mapping["openapi_name"]
107
+
108
+ if location == "path":
109
+ path_params[openapi_name] = value
110
+ elif location == "query":
111
+ query_params[openapi_name] = value
112
+ elif location == "header":
113
+ header_params[openapi_name] = value
114
+ elif location == "body":
115
+ body_props[openapi_name] = value
116
+ else:
117
+ logger.warning(
118
+ f"Unknown parameter location '{location}' for {arg_name}"
119
+ )
120
+ else:
121
+ # Fallback: try to map arguments based on parameter definitions
122
+ logger.debug("No parameter map available, using fallback mapping")
123
+
124
+ # Create a mapping from parameter names to their locations
125
+ param_locations = {}
126
+ for param in route.parameters:
127
+ param_locations[param.name] = param.location
128
+
129
+ # Map arguments to locations
130
+ for arg_name, value in flat_args.items():
131
+ if value is None:
132
+ continue
133
+
134
+ # Check if it's a suffixed parameter (e.g., id__path)
135
+ if "__" in arg_name:
136
+ base_name, location = arg_name.rsplit("__", 1)
137
+ if location in ["path", "query", "header"]:
138
+ if location == "path":
139
+ path_params[base_name] = value
140
+ elif location == "query":
141
+ query_params[base_name] = value
142
+ elif location == "header":
143
+ header_params[base_name] = value
144
+ continue
145
+
146
+ # Check if it's a known parameter
147
+ if arg_name in param_locations:
148
+ location = param_locations[arg_name]
149
+ if location == "path":
150
+ path_params[arg_name] = value
151
+ elif location == "query":
152
+ query_params[arg_name] = value
153
+ elif location == "header":
154
+ header_params[arg_name] = value
155
+ else:
156
+ # Assume it's a body property
157
+ body_props[arg_name] = value
158
+
159
+ # Handle body construction
160
+ body = None
161
+ if body_props:
162
+ # If we have body properties, construct the body object
163
+ if route.request_body and route.request_body.content_schema:
164
+ # Check if the request body expects an object with properties
165
+ content_type = next(iter(route.request_body.content_schema))
166
+ body_schema = route.request_body.content_schema[content_type]
167
+
168
+ if body_schema.get("type") == "object":
169
+ body = body_props
170
+ elif len(body_props) == 1:
171
+ # If body schema is not an object and we have exactly one property,
172
+ # use the property value directly
173
+ body = next(iter(body_props.values()))
174
+ else:
175
+ # Multiple properties but schema is not object - wrap in object
176
+ body = body_props
177
+ else:
178
+ body = body_props
179
+
180
+ return path_params, query_params, header_params, body
181
+
182
+ def _build_url(
183
+ self, path_template: str, path_params: dict[str, Any], base_url: str
184
+ ) -> str:
185
+ """
186
+ Build URL by substituting path parameters in the template.
187
+
188
+ Args:
189
+ path_template: OpenAPI path template (e.g., "/users/{id}")
190
+ path_params: Path parameter values
191
+ base_url: Base URL to prepend
192
+
193
+ Returns:
194
+ Complete URL with path parameters substituted
195
+ """
196
+ # Substitute path parameters
197
+ url_path = path_template
198
+ for param_name, param_value in path_params.items():
199
+ placeholder = f"{{{param_name}}}"
200
+ if placeholder in url_path:
201
+ url_path = url_path.replace(placeholder, str(param_value))
202
+
203
+ # Combine with base URL
204
+ return urljoin(base_url.rstrip("/") + "/", url_path.lstrip("/"))
205
+
206
+
207
+ # Export public symbols
208
+ __all__ = ["RequestDirector"]