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,
OpenAISettingsEntrypoint,
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 (
OpenAIExtensions,
OpenAIMentionSearchParams,
OpenAIMentionSearchResult,
)
openai_extensions = OpenAIExtensions()
@openai_extensions.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=[openai_extensions])
Form Elicitation
NOTE: OpenAI-registered MCP servers require MRTR for form elicitation. Direct MCP connections still support legacy forms through elicit_input, which does not implement MRTR.
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(
context,
mode="form",
message="Choose reference images",
schema=PresentationForm,
)
Release files for openai-mcp-extensions 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| openai_mcp_extensions-0.1.0.tar.gz | 22.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| openai_mcp_extensions-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.6 kB
Release files / openai_mcp_extensions-0.1.0.tar.gz
| Download URL | openai_mcp_extensions-0.1.0.tar.gz |
|---|---|
| Size | 22.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fc7e0f7805719ef40dd3654a2a9f876ece313d61b4268f0841cea27ba05afe00
|
|
BLAKE2b-256 checksum How to use checksums |
1a3124c376a66f1fd6f3093929b4bec7f0a0b87e688f04dcb2382dacecb64cbf
|
| 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 Sep 29, 2026.
Transparency logRelease files / openai_mcp_extensions-0.1.0-py3-none-any.whl
| Download URL | openai_mcp_extensions-0.1.0-py3-none-any.whl |
|---|---|
| Size | 25.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6694e102c746dd4f9c0a6c0f668dd11ad627d676153bf5d02e9f152c9a913e4c
|
|
BLAKE2b-256 checksum How to use checksums |
15b50be87c1355db5587d92b8c3979a14baaa02ae8a518a1b7c6d956a15caf69
|
| 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 Sep 29, 2026.
Transparency log