Skip to main content

OpenAI MCP Extensions for Python

The Python SDK provides server-side OpenAI extensions for the official MCP Python SDK. Use @openai/mcp-extensions/app for MCP App extensions.

Installation

Install the Python extension SDK from PyPI.

uv add openai-mcp-extensions

Server Setup

Initialize the MCP Apps and OpenAI extensions for use with an MCP Server.

from mcp.server.apps import Apps
from mcp.server.mcpserver import MCPServer

from openai_mcp_extensions import OpenAIExtensions

apps = Apps()
openai_extensions = OpenAIExtensions()

Register extension handlers and resources before constructing MCPServer.

The following examples are separate configurations. Include each extension your server uses in its extensions list.

Structured Settings

from typing import Any, Literal

from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.context import Context
from pydantic import BaseModel, Field

from openai_mcp_extensions import (
    OpenAISettings,
    OpenAISettingsGroup,
    OpenAISettingsProperty,
)
from preferences import load_preferences, update_preferences


class Preferences(BaseModel):
    units: Literal["mm", "in"] = Field(title="Measurement units")
    show_grid: bool = Field(alias="showGrid", title="Show grid")


settings = OpenAISettings(
    schema=Preferences,
    # Optionally arrange fields into groups.
    # Omitted properties appear in an "Other settings" group below the listed groups.
    layout=[
        OpenAISettingsGroup(
            title="Display",
            items=[OpenAISettingsProperty(property="units"), OpenAISettingsProperty(property="showGrid")],
        ),
    ],
)


# Synchronous handlers run in a worker thread. Async handlers run on the event loop.
@settings.read
async def read_settings(context: Context[Any, Any]) -> Preferences:
    return await load_preferences(context)


# Synchronous handlers run in a worker thread. Async handlers run on the event loop.
@settings.update
async def update_settings(set: dict[str, Any], context: Context[Any, Any]) -> Preferences:
    return await update_preferences(set, context)


server = MCPServer(
    "viewer",
    extensions=[settings],
    # Only needed if your server does not support the 2026-07-28 spec and/or supports
    # the legacy initialize handshake.
    # https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle#initialization
    middleware=[settings.advertise_legacy_capability],
)

UI Entrypoints

from mcp.server.apps import APP_MIME_TYPE
from mcp.server.mcpserver.resources import TextResource
from mcp_types import Icon

from openai_mcp_extensions import (
    OpenAIFileEntrypoint,
    OpenAIGlobalEntrypoint,
    OpenAIThreadEntrypoint,
    OpenAIUiQuickAction,
    OpenAIUiQuickActionToolTarget,
    OpenAIUiResourceMetadata,
    OpenAIUiToolMetadata,
)

apps.add_resource(
    TextResource(
        uri="ui://table/viewer",
        name="table",
        mime_type=APP_MIME_TYPE,
        text="<!doctype html><title>Table</title><main>Table viewer</main>",
        meta={
            "openai/ui": OpenAIUiResourceMetadata(
                preferred_display_mode="fullscreen",
                available_display_modes=["inline", "fullscreen"],
            ).model_dump(by_alias=True, exclude_none=True),
        },
    ),
)


@apps.tool(
    resource_uri="ui://table/viewer",
    meta={
        "openai/ui": OpenAIUiToolMetadata(
            entrypoints=[
                OpenAIGlobalEntrypoint(
                    quick_action=OpenAIUiQuickAction(
                        title="New table",
                        icons=[Icon(src="https://example.com/plus.svg")],
                        target=OpenAIUiQuickActionToolTarget(name="create_table", arguments={}),
                    ),
                ),
                OpenAIThreadEntrypoint(),
                OpenAIFileEntrypoint(extensions=[".csv", ".tsv"]),
            ],
        ).model_dump(by_alias=True, exclude_none=True),
    },
)
def open_table() -> str:
    return "Open the table viewer."


server = MCPServer("my-server", extensions=[apps, openai_extensions])

Filesystem Access

from typing import Any

from mcp.server.mcpserver.context import Context

from openai_mcp_extensions import get_resource_path


def opened_file_path(context: Context[Any, Any]) -> str | None:
    return get_resource_path(context.request_context.meta)

Composer Mentions

from typing import Any

from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.context import Context
from mcp_types import ResourceLink

from openai_mcp_extensions import (
    OpenAIMentions,
    OpenAIMentionSearchParams,
    OpenAIMentionSearchResult,
)

mentions = OpenAIMentions()


@mentions.search
async def search_mentions(
    params: OpenAIMentionSearchParams,
    context: Context[Any, Any],
) -> OpenAIMentionSearchResult:
    return OpenAIMentionSearchResult(
        items=[
            ResourceLink(
                uri=f"mcp://issues/{params.query}",
                name=params.query,
            ),
        ],
    )


