From 6d46192659200eb1d93f1741c4cf42be432e291b Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Fri, 29 Aug 2025 21:37:39 -0400 Subject: [PATCH] Internal refactor: fastmcp config to mcp server config (#1672) --- .github/workflows/update-config-schema.yml | 18 ++--- .../latest.json | 0 .../v1.json | 0 docs/docs.json | 42 ++++++------ docs/python-sdk/fastmcp-cli-cli.mdx | 12 ++-- .../fastmcp-cli-install-claude_code.mdx | 2 +- .../fastmcp-cli-install-claude_desktop.mdx | 2 +- .../python-sdk/fastmcp-cli-install-cursor.mdx | 4 +- .../fastmcp-cli-install-mcp_json.mdx | 2 +- docs/python-sdk/fastmcp-cli-run.mdx | 23 ++++--- docs/python-sdk/fastmcp-client-auth-oauth.mdx | 34 ++++++---- docs/python-sdk/fastmcp-client-client.mdx | 66 +++++++++---------- .../fastmcp-server-auth-oauth_proxy.mdx | 20 +++--- .../fastmcp-server-auth-providers-workos.mdx | 6 +- ...astmcp-server-auth-redirect_validation.mdx | 5 +- .../fastmcp-server-middleware-logging.mdx | 24 +++++-- docs/python-sdk/fastmcp-utilities-cli.mdx | 32 ++++++++- ...-utilities-mcp_server_config-__init__.mdx} | 2 +- ...ilities-mcp_server_config-v1-__init__.mdx} | 2 +- ...cp_server_config-v1-mcp_server_config.mdx} | 62 ++++++++--------- ...mcp_server_config-v1-sources-__init__.mdx} | 2 +- ...ies-mcp_server_config-v1-sources-base.mdx} | 8 +-- ...p_server_config-v1-sources-filesystem.mdx} | 8 +-- examples/fastmcp_config_demo/README.md | 6 +- src/fastmcp/cli/claude.py | 2 +- src/fastmcp/cli/cli.py | 6 +- src/fastmcp/cli/install/claude_code.py | 2 +- src/fastmcp/cli/install/claude_desktop.py | 2 +- src/fastmcp/cli/install/cursor.py | 2 +- src/fastmcp/cli/install/mcp_json.py | 2 +- src/fastmcp/cli/install/shared.py | 12 ++-- src/fastmcp/cli/run.py | 22 +++---- src/fastmcp/client/transports.py | 2 +- src/fastmcp/utilities/cli.py | 20 +++--- .../__init__.py | 10 +-- .../v1/__init__.py | 0 .../v1/mcp_server_config.py} | 16 ++--- .../v1/schema.json | 0 .../v1/sources/__init__.py | 0 .../v1/sources/base.py | 0 .../v1/sources/filesystem.py | 2 +- tests/cli/test_cli.py | 2 +- tests/cli/test_config.py | 62 ++++++++--------- ... => test_mcp_server_config_integration.py} | 30 ++++----- ...ma.py => test_mcp_server_config_schema.py} | 6 +- tests/cli/test_project_prepare.py | 42 ++++++------ tests/cli/test_run.py | 6 +- tests/cli/test_run_config.py | 28 ++++---- tests/cli/test_server_args.py | 12 ++-- tests/utilities/test_cli.py | 2 +- 50 files changed, 363 insertions(+), 309 deletions(-) rename docs/assets/schemas/{fastmcp_config => mcp_server_config}/latest.json (100%) rename docs/assets/schemas/{fastmcp_config => mcp_server_config}/v1.json (100%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-__init__.mdx => fastmcp-utilities-mcp_server_config-__init__.mdx} (84%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-v1-__init__.mdx => fastmcp-utilities-mcp_server_config-v1-__init__.mdx} (74%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-v1-fastmcp_config.mdx => fastmcp-utilities-mcp_server_config-v1-mcp_server_config.mdx} (65%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-v1-sources-__init__.mdx => fastmcp-utilities-mcp_server_config-v1-sources-__init__.mdx} (70%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-v1-sources-base.mdx => fastmcp-utilities-mcp_server_config-v1-sources-base.mdx} (63%) rename docs/python-sdk/{fastmcp-utilities-fastmcp_config-v1-sources-filesystem.mdx => fastmcp-utilities-mcp_server_config-v1-sources-filesystem.mdx} (51%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/__init__.py (56%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/v1/__init__.py (100%) rename src/fastmcp/utilities/{fastmcp_config/v1/fastmcp_config.py => mcp_server_config/v1/mcp_server_config.py} (98%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/v1/schema.json (100%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/v1/sources/__init__.py (100%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/v1/sources/base.py (100%) rename src/fastmcp/utilities/{fastmcp_config => mcp_server_config}/v1/sources/filesystem.py (99%) rename tests/cli/{test_fastmcp_config_integration.py => test_mcp_server_config_integration.py} (93%) rename tests/cli/{test_fastmcp_config_schema.py => test_mcp_server_config_schema.py} (96%) diff --git a/.github/workflows/update-config-schema.yml b/.github/workflows/update-config-schema.yml index 9b3fa71a6..eb4115dd7 100644 --- a/.github/workflows/update-config-schema.yml +++ b/.github/workflows/update-config-schema.yml @@ -1,4 +1,4 @@ -name: Update FastMCPConfig Schema +name: Update MCPServerConfig Schema # This workflow runs on merges to main to automatically update the config schema # by creating a PR when changes are needed. @@ -7,8 +7,8 @@ on: push: branches: ["main"] paths: - - "src/fastmcp/utilities/fastmcp_config/**" - - "!src/fastmcp/utilities/fastmcp_config/v1/schema.json" # Exclude the local schema file + - "src/fastmcp/utilities/mcp_server_config/**" + - "!src/fastmcp/utilities/mcp_server_config/v1/schema.json" # Exclude the local schema file workflow_dispatch: permissions: @@ -45,23 +45,23 @@ jobs: # Generate schema in docs/public for web access uv run python -c " - from fastmcp.utilities.fastmcp_config import generate_schema + from fastmcp.utilities.mcp_server_config import generate_schema generate_schema('docs/public/schemas/fastmcp.json/latest.json') print('✅ Latest schema generated in docs/public') " # Also update the v1 schema in docs/public uv run python -c " - from fastmcp.utilities.fastmcp_config import generate_schema + from fastmcp.utilities.mcp_server_config import generate_schema generate_schema('docs/public/schemas/fastmcp.json/v1.json') print('✅ v1 schema generated in docs/public') " # Generate schema in the source directory for local development uv run python -c " - from fastmcp.utilities.fastmcp_config import generate_schema - generate_schema('src/fastmcp/utilities/fastmcp_config/v1/schema.json') - print('✅ Schema generated in utilities/fastmcp_config/v1/') + from fastmcp.utilities.mcp_server_config import generate_schema + generate_schema('src/fastmcp/utilities/mcp_server_config/v1/schema.json') + print('✅ Schema generated in utilities/mcp_server_config/v1/') " - name: Create Pull Request @@ -73,7 +73,7 @@ jobs: body: | This PR updates the fastmcp.json schema files to match the current source code. - The schema is automatically generated from `src/fastmcp/utilities/fastmcp_config/` to ensure consistency. + The schema is automatically generated from `src/fastmcp/utilities/mcp_server_config/` to ensure consistency. **Note:** This PR is fully automated and will update itself with any subsequent changes to the schema, or close automatically if the schema becomes up-to-date through other means. Feel free to leave it open until you're ready to merge. diff --git a/docs/assets/schemas/fastmcp_config/latest.json b/docs/assets/schemas/mcp_server_config/latest.json similarity index 100% rename from docs/assets/schemas/fastmcp_config/latest.json rename to docs/assets/schemas/mcp_server_config/latest.json diff --git a/docs/assets/schemas/fastmcp_config/v1.json b/docs/assets/schemas/mcp_server_config/v1.json similarity index 100% rename from docs/assets/schemas/fastmcp_config/v1.json rename to docs/assets/schemas/mcp_server_config/v1.json diff --git a/docs/docs.json b/docs/docs.json index 9687185dc..3cd031270 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -380,33 +380,33 @@ "python-sdk/fastmcp-utilities-cli", "python-sdk/fastmcp-utilities-components", "python-sdk/fastmcp-utilities-exceptions", - { - "group": "fastmcp_config", - "pages": [ - "python-sdk/fastmcp-utilities-fastmcp_config-__init__", - { - "group": "v1", - "pages": [ - "python-sdk/fastmcp-utilities-fastmcp_config-v1-__init__", - "python-sdk/fastmcp-utilities-fastmcp_config-v1-fastmcp_config", - { - "group": "sources", - "pages": [ - "python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-__init__", - "python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-base", - "python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-filesystem" - ] - } - ] - } - ] - }, "python-sdk/fastmcp-utilities-http", "python-sdk/fastmcp-utilities-inspect", "python-sdk/fastmcp-utilities-json_schema", "python-sdk/fastmcp-utilities-json_schema_type", "python-sdk/fastmcp-utilities-logging", "python-sdk/fastmcp-utilities-mcp_config", + { + "group": "mcp_server_config", + "pages": [ + "python-sdk/fastmcp-utilities-mcp_server_config-__init__", + { + "group": "v1", + "pages": [ + "python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__", + "python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config", + { + "group": "sources", + "pages": [ + "python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__", + "python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base", + "python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem" + ] + } + ] + } + ] + }, "python-sdk/fastmcp-utilities-openapi", "python-sdk/fastmcp-utilities-tests", "python-sdk/fastmcp-utilities-types" diff --git a/docs/python-sdk/fastmcp-cli-cli.mdx b/docs/python-sdk/fastmcp-cli-cli.mdx index 60838c769..83677c671 100644 --- a/docs/python-sdk/fastmcp-cli-cli.mdx +++ b/docs/python-sdk/fastmcp-cli-cli.mdx @@ -10,7 +10,7 @@ FastMCP CLI tools using Cyclopts. ## Functions -### `with_argv` +### `with_argv` ```python with_argv(args: list[str] | None) @@ -27,7 +27,7 @@ Args are provided without the script name, so we preserve sys.argv[0] and replace the rest. -### `version` +### `version` ```python version() @@ -37,7 +37,7 @@ version() Display version information and platform details. -### `dev` +### `dev` ```python dev(server_spec: str | None = None) -> None @@ -50,7 +50,7 @@ Run an MCP server with the MCP Inspector for development. - `server_spec`: Python file to run, optionally with \:object suffix, or None to auto-detect fastmcp.json -### `run` +### `run` ```python run(server_spec: str | None = None, *server_args: str) -> None @@ -74,7 +74,7 @@ fastmcp run server.py -- --config config.json --debug - `server_spec`: Python file, object specification (file\:obj), config file, URL, or None to auto-detect -### `inspect` +### `inspect` ```python inspect(server_spec: str | None = None) -> None @@ -105,7 +105,7 @@ fastmcp inspect # auto-detect fastmcp.json - `server_spec`: Python file to inspect, optionally with \:object suffix, or fastmcp.json -### `prepare` +### `prepare` ```python prepare(config_path: Annotated[str | None, cyclopts.Parameter(help='Path to fastmcp.json configuration file')] = None, output_dir: Annotated[str | None, cyclopts.Parameter(help='Directory to create the persistent environment in')] = None, skip_source: Annotated[bool, cyclopts.Parameter(help='Skip source preparation (e.g., git clone)')] = False) -> None diff --git a/docs/python-sdk/fastmcp-cli-install-claude_code.mdx b/docs/python-sdk/fastmcp-cli-install-claude_code.mdx index fd1d888ff..3889da224 100644 --- a/docs/python-sdk/fastmcp-cli-install-claude_code.mdx +++ b/docs/python-sdk/fastmcp-cli-install-claude_code.mdx @@ -57,7 +57,7 @@ Install FastMCP server in Claude Code. - True if installation was successful, False otherwise -### `claude_code_command` +### `claude_code_command` ```python claude_code_command(server_spec: str) -> None diff --git a/docs/python-sdk/fastmcp-cli-install-claude_desktop.mdx b/docs/python-sdk/fastmcp-cli-install-claude_desktop.mdx index e31704d32..145de8ea8 100644 --- a/docs/python-sdk/fastmcp-cli-install-claude_desktop.mdx +++ b/docs/python-sdk/fastmcp-cli-install-claude_desktop.mdx @@ -44,7 +44,7 @@ Install FastMCP server in Claude Desktop. - True if installation was successful, False otherwise -### `claude_desktop_command` +### `claude_desktop_command` ```python claude_desktop_command(server_spec: str) -> None diff --git a/docs/python-sdk/fastmcp-cli-install-cursor.mdx b/docs/python-sdk/fastmcp-cli-install-cursor.mdx index 218575f86..6c9ada03c 100644 --- a/docs/python-sdk/fastmcp-cli-install-cursor.mdx +++ b/docs/python-sdk/fastmcp-cli-install-cursor.mdx @@ -68,7 +68,7 @@ Install FastMCP server to workspace-specific Cursor configuration. - True if installation was successful, False otherwise -### `install_cursor` +### `install_cursor` ```python install_cursor(file: Path, server_object: str | None, name: str) -> bool @@ -93,7 +93,7 @@ Install FastMCP server in Cursor. - True if installation was successful, False otherwise -### `cursor_command` +### `cursor_command` ```python cursor_command(server_spec: str) -> None diff --git a/docs/python-sdk/fastmcp-cli-install-mcp_json.mdx b/docs/python-sdk/fastmcp-cli-install-mcp_json.mdx index 0d218623e..e13343bd7 100644 --- a/docs/python-sdk/fastmcp-cli-install-mcp_json.mdx +++ b/docs/python-sdk/fastmcp-cli-install-mcp_json.mdx @@ -35,7 +35,7 @@ Generate MCP configuration JSON for manual installation. - True if generation was successful, False otherwise -### `mcp_json_command` +### `mcp_json_command` ```python mcp_json_command(server_spec: str) -> None diff --git a/docs/python-sdk/fastmcp-cli-run.mdx b/docs/python-sdk/fastmcp-cli-run.mdx index 9f352a1e1..f88d8f431 100644 --- a/docs/python-sdk/fastmcp-cli-run.mdx +++ b/docs/python-sdk/fastmcp-cli-run.mdx @@ -10,7 +10,7 @@ FastMCP run command implementation with enhanced type hints. ## Functions -### `is_url` +### `is_url` ```python is_url(path: str) -> bool @@ -20,7 +20,7 @@ is_url(path: str) -> bool Check if a string is a URL. -### `run_with_uv` +### `run_with_uv` ```python run_with_uv(server_spec: str, python_version: str | None = None, with_packages: list[str] | None = None, with_requirements: Path | None = None, project: Path | None = None, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, show_banner: bool = True, editable: str | list[str] | None = None) -> None @@ -29,6 +29,10 @@ run_with_uv(server_spec: str, python_version: str | None = None, with_packages: Run a MCP server using uv run subprocess. +This function is called when we need to set up a Python environment with specific +dependencies before running the server. The config parsing and merging should already +be done by the caller. + **Args:** - `server_spec`: Python file, object specification (file\:obj), config file, or URL - `python_version`: Python version to use (e.g. "3.10") @@ -41,9 +45,10 @@ Run a MCP server using uv run subprocess. - `path`: Path to bind to when using http transport - `log_level`: Log level - `show_banner`: Whether to show the server banner +- `editable`: Editable package paths -### `create_client_server` +### `create_client_server` ```python create_client_server(url: str) -> Any @@ -59,7 +64,7 @@ Create a FastMCP server from a client URL. - A FastMCP server instance -### `create_mcp_config_server` +### `create_mcp_config_server` ```python create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] @@ -69,10 +74,10 @@ create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None] Create a FastMCP server from a MCPConfig. -### `load_fastmcp_config` +### `load_mcp_server_config` ```python -load_fastmcp_config(config_path: Path) -> FastMCPConfig +load_mcp_server_config(config_path: Path) -> MCPServerConfig ``` @@ -82,10 +87,10 @@ Load a FastMCP configuration from a fastmcp.json file. - `config_path`: Path to fastmcp.json file **Returns:** -- FastMCPConfig object +- MCPServerConfig object -### `run_command` +### `run_command` ```python run_command(server_spec: str, transport: TransportType | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: LogLevelType | None = None, server_args: list[str] | None = None, show_banner: bool = True, use_direct_import: bool = False, skip_source: bool = False) -> None @@ -107,7 +112,7 @@ Run a MCP server or connect to a remote one. - `skip_source`: Whether to skip source preparation step -### `run_v1_server` +### `run_v1_server` ```python run_v1_server(server: FastMCP1x, host: str | None = None, port: int | None = None, transport: TransportType | None = None) -> None diff --git a/docs/python-sdk/fastmcp-client-auth-oauth.mdx b/docs/python-sdk/fastmcp-client-auth-oauth.mdx index 0b261f9b0..1bc7c1704 100644 --- a/docs/python-sdk/fastmcp-client-auth-oauth.mdx +++ b/docs/python-sdk/fastmcp-client-auth-oauth.mdx @@ -7,13 +7,13 @@ sidebarTitle: oauth ## Functions -### `default_cache_dir` +### `default_cache_dir` ```python default_cache_dir() -> Path ``` -### `check_if_auth_required` +### `check_if_auth_required` ```python check_if_auth_required(mcp_url: str, httpx_kwargs: dict[str, Any] | None = None) -> bool @@ -28,7 +28,13 @@ Check if the MCP endpoint requires authentication by making a test request. ## Classes -### `FileTokenStorage` +### `StoredToken` + + +Token storage format with absolute expiry time. + + +### `FileTokenStorage` File-based token storage implementation for OAuth credentials and tokens. @@ -39,7 +45,7 @@ Each instance is tied to a specific server URL for proper token isolation. **Methods:** -#### `get_base_url` +#### `get_base_url` ```python get_base_url(url: str) -> str @@ -48,7 +54,7 @@ get_base_url(url: str) -> str Extract the base URL (scheme + host) from a URL. -#### `get_cache_key` +#### `get_cache_key` ```python get_cache_key(self) -> str @@ -57,7 +63,7 @@ get_cache_key(self) -> str Generate a safe filesystem key from the server's base URL. -#### `get_tokens` +#### `get_tokens` ```python get_tokens(self) -> OAuthToken | None @@ -66,7 +72,7 @@ get_tokens(self) -> OAuthToken | None Load tokens from file storage. -#### `set_tokens` +#### `set_tokens` ```python set_tokens(self, tokens: OAuthToken) -> None @@ -75,7 +81,7 @@ set_tokens(self, tokens: OAuthToken) -> None Save tokens to file storage. -#### `get_client_info` +#### `get_client_info` ```python get_client_info(self) -> OAuthClientInformationFull | None @@ -84,7 +90,7 @@ get_client_info(self) -> OAuthClientInformationFull | None Load client information from file storage. -#### `set_client_info` +#### `set_client_info` ```python set_client_info(self, client_info: OAuthClientInformationFull) -> None @@ -93,7 +99,7 @@ set_client_info(self, client_info: OAuthClientInformationFull) -> None Save client information to file storage. -#### `clear` +#### `clear` ```python clear(self) -> None @@ -102,7 +108,7 @@ clear(self) -> None Clear all cached data for this server. -#### `clear_all` +#### `clear_all` ```python clear_all(cls, cache_dir: Path | None = None) -> None @@ -111,7 +117,7 @@ clear_all(cls, cache_dir: Path | None = None) -> None Clear all cached data for all servers. -### `OAuth` +### `OAuth` OAuth client provider for MCP servers with browser-based authentication. @@ -122,7 +128,7 @@ a browser for user authorization and running a local callback server. **Methods:** -#### `redirect_handler` +#### `redirect_handler` ```python redirect_handler(self, authorization_url: str) -> None @@ -131,7 +137,7 @@ redirect_handler(self, authorization_url: str) -> None Open browser for authorization. -#### `callback_handler` +#### `callback_handler` ```python callback_handler(self) -> tuple[str, str | None] diff --git a/docs/python-sdk/fastmcp-client-client.mdx b/docs/python-sdk/fastmcp-client-client.mdx index 406d0c329..cc92038eb 100644 --- a/docs/python-sdk/fastmcp-client-client.mdx +++ b/docs/python-sdk/fastmcp-client-client.mdx @@ -7,7 +7,7 @@ sidebarTitle: client ## Classes -### `ClientSessionState` +### `ClientSessionState` Holds all session-related state for a Client instance. @@ -16,7 +16,7 @@ This allows clean separation of configuration (which is copied) from session state (which should be fresh for each new client instance). -### `Client` +### `Client` MCP client that delegates connection management to a Transport instance. @@ -79,7 +79,7 @@ async with client: **Methods:** -#### `session` +#### `session` ```python session(self) -> ClientSession @@ -88,7 +88,7 @@ session(self) -> ClientSession Get the current active session. Raises RuntimeError if not connected. -#### `initialize_result` +#### `initialize_result` ```python initialize_result(self) -> mcp.types.InitializeResult @@ -97,7 +97,7 @@ initialize_result(self) -> mcp.types.InitializeResult Get the result of the initialization request. -#### `set_roots` +#### `set_roots` ```python set_roots(self, roots: RootsList | RootsHandler) -> None @@ -106,7 +106,7 @@ set_roots(self, roots: RootsList | RootsHandler) -> None Set the roots for the client. This does not automatically call `send_roots_list_changed`. -#### `set_sampling_callback` +#### `set_sampling_callback` ```python set_sampling_callback(self, sampling_callback: ClientSamplingHandler) -> None @@ -115,7 +115,7 @@ set_sampling_callback(self, sampling_callback: ClientSamplingHandler) -> None Set the sampling callback for the client. -#### `set_elicitation_callback` +#### `set_elicitation_callback` ```python set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None @@ -124,7 +124,7 @@ set_elicitation_callback(self, elicitation_callback: ElicitationHandler) -> None Set the elicitation callback for the client. -#### `is_connected` +#### `is_connected` ```python is_connected(self) -> bool @@ -133,7 +133,7 @@ is_connected(self) -> bool Check if the client is currently connected. -#### `new` +#### `new` ```python new(self) -> Client[ClientTransportT] @@ -149,13 +149,13 @@ share state with the original client. - A new Client instance with the same configuration but disconnected state. -#### `close` +#### `close` ```python close(self) ``` -#### `ping` +#### `ping` ```python ping(self) -> bool @@ -164,7 +164,7 @@ ping(self) -> bool Send a ping request. -#### `cancel` +#### `cancel` ```python cancel(self, request_id: str | int, reason: str | None = None) -> None @@ -173,7 +173,7 @@ cancel(self, request_id: str | int, reason: str | None = None) -> None Send a cancellation notification for an in-progress request. -#### `progress` +#### `progress` ```python progress(self, progress_token: str | int, progress: float, total: float | None = None, message: str | None = None) -> None @@ -182,7 +182,7 @@ progress(self, progress_token: str | int, progress: float, total: float | None = Send a progress notification. -#### `set_logging_level` +#### `set_logging_level` ```python set_logging_level(self, level: mcp.types.LoggingLevel) -> None @@ -191,7 +191,7 @@ set_logging_level(self, level: mcp.types.LoggingLevel) -> None Send a logging/setLevel request. -#### `send_roots_list_changed` +#### `send_roots_list_changed` ```python send_roots_list_changed(self) -> None @@ -200,7 +200,7 @@ send_roots_list_changed(self) -> None Send a roots/list_changed notification. -#### `list_resources_mcp` +#### `list_resources_mcp` ```python list_resources_mcp(self) -> mcp.types.ListResourcesResult @@ -216,7 +216,7 @@ containing the list of resources and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `list_resources` +#### `list_resources` ```python list_resources(self) -> list[mcp.types.Resource] @@ -231,7 +231,7 @@ Retrieve a list of resources available on the server. - `RuntimeError`: If called while the client is not connected. -#### `list_resource_templates_mcp` +#### `list_resource_templates_mcp` ```python list_resource_templates_mcp(self) -> mcp.types.ListResourceTemplatesResult @@ -247,7 +247,7 @@ containing the list of resource templates and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `list_resource_templates` +#### `list_resource_templates` ```python list_resource_templates(self) -> list[mcp.types.ResourceTemplate] @@ -262,7 +262,7 @@ Retrieve a list of resource templates available on the server. - `RuntimeError`: If called while the client is not connected. -#### `read_resource_mcp` +#### `read_resource_mcp` ```python read_resource_mcp(self, uri: AnyUrl | str) -> mcp.types.ReadResourceResult @@ -281,7 +281,7 @@ containing the resource contents and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `read_resource` +#### `read_resource` ```python read_resource(self, uri: AnyUrl | str) -> list[mcp.types.TextResourceContents | mcp.types.BlobResourceContents] @@ -300,7 +300,7 @@ objects, typically containing either text or binary data. - `RuntimeError`: If called while the client is not connected. -#### `list_prompts_mcp` +#### `list_prompts_mcp` ```python list_prompts_mcp(self) -> mcp.types.ListPromptsResult @@ -316,7 +316,7 @@ containing the list of prompts and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `list_prompts` +#### `list_prompts` ```python list_prompts(self) -> list[mcp.types.Prompt] @@ -331,7 +331,7 @@ Retrieve a list of prompts available on the server. - `RuntimeError`: If called while the client is not connected. -#### `get_prompt_mcp` +#### `get_prompt_mcp` ```python get_prompt_mcp(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult @@ -351,7 +351,7 @@ containing the prompt messages and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `get_prompt` +#### `get_prompt` ```python get_prompt(self, name: str, arguments: dict[str, Any] | None = None) -> mcp.types.GetPromptResult @@ -371,7 +371,7 @@ containing the prompt messages and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `complete_mcp` +#### `complete_mcp` ```python complete_mcp(self, ref: mcp.types.ResourceReference | mcp.types.PromptReference, argument: dict[str, str]) -> mcp.types.CompleteResult @@ -391,7 +391,7 @@ containing the completion and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `complete` +#### `complete` ```python complete(self, ref: mcp.types.ResourceReference | mcp.types.PromptReference, argument: dict[str, str]) -> mcp.types.Completion @@ -410,7 +410,7 @@ Send a completion request to the server. - `RuntimeError`: If called while the client is not connected. -#### `list_tools_mcp` +#### `list_tools_mcp` ```python list_tools_mcp(self) -> mcp.types.ListToolsResult @@ -426,7 +426,7 @@ containing the list of tools and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `list_tools` +#### `list_tools` ```python list_tools(self) -> list[mcp.types.Tool] @@ -441,7 +441,7 @@ Retrieve a list of tools available on the server. - `RuntimeError`: If called while the client is not connected. -#### `call_tool_mcp` +#### `call_tool_mcp` ```python call_tool_mcp(self, name: str, arguments: dict[str, Any], progress_handler: ProgressHandler | None = None, timeout: datetime.timedelta | float | int | None = None) -> mcp.types.CallToolResult @@ -466,7 +466,7 @@ containing the tool result and any additional metadata. - `RuntimeError`: If called while the client is not connected. -#### `call_tool` +#### `call_tool` ```python call_tool(self, name: str, arguments: dict[str, Any] | None = None, timeout: datetime.timedelta | float | int | None = None, progress_handler: ProgressHandler | None = None, raise_on_error: bool = True) -> CallToolResult @@ -496,10 +496,10 @@ raw result object. - `RuntimeError`: If called while the client is not connected. -#### `generate_name` +#### `generate_name` ```python generate_name(cls, name: str | None = None) -> str ``` -### `CallToolResult` +### `CallToolResult` diff --git a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx index b592bdc8c..163843467 100644 --- a/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx +++ b/docs/python-sdk/fastmcp-server-auth-oauth_proxy.mdx @@ -182,7 +182,7 @@ Handles provider-specific requirements: **Methods:** -#### `get_client` +#### `get_client` ```python get_client(self, client_id: str) -> OAuthClientInformationFull | None @@ -199,7 +199,7 @@ handles the case where a client with cached tokens reconnects on a different port. -#### `register_client` +#### `register_client` ```python register_client(self, client_info: OAuthClientInformationFull) -> None @@ -226,7 +226,7 @@ The flow: 4. When client reconnects with a different port, ProxyDCRClient accepts it -#### `authorize` +#### `authorize` ```python authorize(self, client: OAuthClientInformationFull, params: AuthorizationParams) -> str @@ -240,7 +240,7 @@ This implements the DCR-compliant proxy pattern: 3. Redirect to IdP with our fixed callback URL -#### `load_authorization_code` +#### `load_authorization_code` ```python load_authorization_code(self, client: OAuthClientInformationFull, authorization_code: str) -> AuthorizationCode | None @@ -252,7 +252,7 @@ Look up our client code and return authorization code object with PKCE challenge for validation. -#### `exchange_authorization_code` +#### `exchange_authorization_code` ```python exchange_authorization_code(self, client: OAuthClientInformationFull, authorization_code: AuthorizationCode) -> OAuthToken @@ -264,7 +264,7 @@ For the DCR-compliant proxy flow, we return the IdP tokens that were obtained during the IdP callback exchange. PKCE validation is handled by the MCP framework. -#### `load_refresh_token` +#### `load_refresh_token` ```python load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) -> RefreshToken | None @@ -273,7 +273,7 @@ load_refresh_token(self, client: OAuthClientInformationFull, refresh_token: str) Load refresh token from local storage. -#### `exchange_refresh_token` +#### `exchange_refresh_token` ```python exchange_refresh_token(self, client: OAuthClientInformationFull, refresh_token: RefreshToken, scopes: list[str]) -> OAuthToken @@ -282,7 +282,7 @@ exchange_refresh_token(self, client: OAuthClientInformationFull, refresh_token: Exchange refresh token for new access token using authlib. -#### `load_access_token` +#### `load_access_token` ```python load_access_token(self, token: str) -> AccessToken | None @@ -294,7 +294,7 @@ Delegates to the JWT verifier which handles signature validation, expiration checking, and claims validation using the upstream JWKS. -#### `revoke_token` +#### `revoke_token` ```python revoke_token(self, token: AccessToken | RefreshToken) -> None @@ -306,7 +306,7 @@ Removes tokens from local storage and attempts to revoke them with the upstream server if a revocation endpoint is configured. -#### `get_routes` +#### `get_routes` ```python get_routes(self) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx b/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx index 60781bf24..31d0145d2 100644 --- a/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx +++ b/docs/python-sdk/fastmcp-server-auth-providers-workos.mdx @@ -65,9 +65,9 @@ Setup Requirements: 4. Note your Client ID and Client Secret -### `AuthKitProviderSettings` +### `AuthKitProviderSettings` -### `AuthKitProvider` +### `AuthKitProvider` AuthKit metadata provider for DCR (Dynamic Client Registration). @@ -93,7 +93,7 @@ https://workos.com/docs/authkit/mcp/integrating/token-verification **Methods:** -#### `get_routes` +#### `get_routes` ```python get_routes(self) -> list[Route] diff --git a/docs/python-sdk/fastmcp-server-auth-redirect_validation.mdx b/docs/python-sdk/fastmcp-server-auth-redirect_validation.mdx index ba44efebe..c7aa26786 100644 --- a/docs/python-sdk/fastmcp-server-auth-redirect_validation.mdx +++ b/docs/python-sdk/fastmcp-server-auth-redirect_validation.mdx @@ -44,8 +44,9 @@ Validate a redirect URI against allowed patterns. **Args:** - `redirect_uri`: The redirect URI to validate -- `allowed_patterns`: List of allowed patterns. If None, defaults to localhost. - If empty list, all URIs are allowed. +- `allowed_patterns`: List of allowed patterns. If None, all URIs are allowed (for DCR compatibility). + If empty list, no URIs are allowed. + To restrict to localhost only, explicitly pass DEFAULT_LOCALHOST_PATTERNS. **Returns:** - True if the redirect URI is allowed diff --git a/docs/python-sdk/fastmcp-server-middleware-logging.mdx b/docs/python-sdk/fastmcp-server-middleware-logging.mdx index 9beb8300a..853725a01 100644 --- a/docs/python-sdk/fastmcp-server-middleware-logging.mdx +++ b/docs/python-sdk/fastmcp-server-middleware-logging.mdx @@ -8,9 +8,21 @@ sidebarTitle: logging Comprehensive logging middleware for FastMCP servers. +## Functions + +### `default_serializer` + +```python +default_serializer(data: Any) -> str +``` + + +The default serializer for Payloads in the logging middleware. + + ## Classes -### `LoggingMiddleware` +### `LoggingMiddleware` Middleware that provides comprehensive request and response logging. @@ -21,16 +33,16 @@ monitoring, and understanding server usage patterns. **Methods:** -#### `on_message` +#### `on_message` ```python -on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any +on_message(self, context: MiddlewareContext[Any], call_next: CallNext[Any, Any]) -> Any ``` Log all messages. -### `StructuredLoggingMiddleware` +### `StructuredLoggingMiddleware` Middleware that provides structured JSON logging for better log analysis. @@ -41,10 +53,10 @@ aggregation tools like ELK stack, Splunk, or cloud logging services. **Methods:** -#### `on_message` +#### `on_message` ```python -on_message(self, context: MiddlewareContext, call_next: CallNext) -> Any +on_message(self, context: MiddlewareContext[Any], call_next: CallNext[Any, Any]) -> Any ``` Log structured message information. diff --git a/docs/python-sdk/fastmcp-utilities-cli.mdx b/docs/python-sdk/fastmcp-utilities-cli.mdx index 6d49ca6e0..eb4691358 100644 --- a/docs/python-sdk/fastmcp-utilities-cli.mdx +++ b/docs/python-sdk/fastmcp-utilities-cli.mdx @@ -7,7 +7,37 @@ sidebarTitle: cli ## Functions -### `log_server_banner` +### `is_already_in_uv_subprocess` + +```python +is_already_in_uv_subprocess() -> bool +``` + + +Check if we're already running in a FastMCP uv subprocess. + + +### `load_and_merge_config` + +```python +load_and_merge_config(server_spec: str | None, **cli_overrides) -> tuple[MCPServerConfig, str] +``` + + +Load config from server_spec and apply CLI overrides. + +This consolidates the config parsing logic that was duplicated across +run, inspect, and dev commands. + +**Args:** +- `server_spec`: Python file, config file, URL, or None to auto-detect +- `cli_overrides`: CLI arguments that override config values + +**Returns:** +- Tuple of (MCPServerConfig, resolved_server_spec) + + +### `log_server_banner` ```python log_server_banner(server: FastMCP[Any], transport: Literal['stdio', 'http', 'sse', 'streamable-http']) -> None diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-__init__.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-__init__.mdx similarity index 84% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-__init__.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-__init__.mdx index f804aa19f..bc8fc9500 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-__init__.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-__init__.mdx @@ -3,7 +3,7 @@ title: __init__ sidebarTitle: __init__ --- -# `fastmcp.utilities.fastmcp_config` +# `fastmcp.utilities.mcp_server_config` FastMCP Configuration module. diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-__init__.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__.mdx similarity index 74% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-__init__.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__.mdx index 4195f2dc2..d29664280 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-__init__.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-__init__.mdx @@ -3,6 +3,6 @@ title: __init__ sidebarTitle: __init__ --- -# `fastmcp.utilities.fastmcp_config.v1` +# `fastmcp.utilities.mcp_server_config.v1` *This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-fastmcp_config.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config.mdx similarity index 65% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-fastmcp_config.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config.mdx index d66382bc1..fe7815a9b 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-fastmcp_config.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-mcp_server_config.mdx @@ -1,9 +1,9 @@ --- -title: fastmcp_config -sidebarTitle: fastmcp_config +title: mcp_server_config +sidebarTitle: mcp_server_config --- -# `fastmcp.utilities.fastmcp_config.v1.fastmcp_config` +# `fastmcp.utilities.mcp_server_config.v1.mcp_server_config` FastMCP Configuration File Support. @@ -15,7 +15,7 @@ command-line arguments. ## Functions -### `generate_schema` +### `generate_schema` ```python generate_schema(output_path: Path | str | None = None) -> dict[str, Any] | None @@ -38,7 +38,7 @@ validation and auto-completion. ## Classes -### `Environment` +### `Environment` Configuration for Python environment setup. @@ -46,22 +46,22 @@ Configuration for Python environment setup. **Methods:** -#### `build_uv_args` +#### `build_uv_run_command` ```python -build_uv_args(self, command: str | list[str] | None = None) -> list[str] +build_uv_run_command(self, command: list[str]) -> list[str] ``` -Build uv run arguments from this environment configuration. +Build complete uv run command with environment args and command to execute. **Args:** -- `command`: Optional command to append (string or list of args) +- `command`: Command to execute (e.g., ["fastmcp", "run", "server.py"]) **Returns:** -- List of arguments for uv run command +- Complete command ready for subprocess.run, including "uv" prefix -#### `run_with_uv` +#### `run_with_uv` ```python run_with_uv(self, command: list[str]) -> None @@ -73,7 +73,7 @@ Execute a command using uv run with this environment configuration. - `command`: Command and arguments to execute (e.g., ["fastmcp", "run", "server.py"]) -#### `needs_uv` +#### `needs_uv` ```python needs_uv(self) -> bool @@ -85,7 +85,7 @@ Check if this environment config requires uv to set up. - True if any environment settings require uv run -#### `prepare` +#### `prepare` ```python prepare(self, output_dir: Path | None = None) -> None @@ -98,7 +98,7 @@ Prepare the Python environment using uv. If None, creates a temporary directory for ephemeral use. -### `Deployment` +### `Deployment` Configuration for server deployment and runtime settings. @@ -106,7 +106,7 @@ Configuration for server deployment and runtime settings. **Methods:** -#### `apply_runtime_settings` +#### `apply_runtime_settings` ```python apply_runtime_settings(self, config_path: Path | None = None) -> None @@ -122,7 +122,7 @@ For example: "API_URL": "https://api.${ENVIRONMENT}.example.com" will substitute the value of the ENVIRONMENT variable at runtime. -### `FastMCPConfig` +### `MCPServerConfig` Configuration for a FastMCP server. @@ -133,7 +133,7 @@ a FastMCP server in a declarative format. **Methods:** -#### `validate_source` +#### `validate_source` ```python validate_source(cls, v: dict | FileSystemSource) -> FileSystemSource @@ -146,10 +146,10 @@ Supports: - FileSystemSource instance (passed through) No string parsing happens here - that's only at CLI boundaries. -FastMCPConfig works only with properly typed objects. +MCPServerConfig works only with properly typed objects. -#### `validate_environment` +#### `validate_environment` ```python validate_environment(cls, v: dict | Environment) -> Environment @@ -162,7 +162,7 @@ Accepts: - dict that can be converted to Environment -#### `validate_deployment` +#### `validate_deployment` ```python validate_deployment(cls, v: dict | Deployment) -> Deployment @@ -175,10 +175,10 @@ Accepts: - dict that can be converted to Deployment -#### `from_file` +#### `from_file` ```python -from_file(cls, file_path: Path) -> FastMCPConfig +from_file(cls, file_path: Path) -> MCPServerConfig ``` Load configuration from a JSON file. @@ -187,7 +187,7 @@ Load configuration from a JSON file. - `file_path`: Path to the configuration file **Returns:** -- FastMCPConfig instance +- MCPServerConfig instance **Raises:** - `FileNotFoundError`: If the file doesn't exist @@ -195,10 +195,10 @@ Load configuration from a JSON file. - `pydantic.ValidationError`: If the configuration is invalid -#### `from_cli_args` +#### `from_cli_args` ```python -from_cli_args(cls, source: FileSystemSource, transport: Literal['stdio', 'http', 'sse', 'streamable-http'] | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | None = None, python: str | None = None, dependencies: list[str] | None = None, requirements: str | None = None, project: str | None = None, editable: str | None = None, env: dict[str, str] | None = None, cwd: str | None = None, args: list[str] | None = None) -> FastMCPConfig +from_cli_args(cls, source: FileSystemSource, transport: Literal['stdio', 'http', 'sse', 'streamable-http'] | None = None, host: str | None = None, port: int | None = None, path: str | None = None, log_level: Literal['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'] | None = None, python: str | None = None, dependencies: list[str] | None = None, requirements: str | None = None, project: str | None = None, editable: str | None = None, env: dict[str, str] | None = None, cwd: str | None = None, args: list[str] | None = None) -> MCPServerConfig ``` Create a config from CLI arguments. @@ -223,10 +223,10 @@ goes through a config object. - `args`: Server arguments **Returns:** -- FastMCPConfig instance +- MCPServerConfig instance -#### `find_config` +#### `find_config` ```python find_config(cls, start_path: Path | None = None) -> Path | None @@ -241,7 +241,7 @@ Find a fastmcp.json file in the specified directory. - Path to the configuration file, or None if not found -#### `prepare` +#### `prepare` ```python prepare(self, skip_source: bool = False, output_dir: Path | None = None) -> None @@ -257,7 +257,7 @@ When output_dir is None, does ephemeral caching (for backwards compatibility). - `output_dir`: Directory to create the persistent uv project in (optional) -#### `prepare_environment` +#### `prepare_environment` ```python prepare_environment(self, output_dir: Path | None = None) -> None @@ -272,7 +272,7 @@ Prepare the Python environment. Delegates to the environment's prepare() method -#### `prepare_source` +#### `prepare_source` ```python prepare_source(self) -> None @@ -283,7 +283,7 @@ Prepare the source for loading. Delegates to the source's prepare() method. -#### `run_server` +#### `run_server` ```python run_server(self, **kwargs: Any) -> None diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-__init__.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__.mdx similarity index 70% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-__init__.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__.mdx index c7259c5a1..38d102b15 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-__init__.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-__init__.mdx @@ -3,6 +3,6 @@ title: __init__ sidebarTitle: __init__ --- -# `fastmcp.utilities.fastmcp_config.v1.sources` +# `fastmcp.utilities.mcp_server_config.v1.sources` *This module is empty or contains only private/internal implementations.* diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-base.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base.mdx similarity index 63% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-base.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base.mdx index 15d2876f7..bf80ca1d6 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-base.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-base.mdx @@ -3,11 +3,11 @@ title: base sidebarTitle: base --- -# `fastmcp.utilities.fastmcp_config.v1.sources.base` +# `fastmcp.utilities.mcp_server_config.v1.sources.base` ## Classes -### `BaseSource` +### `BaseSource` Abstract base class for all source types. @@ -15,7 +15,7 @@ Abstract base class for all source types. **Methods:** -#### `prepare` +#### `prepare` ```python prepare(self) -> None @@ -28,7 +28,7 @@ this method performs that preparation. For sources that don't need preparation (e.g., local files), this is a no-op. -#### `load_server` +#### `load_server` ```python load_server(self) -> Any diff --git a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-filesystem.mdx b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem.mdx similarity index 51% rename from docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-filesystem.mdx rename to docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem.mdx index 68f3bd6fb..9ff83063b 100644 --- a/docs/python-sdk/fastmcp-utilities-fastmcp_config-v1-sources-filesystem.mdx +++ b/docs/python-sdk/fastmcp-utilities-mcp_server_config-v1-sources-filesystem.mdx @@ -3,11 +3,11 @@ title: filesystem sidebarTitle: filesystem --- -# `fastmcp.utilities.fastmcp_config.v1.sources.filesystem` +# `fastmcp.utilities.mcp_server_config.v1.sources.filesystem` ## Classes -### `FileSystemSource` +### `FileSystemSource` Source for local Python files. @@ -15,7 +15,7 @@ Source for local Python files. **Methods:** -#### `parse_path_with_object` +#### `parse_path_with_object` ```python parse_path_with_object(cls, v: str) -> str @@ -27,7 +27,7 @@ This validator runs before the model is created, allowing us to handle the "file.py:object" syntax at the model boundary. -#### `load_server` +#### `load_server` ```python load_server(self) -> Any diff --git a/examples/fastmcp_config_demo/README.md b/examples/fastmcp_config_demo/README.md index 23c17975b..7abf11c3d 100644 --- a/examples/fastmcp_config_demo/README.md +++ b/examples/fastmcp_config_demo/README.md @@ -26,14 +26,14 @@ With the configuration file in place, you can run the server in several ways: ```bash # Auto-detect fastmcp.json in current directory -cd examples/fastmcp_config_demo +cd examples/mcp_server_config_demo fastmcp run # Or specify the config file explicitly -fastmcp run examples/fastmcp_config_demo/fastmcp.json +fastmcp run examples/mcp_server_config_demo/fastmcp.json # Or use development mode with the Inspector UI -fastmcp dev examples/fastmcp_config_demo/fastmcp.json +fastmcp dev examples/mcp_server_config_demo/fastmcp.json ``` ## Benefits diff --git a/src/fastmcp/cli/claude.py b/src/fastmcp/cli/claude.py index af42da058..5004ccebd 100644 --- a/src/fastmcp/cli/claude.py +++ b/src/fastmcp/cli/claude.py @@ -6,8 +6,8 @@ import sys from pathlib import Path from typing import Any -from fastmcp.utilities.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment logger = get_logger(__name__) diff --git a/src/fastmcp/cli/cli.py b/src/fastmcp/cli/cli.py index 09de389db..dd7e65a01 100644 --- a/src/fastmcp/cli/cli.py +++ b/src/fastmcp/cli/cli.py @@ -20,13 +20,13 @@ import fastmcp from fastmcp.cli import run as run_module from fastmcp.cli.install import install_app from fastmcp.server.server import FastMCP -from fastmcp.utilities.fastmcp_config import Environment, FastMCPConfig from fastmcp.utilities.inspect import ( InspectFormat, format_info, inspect_fastmcp, ) from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment, MCPServerConfig logger = get_logger("cli") console = Console() @@ -796,7 +796,7 @@ async def prepare( # Auto-detect fastmcp.json if not provided if config_path is None: - found_config = FastMCPConfig.find_config() + found_config = MCPServerConfig.find_config() if found_config: config_path = str(found_config) logger.info(f"Using configuration from {config_path}") @@ -816,7 +816,7 @@ async def prepare( try: # Load the configuration - config = FastMCPConfig.from_file(config_file) + config = MCPServerConfig.from_file(config_file) # Prepare environment and source await config.prepare( diff --git a/src/fastmcp/cli/install/claude_code.py b/src/fastmcp/cli/install/claude_code.py index 0117f67b4..7c04c02e2 100644 --- a/src/fastmcp/cli/install/claude_code.py +++ b/src/fastmcp/cli/install/claude_code.py @@ -9,8 +9,8 @@ from typing import Annotated import cyclopts from rich import print -from fastmcp.utilities.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment from .shared import process_common_args diff --git a/src/fastmcp/cli/install/claude_desktop.py b/src/fastmcp/cli/install/claude_desktop.py index d4184f95d..df93a8856 100644 --- a/src/fastmcp/cli/install/claude_desktop.py +++ b/src/fastmcp/cli/install/claude_desktop.py @@ -9,8 +9,8 @@ import cyclopts from rich import print from fastmcp.mcp_config import StdioMCPServer, update_config_file -from fastmcp.utilities.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment from .shared import process_common_args diff --git a/src/fastmcp/cli/install/cursor.py b/src/fastmcp/cli/install/cursor.py index f10192e16..650ef57ff 100644 --- a/src/fastmcp/cli/install/cursor.py +++ b/src/fastmcp/cli/install/cursor.py @@ -10,8 +10,8 @@ import cyclopts from rich import print from fastmcp.mcp_config import StdioMCPServer, update_config_file -from fastmcp.utilities.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment from .shared import process_common_args diff --git a/src/fastmcp/cli/install/mcp_json.py b/src/fastmcp/cli/install/mcp_json.py index 5ea466590..7fab1e067 100644 --- a/src/fastmcp/cli/install/mcp_json.py +++ b/src/fastmcp/cli/install/mcp_json.py @@ -9,8 +9,8 @@ import cyclopts import pyperclip from rich import print -from fastmcp.utilities.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import Environment from .shared import process_common_args diff --git a/src/fastmcp/cli/install/shared.py b/src/fastmcp/cli/install/shared.py index 54b61977d..3544a2b6b 100644 --- a/src/fastmcp/cli/install/shared.py +++ b/src/fastmcp/cli/install/shared.py @@ -8,9 +8,9 @@ from dotenv import dotenv_values from pydantic import ValidationError from rich import print -from fastmcp.utilities.fastmcp_config import FastMCPConfig -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import MCPServerConfig +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource logger = get_logger(__name__) @@ -40,7 +40,7 @@ async def process_common_args( # Convert None to empty lists for list parameters with_packages = with_packages or [] env_vars = env_vars or [] - # Create FastMCPConfig from server_spec + # Create MCPServerConfig from server_spec config = None if server_spec.endswith(".json"): config_path = Path(server_spec).resolve() @@ -58,8 +58,8 @@ async def process_common_args( print("[red]MCPConfig files are not supported for installation[/red]") sys.exit(1) else: - # It's a FastMCPConfig - config = FastMCPConfig.from_file(config_path) + # It's a MCPServerConfig + config = MCPServerConfig.from_file(config_path) # Merge packages from config if not overridden if config.environment.dependencies: @@ -72,7 +72,7 @@ async def process_common_args( else: # Create config from file path source = FileSystemSource(path=server_spec) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) # Extract file and server_object from the source # The FileSystemSource handles parsing path:object syntax diff --git a/src/fastmcp/cli/run.py b/src/fastmcp/cli/run.py index 2b812e625..c01ea0085 100644 --- a/src/fastmcp/cli/run.py +++ b/src/fastmcp/cli/run.py @@ -11,12 +11,12 @@ from typing import Any, Literal from mcp.server.fastmcp import FastMCP as FastMCP1x from fastmcp.server.server import FastMCP -from fastmcp.utilities.fastmcp_config import ( - Environment, - FastMCPConfig, -) -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import ( + Environment, + MCPServerConfig, +) +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource logger = get_logger("cli.run") @@ -143,16 +143,16 @@ def create_mcp_config_server(mcp_config_path: Path) -> FastMCP[None]: return server -def load_fastmcp_config(config_path: Path) -> FastMCPConfig: +def load_mcp_server_config(config_path: Path) -> MCPServerConfig: """Load a FastMCP configuration from a fastmcp.json file. Args: config_path: Path to fastmcp.json file Returns: - FastMCPConfig object + MCPServerConfig object """ - config = FastMCPConfig.from_file(config_path) + config = MCPServerConfig.from_file(config_path) # Apply runtime settings from deployment config config.deployment.apply_runtime_settings(config_path) @@ -204,7 +204,7 @@ async def run_command( server = create_mcp_config_server(config_path) else: # It's a FastMCP config - load it properly - config = load_fastmcp_config(config_path) + config = load_mcp_server_config(config_path) # Merge deployment config with CLI arguments (CLI takes precedence) transport = transport or config.deployment.transport @@ -232,9 +232,9 @@ async def run_command( logger.debug(f'Found server "{server.name}" from config {config_path}') else: - # Regular file case - create a FastMCPConfig with FileSystemSource + # Regular file case - create a MCPServerConfig with FileSystemSource source = FileSystemSource(path=server_spec) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) # Prepare source only (environment is handled by uv run) await config.prepare_source() if not skip_source else None diff --git a/src/fastmcp/client/transports.py b/src/fastmcp/client/transports.py index ec94a1f17..c2b4c596f 100644 --- a/src/fastmcp/client/transports.py +++ b/src/fastmcp/client/transports.py @@ -36,8 +36,8 @@ from fastmcp.client.auth.oauth import OAuth from fastmcp.mcp_config import MCPConfig, infer_transport_type_from_url from fastmcp.server.dependencies import get_http_headers from fastmcp.server.server import FastMCP -from fastmcp.utilities.fastmcp_config.v1.fastmcp_config import Environment from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config.v1.mcp_server_config import Environment logger = get_logger(__name__) diff --git a/src/fastmcp/utilities/cli.py b/src/fastmcp/utilities/cli.py index 55081a5ad..2ae540d89 100644 --- a/src/fastmcp/utilities/cli.py +++ b/src/fastmcp/utilities/cli.py @@ -14,9 +14,9 @@ from rich.table import Table from rich.text import Text import fastmcp -from fastmcp.utilities.fastmcp_config import FastMCPConfig -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config import MCPServerConfig +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.types import get_cached_typeadapter if TYPE_CHECKING: @@ -33,7 +33,7 @@ def is_already_in_uv_subprocess() -> bool: def load_and_merge_config( server_spec: str | None, **cli_overrides, -) -> tuple[FastMCPConfig, str]: +) -> tuple[MCPServerConfig, str]: """Load config from server_spec and apply CLI overrides. This consolidates the config parsing logic that was duplicated across @@ -44,7 +44,7 @@ def load_and_merge_config( cli_overrides: CLI arguments that override config values Returns: - Tuple of (FastMCPConfig, resolved_server_spec) + Tuple of (MCPServerConfig, resolved_server_spec) """ config = None config_path = None @@ -53,7 +53,7 @@ def load_and_merge_config( if server_spec is None: config_path = Path("fastmcp.json") if not config_path.exists(): - found_config = FastMCPConfig.find_config() + found_config = MCPServerConfig.find_config() if found_config: config_path = found_config else: @@ -81,9 +81,9 @@ def load_and_merge_config( # MCPConfig - we don't process these here, just pass through pass else: - # Try to parse as FastMCPConfig + # Try to parse as MCPServerConfig try: - adapter = get_cached_typeadapter(FastMCPConfig) + adapter = get_cached_typeadapter(MCPServerConfig) config = adapter.validate_python(data) # Apply deployment settings @@ -91,7 +91,7 @@ def load_and_merge_config( config.deployment.apply_runtime_settings(config_path) except ValidationError: - # Not a valid FastMCPConfig, just pass through + # Not a valid MCPServerConfig, just pass through pass except (json.JSONDecodeError, FileNotFoundError): # Not a valid JSON file, just pass through @@ -100,7 +100,7 @@ def load_and_merge_config( # If we don't have a config object yet, create one from filesystem source if config is None: source = FileSystemSource(path=resolved_spec) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) # Convert to dict for immutable transformation config_dict = config.model_dump() @@ -134,7 +134,7 @@ def load_and_merge_config( config_dict["deployment"]["args"] = server_args_override # Create new config from modified dict - new_config = FastMCPConfig(**config_dict) + new_config = MCPServerConfig(**config_dict) return new_config, resolved_spec diff --git a/src/fastmcp/utilities/fastmcp_config/__init__.py b/src/fastmcp/utilities/mcp_server_config/__init__.py similarity index 56% rename from src/fastmcp/utilities/fastmcp_config/__init__.py rename to src/fastmcp/utilities/mcp_server_config/__init__.py index 28236fe42..363aa3b30 100644 --- a/src/fastmcp/utilities/fastmcp_config/__init__.py +++ b/src/fastmcp/utilities/mcp_server_config/__init__.py @@ -4,20 +4,20 @@ This module provides versioned configuration support for FastMCP servers. The current version is v1, which is re-exported here for convenience. """ -from fastmcp.utilities.fastmcp_config.v1.fastmcp_config import ( +from fastmcp.utilities.mcp_server_config.v1.mcp_server_config import ( Deployment, Environment, - FastMCPConfig, + MCPServerConfig, generate_schema, ) -from fastmcp.utilities.fastmcp_config.v1.sources.base import BaseSource -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config.v1.sources.base import BaseSource +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource __all__ = [ "BaseSource", "Deployment", "Environment", - "FastMCPConfig", + "MCPServerConfig", "FileSystemSource", "generate_schema", ] diff --git a/src/fastmcp/utilities/fastmcp_config/v1/__init__.py b/src/fastmcp/utilities/mcp_server_config/v1/__init__.py similarity index 100% rename from src/fastmcp/utilities/fastmcp_config/v1/__init__.py rename to src/fastmcp/utilities/mcp_server_config/v1/__init__.py diff --git a/src/fastmcp/utilities/fastmcp_config/v1/fastmcp_config.py b/src/fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py similarity index 98% rename from src/fastmcp/utilities/fastmcp_config/v1/fastmcp_config.py rename to src/fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py index 01cd1b846..ca50470e7 100644 --- a/src/fastmcp/utilities/fastmcp_config/v1/fastmcp_config.py +++ b/src/fastmcp/utilities/mcp_server_config/v1/mcp_server_config.py @@ -17,8 +17,8 @@ from typing import TYPE_CHECKING, Any, Literal, overload from pydantic import BaseModel, Field, field_validator -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource logger = get_logger("cli.config") @@ -406,7 +406,7 @@ class Deployment(BaseModel): return re.sub(r"\$\{([^}]+)\}", replace_var, value) -class FastMCPConfig(BaseModel): +class MCPServerConfig(BaseModel): """Configuration for a FastMCP server. This configuration file allows you to specify all settings needed to run @@ -463,7 +463,7 @@ class FastMCPConfig(BaseModel): - FileSystemSource instance (passed through) No string parsing happens here - that's only at CLI boundaries. - FastMCPConfig works only with properly typed objects. + MCPServerConfig works only with properly typed objects. """ if isinstance(v, FileSystemSource): # Already a FileSystemSource instance, return as-is @@ -510,14 +510,14 @@ class FastMCPConfig(BaseModel): raise ValueError("deployment must be a dict, Deployment instance") @classmethod - def from_file(cls, file_path: Path) -> FastMCPConfig: + def from_file(cls, file_path: Path) -> MCPServerConfig: """Load configuration from a JSON file. Args: file_path: Path to the configuration file Returns: - FastMCPConfig instance + MCPServerConfig instance Raises: FileNotFoundError: If the file doesn't exist @@ -550,7 +550,7 @@ class FastMCPConfig(BaseModel): env: dict[str, str] | None = None, cwd: str | None = None, args: list[str] | None = None, - ) -> FastMCPConfig: + ) -> MCPServerConfig: """Create a config from CLI arguments. This allows us to have a single code path where everything @@ -573,7 +573,7 @@ class FastMCPConfig(BaseModel): args: Server arguments Returns: - FastMCPConfig instance + MCPServerConfig instance """ # Build environment config if any env args provided environment = None @@ -716,7 +716,7 @@ def generate_schema(output_path: Path | str | None = None) -> dict[str, Any] | N Returns: JSON schema as a dictionary if output_path is None, otherwise None """ - schema = FastMCPConfig.model_json_schema() + schema = MCPServerConfig.model_json_schema() # Add some metadata schema["$id"] = FASTMCP_JSON_SCHEMA diff --git a/src/fastmcp/utilities/fastmcp_config/v1/schema.json b/src/fastmcp/utilities/mcp_server_config/v1/schema.json similarity index 100% rename from src/fastmcp/utilities/fastmcp_config/v1/schema.json rename to src/fastmcp/utilities/mcp_server_config/v1/schema.json diff --git a/src/fastmcp/utilities/fastmcp_config/v1/sources/__init__.py b/src/fastmcp/utilities/mcp_server_config/v1/sources/__init__.py similarity index 100% rename from src/fastmcp/utilities/fastmcp_config/v1/sources/__init__.py rename to src/fastmcp/utilities/mcp_server_config/v1/sources/__init__.py diff --git a/src/fastmcp/utilities/fastmcp_config/v1/sources/base.py b/src/fastmcp/utilities/mcp_server_config/v1/sources/base.py similarity index 100% rename from src/fastmcp/utilities/fastmcp_config/v1/sources/base.py rename to src/fastmcp/utilities/mcp_server_config/v1/sources/base.py diff --git a/src/fastmcp/utilities/fastmcp_config/v1/sources/filesystem.py b/src/fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py similarity index 99% rename from src/fastmcp/utilities/fastmcp_config/v1/sources/filesystem.py rename to src/fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py index 92fb7ae3e..29c2aeede 100644 --- a/src/fastmcp/utilities/fastmcp_config/v1/sources/filesystem.py +++ b/src/fastmcp/utilities/mcp_server_config/v1/sources/filesystem.py @@ -6,8 +6,8 @@ from typing import Any, Literal from pydantic import Field, field_validator -from fastmcp.utilities.fastmcp_config.v1.sources.base import BaseSource from fastmcp.utilities.logging import get_logger +from fastmcp.utilities.mcp_server_config.v1.sources.base import BaseSource logger = get_logger(__name__) diff --git a/tests/cli/test_cli.py b/tests/cli/test_cli.py index 1d0d078a6..0d8b57c08 100644 --- a/tests/cli/test_cli.py +++ b/tests/cli/test_cli.py @@ -458,7 +458,7 @@ class TestWindowsSpecific: """Test parsing Windows paths with drive letters and colons.""" from pathlib import Path - from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import ( + from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import ( FileSystemSource, ) diff --git a/tests/cli/test_config.py b/tests/cli/test_config.py index ec8b413c1..6d187f934 100644 --- a/tests/cli/test_config.py +++ b/tests/cli/test_config.py @@ -7,12 +7,12 @@ from pathlib import Path import pytest from pydantic import ValidationError -from fastmcp.utilities.fastmcp_config import ( +from fastmcp.utilities.mcp_server_config import ( Deployment, Environment, - FastMCPConfig, + MCPServerConfig, ) -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource class TestFileSystemSource: @@ -20,7 +20,7 @@ class TestFileSystemSource: def test_dict_source_minimal(self): """Test that dict source is converted to FileSystemSource.""" - config = FastMCPConfig(source={"path": "server.py"}) + config = MCPServerConfig(source={"path": "server.py"}) # Dict is converted to FileSystemSource assert isinstance(config.source, FileSystemSource) assert config.source.path == "server.py" @@ -29,7 +29,7 @@ class TestFileSystemSource: def test_dict_source_with_entrypoint(self): """Test dict source with entrypoint field.""" - config = FastMCPConfig(source={"path": "server.py", "entrypoint": "app"}) + config = MCPServerConfig(source={"path": "server.py", "entrypoint": "app"}) # Dict with entrypoint is converted to FileSystemSource assert isinstance(config.source, FileSystemSource) assert config.source.path == "server.py" @@ -38,7 +38,7 @@ class TestFileSystemSource: def test_filesystem_source_entrypoint(self): """Test FileSystemSource entrypoint format.""" - config = FastMCPConfig( + config = MCPServerConfig( source=FileSystemSource(path="src/server.py", entrypoint="mcp") ) assert isinstance(config.source, FileSystemSource) @@ -52,7 +52,7 @@ class TestEnvironment: def test_environment_config_fields(self): """Test all Environment fields.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={ "python": "3.12", @@ -73,28 +73,28 @@ class TestEnvironment: def test_needs_uv(self): """Test needs_uv() method.""" # No environment config - doesn't need UV - config = FastMCPConfig(source={"path": "server.py"}) + config = MCPServerConfig(source={"path": "server.py"}) assert not config.environment.needs_uv() # Empty environment - doesn't need UV - config = FastMCPConfig(source={"path": "server.py"}, environment={}) + config = MCPServerConfig(source={"path": "server.py"}, environment={}) assert not config.environment.needs_uv() # With dependencies - needs UV - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"dependencies": ["requests"]} ) assert config.environment.needs_uv() # With Python version - needs UV - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"python": "3.12"} ) assert config.environment.needs_uv() def test_build_uv_run_command(self): """Test build_uv_run_command() method.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={ "python": "3.12", @@ -125,7 +125,7 @@ class TestEnvironment: def test_run_with_uv(self): """Test run_with_uv() subprocess execution.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"dependencies": ["requests"]} ) @@ -144,7 +144,7 @@ class TestDeployment: def test_deployment_config_fields(self): """Test all Deployment fields.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={ "transport": "http", @@ -176,7 +176,7 @@ class TestDeployment: work_dir = tmp_path / "work" work_dir.mkdir() - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={ "env": {"TEST_VAR": "test_value"}, @@ -212,7 +212,7 @@ class TestDeployment: os.environ["BASE_URL"] = "example.com" os.environ["ENV_NAME"] = "production" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={ "env": { @@ -253,12 +253,12 @@ class TestDeployment: os.environ[key] = value -class TestFastMCPConfig: - """Test FastMCPConfig root configuration.""" +class TestMCPServerConfig: + """Test MCPServerConfig root configuration.""" def test_minimal_config(self): """Test creating a config with only required fields.""" - config = FastMCPConfig(source={"path": "server.py"}) + config = MCPServerConfig(source={"path": "server.py"}) assert isinstance(config.source, FileSystemSource) assert config.source.path == "server.py" assert config.source.entrypoint is None @@ -274,7 +274,7 @@ class TestFastMCPConfig: def test_nested_structure(self): """Test the nested configuration structure.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={ "python": "3.12", @@ -304,7 +304,7 @@ class TestFastMCPConfig: config_file = tmp_path / "fastmcp.json" config_file.write_text(json.dumps(config_data)) - config = FastMCPConfig.from_file(config_file) + config = MCPServerConfig.from_file(config_file) # When loaded from JSON with entrypoint format, it becomes EntrypointConfig assert isinstance(config.source, FileSystemSource) @@ -325,7 +325,7 @@ class TestFastMCPConfig: config_file = tmp_path / "fastmcp.json" config_file.write_text(json.dumps(config_data)) - config = FastMCPConfig.from_file(config_file) + config = MCPServerConfig.from_file(config_file) # String entrypoint with : should be converted to EntrypointConfig assert isinstance(config.source, FileSystemSource) assert config.source.path == "server.py" @@ -342,7 +342,7 @@ class TestFastMCPConfig: config_file = tmp_path / "fastmcp.json" config_file.write_text(json.dumps(config_data)) - config = FastMCPConfig.from_file(config_file) + config = MCPServerConfig.from_file(config_file) # Should be parsed into EntrypointConfig assert isinstance(config.source, FileSystemSource) @@ -365,7 +365,7 @@ class TestFastMCPConfig: original_cwd = os.getcwd() try: os.chdir(tmp_path) - found = FastMCPConfig.find_config() + found = MCPServerConfig.find_config() assert found == config_file finally: os.chdir(original_cwd) @@ -379,7 +379,7 @@ class TestFastMCPConfig: subdir.mkdir() # Should NOT find config in parent directory - found = FastMCPConfig.find_config(subdir) + found = MCPServerConfig.find_config(subdir) assert found is None def test_find_config_in_specified_dir(self, tmp_path): @@ -388,12 +388,12 @@ class TestFastMCPConfig: config_file.write_text(json.dumps({"source": {"path": "server.py"}})) # Should find config when looking in the directory that contains it - found = FastMCPConfig.find_config(tmp_path) + found = MCPServerConfig.find_config(tmp_path) assert found == config_file def test_find_config_not_found(self, tmp_path): """Test when config is not found.""" - found = FastMCPConfig.find_config(tmp_path) + found = MCPServerConfig.find_config(tmp_path) assert found is None def test_invalid_transport(self, tmp_path): @@ -407,12 +407,12 @@ class TestFastMCPConfig: config_file.write_text(json.dumps(config_data)) with pytest.raises(ValidationError): - FastMCPConfig.from_file(config_file) + MCPServerConfig.from_file(config_file) def test_optional_sections(self): """Test that all config sections are optional except source.""" # Only source is required - config = FastMCPConfig(source={"path": "server.py"}) + config = MCPServerConfig(source={"path": "server.py"}) assert isinstance(config.source, FileSystemSource) assert config.source.path == "server.py" # Environment and deployment are now always present but may be empty @@ -420,7 +420,7 @@ class TestFastMCPConfig: assert isinstance(config.deployment, Deployment) # Only environment with values - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"python": "3.12"} ) assert config.environment.python == "3.12" @@ -431,7 +431,7 @@ class TestFastMCPConfig: ) # Only deployment with values - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={"transport": "http"} ) assert isinstance(config.environment, Environment) diff --git a/tests/cli/test_fastmcp_config_integration.py b/tests/cli/test_mcp_server_config_integration.py similarity index 93% rename from tests/cli/test_fastmcp_config_integration.py rename to tests/cli/test_mcp_server_config_integration.py index 800b23b08..e925f1bb4 100644 --- a/tests/cli/test_fastmcp_config_integration.py +++ b/tests/cli/test_mcp_server_config_integration.py @@ -7,7 +7,7 @@ from pathlib import Path import pytest from fastmcp.client import Client -from fastmcp.utilities.fastmcp_config import FastMCPConfig +from fastmcp.utilities.mcp_server_config import MCPServerConfig @pytest.fixture @@ -89,7 +89,7 @@ class TestConfigWithClient: """Test that a server loaded from config works with a client.""" # Load the config config_file = server_with_config / "fastmcp.json" - config = FastMCPConfig.from_file(config_file) + config = MCPServerConfig.from_file(config_file) # Import the server using the source import importlib.util @@ -132,7 +132,7 @@ class TestEnvironmentExecution: def test_needs_uv_with_dependencies(self): """Test that environment with dependencies needs UV.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"dependencies": ["requests", "numpy"]}, # type: ignore[arg-type] ) @@ -142,7 +142,7 @@ class TestEnvironmentExecution: def test_needs_uv_with_python_version(self): """Test that environment with Python version needs UV.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"python": "3.12"}, # type: ignore[arg-type] ) @@ -152,7 +152,7 @@ class TestEnvironmentExecution: def test_no_uv_needed_without_environment(self): """Test that no UV is needed without environment config.""" - config = FastMCPConfig(source={"path": "server.py"}) + config = MCPServerConfig(source={"path": "server.py"}) # Environment is now always present but may be empty assert config.environment is not None @@ -160,7 +160,7 @@ class TestEnvironmentExecution: def test_no_uv_needed_with_empty_environment(self): """Test that no UV is needed with empty environment config.""" - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={}, # type: ignore[arg-type] ) @@ -184,7 +184,7 @@ class TestPathResolution: server_file = src_dir / "server.py" server_file.write_text("# Server") - config = FastMCPConfig(source={"path": "../src/server.py"}) + config = MCPServerConfig(source={"path": "../src/server.py"}) # The source path is resolved during load_server # For now, just check that the source is created correctly @@ -198,7 +198,7 @@ class TestPathResolution: work_dir = tmp_path / "work" work_dir.mkdir() - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={"cwd": "work"}, # type: ignore[arg-type] ) @@ -222,7 +222,7 @@ class TestPathResolution: reqs_file = tmp_path / "requirements.txt" reqs_file.write_text("fastmcp>=2.0") - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, environment={"requirements": "requirements.txt"}, # type: ignore[arg-type] ) @@ -243,7 +243,7 @@ class TestConfigValidation: def test_invalid_transport_rejected(self): """Test that invalid transport values are rejected.""" with pytest.raises(ValueError): - FastMCPConfig( + MCPServerConfig( source={"path": "server.py"}, deployment={"transport": "invalid_transport"}, # type: ignore[arg-type] ) @@ -251,7 +251,7 @@ class TestConfigValidation: def test_streamable_http_transport_rejected(self): """Test that streamable-http transport is rejected in fastmcp.json config.""" with pytest.raises(ValueError): - FastMCPConfig( + MCPServerConfig( source={"path": "server.py"}, deployment={"transport": "streamable-http"}, # type: ignore[arg-type] ) @@ -259,7 +259,7 @@ class TestConfigValidation: def test_invalid_log_level_rejected(self): """Test that invalid log level values are rejected.""" with pytest.raises(ValueError): - FastMCPConfig( + MCPServerConfig( source={"path": "server.py"}, deployment={"log_level": "INVALID"}, # type: ignore[arg-type] ) @@ -267,12 +267,12 @@ class TestConfigValidation: def test_missing_source_rejected(self): """Test that config without source is rejected.""" with pytest.raises(ValueError): - FastMCPConfig() # type: ignore[call-arg] + MCPServerConfig() # type: ignore[call-arg] def test_valid_transport_values(self): """Test that all valid transport values are accepted.""" for transport in ["stdio", "http", "sse"]: - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={"transport": transport}, # type: ignore[arg-type] ) @@ -282,7 +282,7 @@ class TestConfigValidation: def test_valid_log_levels(self): """Test that all valid log levels are accepted.""" for level in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]: - config = FastMCPConfig( + config = MCPServerConfig( source={"path": "server.py"}, deployment={"log_level": level}, # type: ignore[arg-type] ) diff --git a/tests/cli/test_fastmcp_config_schema.py b/tests/cli/test_mcp_server_config_schema.py similarity index 96% rename from tests/cli/test_fastmcp_config_schema.py rename to tests/cli/test_mcp_server_config_schema.py index 5c05cd65f..1fda6e91b 100644 --- a/tests/cli/test_fastmcp_config_schema.py +++ b/tests/cli/test_mcp_server_config_schema.py @@ -3,7 +3,7 @@ import json from pathlib import Path -from fastmcp.utilities.fastmcp_config.v1.fastmcp_config import generate_schema +from fastmcp.utilities.mcp_server_config.v1.mcp_server_config import generate_schema def test_schema_file_matches_pydantic_model(): @@ -14,7 +14,7 @@ def test_schema_file_matches_pydantic_model(): / "src" / "fastmcp" / "utilities" - / "fastmcp_config" + / "mcp_server_config" / "v1" / "schema.json" ) @@ -30,7 +30,7 @@ def test_schema_file_matches_pydantic_model(): assert file_schema == generated_schema, ( "The schema.json file does not match the Pydantic model schema. " "Please regenerate the schema file by running:\n" - 'uv run python -c "from fastmcp.utilities.fastmcp_config.v1.fastmcp_config import generate_schema; ' + 'uv run python -c "from fastmcp.utilities.mcp_server_config.v1.mcp_server_config import generate_schema; ' 'import json; print(json.dumps(generate_schema(), indent=2))" > ' f"{schema_file}" ) diff --git a/tests/cli/test_project_prepare.py b/tests/cli/test_project_prepare.py index d23f61f32..5a2ad15b1 100644 --- a/tests/cli/test_project_prepare.py +++ b/tests/cli/test_project_prepare.py @@ -6,24 +6,24 @@ from unittest.mock import AsyncMock, MagicMock, patch import pytest -from fastmcp.utilities.fastmcp_config import Environment, FastMCPConfig -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config import Environment, MCPServerConfig +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource -class TestFastMCPConfigPrepare: - """Test the FastMCPConfig.prepare() method.""" +class TestMCPServerConfigPrepare: + """Test the MCPServerConfig.prepare() method.""" @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_source", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_source", new_callable=AsyncMock, ) @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_environment", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_environment", new_callable=AsyncMock, ) async def test_prepare_calls_both_methods(self, mock_env, mock_src): """Test that prepare() calls both prepare_environment and prepare_source.""" - config = FastMCPConfig( + config = MCPServerConfig( source=FileSystemSource(path="server.py"), environment=Environment(python="3.10"), ) @@ -34,16 +34,16 @@ class TestFastMCPConfigPrepare: mock_src.assert_called_once() @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_source", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_source", new_callable=AsyncMock, ) @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_environment", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_environment", new_callable=AsyncMock, ) async def test_prepare_with_output_dir(self, mock_env, mock_src): """Test that prepare() with output_dir calls prepare_environment with it.""" - config = FastMCPConfig( + config = MCPServerConfig( source=FileSystemSource(path="server.py"), environment=Environment(python="3.10"), ) @@ -55,16 +55,16 @@ class TestFastMCPConfigPrepare: mock_src.assert_called_once() @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_source", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_source", new_callable=AsyncMock, ) @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_environment", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_environment", new_callable=AsyncMock, ) async def test_prepare_skip_source(self, mock_env, mock_src): """Test that prepare() skips source when skip_source=True.""" - config = FastMCPConfig( + config = MCPServerConfig( source=FileSystemSource(path="server.py"), environment=Environment(python="3.10"), ) @@ -75,16 +75,16 @@ class TestFastMCPConfigPrepare: mock_src.assert_not_called() @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.FastMCPConfig.prepare_source", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.MCPServerConfig.prepare_source", new_callable=AsyncMock, ) @patch( - "fastmcp.utilities.fastmcp_config.v1.fastmcp_config.Environment.prepare", + "fastmcp.utilities.mcp_server_config.v1.mcp_server_config.Environment.prepare", new_callable=AsyncMock, ) async def test_prepare_no_environment_settings(self, mock_env_prepare, mock_src): """Test that prepare() works with default empty environment config.""" - config = FastMCPConfig( + config = MCPServerConfig( source=FileSystemSource(path="server.py"), # environment defaults to empty Environment() ) @@ -188,8 +188,8 @@ class TestEnvironmentPrepare: class TestProjectPrepareCommand: """Test the CLI project prepare command.""" - @patch("fastmcp.utilities.fastmcp_config.FastMCPConfig.from_file") - @patch("fastmcp.utilities.fastmcp_config.FastMCPConfig.find_config") + @patch("fastmcp.utilities.mcp_server_config.MCPServerConfig.from_file") + @patch("fastmcp.utilities.mcp_server_config.MCPServerConfig.find_config") async def test_project_prepare_auto_detect(self, mock_find, mock_from_file): """Test project prepare with auto-detected config.""" from fastmcp.cli.cli import prepare @@ -220,7 +220,7 @@ class TestProjectPrepareCommand: assert "Project prepared successfully" in success_call @patch("pathlib.Path.exists") - @patch("fastmcp.utilities.fastmcp_config.FastMCPConfig.from_file") + @patch("fastmcp.utilities.mcp_server_config.MCPServerConfig.from_file") async def test_project_prepare_explicit_path(self, mock_from_file, mock_exists): """Test project prepare with explicit config path.""" from fastmcp.cli.cli import prepare @@ -243,7 +243,7 @@ class TestProjectPrepareCommand: output_dir=Path("./test-env"), ) - @patch("fastmcp.utilities.fastmcp_config.FastMCPConfig.find_config") + @patch("fastmcp.utilities.mcp_server_config.MCPServerConfig.find_config") async def test_project_prepare_no_config_found(self, mock_find): """Test project prepare when no config is found.""" from fastmcp.cli.cli import prepare @@ -280,7 +280,7 @@ class TestProjectPrepareCommand: assert "--output-dir parameter is required" in error_msg @patch("pathlib.Path.exists") - @patch("fastmcp.utilities.fastmcp_config.FastMCPConfig.from_file") + @patch("fastmcp.utilities.mcp_server_config.MCPServerConfig.from_file") async def test_project_prepare_failure(self, mock_from_file, mock_exists): """Test project prepare when prepare() fails.""" from fastmcp.cli.cli import prepare diff --git a/tests/cli/test_run.py b/tests/cli/test_run.py index f5375f7e0..685e8aff0 100644 --- a/tests/cli/test_run.py +++ b/tests/cli/test_run.py @@ -13,7 +13,7 @@ from fastmcp.client.client import Client from fastmcp.client.transports import FastMCPTransport from fastmcp.mcp_config import MCPConfig, StdioMCPServer from fastmcp.server.server import FastMCP -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource class TestUrlDetection: @@ -339,7 +339,7 @@ mcp = fastmcp.FastMCP("TestServer") from unittest.mock import AsyncMock, patch from fastmcp.cli.run import run_command - from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import ( + from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import ( FileSystemSource, ) @@ -368,7 +368,7 @@ mcp = fastmcp.FastMCP("TestServer") from unittest.mock import AsyncMock, patch from fastmcp.cli.run import run_command - from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import ( + from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import ( FileSystemSource, ) diff --git a/tests/cli/test_run_config.py b/tests/cli/test_run_config.py index 07443414f..abc1f10af 100644 --- a/tests/cli/test_run_config.py +++ b/tests/cli/test_run_config.py @@ -6,13 +6,13 @@ from pathlib import Path import pytest -from fastmcp.cli.run import load_fastmcp_config -from fastmcp.utilities.fastmcp_config import ( +from fastmcp.cli.run import load_mcp_server_config +from fastmcp.utilities.mcp_server_config import ( Deployment, Environment, - FastMCPConfig, + MCPServerConfig, ) -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource @pytest.fixture @@ -43,17 +43,17 @@ def test_tool(message: str) -> str: return config_file -def test_load_fastmcp_config(sample_config, monkeypatch): +def test_load_mcp_server_config(sample_config, monkeypatch): """Test loading configuration and returning config subsets.""" # Capture environment changes original_env = dict(os.environ) try: - config = load_fastmcp_config(sample_config) + config = load_mcp_server_config(sample_config) # Check that we got the right types - assert isinstance(config, FastMCPConfig) + assert isinstance(config, MCPServerConfig) assert isinstance(config.source, FileSystemSource) assert isinstance(config.deployment, Deployment) assert isinstance(config.environment, Environment) @@ -95,7 +95,7 @@ def test_load_config_with_entrypoint_source(tmp_path): server_file = src_dir / "server.py" server_file.write_text("# Server") - config = load_fastmcp_config(config_file) + config = load_mcp_server_config(config_file) # Check source - path is not resolved yet, only during load_server assert config.source.path == "src/server.py" @@ -125,7 +125,7 @@ def test_load_config_with_cwd(tmp_path): original_cwd = os.getcwd() try: - config = load_fastmcp_config(config_file) # noqa: F841 + config = load_mcp_server_config(config_file) # noqa: F841 # Check that working directory was changed assert Path.cwd() == subdir.resolve() @@ -160,7 +160,7 @@ def test_load_config_with_relative_cwd(tmp_path): original_cwd = os.getcwd() try: - config = load_fastmcp_config(config_file) # noqa: F841 + config = load_mcp_server_config(config_file) # noqa: F841 # Should change to parent directory of config file assert Path.cwd() == subdir1.resolve() @@ -180,7 +180,7 @@ def test_load_minimal_config(tmp_path): server_file = tmp_path / "server.py" server_file.write_text("# Server") - config = load_fastmcp_config(config_file) + config = load_mcp_server_config(config_file) # Check we got source - path is not resolved yet, only during load_server assert isinstance(config.source, FileSystemSource) @@ -201,7 +201,7 @@ def test_load_config_with_server_args(tmp_path): server_file = tmp_path / "server.py" server_file.write_text("# Server") - config = load_fastmcp_config(config_file) + config = load_mcp_server_config(config_file) assert config.deployment.args == ["--debug", "--config", "custom.json"] @@ -221,7 +221,7 @@ def test_config_subset_independence(tmp_path): server_file = tmp_path / "server.py" server_file.write_text("# Server") - config = load_fastmcp_config(config_file) + config = load_mcp_server_config(config_file) # Each subset should be independently usable # Path is not resolved yet, only during load_server @@ -259,7 +259,7 @@ def test_environment_config_path_resolution(tmp_path): server_file = tmp_path / "server.py" server_file.write_text("# Server") - config = load_fastmcp_config(config_file) + config = load_mcp_server_config(config_file) # Check that UV command is built with resolved paths uv_cmd = config.environment.build_uv_run_command(["fastmcp", "run", "server.py"]) diff --git a/tests/cli/test_server_args.py b/tests/cli/test_server_args.py index 5ec7ccc6a..8f1782dbf 100644 --- a/tests/cli/test_server_args.py +++ b/tests/cli/test_server_args.py @@ -4,8 +4,8 @@ from pathlib import Path import pytest -from fastmcp.utilities.fastmcp_config import FastMCPConfig -from fastmcp.utilities.fastmcp_config.v1.sources.filesystem import FileSystemSource +from fastmcp.utilities.mcp_server_config import MCPServerConfig +from fastmcp.utilities.mcp_server_config.v1.sources.filesystem import FileSystemSource class TestServerArguments: @@ -39,7 +39,7 @@ def get_config() -> dict: # Test with arguments source = FileSystemSource(path=str(server_file)) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) from fastmcp.cli.cli import with_argv @@ -69,7 +69,7 @@ mcp = FastMCP(args.name) """) source = FileSystemSource(path=str(server_file)) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) from fastmcp.cli.cli import with_argv @@ -96,7 +96,7 @@ mcp = FastMCP(name) """) source = FileSystemSource(path=str(server_file)) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) from fastmcp.cli.cli import with_argv @@ -123,7 +123,7 @@ mcp = FastMCP(name) pytest.skip("config_server.py example not found") source = FileSystemSource(path=str(config_server)) - config = FastMCPConfig(source=source) + config = MCPServerConfig(source=source) from fastmcp.cli.cli import with_argv diff --git a/tests/utilities/test_cli.py b/tests/utilities/test_cli.py index cd8b55dcb..56d5a5255 100644 --- a/tests/utilities/test_cli.py +++ b/tests/utilities/test_cli.py @@ -1,6 +1,6 @@ """Tests for CLI utility functions.""" -from fastmcp.utilities.fastmcp_config.v1.fastmcp_config import Environment +from fastmcp.utilities.mcp_server_config.v1.mcp_server_config import Environment class TestEnvironmentBuildUVRunCommand: