Skip to content

Commit 73177d1

Browse files
committed
add MCP tutorial for python
1 parent f8ced61 commit 73177d1

9 files changed

Lines changed: 265 additions & 5 deletions
231 KB
Loading
41.6 KB
Loading
48.9 KB
Loading
45.9 KB
Loading

articles/app-service/overview-ai-integration.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,9 @@ Implement AI capabilities in your Python web applications:
7171
- [Build a RAG application with Azure OpenAI and Azure AI Search (Python)](tutorial-ai-openai-search-python.md) - Implement RAG with Python.
7272
- [Run a chatbot with a local SLM (FastAPI)](tutorial-ai-slm-fastapi.md) - Deploy a FastAPI application with a local SLM sidecar.
7373
- [Azure AI Foundry tutorial: Deploy an enterprise chat web app](/azure/ai-foundry/tutorials/deploy-chat-web-app?toc=/azure/app-service/toc.json&bc=/azure/bread/toc.json) - Deploy fully integrated AI web app straight from your deployment in Azure AI Foundry.
74-
- [Build an agentic web app with LangGraph or Azure AI Foundry Agent Service (Python)](tutorial-ai-agent-web-app-langgraph-foundry-python.md) - Create an intelligent agent-based application using Python and a choice of LangGraph or Azure AI Foundry Agent Service.
74+
- [Integrate an App Service app as an MCP Server for GitHub Copilot Chat (Python)](tutorial-ai-model-context-protocol-server-python.md) - Host an MCP server with Python to extend GitHub Copilot Chat capabilities.
75+
- [Add an App Service app as a tool in Azure AI Foundry Agent Service (Python)](tutorial-ai-integrate-azure-ai-agent-python.md) - Expose your Python web app’s capabilities to Azure AI Foundry Agent Service using OpenAPI, enabling agents to perform real-world tasks.
76+
- [Build an agentic web app with LangGraph or Azure AI Foundry Agent Service (Python)](tutorial-ai-agent-web-app-langgraph-foundry-python.md) - Create an intelligent agent-based application using Python, LangGraph, or Azure AI Foundry Agent Service.
7577

7678
## Model Context Protocol (MCP) servers
7779

articles/app-service/toc.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,14 @@ items:
178178
href: tutorial-ai-openai-search-python.md
179179
- name: Chatbot with local SLM
180180
href: tutorial-ai-slm-fastapi.md
181-
- name: Web app from Azure AI Foundry
182-
href: /azure/ai-foundry/tutorials/deploy-chat-web-app?toc=/azure/app-service/toc.json&bc=/azure/bread/toc.json
181+
- name: Add MCP server to app
182+
href: tutorial-ai-model-context-protocol-server-python.md
183+
- name: AI Foundry Agent calling web app
184+
href: tutorial-ai-integrate-azure-ai-agent-python.md
183185
- name: Agentic web app
184186
href: tutorial-ai-agent-web-app-langgraph-foundry-python.md
187+
- name: Web app from Azure AI Foundry
188+
href: /azure/ai-foundry/tutorials/deploy-chat-web-app?toc=/azure/app-service/toc.json&bc=/azure/bread/toc.json
185189
- name: PHP
186190
items:
187191
- name: Quickstart

articles/app-service/tutorial-ai-integrate-azure-ai-agent-python.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ms.service: azure-app-service
1515

1616
In this tutorial, you'll learn how to expose an FastAPI app's functionality through OpenAPI, add it as a tool to Azure AI Foundry Agent Service, and interact with your app using natural language in the agents playground.
1717

