From fd79b3373da26c4540789734519792947a1216cd Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 20:00:33 +0000 Subject: [PATCH 1/8] feat(mcp): serve HTML landing page on browser GET to MCP endpoint Co-Authored-By: AJ Steers --- airbyte/mcp/http_main.py | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/airbyte/mcp/http_main.py b/airbyte/mcp/http_main.py index 226198ba1..dd5189256 100644 --- a/airbyte/mcp/http_main.py +++ b/airbyte/mcp/http_main.py @@ -17,10 +17,14 @@ from __future__ import annotations +import html import logging import os +from typing import TYPE_CHECKING from urllib.parse import urlparse +from starlette.responses import HTMLResponse + from airbyte.mcp.server import ( DEFAULT_HTTP_HOST, DEFAULT_HTTP_PORT, @@ -29,8 +33,79 @@ ) +if TYPE_CHECKING: + from starlette.requests import Request + + logger = logging.getLogger(__name__) +# Human-facing landing page shown when a browser GETs the MCP endpoint. +MCP_LANDING_TITLE = "Airbyte MCP Server" +MCP_LANDING_DOCS_URL = "https://docs.airbyte.com/ai-agents/" + + +def _render_mcp_landing_html(endpoint_url: str, docs_url: str) -> str: + """Render the landing page shown when a browser opens the MCP endpoint. + + MCP endpoints only speak the streamable HTTP protocol (`POST`/`DELETE`), so + a browser visit would otherwise return a bare `405 Method Not Allowed`. This + page explains what the URL is and links to setup instructions. + """ + safe_url = html.escape(endpoint_url) + safe_docs = html.escape(docs_url) + return f""" + + + + +{MCP_LANDING_TITLE} + + + +
+
+
Model Context Protocol
+

{MCP_LANDING_TITLE}

+

This URL is an MCP endpoint, not a web page. Add it to an + MCP client such as Claude, Cursor, VS Code, or Goose — it isn't meant + to be opened directly in a browser.

+

Configure your MCP client with this streamable-HTTP endpoint:

+ {safe_url} + Setup instructions → +

Powered by Airbyte.

