Issue
Payment business logic was incorrectly implemented in the HTTP infrastructure layer (main.py) rather than in a domain-specific FastMCP server. This violated the intended three-layer architecture by mixing:
- HTTP layer: Handling signature verification and routing.
- Business logic: Payment processing logic, which should reside in a dedicated domain-specific server.
The tight coupling led to:
- Duplicate logic: Payment logic was repeated across
main.pyandpayment_mcp_server.py. - Testing challenges: Full HTTP stack was required to test payment logic.
- Security risks: Stripe API keys and webhook secrets were exposed in the HTTP layer.
Culprit
The architectural misunderstanding stemmed from treating FastMCP as a secondary component rather than the primary owner of business logic. Key issues included:
- Improper Server Composition: The
mount()pattern in FastMCP was not fully utilized, preventing proper delegation of payment logic to a dedicated domain server. - Missing Domain Tool: The
payment_mcp_server.pydid not include a dedicated tool for handling Stripe webhooks, leading to direct processing in the HTTP layer. - Direct Processing: The HTTP layer processed Stripe events directly instead of delegating to the payment domain, creating unnecessary complexity.
- Configuration Leak: Sensitive Stripe API keys and secrets were accessible in the HTTP layer rather than being encapsulated within the payment domain.
Solution
Domain-Specific Tool Implementation
A dedicated tool for handling Stripe webhooks was implemented in the payment domain server (payment_mcp_server.py). This tool encapsulates all payment business logic and returns structured responses:
- Tool Design: The
handle_stripe_webhooktool processes different Stripe event types (e.g.,payment_intent.succeeded) and returns domain-specific actions.
Thin HTTP Infrastructure Layer
The HTTP layer (main.py) was refactored to focus exclusively on:
- Signature Verification: Ensuring the integrity of incoming Stripe webhooks.
- Deduplication: Preventing duplicate processing of events.
- Delegation: Routing events to the FastMCP orchestrator for domain-specific processing.
The HTTP layer delegates all payment logic to the FastMCP orchestrator, ensuring clean separation of concerns:
async with Client("/app/src/main_mcp_server.py") as mcp_client:
result = await mcp_client.call_tool(
"payment_handle_stripe_webhook",
event_type=event_type,
event_data=event_data
)
Orchestrator Configuration
The FastMCP orchestrator was configured to properly mount the payment server, enabling namespace-based routing:
main_mcp.mount(payment_mcp, prefix="payment") # Routes payment_* calls
Key Technical Improvements
- 100% Business Logic Removal: Eliminated redundant payment logic from the HTTP layer.
- Automatic Routing: Tools are now accessible via
payment_handle_stripe_webhookthrough mounting. - Isolated Testing: The payment domain can be tested independently without requiring the HTTP stack.
- Encapsulated Configuration: Stripe secrets and API keys are now fully encapsulated within the payment domain.
- Structured Responses: Tools return standardized responses for downstream processing.
Architecture Benefits
The refactored architecture ensures:
- FastAPI: Handles only HTTP-related concerns (e.g., routing, security).
- FastMCP Orchestrator: Routes calls to domain-specific servers using namespace prefixes.
- Domain Servers: Contain all business logic, improving maintainability and testability.
This approach adheres to the FastMCP 2.0 server composition pattern, where domain-specific logic is isolated and accessible through a clean, prefixed routing mechanism.