Project: Tenant Management: An Evolutionary Project
Evolution: Evolution 4: AI Integration Layer LLM APIs + MCP Enhanced UX
Focus: MCP Protocol Integration
Status: 📋 Planned
Today marks a significant milestone in the Tenant Management evolutionary journey: we’re taking our first step into the AI/LLM world by creating a Model Context Protocol (MCP) server that bridges our Spring Boot REST API with AI-powered applications. This conversion transforms our enterprise Java backend into an AI-ready system that can be seamlessly integrated with LLM-supported tools like Cursor, Claude Desktop, and other MCP-compatible clients.
Evolution Context: This post is part of Evolution 4: AI Integration Layer LLM APIs + MCP Enhanced UX in the Tenant Management Evolutionary Project. This evolution focuses on AI integration and enhanced user experience, building upon the enterprise-ready Spring Boot application established in Evolution 3.
Requirements Context: This iteration continues to fulfill the functional goals from Landlord-Tenant Management System: Requirements and Objectives while introducing AI-powered capabilities that enhance the user experience.
The Model Context Protocol (MCP) is an open protocol that enables AI applications to securely access external data sources and tools. By converting our REST API into an MCP server, we’re creating a standardized interface that allows LLMs to interact with our tenant management system naturally, without requiring custom API integrations.
Traditional REST APIs require:
MCP solves these challenges by:
The MCP server acts as a translation layer between our Spring Boot REST API and AI applications:
graph LR
A[LLM Application<br/>Cursor/Claude Desktop] -->|MCP Protocol| B[MCP Server<br/>Python/FastMCP]
B -->|HTTP REST| C[Spring Boot Backend<br/>Java/Spring Boot]
C -->|JPA| D[PostgreSQL Database]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#f3e5f5
style D fill:#e8f5e9
httpx) that communicates with Spring Boot backendThe MCP project follows a clean, modular architecture:
tenant-management-mcp/
├── pyproject.toml # uv project definition
├── README.md # Tool catalogue and usage
├── .env.example # Configuration template
└── src/tm_mcp/
├── __init__.py
├── __main__.py # Entry point
├── config.py # Pydantic settings
├── http_client.py # HTTP wrapper + error handling
├── schemas.py # Pydantic models and adapters
├── server.py # FastMCP server factory
└── tools/ # Tool registrations by domain
├── properties.py
├── tenants.py
└── transactions.py
BackendApiErrorThe server uses Pydantic for environment-based configuration:
# config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
base_url: str = "http://localhost:8080"
api_token: Optional[str] = None
class Config:
env_prefix = "BACKEND_MCP_"
This approach provides:
The HTTP client layer encapsulates all backend communication:
# http_client.py
async def request_json(
method: str,
path: str,
*,
params: Optional[Mapping[str, Any]] = None,
body: Optional[Any] = None,
adapter: TypeAdapter[T],
) -> T:
"""Send a request and parse the JSON body with type validation."""
response = await _send(method, path, params=params, body=body)
payload = response.json()
return adapter.validate_python(payload)
Key features:
BackendApiErrorEach domain (properties, tenants, transactions) follows a consistent pattern:
# tools/properties.py
def register_property_tools(server) -> None:
@server.tool(
name="list_properties",
description="Retrieve every property managed by the backend.",
)
async def list_properties() -> list[Property]:
return await request_json(
"GET",
"/api/properties",
adapter=property_list_adapter
)
@server.tool(
name="create_property",
description="Create a property. Requires address, rent, and maintenance.",
)
async def create_property(payload: PropertyInput) -> Property:
return await request_json(
"POST",
"/api/properties",
body=payload.model_dump(),
adapter=property_adapter,
)
Benefits:
Pydantic models ensure data integrity at both boundaries:
# schemas.py
class Property(BaseModel):
id: PositiveInt
address: str = Field(min_length=1)
rent: float
maintenance: float
model_config = {"extra": "ignore"}
class PropertyInput(BaseModel):
address: str = Field(min_length=1)
rent: float
maintenance: float
The schema layer provides:
The MCP server exposes 15 tools across three domains:
list_properties - Fetch all propertiesget_property - Load property by IDcreate_property - Create new propertyupdate_property - Update existing propertydelete_property - Remove propertylist_property_transactions - Get transactions for a propertylist_tenants - Fetch all tenants with property infoget_tenant - Load tenant by IDcreate_tenant - Create new tenantupdate_tenant - Update tenant fieldsdelete_tenant - Remove tenantlist_tenant_transactions - Get transactions for a tenantlist_transactions - Fetch all transactionsget_transaction - Load transaction by IDcreate_transaction - Create new transactionupdate_transaction - Update transaction fieldsdelete_transaction - Remove transactionPayment Logic: Transactions with type payment_received represent money collected from tenants. All other types (rent, security, utilities, maintenance) represent outstanding charges.
With the MCP server running, an LLM can now interact with the tenant management system naturally:
User: “Show me all tenants who haven’t paid rent this month”
LLM Response (using MCP tools):
list_tenants to get all tenantslist_transactions to get recent transactionspayment_received transactions this monthUser: “Create a new property at 400 Market Street with rent $1800 and maintenance $150”
LLM Response:
create_property with the provided detailsThis natural interaction pattern is what makes MCP powerful—it bridges the gap between human language and API calls.
mcp library’s FastMCP serverThis MCP conversion represents a significant architectural evolution:
The MCP server doesn’t replace the REST API—it enhances it by making it accessible to AI applications. The Spring Boot backend remains the source of truth, and the MCP server acts as a protocol adapter.
The MCP server can be configured in Cursor’s MCP settings:
{
"mcpServers": {
"tenant-management": {
"command": "uv",
"args": ["run", "tm-mcp"],
"env": {
"BACKEND_MCP_BASE_URL": "http://localhost:8080"
}
}
}
}
Once configured, Cursor’s AI assistant can use all tenant management tools directly.
Similar configuration enables Claude Desktop to access the tenant management system:
{
"mcpServers": {
"tenant-management": {
"command": "uv",
"args": ["run", "tm-mcp"],
"env": {
"BACKEND_MCP_BASE_URL": "http://localhost:8080"
}
}
}
}
With the MCP foundation in place, we can now explore:
The MCP server opens the door to these AI-powered features while maintaining the robust backend architecture established in Evolution 3.
@server.tool(
name="create_property",
description="Create a property. Requires address, rent, and maintenance amount.",
)
async def create_property(payload: PropertyInput) -> Property:
"""Create a new property in the system."""
return await request_json(
"POST",
"/api/properties",
body=payload.model_dump(),
adapter=property_adapter,
)
@server.tool(
name="list_transactions",
description="Retrieve all property and tenant transactions.",
)
async def list_transactions() -> list[Transaction]:
"""Fetch all transactions from the backend."""
return await request_json(
"GET",
"/api/transactions",
adapter=transaction_list_adapter,
)
These examples demonstrate the simplicity of the tool layer—each tool is a thin wrapper that validates input, calls the backend, and returns typed data.
# Install dependencies
uv sync
# Run MCP server (stdio transport for MCP clients)
uv run tm-mcp
# Or run with HTTP transport for testing
uv run tm-mcp --transport streamable-http --host 0.0.0.0 --port 8000
The MCP server can be tested independently:
mvn spring-boot:runuv run tm-mcpMCP Enables AI Integration: The Model Context Protocol provides a standardized way to expose APIs to AI applications
Thin Adapter Layer: MCP tools should be thin wrappers around backend calls, not business logic containers
Type Safety Matters: Pydantic validation ensures data integrity at both API boundaries
Protocol Benefits: MCP abstracts HTTP complexity from AI applications, enabling natural language interaction
Evolutionary Approach: This conversion enhances the existing REST API without replacing it, maintaining backward compatibility
Future-Ready: The MCP foundation enables advanced AI features like natural language queries and intelligent automation
The conversion of our Tenant Management REST API into an MCP server represents a significant step forward in making enterprise applications AI-ready. By creating a standardized protocol adapter, we’ve enabled natural language interaction with our system while maintaining the robust backend architecture established in Evolution 3.
This foundation sets the stage for advanced AI capabilities, including conversational interfaces, intelligent automation, and enhanced user experiences. The MCP server doesn’t replace our REST API—it enhances it by making it accessible to the next generation of AI-powered applications.
As we continue Evolution 4, we’ll explore how these AI capabilities can transform the user experience, making property and tenant management more intuitive and efficient than ever before.