Skip to content

fix: substitute server variable defaults when building base URL from OpenAPI spec#3770

Merged
jlowin merged 3 commits intoPrefectHQ:mainfrom
mrishav:fix/openapi-server-variable-substitution
Apr 6, 2026
Merged

fix: substitute server variable defaults when building base URL from OpenAPI spec#3770
jlowin merged 3 commits intoPrefectHQ:mainfrom
mrishav:fix/openapi-server-variable-substitution

Conversation

@mrishav
Copy link
Copy Markdown
Contributor

@mrishav mrishav commented Apr 6, 2026

Ran into this while building a platform that dynamically creates MCP servers from OpenAPI specs. If the spec uses server variables (e.g. https://{region}.api.example.com/v1), the current _create_default_client passes the template string directly to httpx as the base URL, which breaks all requests.

The fix substitutes variable defaults before constructing the client, which is what the OpenAPI spec says should happen when no explicit value is provided.

# before: passes raw template to httpx
base_url = servers[0]["url"]

# after: substitutes defaults first
variables = servers[0].get("variables", {})
if variables:
    substitutions = {name: var.get("default", "") for name, var in variables.items()}
    base_url = base_url.format_map(substitutions)

Added tests for single variable, multiple variables, and static URLs (no regression).

Closes #1681

When an OpenAPI spec defines server variables (e.g. `https://{region}.api.example.com/v1`),
the default values are now substituted before constructing the httpx client base URL.
Previously, the URL was used as-is, causing all requests to fail for specs that use
server variable templating.

Fixes PrefectHQ#1681
@marvin-context-protocol marvin-context-protocol Bot added bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. openapi Related to OpenAPI integration, parsing, or code generation features. labels Apr 6, 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: 69624f3b1b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/fastmcp/server/providers/openapi/provider.py Outdated
mrishav added 2 commits April 5, 2026 23:08
…tution

format_map applies Python string formatting rules, so variable names
like {api.version} would be treated as attribute access and raise errors.
Literal token replacement handles all valid OpenAPI variable names safely.
@marvin-context-protocol
Copy link
Copy Markdown
Contributor

Test Failure Analysis

Summary: The Windows Python 3.10 CI job failed due to a timeout in an unrelated test (test_run_mcp_config). This failure appears to be a pre-existing flakiness issue with subprocess/stdio tests on Windows, not caused by this PR's changes.

Root Cause: The test tests/cli/test_run.py::TestMCPConfig::test_run_mcp_config hit the 5-second timeout on Windows while waiting for a stdio subprocess to become ready. The test spawns a Python subprocess (via StdioMCPServer) and tries to connect via stdio transport — Windows process startup latency occasionally causes this to exceed the timeout. The debug output shows the client was blocked waiting on queue.get() in an AnyIO worker thread while trying to call list_tools through the stdio transport.

Suggested Solution: This failure is unrelated to this PR's changes (which only touch src/fastmcp/server/providers/openapi/provider.py). The PR's own tests all pass. This looks like a pre-existing flaky test on Windows — a retry or rerun of the failing job should confirm.

Detailed Analysis

Failing job: Tests: Python 3.10 on windows-latest (job ID 70130805583)

Failing test: tests/cli/test_run.py::TestMCPConfig::test_run_mcp_config

Relevant log excerpt:

tests\cli\test_run.py ........+++++++++++++++++++++++++++++++++++ Timeout +++++++++++++++++++++++++++++++++++
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Captured stderr ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                    DEBUG    Inferred transport:               inference.py:153
                             <MCPConfigTransport(config='mcpServers={'test_server':
                             StdioMCPServer(command='python',
                             args=['C:\...\test_run_mcp_config0\test.py'], ...)
                    DEBUG    Stdio transport connected             stdio.py:218
~~~~~~~~~~~~~~~~~~~~~ Stack of AnyIO worker thread ~~~~~~~~~~~~~~~~~~~~~
  File ...queue.py, line 171, in get
    self.not_empty.wait()

What the test does: Creates a FastMCP script, writes it to a temp file, wraps it in an MCPConfig/StdioMCPServer, creates a proxy server, then uses a Client to call list_tools. On Windows, subprocess startup is slower and this occasionally exceeds the 5-second test timeout.

PR changes: Only modify src/fastmcp/server/providers/openapi/provider.py (server variable substitution) and add tests in tests/server/providers/openapi/test_server.py. Completely unrelated to the failing test.

Evidence it's pre-existing: The same Windows runner passed in earlier main-branch runs; there's no code path overlap between this PR and test_run_mcp_config.

Related Files
  • tests/cli/test_run.py — Contains test_run_mcp_config (the failing test, line 104)
  • src/fastmcp/server/providers/openapi/provider.py — Modified by this PR (unrelated to failure)
  • tests/server/providers/openapi/test_server.py — New tests added by this PR (all pass)

Copy link
Copy Markdown
Member

@jlowin jlowin left a comment

Choose a reason for hiding this comment

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

Thanks!

@jlowin jlowin merged commit 99eaeb8 into PrefectHQ:main Apr 6, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working. Reports of errors, unexpected behavior, or broken functionality. openapi Related to OpenAPI integration, parsing, or code generation features.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Respect OpenAPI Server Object

2 participants