This is a quickstart template to easily build and deploy a custom remote MCP server to the cloud using Azure functions. You can clone/restore/run on your local machine with debugging, and azd up to have it in the cloud in a couple minutes.
The MCP server is configured with built-in authentication using Microsoft Entra as the identity provider.
You can also use API Management to secure the server, as well as network isolation using VNET.
If you're looking for this sample in more languages check out the Node.js/TypeScript and Python samples.
- .NET 10 SDK
- Azure Functions Core Tools >=
4.5.0 - Azure Developer CLI 1.23.x or above (for deployment)
- Visual Studio 2022
- Make sure to select the Azure development workload during installation
Choose one: You can use either Visual Studio OR Visual Studio Code. Both provide full debugging support, but the setup steps differ slightly.
Below is the architecture diagram for the Remote MCP Server using Azure Functions:
An Azure Storage Emulator is needed for this particular sample because we will save and get snippets from blob storage. Start Azurite emulator:
docker run -d -p 10000:10000 -p 10001:10001 -p 10002:10002 \
mcr.microsoft.com/azure-storage/azuriteNote if you use Azurite coming from VS Code extension you need to run
Azurite: Startnow or you will see errors.
From the src/FunctionsMcpTool folder, run this command to start the Functions host locally:
cd src/FunctionsMcpTool
func startNote: The MCP Resources and Prompts projects run as separate Function Apps. To start them locally alongside the tools server, open additional terminals:
cd src/FunctionsMcpResources func start --port 7072cd src/FunctionsMcpPrompts func start --port 7073
You can connect to your local MCP server using VS Code with GitHub Copilot or MCP Inspector.
-
Open .vscode/mcp.json. Find the server called local-mcp-function and click Start above the name. The server is already set up with the running Function app's MCP endpoint:
http://localhost:7071/runtime/webhooks/mcp
-
In Copilot chat agent mode enter a prompt to trigger the tool, e.g., select some code and enter this prompt
Say HelloSave this snippet as snippet1Retrieve snippet1 and apply to NewFile.cs -
When prompted to run the tool, consent by clicking Continue
-
When you're done, press Ctrl+C in the terminal window to stop the
func.exehost process.
-
In a new terminal window, install and run MCP Inspector
npx @modelcontextprotocol/inspector
-
CTRL click to load the MCP Inspector web app from the URL displayed by the app (e.g.
http://0.0.0.0:5173/#resources) -
Set the transport type to
Streamable HTTP -
Set the URL to your running Function app's MCP endpoint and Connect:
http://0.0.0.0:7071/runtime/webhooks/mcp
-
List Tools. Click on a tool and Run Tool.
After testing the snippet save functionality locally, you can verify that blobs are being stored correctly in your local Azurite storage emulator.
Using Azure Storage Explorer:
- Open Azure Storage Explorer
- In the left panel, expand Emulator & Attached → Storage Accounts → (Emulator - Default Ports) (Key)
- Navigate to Blob Containers → snippets
- You should see any saved snippets as blob files in this container
- Double-click on any blob to view its contents and verify the snippet data was saved correctly
Using Azure CLI (alternative):
# List blobs in the snippets container
az storage blob list --container-name snippets --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"# Download a specific blob to view its contents
az storage blob download --container-name snippets --name <blob-name> --file <local-file-path> --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"This repository also includes a Weather App sample that demonstrates MCP tools with an interactive UI. See src/McpWeatherApp/README.md for details.
-
Build the UI:
cd src/McpWeatherApp/app npm install npm run build cd ..
-
Run the function app:
func start
-
In .vscode/mcp.json, click Start above local-mcp-function
-
Ask Copilot: "What's the weather in Seattle?"
The FunctionsMcpApp sample shows how to render dynamic data with the fluent API. The dashboard UI is a Vite-bundled TypeScript app that receives tool results via @modelcontextprotocol/ext-apps and renders tiles, charts, and status indicators in real time. See src/FunctionsMcpApp/README.md for details.
-
Build the dashboard UI:
cd src/FunctionsMcpApp/app npm install npm run build cd ..
-
Run the function app:
func start
-
In .vscode/mcp.json, click Start above local-mcp-function
-
Ask Copilot to open the Snippet Dashboard
Stop the local server with Ctrl+C and switch back to the root directory.
az login
azd auth loginazd env new <environment-name>Configure VS Code as an allowed client application to request access tokens from Microsoft Entra:
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56Optional: Enable VNet isolation:
azd env set VNET_ENABLED true-
Set
DEPLOY_SERVICEto provision one of the MCP server projects:azd env set DEPLOY_SERVICE <tools, resources, prompts, weather, or apps>
-
Provision the resources for the app:
azd provision
When prompted, pick your subscription, an Azure region for the resources, and choose
falseto skip creating virtual network resources to simplify the deployment. -
Deploy the app of your choice:
# Deploy only the MCP Tools (with Entra auth) azd deploy --service tools # Deploy only the MCP Resources azd deploy --service resources # Deploy only the MCP Prompts azd deploy --service prompts # Deploy only the Weather App (with access token) azd deploy --service weather # Deploy only the Fluent API App (dynamic dashboard) azd deploy --service apps
After deployment finishes, open .vscode/mcp.json and click Start above remote-mcp-function. You'll be prompted for functionapp-name (find it in your azd command output or /.azure/*/.env file). You'll also be prompted to authenticate with Microsoft—click Allow and login with your Azure subscription email.
Tip
Successful connect shows the number of tools the server has. You can see more details on the interactions between VS Code and server by clicking on More... -> Show Output above the server name.
Redeploy all: Run azd up as many times as needed to deploy code updates.
Redeploy one service: Use azd deploy tools, azd deploy resources, azd deploy prompts, azd deploy weather, or azd deploy apps to redeploy a specific app.
Clean up: Delete all Azure resources when done:
azd downThe solution is organized into separate Azure Function projects by MCP capability:
| Project | Path | Description |
|---|---|---|
| FunctionsMcpTool | src/FunctionsMcpTool/ |
MCP Tools — snippet CRUD, QR code generation, badges, website preview |
| FunctionsMcpResources | src/FunctionsMcpResources/ |
MCP Resources — snippet resource template, server info resource |
| FunctionsMcpPrompts | src/FunctionsMcpPrompts/ |
MCP Prompts — code review checklist, summarize content, generate docs |
| FunctionsMcpApp | src/FunctionsMcpApp/ |
MCP Apps (Fluent API) — dynamic dashboard with Vite UI, static hello app |
| McpWeatherApp | src/McpWeatherApp/ |
Weather App — standalone MCP demo with interactive UI |
The function code for the GetSnippet and SaveSnippet endpoints are defined in SnippetsTool.cs. The McpToolsTrigger attribute applied to the async Run method exposes the code function as an MCP Server.
The following shows the code for a few MCP server examples (get string, get object, save object):
[Function(nameof(GetSnippet))]
public object GetSnippet(
[McpToolTrigger(GetSnippetToolName, GetSnippetToolDescription)] ToolInvocationContext context,
[BlobInput(BlobPath)] string snippetContent)
{
return snippetContent;
}
[Function(nameof(SaveSnippet))]
[BlobOutput(BlobPath)]
public string SaveSnippet(
[McpToolTrigger(SaveSnippetToolName, SaveSnippetToolDescription)] ToolInvocationContext context,
[McpToolProperty(SnippetNamePropertyName, PropertyType, SnippetNamePropertyDescription)] string name,
[McpToolProperty(SnippetPropertyName, PropertyType, SnippetPropertyDescription)] string snippet)
{
return snippet;
}
[Function(nameof(SayHello))]
public string SayHello(
[McpToolTrigger(HelloToolName, HelloToolDescription)] ToolInvocationContext context
)
{
logger.LogInformation("C# MCP tool trigger function processed a request.");
return "Hello I am MCP Tool!";
}- Add API Management to your MCP server
- Enable VNET using VNET_ENABLED=true flag
- Learn more about related MCP efforts from Microsoft
| Problem | Solution |
|---|---|
| Connection refused | Ensure Azurite is running (docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite) |
| API version not supported by Azurite | Pull the latest Azurite image (docker pull mcr.microsoft.com/azure-storage/azurite) then restart Azurite and the app |