18-
If your web application already has useful features, like shopping, hotel booking, or data management, it's easy to make those capabilities available to an AI agent in Azure AI Foundry Agent Service. By simply adding an OpenAPI schema to your app, you enable the agent to understand and use your app's capabilities when it responds to users' prompts. This means anything your app can do, your AI agent can do too, with minimal effort beyond creating an OpenAPI endpoint for your app. In this tutorial, you start with a simple to-do list app. By the end, you'll be able to create, update, and manage tasks with an agent through conversational AI.
18+
If your web application already has useful features, like shopping, hotel booking, or data management, it's easy to make those capabilities available to an AI agent in Azure AI Foundry Agent Service. By simply adding an OpenAPI schema to your app, you enable the agent to understand and use your app's capabilities when it responds to users' prompts. This means anything your app can do, your AI agent can do too, with minimal effort beyond creating an OpenAPI endpoint for your app. In this tutorial, you start with a simple restaurant ratings app. By the end, you'll be able to see restaurant ratings as well as create new restaurants and new reviews with an agent through conversational AI.
1919

2020
:::image type="content" source="media/tutorial-ai-integrate-azure-ai-agent-dotnet/agents-playground.png" alt-text="Screenshot showing the agents playground in the middle of a conversation that takes actions by using the OpenAPI tool.":::
2121

@@ -115,6 +115,8 @@ FasAPI already contains OpenAPI functionality at the default path `/openapi.json
115115
return review
116116
```
117117

118+
This code only shows the create API for brevity and for parity with the existing sample application. If you want, you can also add other APIs, such as update and delete.
119+
118120
1. Start the development server for the sample app with the following commands:
119121