server = MCPServer("issue-tracker", extensions=[mentions])

Form Elicitation

Multi-round-trip Requests

For MCP 2026-07-28 or later, register the middleware and return pending input requests:

from typing import Any

from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.context import Context
from mcp_types import CallToolResult, InputRequiredResult
from pydantic import BaseModel

from openai_mcp_extensions import OpenAIExtensions

extensions = OpenAIExtensions()
server = MCPServer("forms", middleware=[extensions.middleware])


class ProjectForm(BaseModel):
    code: str


@server.tool()
async def choose_project(context: Context[Any, Any]) -> CallToolResult | InputRequiredResult:
    result = extensions.request_input(
        context, key="project", message="Enter a project code", schema=ProjectForm
    )
    if isinstance(result, InputRequiredResult):
        return result
    if result.action != "accept":
        return CallToolResult(content=[], structured_content={"action": result.action})
    return CallToolResult(content=[], structured_content=result.data.model_dump())

Suggested Values

Users can enter values that are not listed. The same field constraints apply to suggested and entered values.

from typing import Annotated

from pydantic import BaseModel, Field


class ReviewForm(BaseModel):
    purpose: str = Field(
        min_length=1,
        json_schema_extra={
            "x-openai-suggestions": [{"const": "prototype", "title": "Prototype"}],
        },
    )
    checks: list[
        Annotated[
            str,
            Field(
                min_length=1,
                json_schema_extra={
                    "x-openai-suggestions": [{"const": "clearance", "title": "Clearance"}],
                },
            ),
        ]
    ]

Resource Selection

from typing import Any

from mcp.server.elicitation import ElicitationResult
from mcp.server.mcpserver import MCPServer
from mcp.server.mcpserver.context import Context
from mcp_types import Resource
from pydantic import BaseModel, Field, FileUrl

from openai_mcp_extensions import OpenAIExtensions
from openai_mcp_extensions.form import UserResourceOptions, resource_input

openai_extensions = OpenAIExtensions()
server = MCPServer("presentations", extensions=[openai_extensions])


class PresentationForm(BaseModel):
    images: list[FileUrl] = Field(
        default_factory=list,
        max_length=5,
        json_schema_extra={
            **resource_input(
                options=[
                    Resource(
                        uri="file:///images/sales.png",
                        name="sales.png",
                        title="Sales image",
                        meta={
                            "openai/thumbnail": {"src": "https://example.com/sales.png"},
                            "openai/preview": {
                                "target": {
                                    "type": "resource_link",
                                    "uri": "file:///images/sales.png",
                                    "name": "sales.png",
                                    "mimeType": "image/png",
                                },
                            },
                        },
                    ),
                ],
                user_options=UserResourceOptions(accept=["image/*"]),
            ),
            "default": ["file:///images/sales.png"],
        },
    )


@server.tool()
async def choose_images(context: Context[Any, Any]) -> ElicitationResult[PresentationForm]:
    return await openai_extensions.elicit_input_legacy(
        context,
        mode="form",
        message="Choose reference images",
        schema=PresentationForm,
    )

Metadata

Release files for openai-mcp-extensions 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for openai-mcp-extensions 0.2.1
File Size Uploaded Provenance Actions
openai_mcp_extensions-0.2.1.tar.gz 27.4 kB With provenance Details for openai_mcp_extensions-0.2.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for openai-mcp-extensions 0.2.1
File Interpreter ABI Platform Actions
openai_mcp_extensions-0.2.1-py3-none-any.whl Python 3 none any Details for openai_mcp_extensions-0.2.1-py3-none-any.whl

Total release size: 55.4 kB

Release files / openai_mcp_extensions-0.2.1.tar.gz

Download URL openai_mcp_extensions-0.2.1.tar.gz
Size 27.4 kB
Tags Source
SHA-256 checksum
How to use checksums
f92f0ba3d2e58c81d058683ade7c3b32099b574515006eb5b21fd223d3d465ba
BLAKE2b-256 checksum
How to use checksums
3da314f0ea7d9c3d5c2acba302ca08aac284f002cae3e184789ff474c439e2cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Release files / openai_mcp_extensions-0.2.1-py3-none-any.whl

Download URL openai_mcp_extensions-0.2.1-py3-none-any.whl
Size 28.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3f2930b2f5fb1c6c8b7658f854ce59edf1f7ff08aedcfa541b1a91fd08fb6c40
BLAKE2b-256 checksum
How to use checksums
7fef0596a3084156cb828896c43e08fd63b7128ce80b172981c9b691c8c8bd7e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

Security for openai-mcp-extensions 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for openai_mcp_extensions-0.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 21, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page