+
+
+ + +""" + + +async def mcp_landing_page(request: Request) -> HTMLResponse: # noqa: ARG001, RUF029 + """Serve a human-friendly landing page for browser `GET`s to the MCP endpoint.""" + server_url = os.getenv( + MCP_SERVER_URL_ENV, + f"http://localhost:{DEFAULT_HTTP_PORT}", + ) + return HTMLResponse(_render_mcp_landing_html(server_url, MCP_LANDING_DOCS_URL)) + def main() -> None: """Start the Airbyte MCP server with HTTP transport.""" @@ -45,6 +120,11 @@ def main() -> None: ) mcp_path = "/" if urlparse(server_url).path.strip("/") else "/mcp" + # Serve a browser-friendly landing page on GET at the MCP path. In stateless + # mode FastMCP only binds POST/DELETE there, so this GET route does not + # interfere with MCP traffic. + app.custom_route(mcp_path, methods=["GET"], name="mcp_landing_page")(mcp_landing_page) + logger.info( "Starting Airbyte MCP HTTP server on %s:%d (mcp_path=%r)", DEFAULT_HTTP_HOST, From 3a8c3b9daf55817ccccba73d600122243324ee74 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 20:06:01 +0000 Subject: [PATCH 2/8] refactor(mcp): extract _get_server_url helper Co-Authored-By: AJ Steers --- airbyte/mcp/http_main.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/airbyte/mcp/http_main.py b/airbyte/mcp/http_main.py index dd5189256..9cf8b75df 100644 --- a/airbyte/mcp/http_main.py +++ b/airbyte/mcp/http_main.py @@ -98,13 +98,17 @@ def _render_mcp_landing_html(endpoint_url: str, docs_url: str) -> str: """ -async def mcp_landing_page(request: Request) -> HTMLResponse: # noqa: ARG001, RUF029 - """Serve a human-friendly landing page for browser `GET`s to the MCP endpoint.""" - server_url = os.getenv( +def _get_server_url() -> str: + """Return the public base URL from `MCP_SERVER_URL`, defaulting to localhost.""" + return os.getenv( MCP_SERVER_URL_ENV, f"http://localhost:{DEFAULT_HTTP_PORT}", ) - return HTMLResponse(_render_mcp_landing_html(server_url, MCP_LANDING_DOCS_URL)) + + +async def mcp_landing_page(request: Request) -> HTMLResponse: # noqa: ARG001, RUF029 + """Serve a human-friendly landing page for browser `GET`s to the MCP endpoint.""" + return HTMLResponse(_render_mcp_landing_html(_get_server_url(), MCP_LANDING_DOCS_URL)) def main() -> None: @@ -114,11 +118,7 @@ def main() -> None: # When deployed behind a path-stripping LB (MCP_SERVER_URL has a path # component like /cloud-mcp), serve the MCP endpoint at root so the # public URL is just the base path. Otherwise keep the FastMCP default. - server_url = os.getenv( - MCP_SERVER_URL_ENV, - f"http://localhost:{DEFAULT_HTTP_PORT}", - ) - mcp_path = "/" if urlparse(server_url).path.strip("/") else "/mcp" + mcp_path = "/" if urlparse(_get_server_url()).path.strip("/") else "/mcp" # Serve a browser-friendly landing page on GET at the MCP path. In stateless # mode FastMCP only binds POST/DELETE there, so this GET route does not From 1db028734118178b8383b6cc6566955898be6909 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 23:05:25 +0000 Subject: [PATCH 3/8] refactor(mcp): use register_landing_page from fastmcp-extensions Co-Authored-By: AJ Steers --- airbyte/mcp/http_main.py | 75 +++++----------------------------------- pyproject.toml | 2 +- 2 files changed, 9 insertions(+), 68 deletions(-) diff --git a/airbyte/mcp/http_main.py b/airbyte/mcp/http_main.py index 9cf8b75df..e6351630f 100644 --- a/airbyte/mcp/http_main.py +++ b/airbyte/mcp/http_main.py @@ -17,13 +17,11 @@ from __future__ import annotations -import html import logging import os -from typing import TYPE_CHECKING from urllib.parse import urlparse -from starlette.responses import HTMLResponse +from fastmcp_extensions import register_landing_page from airbyte.mcp.server import ( DEFAULT_HTTP_HOST, @@ -33,10 +31,6 @@ ) -if TYPE_CHECKING: - from starlette.requests import Request - - logger = logging.getLogger(__name__) # Human-facing landing page shown when a browser GETs the MCP endpoint. @@ -44,60 +38,6 @@ MCP_LANDING_DOCS_URL = "https://docs.airbyte.com/ai-agents/" -def _render_mcp_landing_html(endpoint_url: str, docs_url: str) -> str: - """Render the landing page shown when a browser opens the MCP endpoint. - - MCP endpoints only speak the streamable HTTP protocol (`POST`/`DELETE`), so - a browser visit would otherwise return a bare `405 Method Not Allowed`. This - page explains what the URL is and links to setup instructions. - """ - safe_url = html.escape(endpoint_url) - safe_docs = html.escape(docs_url) - return f""" - - - - -{MCP_LANDING_TITLE} - - - -
-
-
Model Context Protocol
-

{MCP_LANDING_TITLE}

-

This URL is an MCP endpoint, not a web page. Add it to an - MCP client such as Claude, Cursor, VS Code, or Goose — it isn't meant - to be opened directly in a browser.

-

Configure your MCP client with this streamable-HTTP endpoint:

- {safe_url} - Setup instructions → -

Powered by Airbyte.

