Building an AI-Ready Product Catalog with FastAPI and MCP
Objective: In this lab, you will create a product catalog API using FastAPI and transform it into an AI-accessible service using the Model Context Protocol (MCP) with FastMCP. The lab is divided into two parts: Part 1 focuses on building and testing a FastAPI-based product catalog API, and Part 2 guides you through creating an MCP server to expose your API as AI-callable tools.
Prerequisites:
- Python 3.10+ installed.
- Basic understanding of Python, REST APIs, and JSON.
- Familiarity with terminal commands and virtual environments.
- Installations:
fastapi[all],fastmcp, anduvicorn(viapiporuv). - Optional: Claude Desktop for testing AI tool calls (free tier sufficient).
- Code editor (e.g., VS Code).
- A project directory (e.g.,
product-catalog-lab).
Part 1: Building the FastAPI Product Catalog API
Objective: Create a FastAPI application with endpoints to list all products and retrieve a product by ID, using Pydantic models for data validation and a mock in-memory database.
Step 1.1: Set Up Your Environment
-
Create a project directory:
mkdir product-catalog-lab cd product-catalog-lab -
Set up a virtual environment:
python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate -
Install required packages:
pip install fastapi[all] uvicornAlternatively, use
uv:uv add fastapi[all] uvicorn
Step 1.2: Create the FastAPI Application
-
Create a file named
main.pyin your project directory. -
Add the following code to define the product catalog API:
# main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Optional # Initialize FastAPI app app = FastAPI(title="Product Catalog API", description="A simple API for managing a product catalog") # Define Pydantic model for Product class Product(BaseModel): id: int name: str price: float description: Optional[str] = None # Mock in-memory database products_db = [ Product(id=1, name="Laptop", price=999.99, description="High-end gaming laptop"), Product(id=2, name="Wireless Mouse", price=29.99, description="Ergonomic wireless mouse"), Product(id=3, name="Keyboard", price=59.99), ] @app.get("/products", response_model=List[Product]) async def list_products(): """Retrieve a list of all products in the catalog.""" return products_db @app.get("/products/{product_id}", response_model=Product) async def get_product(product_id: int): """Retrieve a specific product by its ID.""" for product in products_db: if product.id == product_id: return product raise HTTPException(status_code=404, detail="Product not found")Explanation:
- Pydantic Model:
Productenforces data structure withid,name,price, and optionaldescription. - Mock Database:
products_dbis a simple list for demonstration. In a real app, youâd use a database like SQLite or PostgreSQL. - Endpoints:
GET /products: Returns all products.GET /products/{product_id}: Returns a single product or a 404 error if not found.
- Docstrings: These improve API documentation and will be used by MCP for AI-friendly tool descriptions.
- Pydantic Model:
Step 1.3: Test the FastAPI Application
-
Run the FastAPI server:
uvicorn main:app --host localhost --port 8000 --reload -
Open a browser or use
curlto test the endpoints:-
List Products:
curl http://localhost:8000/productsExpected output:
[ { "id": 1, "name": "Laptop", "price": 999.99, "description": "High-end gaming laptop" }, { "id": 2, "name": "Wireless Mouse", "price": 29.99, "description": "Ergonomic wireless mouse" }, { "id": 3, "name": "Keyboard", "price": 59.99, "description": null } ] -
Get Product by ID:
curl http://localhost:8000/products/1Expected output:
{ "id": 1, "name": "Laptop", "price": 999.99, "description": "High-end gaming laptop" }Test a non-existent ID:
curl http://localhost:8000/products/999Expected output:
{ "detail": "Product not found" }
-
-
Visit
http://localhost:8000/docsin a browser to explore the interactive Swagger UI. Test both endpoints there.
Step 1.4: Verify OpenAPI Schema
-
Access the OpenAPI schema at
http://localhost:8000/openapi.json. -
Save this schema to a file (
openapi.json) for reference:curl http://localhost:8000/openapi.json > openapi.jsonThis schema will be used by FastMCP to generate AI tools.
Step 1.5: Reflection Questions
- How does Pydantic ensure data validation in the API responses?
- What happens if you send an invalid
product_id(e.g., a string instead of an integer)? - How could you extend this API to include a
POST /productsendpoint for adding new products?
Deliverable: A running FastAPI server with two functional endpoints, verified via curl or Swagger UI.
Part 2: Creating the MCP Server
Objective: Use FastMCP to expose your FastAPI endpoints as AI-callable tools via an MCP server, enabling integration with AI agents like Claude.
Step 2.1: Install FastMCP
-
Install FastMCP in your virtual environment:
pip install fastmcpOr with
uv:uv add fastmcp
Step 2.2: Create the MCP Server
-
Create a new file named
mcp_server.pyin your project directory. -
Add the following code to set up the MCP server:
# mcp_server.py import sys from pathlib import Path import asyncio from contextlib import asynccontextmanager # Ensure the project root is in the Python path sys.path.append(str(Path(__file__).parent)) from fastmcp import FastMCP from main import products_db, Product from typing import List # Initialize FastMCP mcp = FastMCP( name="Product Catalog MCP Server", ) @mcp.tool() def list_products() -> List[dict]: """List all available products with their ID, name, price, and description.""" return [product.model_dump() for product in products_db] @mcp.tool() def get_product(product_id: int) -> dict: """Retrieve details of a specific product by its ID. Args: product_id: The unique identifier of the product """ for product in products_db: if product.id == product_id: return product.model_dump() return {"error": "Product not found"} if __name__ == "__main__": mcp.run()Explanation:
- FastMCP Integration: Links your FastAPI app to MCP, auto-converting routes to tools.
- Tool Decorators:
@mcp.toolcustomizes how routes appear to AI agents, using your routeâs logic and Pydantic schemas. - Separate Port: Runs on
8001to avoid conflicting with your FastAPI server (8000). - Path Fix:
sys.path.appendensures yourmain.pyis importable.
Step 2.3: Run the MCP Server
-
Keep your FastAPI server running (
uvicorn main:app --port 8000). -
In a new terminal, activate the virtual environment and start the MCP server:
source venv/bin/activate # On Windows: venv\Scripts\activate python mcp_server.py -
Look for console output showing the MCP server running on
http://localhost:8001and a JSON config snippet for AI clients.
Step 2.4: Test with an AI Client (Claude Desktop)
-
Install Claude Desktop if not already installed.
-
Open Claude Desktop and navigate to Settings > Developer > Edit config to edit
claude-desktop-config.json. -
Add the MCP server configuration (adjust paths as needed):
{ "mcpServers": { "product-catalog": { "command": "\\full\\path\\to\\product-catalog-lab\\venv\\Scripts\\python.exe", "args": ["\\full\\path\\to\\product-catalog-lab\\mcp_server.py"] } } }- Replace
/full/path/to/product-catalog-labwith your project directory path.
- Replace
-
Restart Claude Desktop.
-
In a new chat, enable tools:
-
Click Search and tools > Select “product-catalog-mcp”.
-
Test queries like:
- âList all products in the catalog.â
- âWhat is the product with ID 2?â
-
Expected outputs:
-
For âList all productsâ:
Tool Call: list_products_tool Response: [ {"id": 1, "name": "Laptop", "price": 999.99, "description": "High-end gaming laptop"}, {"id": 2, "name": "Wireless Mouse", "price": 29.99, "description": "Ergonomic wireless mouse"}, {"id": 3, "name": "Keyboard", "price": 59.99, "description": null} ] -
For âProduct with ID 2â:
Tool Call: get_product_tool Response: {"id": 2, "name": "Wireless Mouse", "price": 29.99, "description": "Ergonomic wireless mouse"}
-
-
Step 2.5: Reflection Questions
- How does FastMCP use your FastAPI appâs OpenAPI schema to create tools?
- What benefits do the
@mcp.tooldecorators provide for AI interactions? - How would you secure the MCP server for production use (e.g., adding authentication)?
Deliverable: A running MCP server exposing your FastAPI endpoints as AI-callable tools, verified via Claude Desktop or another MCP-compatible client.
Additional Notes
- Extending the Lab:
- Add a
POST /productsendpoint to your FastAPI app and test excluding it from MCP exposure. - Replace the mock
products_dbwith a PostgreSQL database usingSQLAlchemy. - Experiment with Speakeasy CLI to generate a standalone MCP server from
openapi.json. - Deploy using Docker.
- Add a
Submission: Submit your main.py and mcp_server.py files, along with screenshots of:
- Swagger UI showing both FastAPI endpoints.
- Claude Desktop tool call outputs for both
list_products_toolandget_product_tool.
By Wahid Hamdi