120122
```bash

articles/app-service/tutorial-ai-model-context-protocol-server-node.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ In this tutorial, you'll learn how to expose an Express.js app's functionality t
2020
If your web application already has useful features, like shopping, hotel booking, or data management, it's easy to make those capabilities available for:
2121

2222
- Any [application that supports MCP integration](https://modelcontextprotocol.io/clients), such as GitHub Copilot Chat agent mode in Visual Studio Code or in GitHub Codespaces.
23-
- A custom agent that accesses remote tools by using an [MCP client](https://modelcontextprotocol.io/quickstart/client#javascript).
23+
- A custom agent that accesses remote tools by using an [MCP client](https://modelcontextprotocol.io/quickstart/client#node).
2424

2525
By adding an MCP server to your web app, you enable an agent to understand and use your app's capabilities when it responds to user prompts. This means anything your app can do, the agent can do too.
2626

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
---
2+
title: Web app as MCP server in GitHub Copilot Chat agent mode (Python)
3+
description: Empower GitHub Copilot Chat with your existing Python web apps by integrating their capabilities as Model Context Protocol servers, enabling Copilot Chat to perform real-world tasks.
4+
author: cephalin
5+
ms.author: cephalin
6+
ms.date: 07/22/2025
7+
ms.topic: tutorial
8+
ms.custom:
9+
- devx-track-python
10+
ms.collection: ce-skilling-ai-copilot
11+
ms.service: azure-app-service
12+
---
13+
14+
# Integrate an App Service app as an MCP Server for GitHub Copilot Chat (Python)
15+
16+
In this tutorial, you'll learn how to expose an FastAPI app's functionality through Model Context Protocol (MCP), add it as a tool to GitHub Copilot, and interact with your app using natural language in Copilot Chat agent mode.
17+
18+
:::image type="content" source="media/tutorial-ai-model-context-protocol-server-dotnet/model-context-protocol-call-intro.png" alt-text="Screenshot showing GitHub Copilot calling restaurant ratings MCP server hosted in Azure App Service.":::
19+
20+
If your web application already has useful features, like shopping, hotel booking, or data management, it's easy to make those capabilities available for:
21+
22+
- Any [application that supports MCP integration](https://modelcontextprotocol.io/clients), such as GitHub Copilot Chat agent mode in Visual Studio Code or in GitHub Codespaces.
23+
- A custom agent that accesses remote tools by using an [MCP client](https://modelcontextprotocol.io/quickstart/client#python).
24+
25+
By adding an MCP server to your web app, you enable an agent to understand and use your app's capabilities when it responds to user prompts. This means anything your app can do, the agent can do too.
26+
27+
> [!div class="checklist"]
28+
> * Add an MCP server to your web app.
29+
> * Test the MCP server locally in GitHub Copilot Chat agent mode.
30+
> * Deploy the MCP server to Azure App Service and connect to it in GitHub Copilot Chat.
31+
32+
## Prerequisites
33+
34+
This tutorial assumes you're working with the sample used in [Deploy a Python FastAPI web app with PostgreSQL in Azure](tutorial-python-postgresql-app-fastapi.md).
35+
36+
At a minimum, open the [sample application](https://github.com/Azure-Samples/msdocs-fastapi-postgresql-sample-app) in GitHub Codespaces and deploy the app by running `azd up`.
37+
38+
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/Azure-Samples/msdocs-fastapi-postgresql-sample-app?quickstart=1)
39+
40+
## Add MCP server to your web app
41+
42+
1. In the codespace explorer, open *src/pyproject.toml*, add `mcp[cli]` to the list of dependencies, as shown in the following example:
43+
44+
```toml
45+
dependencies = [
46+
...
47+
"mcp[cli]",
48+
]
49+
```
50+
51+
1. In *src/fastapi_app*, create a file called *mcp_server.py* and paste the following MCP server initialization code into the file:
52+
53+
```python
54+
import asyncio
55+
import contextlib
56+
from contextlib import asynccontextmanager
57+
58+
from mcp.server.fastmcp import FastMCP
59+
from sqlalchemy.sql import func
60+
from sqlmodel import Session, select
61+
62+
from .models import Restaurant, Review, engine
63+
64+
# Create a FastMCP server. Use stateless_http=True for simple mounting. Default path is .../mcp
65+
mcp = FastMCP("RestaurantReviewsMCP", stateless_http=True)
66+
67+
# Lifespan context manager to start/stop the MCP session manager with the FastAPI app
68+
@asynccontextmanager
69+
async def mcp_lifespan(app):
70+
async with contextlib.AsyncExitStack() as stack:
71+
await stack.enter_async_context(mcp.session_manager.run())
72+
yield
73+
74+
# MCP tool: List all restaurants with their average rating and review count
75+
@mcp.tool()
76+
async def list_restaurants_mcp() -> list[dict]:
77+
"""List restaurants with their average rating and review count."""
78+
79+
def sync():
80+
with Session(engine) as session:
81+
statement = (
82+
select(
83+
Restaurant,
84+
func.avg(Review.rating).label("avg_rating"),
85+
func.count(Review.id).label("review_count"),
86+
)
87+
.outerjoin(Review, Review.restaurant == Restaurant.id)
88+
.group_by(Restaurant.id)
89+
)
90+
results = session.exec(statement).all()
91+
rows = []
92+
for restaurant, avg_rating, review_count in results:
93+
r = restaurant.dict()
94+
r["avg_rating"] = float(avg_rating) if avg_rating is not None else None
95+
r["review_count"] = review_count
96+
r["stars_percent"] = (
97+
round((float(avg_rating) / 5.0) * 100) if review_count > 0 and avg_rating is not None else 0
98+
)
99+
rows.append(r)
100+
return rows
101+
102+
return await asyncio.to_thread(sync)
103+
104+
# MCP tool: Get a restaurant and all its reviews by restaurant_id
105+
@mcp.tool()
106+
async def get_details_mcp(restaurant_id: int) -> dict:
107+
"""Return the restaurant and its related reviews as objects."""
108+
109+
def sync():
110+
with Session(engine) as session:
111+
restaurant = session.exec(select(Restaurant).where(Restaurant.id == restaurant_id)).first()
112+
if restaurant is None:
113+
return None
114+
reviews = session.exec(select(Review).where(Review.restaurant == restaurant_id)).all()
115+
return {"restaurant": restaurant.dict(), "reviews": [r.dict() for r in reviews]}
116+
117+
return await asyncio.to_thread(sync)
118+
119+
# MCP tool: Create a new review for a restaurant
120+
@mcp.tool()
121+
async def create_review_mcp(restaurant_id: int, user_name: str, rating: int, review_text: str) -> dict:
122+
"""Create a new review for a restaurant and return the created review dict."""
123+
124+
def sync():
125+
with Session(engine) as session:
126+
review = Review()
127+
review.restaurant = restaurant_id
128+
review.review_date = __import__("datetime").datetime.now()
129+
review.user_name = user_name
130+
review.rating = int(rating)
131+
review.review_text = review_text
132+
session.add(review)
133+
session.commit()
134+
session.refresh(review)
135+
return review.dict()
136+
137+
return await asyncio.to_thread(sync)
138+
139+
# MCP tool: Create a new restaurant
140+
@mcp.tool()
141+
async def create_restaurant_mcp(restaurant_name: str, street_address: str, description: str) -> dict:
142+
"""Create a new restaurant and return the created restaurant dict."""
143+
144+
def sync():
145+
with Session(engine) as session:
146+
restaurant = Restaurant()
147+
restaurant.name = restaurant_name
148+
restaurant.street_address = street_address
149+
restaurant.description = description
150+
session.add(restaurant)
151+
session.commit()
152+
session.refresh(restaurant)
153+
return restaurant.dict()
154+
155+
return await asyncio.to_thread(sync)
156+
```
157+
158+
The FastMCP() initializer creates an MCP server using the stateless mode pattern in the [MCP Python SDK](https://github.com/modelcontextprotocol/python-sdk). By default, its streamable HTTP endpoint is set to the `/mcp` subpath.
159+
160+
- The `@mcp.tool()` decorator adds a [tool](https://github.com/modelcontextprotocol/python-sdk?tab=readme-ov-file#tools) to the MCP server with its implementation.
161+
- The tool function's description helps the calling agent to understand how to use the tool and its parameters.
162+
163+
The tools duplicate the existing restaurant reviews functionality in the form-based FastAPI web application. If you want, you can add more tools for update and delete funcionality.
164+
165+
1. In *src/fastapi_app/app.py*, find the line for `app = FastAPI()` (line 24) and replace it with the following code:
166+
167+
```python
168+
from .mcp_server import mcp, mcp_lifespan
169+
app = FastAPI(lifespan=mcp_lifespan)
170+
app.mount("/mcp", mcp.streamable_http_app())
171+
```
172+
173+
This code mounts the MCP server's streamable HTTP endpoint to the existing FastAPI app at the the path `/mcp`. Together with the default path of the streamable HTTP endpoint, the full path is `/mcp/mcp`.
174+
175+
## Test the MCP server locally
176+
177+
1. In the codespace terminal, run the application with the following commands:
178+
179+
```bash
180+
python3 -m venv .venv
181+
source .venv/bin/activate
182+
pip install -r src/requirements.txt
183+
pip install -e src
184+
python3 src/fastapi_app/seed_data.py
185+
python3 -m uvicorn fastapi_app:app --reload --port=8000
186+
```
187+
188+
1. Select **Open in Browser**, then add a few restaurants and reviews.
189+
190+
Leave `uvicorn` running. Your MCP server is running at `http://localhost:8000/mcp/mcp` now.
191+
192+
1. Back in the codespace, open Copilot Chat, then select **Agent** mode in the prompt box.
193+
194+
1. Select the **Tools** button, then select the **Add MCP Server** icon in the popup's top right corner.
195+
196+
:::image type="content" source="media/tutorial-ai-model-context-protocol-server-python/add-model-context-protocol-tool.png" alt-text="Screenshot showing how to add an MCP server in GitHub Copilot Chat agent mode.":::
197+
198+
1. Select **HTTP (HTTP or Server-Sent Events)**.
199+
200+
1. In **Enter Server URL**, type *http://localhost:8000/mcp/mcp*.
201+
202+
1. In **Enter Server ID**, type *restaurant_ratings* or any name you like.
203+
204+
1. Select **Workspace Settings**.
205+
206+
1. In a new Copilot Chat window, type something like *"Show me the restaurant ratings."*
207+
208+
1. By default, GitHub Copilot shows you a security confirmation when you invoke an MCP server. Select **Continue**.
209+
210+
:::image type="content" source="media/tutorial-ai-model-context-protocol-server-python/model-context-protocol-call-confirmation.png" alt-text="Screenshot showing the default security message from an MCP invocation in GitHub Copilot Chat.":::
211+
212+
You should now see a response that indicates that the MCP tool call is successful.
213+
214+
:::image type="content" source="media/tutorial-ai-model-context-protocol-server-python/model-context-protocol-call-success.png" alt-text="Screenshot showing that the response from the MCP tool call in the GitHub Copilot Chat window.":::
215+
216+
## Deploy your MCP server to App Service
217+
218+
1. Back in the codespace terminal, deploy your changes by committing your changes (GitHub Actions method) or run `azd up` (Azure Developer CLI method).
219+
220+
1. In the AZD output, find the URL of your app. The URL looks like this in the AZD output:
221+
222+
<pre>
223+
Deploying services (azd deploy)
224+
225+
(✓) Done: Deploying service web
226+
- Endpoint: &lt;app-url>
227+
</pre>
228+
229+
1. Once `azd up` finishes, open *.vscode/mcp.json*. Change the URL to `<app-url>/mcp/mcp`.
230+
231+
1. Above your modified MCP server configuration, select **Start**.
232+
233+
:::image type="content" source="media/tutorial-ai-model-context-protocol-server-python/model-context-protocol-server-start.png" alt-text="Screenshot showing how to manually start an MCP server from the local mcp.json file.":::
234+
235+
1. Start a new GitHub Copilot Chat window. You should be able to view restaurant ratings, as well as create new restaurants and new ratings in the Copilot agent.
236+
237+
## Security best practices
238+
239+
When your MCP server is called by an agent powered by large language models (LLM), be aware of [prompt injection](https://genai.owasp.org/llmrisk/llm01-prompt-injection/) attacks. Consider the following security best practices:
240+
241+
- **Authentication and Authorization**: Protect your MCP endpoints in App Service behind [Azure API Management with Microsoft Entra ID](/azure/api-management/api-management-howto-protect-backend-with-aad) and ensure only authorized users or agents can access the tools.
242+
- **Input Validation and Sanitization**: Always validate incoming data to prevent invalid or malicious input. For Python apps, use libraries such as [Pydantic](https://pypi.org/project/pydantic/) to enforce data validation rules with dedicated input models (such as RestaurantCreate and ReviewCreate). Refer to their documentation for best practices and implementation details.
243+
- **HTTPS:** The sample relies on Azure App Service, which enforces HTTPS by default and provides free TLS/SSL certificates to encrypt data in transit.
244+
- **Least Privilege Principle**: Expose only the necessary tools and data required for your use case. Avoid exposing sensitive operations unless necessary.
245+
- **Rate Limiting and Throttling**: Use [API Management](/azure/api-management/api-management-sample-flexible-throttling) or custom middleware to prevent abuse and denial-of-service attacks.
246+
- **Logging and Monitoring**: Log access and usage of MCP endpoints for auditing and anomaly detection. Monitor for suspicious activity.
247+
- **CORS Configuration**: Restrict cross-origin requests to trusted domains if your MCP server is accessed from browsers. For more information, see [Enable CORS](app-service-web-tutorial-rest-api.md#enable-cors).
248+
- **Regular Updates**: Keep your dependencies up to date to mitigate known vulnerabilities.
249+
250+
## More resources
251+
252+
[Integrate AI into your Azure App Service applications](overview-ai-integration.md)

0 commit comments

Comments
 (0)