container app - deploying and setting up mcp server and calling it remotely from your vscode agent
To create a container app, we need to setup our mcp server code, then deploy to the server in Azure. Finally we will test it using our vscode.
The following are the code we using for todo task.
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
from task_store import store
mcp = FastMCP("TasksMCP", stateless_http=True,
transport_security=TransportSecuritySettings(enable_dns_rebinding_protection=False)
)
@mcp.tool()
async def list_tasks() -> list[dict]:
"""List all tasks with their ID, title, description, and completion status."""
return store.get_all()
@mcp.tool()
async def get_task(task_id: int) -> dict | None:
"""Get a single task by its numeric ID.
Args:
task_id: The numeric ID of the task to retrieve.
"""
return store.get_by_id(task_id)
@mcp.tool()
async def create_task(title: str, description: str) -> dict:
"""Create a new task with the given title and description. Returns the created task.
Args:
title: A short title for the task.
description: A detailed description of what the task involves.
"""
return store.create(title, description)
@mcp.tool()
async def toggle_task_complete(task_id: int) -> str:
"""Toggle a task's completion status between complete and incomplete.
Args:
task_id: The numeric ID of the task to toggle.
"""
task = store.toggle_complete(task_id)
if task:
status = "complete" if task["is_complete"] else "incomplete"
return f"Task {task['id']} is now {status}."
return f"Task with ID {task_id} not found."
@mcp.tool()
async def delete_task(task_id: int) -> str:
"""Delete a task by its numeric ID.
Args:
task_id: The numeric ID of the task to delete.
"""
if store.delete(task_id):
return f"Task {task_id} deleted."
return f"Task with ID {task_id} not found."
And this is what our main.py looks like
from contextlib import AsyncExitStack, asynccontextmanager
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from mcp_server import mcp
@asynccontextmanager
async def lifespan(app: FastAPI):
async with AsyncExitStack() as stack:
await stack.enter_async_context(mcp.session_manager.run())
yield
app = FastAPI(lifespan=lifespan)
@app.get("/health")
async def health():
return JSONResponse({"status": "healthy"})
app.mount("/", mcp.streamable_http_app())
And this is the task_store.py
from dataclasses import dataclass, field
from datetime import datetime, timezone
@dataclass
class TaskItem:
id: int
title: str
description: str
is_complete: bool = False
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
def to_dict(self) -> dict:
return {
"id": self.id,
"title": self.title,
"description": self.description,
"is_complete": self.is_complete,
"created_at": self.created_at.isoformat(),
}
class TaskStore:
def __init__(self):
self._tasks: list[TaskItem] = [
TaskItem(1, "Buy groceries", "Milk, eggs, bread"),
TaskItem(2, "Write docs", "Draft the MCP tutorial", True),
]
self._next_id = 3
def get_all(self) -> list[dict]:
return [t.to_dict() for t in self._tasks]
def get_by_id(self, task_id: int) -> dict | None:
task = next((t for t in self._tasks if t.id == task_id), None)
return task.to_dict() if task else None
def create(self, title: str, description: str) -> dict:
task = TaskItem(self._next_id, title, description)
self._next_id += 1
self._tasks.append(task)
return task.to_dict()
def toggle_complete(self, task_id: int) -> dict | None:
task = next((t for t in self._tasks if t.id == task_id), None)
if task is None:
return None
task.is_complete = not task.is_complete
return task.to_dict()
def delete(self, task_id: int) -> bool:
task = next((t for t in self._tasks if t.id == task_id), None)
if task is None:
return False
self._tasks.remove(task)
return True
# For demonstration only — not thread-safe.
store = TaskStore()
To get it deploy (we are using powershell) you need to run the following code.
$RESOURCE_GROUP="my-mcp-rg"
$LOCATION="australiaeast"
$ENVIRONMENT_NAME="mcp-env"
$APP_NAME="tasks-mcp-server-py"
-- create container app environment
az group create --name $RESOURCE_GROUP --location $LOCATION
az containerapp env create --name $ENVIRONMENT_NAME
--resource-group $RESOURCE_GROUP --location $LOCATION
-- deploy container app
az containerapp up --name $APP_NAME
--resource-group $RESOURCE_GROUP --environment $ENVIRONMENT_NAME
--source . --ingress external --target-port 8080
az containerapp ingress cors enable --name $APP_NAME
--resource-group $RESOURCE_GROUP --allowed-origins
"*" --allowed-methods "GET,POST,DELETE,OPTIONS" --allowed-headers "*"
verify the deployment
$APP_URL=$(az containerapp show --name $APP_NAME
--resource-group $RESOURCE_GROUP --query
"properties.configuration.ingress.fqdn" -o tsv)
$APP_URL=https://<endpoint>
curl https://$APP_URL/health
az containerapp update --name $APP_NAME
--resource-group $RESOURCE_GROUP --min-replicas 1
Please check to ensure the container app has been deployed successfully,
And you should get the following outputs :-And ensuring the endpoint is healthy too
Finally let's setup and test out environment. Press Ctrl + Alt + P) and look for "Add MCP server" and select 2nd option - HTTP or SSE event)
And then provide the full URL of your server from Azure and next provide server id to it
And ensure you have selected 'workspace'
Please open .vscode/mcp.json and start the server and it should look like this :-
And then open your chat prompt and issue the following prompt (you can choose your own). As you can see we have created the following tasks:-
And finally let's list down all our task so far
Comments