Skip to content

Conversation

@maxisbey
Copy link
Contributor

Summary

Add a conformance testing pipeline that validates the Python SDK against the @modelcontextprotocol/conformance npm package. This mirrors the approach used in the TypeScript SDK.

Changes

New Files

  • examples/clients/conformance-client/ - Unified conformance test client

    • Handles all test scenarios via environment variables and CLI arguments
    • Supports OAuth authorization code flow (default for all auth scenarios)
    • Supports client credentials with client_secret_basic and private_key_jwt
  • scripts/run-server-conformance.sh - Server conformance test runner

    • Starts the everything-server on port 3001
    • Waits for server readiness
    • Runs npx @modelcontextprotocol/conformance server
  • .github/workflows/conformance.yml - CI workflow

    • server-conformance job: Tests the Python SDK server implementation
    • client-conformance job: Tests the Python SDK client implementation
    • Both jobs use continue-on-error: true to match the TS SDK approach

Test Results (Local)

Test Type Passed
Server 24/24
Client (metadata suite) 36/36
Client (auth suite) 108/108
Client (sep-835 suite) 48/48

Running Locally

# Server conformance
./scripts/run-server-conformance.sh

# Client conformance (individual suites)
npx @modelcontextprotocol/conformance client --command 'uv run --frozen mcp-conformance-client' --suite metadata
npx @modelcontextprotocol/conformance client --command 'uv run --frozen mcp-conformance-client' --suite auth
npx @modelcontextprotocol/conformance client --command 'uv run --frozen mcp-conformance-client' --suite sep-835

Add a conformance testing pipeline that validates the Python SDK against
the @modelcontextprotocol/conformance npm package.

Changes:
- Add conformance-client example that handles all test scenarios
  (auth flows, metadata, scope handling)
- Add run-server-conformance.sh script to test the everything-server
- Add GitHub Actions workflow with server-conformance and client-conformance jobs
- Both jobs use continue-on-error: true to match TS SDK approach

The conformance client supports:
- OAuth authorization code flow (default)
- Client credentials with client_secret_basic
- Client credentials with private_key_jwt
- All auth/metadata and sep-835 scope scenarios

Test results:
- Server: 24/24 passed
- Client: 192/192 passed (metadata, auth, sep-835 suites)
The --all-packages flag triggers a pre-existing issue where
mcp-simple-chatbot is missing its README.md. Use --package to
install only the specific packages needed for each job.
The conformance package requires either --suite or --scenario.
The newer version (0.1.10) supports --suite all.
Copy link
Member

@Kludex Kludex Jan 20, 2026

Choose a reason for hiding this comment

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

Can this not be a package, and not be in examples? You can create its own GitHub action for it under .github/actions, and use it in conformance.yml.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yep fair, this draft was claude mostly one shotting it and then I was gonna clean it up. Any thoughts on where it should go?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed it, let me know what you think

Copy link
Member

Choose a reason for hiding this comment

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

You didn't do what I proposed. 🤔

You can create a folder in .github/actions directory that is called conformance, and paste what you have in scripts there.

- Extract shared default_elicitation_callback that applies schema defaults
  from ElicitRequestFormParams.requested_schema.properties.*.default
- Add elicitation callback to _run_auth_session so all OAuth flows
  advertise elicitation capability
- Call first available tool dynamically instead of hardcoded test-tool
- Add results/ directory to .gitignore
The conformance client now passes a client_metadata_url to
OAuthClientProvider, matching the TypeScript SDK's conformance client.
This allows the SDK's existing CIMD support to activate when the server
advertises client_id_metadata_document_supported=true, resolving the
auth/basic-cimd warning.
Address review feedback: the conformance client is CI test
infrastructure, not a user-facing example. Move it to
scripts/conformance/client.py as a standalone script and colocate it
with the server script (renamed from scripts/run-server-conformance.sh
to scripts/conformance/run-server.sh).

This removes the mcp-conformance-client workspace member and its
lockfile entry. The client script is now run directly via
uv run python scripts/conformance/client.py.
Same treatment as the unified conformance client: this is CI test
infrastructure, not a user-facing example. Moved to
scripts/conformance/auth-client.py and removed as a workspace member.
@maxisbey maxisbey marked this pull request as ready for review January 21, 2026 14:49
@maxisbey maxisbey requested a review from pcarleton January 21, 2026 14:49
@maxisbey maxisbey marked this pull request as draft January 21, 2026 14:56
Copy link
Member

@pcarleton pcarleton left a comment

Choose a reason for hiding this comment

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

approach lgtm

Match the pinning pattern used in shared.yml for supply chain security.
@maxisbey maxisbey marked this pull request as ready for review January 21, 2026 15:15
pcarleton
pcarleton previously approved these changes Jan 21, 2026
Copy link
Member

@pcarleton pcarleton left a comment

Choose a reason for hiding this comment

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

LGTM

Comment on lines 351 to 355
try:
asyncio.run(run_auth_code_client(server_url))
except Exception:
logger.exception("Client failed")
sys.exit(1)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
try:
asyncio.run(run_auth_code_client(server_url))
except Exception:
logger.exception("Client failed")
sys.exit(1)
asyncio.run(run_auth_code_client(server_url))

Comment on lines +70 to +81
def get_conformance_context() -> dict[str, Any]:
"""Load conformance test context from MCP_CONFORMANCE_CONTEXT environment variable."""
context_json = os.environ.get("MCP_CONFORMANCE_CONTEXT")
if not context_json:
raise RuntimeError(
"MCP_CONFORMANCE_CONTEXT environment variable not set. "
"Expected JSON with client_id, client_secret, and/or private_key_pem."
)
try:
return json.loads(context_json)
except json.JSONDecodeError as e:
raise RuntimeError(f"Failed to parse MCP_CONFORMANCE_CONTEXT as JSON: {e}") from e
Copy link
Member

Choose a reason for hiding this comment

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

You do have the same in the other auth-client.py file.

Comment on lines 335 to 345
try:
if handler:
asyncio.run(handler(server_url))
elif scenario.startswith("auth/"):
asyncio.run(run_auth_code_client(server_url))
else:
print(f"Unknown scenario: {scenario}", file=sys.stderr)
sys.exit(1)
except Exception:
logger.exception("Client failed")
sys.exit(1)
Copy link
Member

Choose a reason for hiding this comment

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

Drop the except here as well, please. It already exits with status 1.

- Move client.py and run-server.sh to .github/actions/conformance/
- Remove redundant auth-client.py (superseded by client.py)
- Drop try/except wrappers in main() - unhandled exceptions already
  exit non-zero with tracebacks
- Update workflow paths accordingly
Copy link
Member

Choose a reason for hiding this comment

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

This should probably be an action by itself that could be released from the conformance repo, but it's okay.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ah yea good idea, will leave that for outside pr unless you think it's a blocker

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done here: modelcontextprotocol/conformance#113

with python sdk test pr here: #1921

@maxisbey maxisbey requested review from Kludex and pcarleton January 21, 2026 15:56
@maxisbey maxisbey merged commit 213cf99 into main Jan 21, 2026
50 of 51 checks passed
@maxisbey maxisbey deleted the add-conformance-testing-ci branch January 21, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants