mcp-clean-architecture
FastMCP Clean Architecture — MCP App UI Template
Продакшн-ориентированный шаблон для создания MCP-серверов и MCP-приложений на Python и FastMCP, следующий принципам Clean Architecture, инверсии зависимостей, разделения ответственности и современным практикам Python.
Проект также задуман как учебный справочник для разработчиков, переходящих с C# / .NET.
Цель — не просто создать работающий MCP-сервер, а создать такой, который остаётся поддерживаемым, тестируемым, расширяемым и независимым от внешних фреймворков и сервисов.
Цели
Этот шаблон демонстрирует, как построить MCP-приложение с использованием:
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
Примерный домен — e-commerce приложение.
Товары получаются из публичного внешнего API и предоставляются через MCP.
Приложение будет развиваться, чтобы поддерживать такие действия, как:
Поиск товаров
Просмотр деталей товара
Добавление товаров в корзину
Просмотр корзины
Удаление товаров из корзины
MCP App UI обеспечит интерактивный интерфейс в совместимых MCP-хостах.
Архитектура
Проект следует принципам Clean Architecture.
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 APIПравило зависимостей
Самое важное правило:
Presentation ---> Application ---> Domain
^
|
Infrastructure ----------+Зависимости направлены к ядру приложения.
Доменный слой никогда не должен зависеть от:
FastMCP
HTTP libraries
Uvicorn
DummyJSON
Claude
Copilot
databases
environment variables
MCP App UIНапример:
MCP Tool
|
v
GetProductUseCase
|
v
ProductRepository
^
|
DummyJsonProductRepository
|
v
DummyJSON REST APIGetProductUseCase знает об абстракции ProductRepository.
Он не знает, что товары получаются через HTTP или DummyJSON.
Это позволяет:
DummyJSONпозже заменить на:
SQL Server
PostgreSQL
MongoDB
another REST API
mock repositoryбез изменения варианта использования приложения.
Структура проекта
Проект будет развиваться в сторону следующей структуры:
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.mdПапки следует вводить, когда у них есть реальная ответственность.
Шаблон не должен создавать абстракции только ради увеличения количества слоёв.
Ответственность слоёв
Related MCP server: NitroStack
Доменный слой
Содержит основные бизнес-концепции и контракты.
Примеры:
Product
Cart
ProductRepository
CartRepository
ProductNotFoundError
CartErrorДоменный слой должен содержать бизнес-концепции, не зная, как внешний мир взаимодействует с приложением.
Прикладной слой
Содержит специфичные для приложения рабочие процессы и варианты использования.
Примеры:
GetProductUseCase
SearchProductsUseCase
AddProductToCartUseCase
GetCartUseCaseВариант использования координирует доменные абстракции.
Он не должен напрямую вызывать внешний API.
Плохо
class GetProductUseCase:
def execute(self, product_id: int):
requests.get(
f"https://external-api/products/{product_id}"
)Теперь вариант использования знает:
что существует HTTP
какая HTTP-библиотека используется
какой внешний провайдер используется
как работает URL провайдера
Предпочтительно
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)Теперь вариант использования знает только контракт:
ProductRepositoryИнфраструктурный слой
Содержит реализации внешних технических задач.
Примеры:
HTTP clients
REST APIs
repositories
databases
cache
environment configuration
external service adaptersНапример:
ProductRepository
^
|
DummyJsonProductRepositoryИнфраструктура реализует доменные абстракции.
Доменный слой не зависит от инфраструктуры.
Презентационный слой
Содержит MCP-специфичные точки входа.
Примеры:
FastMCP Server
MCP Tools
MCP Resources
MCP Prompts
MCP AppsMCP-инструмент должен оставаться тонким.
Его ответственность в первую очередь:
MCP Request
|
v
Validate / map input
|
v
Use Case
|
v
Map result
|
v
MCP ResponseБизнес-логика не должна жить внутри MCP-декораторов.
MCP-архитектура
MCP и FastMCP — это разные концепции.
MCP
|
`-- Protocol
FastMCP
|
`-- Python framework implementing MCPПриложение использует MCP через Streamable HTTP.
MCP Host
|
| Streamable HTTP
v
http://localhost:8000/mcp
|
v
FastMCP ServerСервер по умолчанию настроен на работу со stateless HTTP.
MCP-компоненты
Инструменты
Действия, которые может выполнять модель.
Примеры:
get_product
search_products
add_product_to_cart
get_cart
remove_product_from_cartКонцептуально:
LLM
|
| tool call
v
MCP Tool
|
v
Use CaseРесурсы
Ресурсы предоставляют данные или контекст, которые MCP-хост может читать.
Они не должны становиться заменой бизнес-логики приложения.
Промпты
Промпты предоставляют переиспользуемые шаблоны промптов через MCP.
Они относятся к границе MCP / презентационного слоя.
MCP App UI
MCP Apps позволяют совместимым MCP-хостам отображать интерактивный интерфейс, связанный с функциональностью MCP.
Наш пример e-commerce в итоге будет отображать нечто концептуально похожее на:
+--------------------------------+
| Product |
| |
| Smartphone |
| |
| $799.99 |
| |
| [ Add to cart ] |
+---------------+----------------+
|
v
MCP Tool Call
|
v
AddProductToCartUseCase
|
v
CartRepositoryВажное архитектурное правило:
MCP App UI — это презентационная задача.
Интерфейс не должен реализовывать бизнес-правила.
Например, клик по:
[ Add to cart ]должен приводить к:
MCP App UI
|
v
MCP Tool
|
v
AddProductToCartUseCase
|
v
CartRepositoryИнтерфейс не манипулирует инфраструктурой напрямую.
Конфигурация окружения
Конфигурация во время выполнения должна поступать из переменных окружения, а не быть захардкоженной.
Текущие переменные:
MCP_SERVER_TRANSPORT
MCP_SERVER_HOST
MCP_SERVER_PORT
MCP_STATELESS_HTTPПример:
$env:MCP_SERVER_PORT="9000"Поток конфигурации:
Operating System / Container
|
| Environment Variables
v
EnvironmentSettings
|
v
server.py
|
v
FastMCPЭто позволяет одному и тому же коду приложения работать в:
Local
Development
Test
Staging
Production
Docker
Kubernetes
Cloud environmentsс разной конфигурацией.
Секреты никогда не должны попадать в Git.
Соглашения о пакетах Python
__init__.py можно использовать для определения публичного API пакета Python.
Например:
from infrastructure.config.environment import EnvironmentSettings
__all__ = [
"EnvironmentSettings",
]Потребители могут затем использовать:
from infrastructure.config import EnvironmentSettingsвместо:
from infrastructure.config.environment import EnvironmentSettingsЭто снижает связанность с внутренней файловой структурой.
Концептуально это похоже на TypeScript:
index.tsиспользуемый как barrel export.
__all__ определяет предполагаемый публичный API.
Это не модификатор доступа, как public или private в C#.
Справочник Python / C#
Этот проект также предназначен для помощи .NET-разработчикам в изучении Python.
Python | Концепция C# |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| примерно |
|
|
|
|
|
|
Repository | часто используется аналогично |
| приблизительно |
|
|
|
|
|
|
| конструктор |
| инициализация пакета / аналогично barrel exports |
Pydantic | типизированная модель + валидация/сериализация |
| концептуально похоже на атрибуты/поведение middleware в зависимости от использования |
Когда вводятся новые концепции Python, их эквиваленты в C# должны документироваться, если это полезно.
Доменные модели
Структурированные модели используют Pydantic там, где полезны валидация и сериализация.
Пример:
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 предоставляет:
validation
type coercion
serialization
JSON-compatible output
JSON Schema generationПаттерн репозитория
Репозитории представляют собой абстракции над данными или внешними системами.
Пример:
from abc import ABC, abstractmethod
from domain.entities import Product
class ProductRepository(ABC):
@abstractmethod
def get_by_id(self, product_id: int) -> Product:
passДля C#-разработчика это концептуально похоже на:
public interface IProductRepository
{
Product GetById(int productId);
}Конкретная реализация в инфраструктуре может затем обеспечить фактическое поведение:
ProductRepository
^
|
DummyJsonProductRepositoryВнешние API
Внешние API должны вызываться из инфраструктурного слоя.
Первоначальная реализация использует публичный DummyJSON API для примера e-commerce.
Архитектура предотвращает прямую зависимость вариантов использования приложения от DummyJSON.
Application
|
v
ProductRepository
^
|
Infrastructure implementation
|
v
DummyJSONЭто позволяет позже заменить внешнего провайдера без переписывания прикладного или доменного слоёв.
Стратегия обработки ошибок
Проект использует централизованную иерархию исключений, вдохновлённую Clean Architecture и распространёнными паттернами обработки исключений в .NET.
Цель — различать:
expected business failures
vs
technical/infrastructure failuresпредоставляя при этом общий структурированный контракт ошибок.
Иерархия ошибок
AppError
|
|-- DomainError
| |
| |-- ProductNotFoundError
| `-- CartError
|
|-- ValidationError
|
`-- InfrastructureError
|
|-- ExternalAPIError
`-- ExternalAPITimeoutErrorВсе известные ошибки приложения в конечном итоге происходят от:
AppErrorБазовое исключение приложения
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,
}Концептуально это похоже на C#:
public abstract class AppException : Exception
{
public string ErrorCode { get; }
protected AppException(
string message,
string errorCode)
: base(message)
{
ErrorCode = errorCode;
}
}Доменные ошибки
Доменные ошибки представляют ожидаемые бизнес-сбои.
Примеры:
Product does not exist
Cart is empty
Product cannot be added to the cart
Requested quantity violates a business ruleПример:
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,
},
)Концептуально похоже на:
public class ProductNotFoundException : DomainException
{
public int ProductId { get; }
public ProductNotFoundException(int productId)
: base($"Product '{productId}' was not found.")
{
ProductId = productId;
}
}Ошибки валидации
Ошибки валидации представляют недопустимый ввод приложения или нарушенные ограничения.
Примеры:
Invalid product ID
Quantity must be greater than zero
Missing required input
Invalid cart operationЭто ожидаемые сбои.
Они должны предоставлять достаточно структурированной информации, чтобы MCP-хост или LLM могли понять, что нужно исправить.
Инфраструктурные ошибки
Инфраструктурные ошибки представляют сбои, связанные с техническими зависимостями.
Примеры:
External API unavailable
HTTP timeout
Connection failure
Unexpected downstream response
Database unavailableНапример:
class InfrastructureError(AppError):
error_code = "INFRASTRUCTURE_ERROR"
class ExternalAPIError(InfrastructureError):
error_code = "EXTERNAL_API_ERROR"Доменный слой не должен зависеть от инфраструктурных исключений.
Сырые исключения библиотек не должны просачиваться через всё приложение.
Например:
httpx.TimeoutException
|
v
ExternalAPITimeoutError
|
v
Application / Presentationвместо:
httpx.TimeoutException
|
+---------------------> MCP HostТрансляция ошибок
Инфраструктура отвечает за трансляцию низкоуровневых технических сбоев, когда это уместно.
Например:
HTTP 404 from product provider
|
v
ProductNotFoundError
HTTP timeout
|
v
ExternalAPITimeoutError
HTTP 500
|
v
ExternalAPIErrorЭто предотвращает связывание остальной части приложения с конкретной HTTP-библиотекой.
Граница обработки ошибок в презентационном слое
MCP-инструменты не должны содержать дублирующую обработку ошибок.
Избегайте:
@mcp.tool
def tool_one():
try:
...
except AppError:
...
@mcp.tool
def tool_two():
try:
...
except AppError:
...
@mcp.tool
def tool_three():
try:
...
except AppError:
...Желаемая архитектура:
MCP Host
|
v
Presentation Error Boundary
|
v
MCP Tool
|
v
Use Case
|
v
Domain / RepositoryИзвестные ошибки приложения могут быть преобразованы в структурированные MCP-дружественные ошибки.
Непредвиденные исключения должны:
logged
|
v
converted to generic internal error
|
v
returned without sensitive detailsЭто концептуально похоже на ASP.NET Core:
Python / MCP ASP.NET Core
AppError AppException
DomainError DomainException
InfrastructureError InfrastructureException
central error boundary IExceptionHandler / Middleware
raise throw
except catchСтруктурированные ошибки
Ошибки должны содержать структурированную информацию, когда это полезно.
Пример:
{
"error_code": "PRODUCT_NOT_FOUND",
"error_type": "ProductNotFoundError",
"message": "Product '123' was not found.",
"details": {
"product_id": 123
}
}Структурированные ошибки улучшают:
поведение MCP-клиента
рассуждения LLM
логирование
наблюдаемость
автоматические тесты
отладку
Правила обработки ошибок
Не раскрывайте сырые инфраструктурные исключения напрямую MCP-клиентам.
Не дублируйте блоки
try/exceptв каждом MCP-инструменте.Используйте специфичные доменные ошибки для ожидаемых бизнес-сбоев.
Используйте ошибки валидации для недопустимого ввода и нарушенных ограничений.
Транслируйте внешние технические сбои в специфичные для приложения ошибки.
Сохраняйте полезный структурированный контекст через
details.Логируйте непредвиденные исключения на границе приложения.
Никогда не раскрывайте секреты, токены, стек-трейсы или чувствительные детали инфраструктуры MCP-клиентам.
Держите коды ошибок стабильными, чтобы клиенты и автоматические тесты могли на них полагаться.
Презентационный слой отвечает за преобразование ошибок приложения в MCP-дружественные ответы.
Внедрение зависимостей и композиция
Зависимости должны быть явными.
Например:
DummyJsonProductRepository
|
v
GetProductUseCase
|
v
MCP ToolКомпозиция/корневая настройка должна находиться рядом с точкой входа приложения, а не внутри доменного слоя.
Проект должен избегать скрытых глобальных зависимостей, когда это практично.
Это будет вводиться постепенно по мере роста приложения.
Стратегия тестирования
Архитектура должна позволять тестировать бизнес-поведение без:
starting FastMCP
calling DummyJSON
opening an HTTP port
running MCP App UIНапример:
Unit Test
|
v
GetProductUseCase
|
v
FakeProductRepositoryЭто делает вариант использования независимо тестируемым.
Модульные тесты
Модульные тесты должны фокусироваться на:
Domain behavior
Use Cases
Validation
Error handlingиспользуя фейковые или мок-зависимости.
Интеграционные тесты
Интеграционные тесты могут проверять границы отдельно:
Infrastructure
|
v
DummyJSON APIи:
MCP Client
|
v
FastMCP ServerТакое разделение предотвращает влияние поведения внешнего API на надёжность всех бизнес-тестов.
Настройка разработки
Требования:
Python 3.12+
uvУстановка/синхронизация зависимостей:
uv syncЗапуск MCP-сервера:
uv run python -m presentation.mcp.serverКонечная точка по умолчанию:
http://localhost:8000/mcpВиртуальное окружение
Проект использует:
.venv/для изолированных зависимостей Python.
uv автоматически управляет окружением проекта.
Команды, как правило, следует выполнять с помощью:
uv run ...Например:
uv run python --versionЭто позволяет не полагаться на глобально установленные зависимости проекта.
Принципы разработки
При расширении этого шаблона:
Держите MCP-специфичный код в Presentation.
Держите бизнес-процессы в Application.
Держите бизнес-модели и контракты независимыми от фреймворков, где это практично.
Держите внешние интеграции в Infrastructure.
Зависите от абстракций, а не от конкретных реализаций Infrastructure.
Держите MCP Tools тонкими.
Не зашивайте конфигурацию, зависящую от окружения.
Не сохраняйте секреты в репозитории.
Предпочитайте типизированный Python.
Проверяйте внешние данные на границах системы.
Держите внешние API DTO отдельно от моделей Domain, когда их структуры расходятся.
Делайте Use Cases независимо тестируемыми.
Предпочитайте явные зависимости скрытому глобальному состоянию.
Добавляйте абстракции, когда они решают реальную архитектурную проблему.
Держите Domain независимым от FastMCP.
Транслируйте ошибки Infrastructure перед их раскрытием за пределами их границы.
Используйте стабильные структурированные коды ошибок.
Держите MCP App UI сосредоточенным на представлении и взаимодействии.
Не помещайте бизнес-логику внутрь MCP-декораторов.
Держите внешний API заменяемым.
Планируемый учебный процесс
Шаблон создается инкрементально.
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 integrationКонечная цель
Финальный проект должен демонстрировать полный поток:
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 Serviceс ошибками, безопасно распространяющимися в обратном направлении:
External failure
|
v
Infrastructure Error
|
v
Application / Domain Error
|
v
Presentation Error Boundary
|
v
Structured MCP Error
|
v
Claude / CopilotНазначение
Этот репозиторий предназначен для того, чтобы стать переиспользуемым шаблоном и учебным справочником для создания FastMCP-серверов и MCP-приложений производственного качества с использованием Clean Architecture.
Проект демонстрирует, как MCP можно рассматривать как границу приложения, а не позволять MCP-специфичным вопросам распространяться по всей кодовой базе.
Основная бизнес-логика должна оставаться независимой от:
FastMCP
MCP transport
MCP App UI
Claude
Copilot
HTTP providers
databases
external APIsЭто делает приложение более простым для:
maintain
test
extend
replace integrations
run in different environments
connect to different MCP hostsсохраняя при этом чёткие архитектурные границы.
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