Skip to content

docs: add multi-tenancy guide, example server, and OAuth e2e tests#15

Merged
andylim-duo merged 10 commits intomainfrom
feature/multi-tenant-docs-testing
Mar 20, 2026
Merged

docs: add multi-tenancy guide, example server, and OAuth e2e tests#15
andylim-duo merged 10 commits intomainfrom
feature/multi-tenant-docs-testing

Conversation

@andylim-duo
Copy link
Owner

Summary

Iteration 6 of the multi-tenancy implementation plan — documentation, example server, and end-to-end tests.

  • Multi-tenancy guide (docs/multi-tenancy.md): Comprehensive documentation covering architecture (with mermaid flow diagram), static and dynamic tenant provisioning (onboarding/offboarding, feature flags, plugin systems), authentication setup with TokenVerifier and identity provider configuration (Duo Security, Auth0, Okta, Microsoft Entra ID, custom JWT), session isolation, security considerations, and backward compatibility.

  • Example server (examples/servers/simple-multi-tenant/): Two-tenant demo (Acme analytics, Globex content) with tenant-scoped tools, resources, prompts, and a shared whoami tool that reads Context.tenant_id. Includes README with usage instructions and in-memory testing guide.

  • OAuth e2e tests (test_multi_tenancy_oauth_e2e.py): Full HTTP stack integration tests proving tenant isolation through the real auth middleware — bearer token → AuthContextMiddlewaretenant_id_var → session manager → handler → tenant-scoped response. Uses StubTokenVerifier to bypass OAuth while exercising the production middleware path.

  • Migration guide update (docs/migration.md): Documents multi-tenancy as a breaking change with migration instructions.

Test Plan

  • All 13 multi-tenancy tests pass (8 in-memory e2e + 5 OAuth e2e)
  • Full test suite passes (1219 passed, 98 skipped, 1 xfailed)
  • pyright passes with 0 errors across the entire project
  • ruff check and ruff format pass cleanly

Add comprehensive documentation and testing for the multi-tenant MCP
server feature (Iteration 6 of the multi-tenancy implementation plan).

Multi-tenancy guide (docs/multi-tenancy.md):
- Architecture overview with mermaid flow diagram
- Static and dynamic tenant provisioning (onboarding, feature flags, plugins)
- Authentication setup with TokenVerifier and identity provider configuration
  (Duo Security, Auth0, Okta, Microsoft Entra ID, custom JWT)
- Session isolation semantics
- Security considerations and backward compatibility

Example server (examples/servers/simple-multi-tenant/):
- Two-tenant demo (Acme analytics, Globex content) with tenant-scoped
  tools, resources, and prompts
- Shared whoami tool demonstrating Context.tenant_id
- README with usage instructions and in-memory testing guide

OAuth e2e tests (test_multi_tenancy_oauth_e2e.py):
- Full HTTP stack test: bearer token -> AuthContextMiddleware ->
  tenant_id_var -> session manager -> handler -> tenant-scoped response
- StubTokenVerifier for testing without a real OAuth server
- ASGI lifespan helper for httpx.ASGITransport
- Tests for tenant tool isolation, tool invocation, whoami identity,
  and unauthenticated request rejection

Also updates migration.md with multi-tenancy breaking change notes.
…nctions

- lowlevel/server.py: pragma no cover on auth_server_provider branch
  (not exercised in multi-tenancy tests), pragma no branch on
  resource_server_url check (always true in tests)
- test_multi_tenancy_e2e.py: pragma no cover on tool/resource/prompt
  functions that exist only for isolation testing, never called
- test_multi_tenancy_oauth_e2e.py: pragma no cover on beta_publish
  (isolation only), pragma no branch on lifespan send elif
Copy link
Owner Author

@andylim-duo andylim-duo left a comment

Choose a reason for hiding this comment

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

PR review with inline comments for 6 concerns identified during review.

- test_multi_tenancy_e2e.py: use `pragma: lax no cover` on
  `tg.cancel_scope.cancel()` lines — covered locally but missed on
  some CI matrix entries due to cancellation unwinding differences
- test_multi_tenancy_oauth_e2e.py: use `pragma: no branch` on nested
  `async with ClientSession` — coverage.py misreports the ->exit arc
  for nested async with on Python 3.11+
Coverage.py misreports the ->exit arc for nested async with on certain
Python versions/platforms. Add # pragma: no branch to the four
streamable_http_client context managers in the OAuth e2e tests.
…review concerns

Add remove_resource() and remove_prompt() methods to MCPServer to match
the existing remove_tool() API, enabling dynamic deprovisioning of all
resource types for multi-tenant servers.

PR review fixes:
- Fix heading grammar: "Simple Registration of..." (concern #1)
- Add private API warning for _lowlevel_server usage in example (concern #3)
- Clarify example server needs TokenVerifier for production (concern #4)
- Guard offboard_tenant against KeyError for unprovisioned tools (concern #5)
- Add remove_resource/remove_prompt to MCPServer and docs (concern #6)

Tests added for both single-tenant and multi-tenant remove operations,
including cross-tenant isolation verification.
Add StubTokenVerifier and --auth CLI flag so the example server can be
tested with curl using bearer tokens. Demo tokens: acme-token (tenant
acme) and globex-token (tenant globex). README updated with step-by-step
curl examples showing session initialization, tool listing, tool
invocation, and unauthenticated rejection.
@andylim-duo andylim-duo self-assigned this Mar 20, 2026
@andylim-duo andylim-duo merged commit 436a108 into main Mar 20, 2026
26 checks passed
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.

1 participant