Skip to content

Commit 9cca618

Browse files
authored
Merge pull request Azure-Samples#11 from pamelafox/secondcontainer
Multi-container setup for agent -> MCP server networking
2 parents d37e062 + d4a8c62 commit 9cca618

16 files changed

+502
-100
lines changed

.dockerignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.azure
2+
.venv
3+
.logfire
4+
.devcontainer
5+
infra
6+
7+
# Common Python and development files to exclude
8+
__pycache__
9+
*.pyc
10+
*.pyo
11+
*.egg-info
12+
.pytest_cache
13+
.ruff_cache
14+
.env
15+
.git

README.md

Lines changed: 203 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,127 @@
11
# Python MCP Demo
22

3-
A demonstration project showcasing Model Context Protocol (MCP) implementations using FastMCP, with examples of stdio, HTTP transports, and integration with LangChain and Agent Framework.
3+
A demonstration project showcasing Model Context Protocol (MCP) implementations using FastMCP, with examples of stdio and HTTP transports, integration with LangChain and Agent Framework, and deployment to Azure Container Apps.
44

55
## Table of Contents
66

7-
- [Prerequisites](#prerequisites)
8-
- [Setup](#setup)
9-
- [Python Scripts](#python-scripts)
10-
- [MCP Server Configuration](#mcp-server-configuration)
11-
- [Debugging](#debugging)
7+
- [Getting started](#getting-started)
8+
- [GitHub Codespaces](#github-codespaces)
9+
- [VS Code Dev Containers](#vs-code-dev-containers)
10+
- [Local environment](#local-environment)
11+
- [Run local MCP servers](#run-local-mcp-servers)
12+
- [Use with GitHub Copilot](#use-with-github-copilot)
13+
- [Debug with VS Code](#debug-with-vs-code)
14+
- [Inspect with MCP inspector](#inspect-with-mcp-inspector)
15+
- [Run local Agents <-> MCP](#run-local-agents---mcp)
16+
- [Deploy to Azure](#deploy-to-azure)
17+
- [Deploy to Azure with private networking](#deploy-to-azure-with-private-networking)
1218

13-
## Prerequisites
19+
## Getting started
1420

15-
- Python 3.13 or higher
16-
- [uv](https://docs.astral.sh/uv/)
17-
- API access to one of the following:
18-
- GitHub Models (GitHub token)
19-
- Azure OpenAI (Azure credentials)
20-
- Ollama (local installation)
21-
- OpenAI API (API key)
21+
You have a few options for setting up this project. The quickest way to get started is GitHub Codespaces, since it will setup all the tools for you, but you can also set it up locally.
2222

23-
## Setup
23+
### GitHub Codespaces
2424

25-
1. Install dependencies using `uv`:
25+
You can run this project virtually by using GitHub Codespaces. Click the button to open a web-based VS Code instance in your browser:
26+
27+
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/pamelafox/python-mcp-demo)
28+
29+
Once the Codespace is open, open a terminal window and continue with the deployment steps.
30+
31+
### VS Code Dev Containers
32+
33+
A related option is VS Code Dev Containers, which will open the project in your local VS Code using the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers):
34+
35+
1. Start Docker Desktop (install it if not already installed)
36+
2. Open the project: [![Open in Dev Containers](https://img.shields.io/static/v1?style=for-the-badge&label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/pamelafox/python-mcp-demo)
37+
3. In the VS Code window that opens, once the project files show up (this may take several minutes), open a terminal window.
38+
4. Continue with the deployment steps.
39+
40+
### Local environment
41+
42+
If you're not using one of the above options, then you'll need to:
43+
44+
1. Make sure the following tools are installed:
45+
- [Azure Developer CLI (azd)](https://aka.ms/install-azd)
46+
- [Python 3.13+](https://www.python.org/downloads/)
47+
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
48+
- [Git](https://git-scm.com/downloads)
49+
50+
2. Clone the repository and open the project folder.
51+
52+
3. Create a [Python virtual environment](https://docs.python.org/3/tutorial/venv.html#creating-virtual-environments) and activate it.
53+
54+
4. Install the dependencies:
2655

2756
```bash
2857
uv sync
2958
```
3059

31-
2. Copy `.env-sample` to `.env` and configure your environment variables:
60+
5. Copy `.env-sample` to `.env` and configure your environment variables:
3261

3362
```bash
3463
cp .env-sample .env
3564
```
3665

37-
3. Edit `.env` with your API credentials. Choose one of the following providers by setting `API_HOST`:
66+
6. Edit `.env` with your API credentials. Choose one of the following providers by setting `API_HOST`:
3867
- `github` - GitHub Models (requires `GITHUB_TOKEN`)
3968
- `azure` - Azure OpenAI (requires Azure credentials)
4069
- `ollama` - Local Ollama instance
4170
- `openai` - OpenAI API (requires `OPENAI_API_KEY`)
4271

43-
## Python Scripts
72+
## Run local MCP servers
73+
74+
This project includes two MCP servers in the [`servers/`](servers/) directory:
4475

45-
Run any script with: `uv run <script_path>`
76+
| File | Description |
77+
|------|-------------|
78+
| [servers/basic_mcp_stdio.py](servers/basic_mcp_stdio.py) | MCP server with stdio transport for VS Code integration |
79+
| [servers/basic_mcp_http.py](servers/basic_mcp_http.py) | MCP server with HTTP transport on port 8000 |
4680

47-
- **servers/basic_mcp_http.py** - MCP server with HTTP transport on port 8000
48-
- **servers/basic_mcp_stdio.py** - MCP server with stdio transport for VS Code integration
49-
- **agents/langchainv1_http.py** - LangChain agent with MCP integration
50-
- **agents/langchainv1_github.py** - LangChain tool filtering demo with GitHub MCP (requires `GITHUB_TOKEN`)
51-
- **agents/agentframework_learn.py** - Microsoft Agent Framework integration with MCP
52-
- **agents/agentframework_http.py** - Microsoft Agent Framework integration with local Expenses MCP server
81+
Both servers implement an "Expenses Tracker" with a tool to add expenses to a CSV file.
5382

54-
## MCP Server Configuration
83+
### Use with GitHub Copilot
5584

56-
### Using with MCP Inspector
85+
The `.vscode/mcp.json` file configures MCP servers for GitHub Copilot integration:
86+
87+
**Available Servers:**
88+
89+
- **expenses-mcp**: stdio transport server for production use
90+
- **expenses-mcp-debug**: stdio server with debugpy on port 5678
91+
- **expenses-mcp-http**: HTTP transport server at `http://localhost:8000/mcp`. You must start this server manually with `uv run servers/basic_mcp_http.py` before using it.
92+
93+
**Switching Servers:**
94+
95+
Configure which server GitHub Copilot uses by opening the Chat panel, selecting the tools icon, and choosing the desired MCP server from the list.
96+
97+
![Servers selection dialog](readme_serverselect.png)
98+
99+
**Example input:**
100+
101+
Use a query like this to test the expenses MCP server:
102+
103+
```text
104+
Log expense for 50 bucks of pizza on my amex today
105+
```
106+
107+
![Example GitHub Copilot Chat Input](readme_samplequery.png)
108+
109+
### Debug with VS Code
110+
111+
The `.vscode/launch.json` provides a debug configuration to attach to an MCP server.
112+
113+
**To debug an MCP server with GitHub Copilot Chat:**
114+
115+
1. Set breakpoints in the MCP server code in `servers/basic_mcp_stdio.py`
116+
2. Start the debug server via `mcp.json` configuration by selecting `expenses-mcp-debug`
117+
3. Press `Cmd+Shift+D` to open Run and Debug
118+
4. Select "Attach to MCP Server (stdio)" configuration
119+
5. Press `F5` or the play button to start the debugger
120+
6. Select the expenses-mcp-debug server in GitHub Copilot Chat tools
121+
7. Use GitHub Copilot Chat to trigger the MCP tools
122+
8. Debugger pauses at breakpoints
123+
124+
### Inspect with MCP inspector
57125

58126
The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is a developer tool for testing and debugging MCP servers.
59127

@@ -86,45 +154,125 @@ The inspector provides a web interface to:
86154
- Inspect server responses and errors
87155
- Debug server communication
88156

89-
### Using with GitHub Copilot
157+
---
90158

91-
The `.vscode/mcp.json` file configures MCP servers for GitHub Copilot integration:
159+
## Run local Agents <-> MCP
92160

93-
**Available Servers:**
161+
This project includes example agents in the [`agents/`](agents/) directory that demonstrate how to connect AI agents to MCP servers:
94162

95-
- **expenses-mcp**: stdio transport server for production use
96-
- **expenses-mcp-debug**: stdio server with debugpy on port 5678
97-
- **expenses-mcp-http**: HTTP transport server at `http://localhost:8000/mcp`. You must start this server manually with `uv run servers/basic_mcp_http.py` before using it.
163+
| File | Description |
164+
|------|-------------|
165+
| [agents/agentframework_learn.py](agents/agentframework_learn.py) | Microsoft Agent Framework integration with MCP |
166+
| [agents/agentframework_http.py](agents/agentframework_http.py) | Microsoft Agent Framework integration with local Expenses MCP server |
167+
| [agents/langchainv1_http.py](agents/langchainv1_http.py) | LangChain agent with MCP integration |
168+
| [agents/langchainv1_github.py](agents/langchainv1_github.py) | LangChain tool filtering demo with GitHub MCP (requires `GITHUB_TOKEN`) |
98169

99-
**Switching Servers:**
170+
**To run an agent:**
100171

101-
Configure which server GitHub Copilot uses by opening the Chat panel, selecting the tools icon, and choosing the desired MCP server from the list.
172+
1. First start the HTTP MCP server:
102173

103-
![Servers selection dialog](readme_serverselect.png)
174+
```bash
175+
uv run servers/basic_mcp_http.py
176+
```
104177

105-
**Example input**
178+
2. In another terminal, run an agent:
106179

107-
Use a query like this to test the expenses MCP server:
180+
```bash
181+
uv run agents/agentframework_http.py
182+
```
108183

109-
```
110-
Log expense for 50 bucks of pizza on my amex today
111-
```
184+
The agents will connect to the MCP server and allow you to interact with the expense tracking tools through a chat interface.
112185

113-
![Example GitHub Copilot Chat Input](readme_samplequery.png)
186+
---
114187

115-
## Debugging
188+
## Deploy to Azure
116189

117-
The `.vscode/launch.json` provides one debug configuration:
190+
This project can be deployed to Azure Container Apps using the Azure Developer CLI (azd). The deployment provisions:
118191

119-
**Attach to MCP Server (stdio)**: Attaches to server started via `expenses-mcp-debug` in `mcp.json`
192+
- **Azure Container Apps** - Hosts both the MCP server and agent
193+
- **Azure OpenAI** - Provides the LLM for the agent
194+
- **Azure Cosmos DB** - Stores expenses data
195+
- **Azure Container Registry** - Stores container images
196+
- **Log Analytics** - Monitoring and diagnostics
120197

121-
To debug an MCP server with GitHub Copilot Chat:
198+
### Azure account setup
122199

123-
1. Set breakpoints in the MCP server code in `servers/basic_mcp_stdio.py`
124-
1. Start the debug server via `mcp.json` configuration by selecting `expenses-mcp-debug`
125-
1. Press `Cmd+Shift+D` to open Run and Debug
126-
1. Select "Attach to MCP Server (stdio)" configuration
127-
1. Press `F5` or the play button to start the debugger
128-
1. Select the expenses-mcp-debug server in GitHub Copilot Chat tools
129-
1. Use GitHub Copilot Chat to trigger the MCP tools
130-
1. Debugger pauses at breakpoints
200+
1. Sign up for a [free Azure account](https://azure.microsoft.com/free/) and create an Azure Subscription.
201+
2. Check that you have the necessary permissions:
202+
- Your Azure account must have `Microsoft.Authorization/roleAssignments/write` permissions, such as [Role Based Access Control Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#role-based-access-control-administrator-preview), [User Access Administrator](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#user-access-administrator), or [Owner](https://learn.microsoft.com/azure/role-based-access-control/built-in-roles#owner).
203+
- Your Azure account also needs `Microsoft.Resources/deployments/write` permissions on the subscription level.
204+
205+
### Deploying with azd
206+
207+
1. Login to Azure:
208+
209+
```bash
210+
azd auth login
211+
```
212+
213+
For GitHub Codespaces users, if the previous command fails, try:
214+
215+
```bash
216+
azd auth login --use-device-code
217+
```
218+
219+
2. Create a new azd environment:
220+
221+
```bash
222+
azd env new
223+
```
224+
225+
This will create a folder inside `.azure` with the name of your environment.
226+
227+
3. Provision and deploy the resources:
228+
229+
```bash
230+
azd up
231+
```
232+
233+
It will prompt you to select a subscription and location. This will take several minutes to complete.
234+
235+
4. Once deployment is complete, a `.env` file will be created with the necessary environment variables to run the agents locally against the deployed resources.
236+
237+
### Costs
238+
239+
Pricing varies per region and usage, so it isn't possible to predict exact costs for your usage.
240+
241+
You can try the [Azure pricing calculator](https://azure.com/e/3987c81282c84410b491d28094030c9a) for the resources:
242+
243+
- **Azure OpenAI Service**: S0 tier, GPT-4o-mini model. Pricing is based on token count. [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/)
244+
- **Azure Container Apps**: Consumption tier. [Pricing](https://azure.microsoft.com/pricing/details/container-apps/)
245+
- **Azure Container Registry**: Standard tier. [Pricing](https://azure.microsoft.com/pricing/details/container-registry/)
246+
- **Azure Cosmos DB**: Serverless tier. [Pricing](https://azure.microsoft.com/pricing/details/cosmos-db/)
247+
- **Log Analytics** (Optional): Pay-as-you-go tier. Costs based on data ingested. [Pricing](https://azure.microsoft.com/pricing/details/monitor/)
248+
249+
⚠️ To avoid unnecessary costs, remember to take down your app if it's no longer in use, either by deleting the resource group in the Portal or running `azd down`.
250+
251+
---
252+
253+
## Deploy to Azure with private networking
254+
255+
To demonstrate enhanced security for production deployments, this project supports deploying with a virtual network (VNet) configuration that restricts public access to Azure resources.
256+
257+
1. Set these azd environment variables to set up a virtual network and private endpoints for the Container App, Cosmos DB, and OpenAI resources:
258+
259+
```bash
260+
azd env set USE_VNET true
261+
azd env set USE_PRIVATE_INGRESS true
262+
```
263+
264+
The Log Analytics and ACR resources will still have public access enabled, so that you can deploy and monitor the app without needing a VPN. In production, you would typically restrict these as well.
265+
266+
2. Provision and deploy:
267+
268+
```bash
269+
azd up
270+
```
271+
272+
### Additional costs for private networking
273+
274+
When using VNet configuration, additional Azure resources are provisioned:
275+
276+
- **Virtual Network**: Pay-as-you-go tier. Costs based on data processed. [Pricing](https://azure.microsoft.com/pricing/details/virtual-network/)
277+
- **Azure Private DNS Resolver**: Pricing per month, endpoints, and zones. [Pricing](https://azure.microsoft.com/pricing/details/dns/)
278+
- **Azure Private Endpoints**: Pricing per hour per endpoint. [Pricing](https://azure.microsoft.com/pricing/details/private-link/)

agents/Dockerfile

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# ------------------- Stage 1: Build Stage ------------------------------
2+
# We use Alpine for smaller image size (~329MB vs ~431MB for Debian slim).
3+
# Trade-off: We must install build tools to compile native extensions (cryptography, etc.)
4+
# since pre-built musl wheels aren't available. Debian -slim would skip compilation
5+
# but produces a larger final image due to glibc wheel sizes.
6+
FROM python:3.13-alpine AS build
7+
8+
# Install build dependencies for packages with native extensions (cryptography, etc.)
9+
# https://cryptography.io/en/latest/installation/#building-cryptography-on-linux
10+
RUN apk add --no-cache gcc g++ musl-dev python3-dev libffi-dev openssl-dev cargo pkgconfig
11+
12+
COPY --from=ghcr.io/astral-sh/uv:0.9.14 /uv /uvx /bin/
13+
14+
WORKDIR /code
15+
16+
# Copy dependency files and install dependencies (for layer caching)
17+
# Note: We can't use --mount=type=cache since Azure Container Apps remote build doesn't support BuildKit:
18+
# https://github.com/Azure/acr/issues/721
19+
COPY uv.lock pyproject.toml ./
20+
RUN uv sync --locked --no-install-project
21+
22+
# Copy the project and sync
23+
COPY . .
24+
RUN uv sync --locked
25+
26+
# ------------------- Stage 2: Final Stage ------------------------------
27+
FROM python:3.13-alpine AS final
28+
29+
RUN addgroup -S app && adduser -S app -G app
30+
31+
COPY --from=build --chown=app:app /code /code
32+
33+
WORKDIR /code/agents
34+
USER app
35+
36+
ENV PATH="/code/.venv/bin:$PATH"
37+
38+
ENTRYPOINT ["python", "agentframework_http.py"]

agents/agentframework_http.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
load_dotenv(override=True)
2121

2222
# Constants
23+
RUNNING_IN_PRODUCTION = os.getenv("RUNNING_IN_PRODUCTION", "false").lower() == "true"
2324
MCP_SERVER_URL = os.getenv("MCP_SERVER_URL", "http://localhost:8000/mcp/")
2425

2526
# Configure chat client based on API_HOST
@@ -69,6 +70,11 @@ async def http_mcp_example() -> None:
6970
result = await agent.run(user_query, tools=mcp_server)
7071
print(result)
7172

73+
# Keep the worker alive in production
74+
while RUNNING_IN_PRODUCTION:
75+
await asyncio.sleep(60)
76+
logger.info("Worker still running...")
77+
7278

7379
if __name__ == "__main__":
7480
asyncio.run(http_mcp_example())

0 commit comments

Comments
 (0)