-
-
- - -""" - - def _get_server_url() -> str: """Return the public base URL from `MCP_SERVER_URL`, defaulting to localhost.""" return os.getenv( @@ -106,11 +46,6 @@ def _get_server_url() -> str: ) -async def mcp_landing_page(request: Request) -> HTMLResponse: # noqa: ARG001, RUF029 - """Serve a human-friendly landing page for browser `GET`s to the MCP endpoint.""" - return HTMLResponse(_render_mcp_landing_html(_get_server_url(), MCP_LANDING_DOCS_URL)) - - def main() -> None: """Start the Airbyte MCP server with HTTP transport.""" logging.basicConfig(level=logging.INFO) @@ -123,7 +58,13 @@ def main() -> None: # Serve a browser-friendly landing page on GET at the MCP path. In stateless # mode FastMCP only binds POST/DELETE there, so this GET route does not # interfere with MCP traffic. - app.custom_route(mcp_path, methods=["GET"], name="mcp_landing_page")(mcp_landing_page) + register_landing_page( + app, + path=mcp_path, + title=MCP_LANDING_TITLE, + endpoint_url=_get_server_url(), + docs_url=MCP_LANDING_DOCS_URL, + ) logger.info( "Starting Airbyte MCP HTTP server on %s:%d (mcp_path=%r)", diff --git a/pyproject.toml b/pyproject.toml index 96cd30ad7..624bf0f65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "typing-extensions", "uuid7>=0.1.0,<1.0", "fastmcp>=3.0,<4.0", - "fastmcp-extensions>=0.7.0,<1.0.0", + "fastmcp-extensions>=0.8.0,<1.0.0", "starlette", "uv>=0.5.0,<0.9.0", "prefab-ui>=0.20.1,<0.21", From 02f3d983aca11cf365f867633dbeb4c9ff8bc7c4 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Mon, 13 Jul 2026 23:10:55 +0000 Subject: [PATCH 4/8] fix(mcp): advertise endpoint URL matching the mounted mcp_path Co-Authored-By: AJ Steers --- airbyte/mcp/http_main.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/airbyte/mcp/http_main.py b/airbyte/mcp/http_main.py index e6351630f..671dbcacc 100644 --- a/airbyte/mcp/http_main.py +++ b/airbyte/mcp/http_main.py @@ -53,7 +53,12 @@ def main() -> None: # When deployed behind a path-stripping LB (MCP_SERVER_URL has a path # component like /cloud-mcp), serve the MCP endpoint at root so the # public URL is just the base path. Otherwise keep the FastMCP default. - mcp_path = "/" if urlparse(_get_server_url()).path.strip("/") else "/mcp" + server_url = _get_server_url() + mcp_path = "/" if urlparse(server_url).path.strip("/") else "/mcp" + + # The advertised endpoint must match where the MCP route is actually mounted: + # the bare server URL when mounted at root, otherwise the server URL + mcp_path. + endpoint_url = server_url if mcp_path == "/" else server_url.rstrip("/") + mcp_path # Serve a browser-friendly landing page on GET at the MCP path. In stateless # mode FastMCP only binds POST/DELETE there, so this GET route does not @@ -62,7 +67,7 @@ def main() -> None: app, path=mcp_path, title=MCP_LANDING_TITLE, - endpoint_url=_get_server_url(), + endpoint_url=endpoint_url, docs_url=MCP_LANDING_DOCS_URL, ) From 9f0a649aac9c89eaa3bc1089dc4ed7c6e370fd31 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:26:21 +0000 Subject: [PATCH 5/8] chore(deps): relock fastmcp-extensions to 0.8.0 Co-Authored-By: AJ Steers --- uv.lock | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/uv.lock b/uv.lock index 42c018db6..ebf53b984 100644 --- a/uv.lock +++ b/uv.lock @@ -201,7 +201,7 @@ requires-dist = [ { name = "duckdb", specifier = "==1.4.3" }, { name = "duckdb-engine", specifier = "==0.17.0" }, { name = "fastmcp", specifier = ">=3.0,<4.0" }, - { name = "fastmcp-extensions", specifier = ">=0.7.0,<1.0.0" }, + { name = "fastmcp-extensions", specifier = ">=0.8.0,<1.0.0" }, { name = "google-auth", specifier = ">=2.27.0,<3.0" }, { name = "google-cloud-bigquery", specifier = ">=3.12.0,<4.0" }, { name = "google-cloud-bigquery-storage", specifier = ">=2.25.0,<3.0" }, @@ -1129,18 +1129,19 @@ wheels = [ [[package]] name = "fastmcp-extensions" -version = "0.7.0" +version = "0.8.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp" }, + { name = "httpx" }, { name = "mcp" }, { name = "segment-analytics-python" }, { name = "sentry-sdk" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bd/62/8ff5abef165be10811847900d33b4b101d14f54fa2cc537c3b7be8a2e933/fastmcp_extensions-0.7.0.tar.gz", hash = "sha256:bab24601c9fe48c9e5ef9c4704bbb3553192ee05815740bac0faaa40fefd99b1", size = 183069, upload-time = "2026-06-29T18:16:00.393Z" } +sdist = { url = "https://files.pythonhosted.org/packages/dc/06/45a95610e89672b195b7fcc1472a0c15e0afd8adf5946c011e7a4f792a48/fastmcp_extensions-0.8.0.tar.gz", hash = "sha256:c208bdff5f2cb806ef4b43335540a15be4c21aa3953b98f22e376a658e882ec1", size = 189660, upload-time = "2026-07-14T01:04:20.487Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/bd/a4b801ed298c11b30595f188fdffecaff0f3a01cab04e0cedc25c2f225c6/fastmcp_extensions-0.7.0-py3-none-any.whl", hash = "sha256:374a227d682e4fbdce948c1e255110dcb2dc76aafc9581c90e502e6eeacb3fe2", size = 51956, upload-time = "2026-06-29T18:15:59.052Z" }, + { url = "https://files.pythonhosted.org/packages/a2/30/39ffeb3635557dd3b9f2c97aaa8408254acf56899c63fe1ea4334fc54dd8/fastmcp_extensions-0.8.0-py3-none-any.whl", hash = "sha256:e11c9f28ffbcb42d268245b97b3d7a8c9572059ca49cb784cc779d1d6e4169cd", size = 56751, upload-time = "2026-07-14T01:04:19.116Z" }, ] [[package]] From 95aced71066286704dc308ea159a2b8e1db2848d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:31:46 +0000 Subject: [PATCH 6/8] chore(deps): pin fastmcp-extensions to 0.9.0 prerelease with register_landing_page Co-Authored-By: AJ Steers --- pyproject.toml | 2 +- uv.lock | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 624bf0f65..753f3ea62 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "typing-extensions", "uuid7>=0.1.0,<1.0", "fastmcp>=3.0,<4.0", - "fastmcp-extensions>=0.8.0,<1.0.0", + "fastmcp-extensions>=0.9.0.dev202607140130,<1.0.0", "starlette", "uv>=0.5.0,<0.9.0", "prefab-ui>=0.20.1,<0.21", diff --git a/uv.lock b/uv.lock index ebf53b984..5c30c1d20 100644 --- a/uv.lock +++ b/uv.lock @@ -201,7 +201,7 @@ requires-dist = [ { name = "duckdb", specifier = "==1.4.3" }, { name = "duckdb-engine", specifier = "==0.17.0" }, { name = "fastmcp", specifier = ">=3.0,<4.0" }, - { name = "fastmcp-extensions", specifier = ">=0.8.0,<1.0.0" }, + { name = "fastmcp-extensions", specifier = ">=0.9.0.dev202607140130,<1.0.0" }, { name = "google-auth", specifier = ">=2.27.0,<3.0" }, { name = "google-cloud-bigquery", specifier = ">=3.12.0,<4.0" }, { name = "google-cloud-bigquery-storage", specifier = ">=2.25.0,<3.0" }, @@ -1129,7 +1129,7 @@ wheels = [ [[package]] name = "fastmcp-extensions" -version = "0.8.0" +version = "0.9.0.dev202607140130" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp" }, @@ -1137,11 +1137,12 @@ dependencies = [ { name = "mcp" }, { name = "segment-analytics-python" }, { name = "sentry-sdk" }, + { name = "starlette" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/dc/06/45a95610e89672b195b7fcc1472a0c15e0afd8adf5946c011e7a4f792a48/fastmcp_extensions-0.8.0.tar.gz", hash = "sha256:c208bdff5f2cb806ef4b43335540a15be4c21aa3953b98f22e376a658e882ec1", size = 189660, upload-time = "2026-07-14T01:04:20.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/85/3a/a8e1985da885231be0f76a39ea55bbf29157f9b6d24d6abdcddd1a25f1cd/fastmcp_extensions-0.9.0.dev202607140130.tar.gz", hash = "sha256:4c562e1d690f20dce0eda8e50183234eb6b561e39da1587e40c8333f4737e113", size = 192948, upload-time = "2026-07-14T01:30:21.624Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/30/39ffeb3635557dd3b9f2c97aaa8408254acf56899c63fe1ea4334fc54dd8/fastmcp_extensions-0.8.0-py3-none-any.whl", hash = "sha256:e11c9f28ffbcb42d268245b97b3d7a8c9572059ca49cb784cc779d1d6e4169cd", size = 56751, upload-time = "2026-07-14T01:04:19.116Z" }, + { url = "https://files.pythonhosted.org/packages/7b/55/7a9b071f3a5c08c0c5c66422c2c99bb5f703585efbcc22bee8c5ab99c679/fastmcp_extensions-0.9.0.dev202607140130-py3-none-any.whl", hash = "sha256:1a035c095675a72801946138dd64da1b4d3bc7bc73df7c2e12e0116c8ad40b5a", size = 59939, upload-time = "2026-07-14T01:30:20.435Z" }, ] [[package]] From 2be4af88ea3d13072f52ca0cb35cc6bd45765c73 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:45:40 +0000 Subject: [PATCH 7/8] chore(deps): bump fastmcp-extensions to prerelease with href scheme validation Co-Authored-By: AJ Steers --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 753f3ea62..952a88da2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "typing-extensions", "uuid7>=0.1.0,<1.0", "fastmcp>=3.0,<4.0", - "fastmcp-extensions>=0.9.0.dev202607140130,<1.0.0", + "fastmcp-extensions>=0.9.0.dev202607140144,<1.0.0", "starlette", "uv>=0.5.0,<0.9.0", "prefab-ui>=0.20.1,<0.21", diff --git a/uv.lock b/uv.lock index 5c30c1d20..c0abc02b1 100644 --- a/uv.lock +++ b/uv.lock @@ -201,7 +201,7 @@ requires-dist = [ { name = "duckdb", specifier = "==1.4.3" }, { name = "duckdb-engine", specifier = "==0.17.0" }, { name = "fastmcp", specifier = ">=3.0,<4.0" }, - { name = "fastmcp-extensions", specifier = ">=0.9.0.dev202607140130,<1.0.0" }, + { name = "fastmcp-extensions", specifier = ">=0.9.0.dev202607140144,<1.0.0" }, { name = "google-auth", specifier = ">=2.27.0,<3.0" }, { name = "google-cloud-bigquery", specifier = ">=3.12.0,<4.0" }, { name = "google-cloud-bigquery-storage", specifier = ">=2.25.0,<3.0" }, @@ -1129,7 +1129,7 @@ wheels = [ [[package]] name = "fastmcp-extensions" -version = "0.9.0.dev202607140130" +version = "0.9.0.dev202607140144" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp" }, @@ -1140,9 +1140,9 @@ dependencies = [ { name = "starlette" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/85/3a/a8e1985da885231be0f76a39ea55bbf29157f9b6d24d6abdcddd1a25f1cd/fastmcp_extensions-0.9.0.dev202607140130.tar.gz", hash = "sha256:4c562e1d690f20dce0eda8e50183234eb6b561e39da1587e40c8333f4737e113", size = 192948, upload-time = "2026-07-14T01:30:21.624Z" } +sdist = { url = "https://files.pythonhosted.org/packages/52/61/a855a6fa4ee6094a562225282c5e45b80a8b93c333c0d53408d76d722e26/fastmcp_extensions-0.9.0.dev202607140144.tar.gz", hash = "sha256:b76fd30dc6e2f62995e51ae26e56ab1473e2dd4e3763f00afee85ee0df9a939b", size = 193511, upload-time = "2026-07-14T01:44:42.278Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7b/55/7a9b071f3a5c08c0c5c66422c2c99bb5f703585efbcc22bee8c5ab99c679/fastmcp_extensions-0.9.0.dev202607140130-py3-none-any.whl", hash = "sha256:1a035c095675a72801946138dd64da1b4d3bc7bc73df7c2e12e0116c8ad40b5a", size = 59939, upload-time = "2026-07-14T01:30:20.435Z" }, + { url = "https://files.pythonhosted.org/packages/39/b4/cced31d77bec7748a549ba52fc9059c27ce7201720c11eb7b1fed93b7067/fastmcp_extensions-0.9.0.dev202607140144-py3-none-any.whl", hash = "sha256:2242a37c86cd3537538f2e169038f654d817585b1496822bc200d30672622a04", size = 60266, upload-time = "2026-07-14T01:44:39.009Z" }, ] [[package]] From e933cf4e92a81a5dee7a5656421a6c4e937d915d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:33:00 +0000 Subject: [PATCH 8/8] chore: pin fastmcp-extensions>=0.9.0 (stable release) Co-Authored-By: AJ Steers --- pyproject.toml | 2 +- uv.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 952a88da2..08ee64f8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,7 +46,7 @@ dependencies = [ "typing-extensions", "uuid7>=0.1.0,<1.0", "fastmcp>=3.0,<4.0", - "fastmcp-extensions>=0.9.0.dev202607140144,<1.0.0", + "fastmcp-extensions>=0.9.0,<1.0.0", "starlette", "uv>=0.5.0,<0.9.0", "prefab-ui>=0.20.1,<0.21", diff --git a/uv.lock b/uv.lock index c0abc02b1..b7684cd1b 100644 --- a/uv.lock +++ b/uv.lock @@ -201,7 +201,7 @@ requires-dist = [ { name = "duckdb", specifier = "==1.4.3" }, { name = "duckdb-engine", specifier = "==0.17.0" }, { name = "fastmcp", specifier = ">=3.0,<4.0" }, - { name = "fastmcp-extensions", specifier = ">=0.9.0.dev202607140144,<1.0.0" }, + { name = "fastmcp-extensions", specifier = ">=0.9.0,<1.0.0" }, { name = "google-auth", specifier = ">=2.27.0,<3.0" }, { name = "google-cloud-bigquery", specifier = ">=3.12.0,<4.0" }, { name = "google-cloud-bigquery-storage", specifier = ">=2.25.0,<3.0" }, @@ -1129,7 +1129,7 @@ wheels = [ [[package]] name = "fastmcp-extensions" -version = "0.9.0.dev202607140144" +version = "0.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "fastmcp" }, @@ -1140,9 +1140,9 @@ dependencies = [ { name = "starlette" }, { name = "uvicorn" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/61/a855a6fa4ee6094a562225282c5e45b80a8b93c333c0d53408d76d722e26/fastmcp_extensions-0.9.0.dev202607140144.tar.gz", hash = "sha256:b76fd30dc6e2f62995e51ae26e56ab1473e2dd4e3763f00afee85ee0df9a939b", size = 193511, upload-time = "2026-07-14T01:44:42.278Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c1/67/921815ecb8bf048ac56c2e538c7716e0b338cc5cd53a0c19abd98ff4d5d0/fastmcp_extensions-0.9.0.tar.gz", hash = "sha256:719a78b4187e2fdcb73ff934d318cb41db228f2435fd2a44090a0a57672d1206", size = 193494, upload-time = "2026-07-14T05:30:50.175Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/39/b4/cced31d77bec7748a549ba52fc9059c27ce7201720c11eb7b1fed93b7067/fastmcp_extensions-0.9.0.dev202607140144-py3-none-any.whl", hash = "sha256:2242a37c86cd3537538f2e169038f654d817585b1496822bc200d30672622a04", size = 60266, upload-time = "2026-07-14T01:44:39.009Z" }, + { url = "https://files.pythonhosted.org/packages/59/f0/a039cf5d66ca66aafd4c3b1cfdb6b298d86dc0c844a0802dde33916aadd0/fastmcp_extensions-0.9.0-py3-none-any.whl", hash = "sha256:eea7536435b346d6bbb7fded81beefd840ce94d3f0cfd3a81226b891b4f9c242", size = 60110, upload-time = "2026-07-14T05:30:48.766Z" }, ] [[package]]