mcp-clean-architecture
Docker is listed in the project dependencies for running the application.
Mentions MongoDB as a possible replacement for the DummyJSON repository when discussing dependency inversion.
The README notes it is intended as a learning reference for developers coming from C# / .NET.
Mentions PostgreSQL as a possible replacement for the DummyJSON repository when discussing dependency inversion.
Uses Pydantic models for data validation and serialization across the clean architecture layers.
The server is built with Python and FastMCP, exposing MCP tools, resources, and prompts for an e-commerce domain, including search products, view product details, add to cart, and view cart.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-clean-architectureSearch for products named 'wireless headphones'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
FastMCP Clean Architecture — MCP App UI Template
A production-oriented template for building MCP Servers and MCP Apps with Python and FastMCP, following Clean Architecture, Dependency Inversion, separation of concerns, and modern Python practices.
The project is also intended as a learning reference for developers coming from C# / .NET.
The goal is not only to build an MCP server that works, but to build one that remains maintainable, testable, extensible, and independent from external frameworks and services.
Goals
This template demonstrates how to build an MCP application with:
Python
FastMCP
Streamable HTTP transport
Stateless HTTP
MCP Tools
MCP Resources
MCP Prompts
MCP Apps / App UI
Clean Architecture
Dependency Inversion
Repository Pattern
Use Cases
Pydantic models
External REST API integrations
Environment-based configuration
Async HTTP communication
Dependency Injection / Composition
Centralized Error Handling
Structured application errors
Logging
Unit tests
Integration tests
The sample domain is an e-commerce application.
Products are retrieved from a public external API and exposed through MCP.
The application will evolve to support actions such as:
Search products
View product details
Add products to a cart
View the cart
Remove products from the cart
An MCP App UI will provide an interactive experience inside compatible MCP hosts.
Architecture
The project follows Clean Architecture principles.
MCP HOST
Claude / Copilot / etc.
|
| MCP over HTTP
v
+---------------------------------------------------------+
| PRESENTATION |
| |
| FastMCP Server |
| MCP Tools |
| MCP Resources |
| MCP Prompts |
| MCP App UI |
| Error Boundary |
+---------------------------+-----------------------------+
|
v
+---------------------------------------------------------+
| APPLICATION |
| |
| Use Cases |
| |
| GetProductUseCase |
| SearchProductsUseCase |
| AddProductToCartUseCase |
| GetCartUseCase |
+---------------------------+-----------------------------+
|
v
+---------------------------------------------------------+
| DOMAIN |
| |
| Entities / Models |
| |
| Product |
| Cart |
| |
| Repository Contracts |
| |
| ProductRepository |
| CartRepository |
| |
| Domain Errors |
+---------------------------+-----------------------------+
^
|
+---------------------------+-----------------------------+
| INFRASTRUCTURE |
| |
| External API implementations |
| HTTP clients |
| Configuration |
| Persistence adapters |
| |
| DummyJsonProductRepository |
| DummyJsonCartRepository |
+---------------------------+-----------------------------+
|
v
External REST APIDependency Rule
The most important rule is:
Presentation ---> Application ---> Domain
^
|
Infrastructure ----------+Dependencies point toward the core application.
The Domain must never depend on:
FastMCP
HTTP libraries
Uvicorn
DummyJSON
Claude
Copilot
databases
environment variables
MCP App UIFor example:
MCP Tool
|
v
GetProductUseCase
|
v
ProductRepository
^
|
DummyJsonProductRepository
|
v
DummyJSON REST APIGetProductUseCase knows about the ProductRepository abstraction.
It does not know that products are retrieved using HTTP or DummyJSON.
This allows:
DummyJSONto later be replaced with:
SQL Server
PostgreSQL
MongoDB
another REST API
mock repositorywithout changing the application use case.
Project Structure
The project will evolve toward the following structure:
mcp-clean-architecture/
|
|-- src/
| |
| |-- domain/
| | |
| | |-- entities/
| | | |-- __init__.py
| | | |-- product.py
| | | `-- cart.py
| | |
| | |-- repositories/
| | | |-- __init__.py
| | | |-- product_repository.py
| | | `-- cart_repository.py
| | |
| | `-- errors/
| | |-- __init__.py
| | `-- domain_errors.py
| |
| |-- application/
| | |
| | |-- use_cases/
| | | |-- __init__.py
| | | |-- get_product.py
| | | |-- search_products.py
| | | |-- add_product_to_cart.py
| | | `-- get_cart.py
| | |
| | `-- errors/
| | |-- __init__.py
| | `-- application_errors.py
| |
| |-- infrastructure/
| | |
| | |-- config/
| | | |-- __init__.py
| | | `-- environment.py
| | |
| | |-- http/
| | |
| | |-- repositories/
| | | |-- __init__.py
| | | |-- dummy_json_product_repository.py
| | | `-- dummy_json_cart_repository.py
| | |
| | `-- errors/
| | |-- __init__.py
| | `-- infrastructure_errors.py
| |
| `-- presentation/
| |
| `-- mcp/
| |-- __init__.py
| |-- server.py
| |
| |-- tools/
| |
| |-- resources/
| |
| |-- prompts/
| |
| `-- apps/
|
|-- tests/
| |
| |-- unit/
| `-- integration/
|
|-- .env.example
|-- .gitignore
|-- .python-version
|-- pyproject.toml
|-- uv.lock
`-- README.mdFolders should be introduced when they have a real responsibility.
The template should not create abstractions only for the sake of having more layers.
Layer Responsibilities
Related MCP server: NitroStack
Domain
Contains the core business concepts and contracts.
Examples:
Product
Cart
ProductRepository
CartRepository
ProductNotFoundError
CartErrorThe Domain should contain business concepts without knowing how the outside world communicates with the application.
Application
Contains application-specific workflows and Use Cases.
Examples:
GetProductUseCase
SearchProductsUseCase
AddProductToCartUseCase
GetCartUseCaseA Use Case coordinates domain abstractions.
It should not directly call an external API.
Bad
class GetProductUseCase:
def execute(self, product_id: int):
requests.get(
f"https://external-api/products/{product_id}"
)The Use Case now knows:
HTTP exists
which HTTP library is used
which external provider is used
how the provider URL works
Preferred
class GetProductUseCase:
def __init__(self, repository: ProductRepository):
self.repository = repository
def execute(self, product_id: int) -> Product:
return self.repository.get_by_id(product_id)Now the Use Case only knows the contract:
ProductRepositoryInfrastructure
Contains implementations for external technical concerns.
Examples:
HTTP clients
REST APIs
repositories
databases
cache
environment configuration
external service adaptersFor example:
ProductRepository
^
|
DummyJsonProductRepositoryInfrastructure implements Domain abstractions.
The Domain does not depend on Infrastructure.
Presentation
Contains MCP-specific entry points.
Examples:
FastMCP Server
MCP Tools
MCP Resources
MCP Prompts
MCP AppsAn MCP Tool should remain thin.
Its responsibility is primarily:
MCP Request
|
v
Validate / map input
|
v
Use Case
|
v
Map result
|
v
MCP ResponseBusiness logic should not live inside MCP decorators.
MCP Architecture
MCP and FastMCP are different concepts.
MCP
|
`-- Protocol
FastMCP
|
`-- Python framework implementing MCPThe application uses MCP over Streamable HTTP.
MCP Host
|
| Streamable HTTP
v
http://localhost:8000/mcp
|
v
FastMCP ServerThe server is configured to run stateless HTTP by default.
MCP Components
Tools
Actions the model can execute.
Examples:
get_product
search_products
add_product_to_cart
get_cart
remove_product_from_cartConceptually:
LLM
|
| tool call
v
MCP Tool
|
v
Use CaseResources
Resources expose data or context that an MCP Host can read.
They should not become a replacement for application business logic.
Prompts
Prompts provide reusable prompt templates through MCP.
They belong to the MCP / Presentation boundary.
MCP App UI
MCP Apps allow compatible MCP hosts to display interactive UI associated with MCP functionality.
Our e-commerce example will eventually render something conceptually similar to:
+--------------------------------+
| Product |
| |
| Smartphone |
| |
| $799.99 |
| |
| [ Add to cart ] |
+---------------+----------------+
|
v
MCP Tool Call
|
v
AddProductToCartUseCase
|
v
CartRepositoryThe important architectural rule is:
MCP App UI is a Presentation concern.
The UI should not implement business rules.
For example, clicking:
[ Add to cart ]should result in:
MCP App UI
|
v
MCP Tool
|
v
AddProductToCartUseCase
|
v
CartRepositoryThe UI does not manipulate infrastructure directly.
Environment Configuration
Runtime configuration must come from environment variables rather than being hardcoded.
Current variables:
MCP_SERVER_TRANSPORT
MCP_SERVER_HOST
MCP_SERVER_PORT
MCP_STATELESS_HTTPExample:
$env:MCP_SERVER_PORT="9000"The configuration flow is:
Operating System / Container
|
| Environment Variables
v
EnvironmentSettings
|
v
server.py
|
v
FastMCPThis allows the same application code to run in:
Local
Development
Test
Staging
Production
Docker
Kubernetes
Cloud environmentswith different configuration.
Secrets must never be committed to Git.
Python Package Conventions
__init__.py can be used to define the public API of a Python package.
For example:
from infrastructure.config.environment import EnvironmentSettings
__all__ = [
"EnvironmentSettings",
]Consumers can then use:
from infrastructure.config import EnvironmentSettingsinstead of:
from infrastructure.config.environment import EnvironmentSettingsThis reduces coupling to the internal file structure.
Conceptually, this is similar to a TypeScript:
index.tsused as a barrel export.
__all__ defines the intended public API.
It is not an access modifier like public or private in C#.
Python / C# Reference
This project is also designed to help .NET developers learn Python.
Python | C# concept |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| roughly |
|
|
|
|
|
|
Repository | often used similarly to |
| approximately |
|
|
|
|
|
|
| constructor |
| package initialization / similar purpose to barrel exports |
Pydantic | typed model + validation/serialization |
| conceptually similar to attributes/middleware behavior depending on usage |
When new Python concepts are introduced, their C# equivalents should be documented when useful.
Domain Models
Structured models use Pydantic where validation and serialization are useful.
Example:
from typing import Annotated
from pydantic import BaseModel
class Product(BaseModel):
id: Annotated[int, "Product identifier"]
title: Annotated[str, "Product title"]
description: Annotated[str, "Product description"]
price: Annotated[float, "Product price"]
thumbnail: Annotated[str, "Product thumbnail URL"]Pydantic provides:
validation
type coercion
serialization
JSON-compatible output
JSON Schema generationRepository Pattern
Repositories represent abstractions over data or external systems.
Example:
from abc import ABC, abstractmethod
from domain.entities import Product
class ProductRepository(ABC):
@abstractmethod
def get_by_id(self, product_id: int) -> Product:
passFor a C# developer, this is conceptually similar to:
public interface IProductRepository
{
Product GetById(int productId);
}A concrete Infrastructure implementation can then provide the actual behavior:
ProductRepository
^
|
DummyJsonProductRepositoryExternal APIs
External APIs must be accessed from Infrastructure.
The initial implementation uses the public DummyJSON API for the e-commerce example.
The architecture prevents application use cases from depending directly on DummyJSON.
Application
|
v
ProductRepository
^
|
Infrastructure implementation
|
v
DummyJSONThis allows the external provider to be replaced later without rewriting the Application or Domain layers.
Error Handling Strategy
The project uses a centralized exception hierarchy inspired by Clean Architecture and common .NET exception-handling patterns.
The goal is to distinguish:
expected business failures
vs
technical/infrastructure failureswhile providing a common structured error contract.
Error Hierarchy
AppError
|
|-- DomainError
| |
| |-- ProductNotFoundError
| `-- CartError
|
|-- ValidationError
|
`-- InfrastructureError
|
|-- ExternalAPIError
`-- ExternalAPITimeoutErrorAll known application errors ultimately derive from:
AppErrorBase Application Error
from typing import Any
class AppError(Exception):
error_code: str = "UNKNOWN_ERROR"
def __init__(
self,
message: str,
details: dict[str, Any] | None = None,
):
self.message = message
self.details = details or {}
super().__init__(message)
def to_dict(self) -> dict:
return {
"error_code": self.error_code,
"error_type": self.__class__.__name__,
"message": self.message,
"details": self.details,
}Conceptually, this is similar to C#:
public abstract class AppException : Exception
{
public string ErrorCode { get; }
protected AppException(
string message,
string errorCode)
: base(message)
{
ErrorCode = errorCode;
}
}Domain Errors
Domain errors represent expected business failures.
Examples:
Product does not exist
Cart is empty
Product cannot be added to the cart
Requested quantity violates a business ruleExample:
class DomainError(AppError):
error_code = "DOMAIN_ERROR"
class ProductNotFoundError(DomainError):
error_code = "PRODUCT_NOT_FOUND"
def __init__(self, product_id: int):
super().__init__(
message=f"Product '{product_id}' was not found.",
details={
"product_id": product_id,
},
)Conceptually similar to:
public class ProductNotFoundException : DomainException
{
public int ProductId { get; }
public ProductNotFoundException(int productId)
: base($"Product '{productId}' was not found.")
{
ProductId = productId;
}
}Validation Errors
Validation errors represent invalid application input or violated constraints.
Examples:
Invalid product ID
Quantity must be greater than zero
Missing required input
Invalid cart operationThese are expected failures.
They should provide enough structured information for the MCP Host or LLM to understand what needs to be corrected.
Infrastructure Errors
Infrastructure errors represent failures involving technical dependencies.
Examples:
External API unavailable
HTTP timeout
Connection failure
Unexpected downstream response
Database unavailableFor example:
class InfrastructureError(AppError):
error_code = "INFRASTRUCTURE_ERROR"
class ExternalAPIError(InfrastructureError):
error_code = "EXTERNAL_API_ERROR"The Domain must not depend on Infrastructure exceptions.
Raw library exceptions should not leak through the entire application.
For example:
httpx.TimeoutException
|
v
ExternalAPITimeoutError
|
v
Application / Presentationinstead of:
httpx.TimeoutException
|
+---------------------> MCP HostError Translation
Infrastructure is responsible for translating low-level technical failures when appropriate.
For example:
HTTP 404 from product provider
|
v
ProductNotFoundError
HTTP timeout
|
v
ExternalAPITimeoutError
HTTP 500
|
v
ExternalAPIErrorThis prevents the rest of the application from becoming coupled to a particular HTTP library.
Presentation Error Boundary
MCP Tools should not contain duplicated error handling.
Avoid:
@mcp.tool
def tool_one():
try:
...
except AppError:
...
@mcp.tool
def tool_two():
try:
...
except AppError:
...
@mcp.tool
def tool_three():
try:
...
except AppError:
...The desired architecture is:
MCP Host
|
v
Presentation Error Boundary
|
v
MCP Tool
|
v
Use Case
|
v
Domain / RepositoryKnown application errors can be converted into structured MCP-friendly errors.
Unexpected exceptions should be:
logged
|
v
converted to generic internal error
|
v
returned without sensitive detailsThis is conceptually similar to ASP.NET Core:
Python / MCP ASP.NET Core
AppError AppException
DomainError DomainException
InfrastructureError InfrastructureException
central error boundary IExceptionHandler / Middleware
raise throw
except catchStructured Errors
Errors should contain structured information when useful.
Example:
{
"error_code": "PRODUCT_NOT_FOUND",
"error_type": "ProductNotFoundError",
"message": "Product '123' was not found.",
"details": {
"product_id": 123
}
}Structured errors improve:
MCP client behavior
LLM reasoning
logging
observability
automated tests
debugging
Error Handling Rules
Do not expose raw infrastructure exceptions directly to MCP clients.
Do not duplicate
try/exceptblocks across every MCP Tool.Use specific Domain errors for expected business failures.
Use Validation errors for invalid input and violated constraints.
Translate external technical failures into application-specific errors.
Preserve useful structured context through
details.Log unexpected exceptions at the application boundary.
Never expose secrets, tokens, stack traces, or sensitive infrastructure details to MCP clients.
Keep error codes stable so clients and automated tests can rely on them.
Presentation is responsible for translating application errors into MCP-friendly responses.
Dependency Injection and Composition
Dependencies should be explicit.
For example:
DummyJsonProductRepository
|
v
GetProductUseCase
|
v
MCP ToolThe composition/root wiring belongs near the application entry point, not inside the Domain.
The project should avoid hidden global dependencies when practical.
This will be introduced incrementally as the application grows.
Testing Strategy
The architecture should allow business behavior to be tested without:
starting FastMCP
calling DummyJSON
opening an HTTP port
running MCP App UIFor example:
Unit Test
|
v
GetProductUseCase
|
v
FakeProductRepositoryThis makes the Use Case independently testable.
Unit Tests
Unit tests should focus on:
Domain behavior
Use Cases
Validation
Error handlingusing fake or mock dependencies.
Integration Tests
Integration tests can validate boundaries separately:
Infrastructure
|
v
DummyJSON APIand:
MCP Client
|
v
FastMCP ServerThis separation prevents external API behavior from making every business test unreliable.
Development Setup
Requirements:
Python 3.12+
uvInstall/synchronize dependencies:
uv syncRun the MCP server:
uv run python -m presentation.mcp.serverDefault endpoint:
http://localhost:8000/mcpVirtual Environment
The project uses:
.venv/for isolated Python dependencies.
uv manages the project environment automatically.
Commands should generally be executed using:
uv run ...For example:
uv run python --versionThis avoids relying on globally installed project dependencies.
Development Principles
When extending this template:
Keep MCP-specific code in Presentation.
Keep business workflows in Application.
Keep business models and contracts independent from frameworks where practical.
Keep external integrations in Infrastructure.
Depend on abstractions instead of concrete Infrastructure implementations.
Keep MCP Tools thin.
Do not hardcode environment-specific configuration.
Do not commit secrets.
Prefer typed Python.
Validate external data at system boundaries.
Keep external API DTOs separate from Domain models when their structures diverge.
Make Use Cases independently testable.
Prefer explicit dependencies over hidden global state.
Add abstractions when they solve a real architectural problem.
Keep the Domain independent from FastMCP.
Translate Infrastructure failures before exposing them outside their boundary.
Use stable structured error codes.
Keep MCP App UI focused on presentation and interaction.
Do not put business logic inside MCP decorators.
Keep the external API replaceable.
Planned Learning Flow
The template is being built incrementally.
FastMCP Server
|
v
HTTP Transport
|
v
Environment Configuration
|
v
Python Package Structure
|
v
Pydantic Models
|
v
Domain Entities
|
v
Repository Contracts
|
v
Error Hierarchy
|
v
Infrastructure / External API
|
v
Application Use Cases
|
v
MCP Tools
|
v
Dependency Composition
|
v
Centralized Error Handling
|
v
MCP Resources
|
v
MCP Prompts
|
v
MCP App UI
|
v
Interactive MCP Actions
|
v
Unit Tests
|
v
Integration Tests
|
v
Claude / Copilot integrationFinal Target
The final project should demonstrate the complete flow:
Claude / Copilot
|
| MCP over HTTP
v
FastMCP Server
|
v
MCP App UI
|
| user action
v
MCP Tool
|
v
Application Use Case
|
v
Domain Contract
|
v
Infrastructure Adapter
|
| HTTP
v
External Servicewith errors flowing safely in the opposite direction:
External failure
|
v
Infrastructure Error
|
v
Application / Domain Error
|
v
Presentation Error Boundary
|
v
Structured MCP Error
|
v
Claude / CopilotPurpose
This repository is intended to become a reusable template and learning reference for creating production-quality FastMCP servers and MCP Apps using Clean Architecture.
The project demonstrates how MCP can be treated as an application boundary rather than allowing MCP-specific concerns to spread throughout the codebase.
The core business logic should remain independent from:
FastMCP
MCP transport
MCP App UI
Claude
Copilot
HTTP providers
databases
external APIsThis makes the application easier to:
maintain
test
extend
replace integrations
run in different environments
connect to different MCP hostswhile preserving clear architectural boundaries.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA Model Context Protocol server that enables AI assistants to interact with a complete e-commerce application, providing authentication, product browsing, and shopping cart management through standardized MCP tools.

NitroStackofficial
FlicenseNot gradedqualityBmaintenanceA Python framework for building MCP servers with modular architecture, dependency injection, and built-in authentication. Enables creating scalable, testable MCP services with features like pipeline interceptors and background tasks.3- AlicenseNot gradedqualityCmaintenanceA production-ready template for developing Model Context Protocol (MCP) servers using Python and FastMCP.Apache 2.0
- FlicenseNot gradedqualityCmaintenanceThis enterprise MCP server template provides a production-ready, architecture-first foundation for building MCP servers in Python, with capability registry, dependency injection, and Docker support.
Related MCP Connectors
FastMCP commerce server starter: product catalog, search, and checkout. Deploy to Vercel in 5 min.
Hosted Amazon Seller and Vendor MCP server for Claude, ChatGPT, Cursor, Codex, Gemini, Copilot.
MCP Server for Slima - AI Writing IDE for Novel Authors with AI Beta Reader.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/renaisanci/mcp-clean-architecture'
If you have feedback or need assistance with the MCP directory API, please join our Discord server