Skip to content

Move app modules to fastmcp.apps package#3616

Merged
jlowin merged 4 commits intomainfrom
refactor-apps-package
Mar 25, 2026
Merged

Move app modules to fastmcp.apps package#3616
jlowin merged 4 commits intomainfrom
refactor-apps-package

Conversation

@jlowin
Copy link
Copy Markdown
Member

@jlowin jlowin commented Mar 25, 2026

Apps are a first-class pillar alongside servers and clients. Their code should live at the top level, not buried under fastmcp.server.

This moves FastMCPApp and AppConfig/ResourceCSP to fastmcp.apps:

from fastmcp.apps import FastMCPApp, AppConfig, ResourceCSP

The old paths (fastmcp.server.app, fastmcp.server.apps) still work but emit DeprecationWarning. The top-level from fastmcp import FastMCPApp is unchanged.

Breaking the circular import required extracting UI_MIME_TYPE and resolve_ui_mime_type to fastmcp.utilities.mime — the resources package needed these from apps, and apps imported Provider from the server package, which imported resources. Moving the leaf dependency out of the apps package breaks the cycle cleanly.

jlowin added 2 commits March 24, 2026 20:27
FastMCPApp and AppConfig now live under fastmcp.apps as a top-level
package, reflecting that apps are a first-class pillar alongside
servers and clients.

- fastmcp.apps.app — FastMCPApp
- fastmcp.apps.config — AppConfig, ResourceCSP, etc.
- fastmcp.apps — re-exports config (FastMCPApp excluded to avoid
  circular imports with server; import from fastmcp.apps.app or fastmcp)
- fastmcp.server.app and fastmcp.server.apps remain as backward-
  compatible re-export shims
Creates fastmcp.apps as a top-level package for all app-related code:
- fastmcp.apps.app — FastMCPApp
- fastmcp.apps.config — AppConfig, ResourceCSP, etc.
- fastmcp.apps — re-exports everything

Moves UI_MIME_TYPE and resolve_ui_mime_type to fastmcp.utilities.mime
to break the circular import between resources and apps.

Old import paths (fastmcp.server.app, fastmcp.server.apps) emit
DeprecationWarnings via __getattr__ and re-export from the new locations.
@marvin-context-protocol marvin-context-protocol Bot added enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality. labels Mar 25, 2026
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b456f32ea8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/fastmcp/server/app.py Outdated
Comment on lines +10 to +14
def __getattr__(name: str) -> object:
warnings.warn(
f"Importing {name!r} from 'fastmcp.server.app' is deprecated. "
"Use 'fastmcp.apps.app' or 'from fastmcp import FastMCPApp' instead.",
DeprecationWarning,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve wildcard imports in deprecated server.app shim

This compatibility module now only exposes __getattr__, so from fastmcp.server.app import * no longer brings in FastMCPApp (or the other previously exported symbols) and instead imports almost nothing. That is a runtime backward-compatibility break for code still on the deprecated path, even though this migration intends old imports to keep working while warning. Re-exporting an __all__ (or explicit symbol bindings) from fastmcp.apps.app would preserve expected behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 27062a33cc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/fastmcp/server/apps.py Outdated
Comment on lines +10 to +19
def __getattr__(name: str) -> object:
warnings.warn(
f"Importing {name!r} from 'fastmcp.server.apps' is deprecated. "
"Use 'from fastmcp.apps import ...' instead.",
DeprecationWarning,
stacklevel=2,
)
microphone: dict[str, Any] | None = Field(
default=None, description="Request microphone access"
)
geolocation: dict[str, Any] | None = Field(
default=None, description="Request geolocation access"
)
clipboard_write: dict[str, Any] | None = Field(
default=None,
alias="clipboardWrite",
description="Request clipboard-write access",
)

model_config = {"populate_by_name": True, "extra": "allow"}


class AppConfig(BaseModel):
"""Configuration for MCP App tools and resources.

Controls how a tool or resource participates in the MCP Apps extension.
On tools, ``resource_uri`` and ``visibility`` specify which UI resource
to render and where the tool appears. On resources, those fields must
be left unset (the resource itself is the UI).

All fields use ``exclude_none`` serialization so only explicitly-set
values appear on the wire. Aliases match the MCP Apps wire format
(camelCase).
"""

resource_uri: str | None = Field(
default=None,
alias="resourceUri",
description="URI of the UI resource (typically ui:// scheme). Tools only.",
)
visibility: list[Literal["app", "model"]] | None = Field(
default=None,
description="Where this tool is visible: 'app', 'model', or both. Tools only.",
)
csp: ResourceCSP | None = Field(
default=None, description="Content Security Policy for the app iframe"
)
permissions: ResourcePermissions | None = Field(
default=None, description="Iframe sandbox permissions"
)
domain: str | None = Field(default=None, description="Domain for the iframe")
prefers_border: bool | None = Field(
default=None,
alias="prefersBorder",
description="Whether the UI prefers a visible border",
)

model_config = {"populate_by_name": True, "extra": "allow"}


def app_config_to_meta_dict(app: AppConfig | dict[str, Any]) -> dict[str, Any]:
"""Convert an AppConfig or dict to the wire-format dict for ``meta["ui"]``."""
if isinstance(app, AppConfig):
return app.model_dump(by_alias=True, exclude_none=True)
return app


def resolve_ui_mime_type(uri: str, explicit_mime_type: str | None) -> str | None:
"""Return the appropriate MIME type for a resource URI.

For ``ui://`` scheme resources, defaults to ``UI_MIME_TYPE`` when no
explicit MIME type is provided. This ensures UI resources are correctly
identified regardless of how they're registered (via FastMCP.resource,
the standalone @resource decorator, or resource templates).

Args:
uri: The resource URI string
explicit_mime_type: The MIME type explicitly provided by the user
from fastmcp.apps import config as _config

Returns:
The resolved MIME type (explicit value, UI default, or None)
"""
if explicit_mime_type is not None:
return explicit_mime_type
# Case-insensitive scheme check per RFC 3986
if uri.lower().startswith("ui://"):
return UI_MIME_TYPE
return None
return getattr(_config, name)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve wildcard imports in deprecated server.apps shim

The deprecation shim now exposes only __getattr__, so from fastmcp.server.apps import * no longer imports legacy symbols like AppConfig, ResourceCSP, or UI_MIME_TYPE and instead pulls almost nothing from the module namespace. This is a runtime backward-compatibility break for code that still relies on the deprecated path while migrating, despite the intent that old imports continue to work with warnings.

Useful? React with 👍 / 👎.

@jlowin jlowin merged commit 1eabe7f into main Mar 25, 2026
7 checks passed
@jlowin jlowin deleted the refactor-apps-package branch March 25, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvement to existing functionality. For issues and smaller PR improvements. server Related to FastMCP server implementation or server-side functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant