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.
Watch the video overview
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.0.7030 - Azure Developer CLI (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 -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.
Choose your preferred development environment:
-
From the
srcfolder, run this command to start the Functions host locally:cd src func start
Once your Azure Functions MCP server is running locally (via either Visual Studio Code or Visual Studio), you can connect to it from various MCP client tools:
-
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
Note: If you're running from Visual Studio (not VS Code), use
http://localhost:7071/runtime/webhooks/mcp/sseinstead. -
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 (or stop debugging in your IDE).
-
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
Note: If you're running from Visual Studio (not VS Code), use
http://localhost:7071/runtime/webhooks/mcp/sseinstead. -
List Tools. Click on a tool and Run Tool.
Problem: Connection refused when trying to connect to MCP server
- Solution: Ensure Azurite is running (
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 mcr.microsoft.com/azure-storage/azurite)
Problem: Wrong URL (0.0.0.0 vs localhost)
- Solution: Use
http://0.0.0.0:7071/runtime/webhooks/mcpfor VS Code,http://localhost:7071/runtime/webhooks/mcp/ssefor Visual Studio
Problem: Visual Studio F5 doesn't work
- Solution: Ensure Azure development workload is installed and
FunctionsMcpToolis set as startup project
Problem: The API version 2025-07-05 is not supported by Azurite
- Solution: Pull the latest Azurite image (
docker pull mcr.microsoft.com/azure-storage/azurite) then restart Azurite and the app.
After testing the snippet save functionality locally, you can verify that blobs are being stored correctly in your local Azurite storage emulator.
- 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
If you prefer using the command line, you can also verify blobs using Azure CLI with the storage emulator:
# 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 verification step ensures your MCP server is correctly interacting with the local storage emulator and that the blob storage functionality is working as expected before deploying to Azure.
Stop the local server with Ctrl+C.
Switch back to the root directory remote-mcp-functions-dotnet.
Sign in to Azure and initialize azd:
az login
azd auth loginCreate a new azd project environment:
azd env new <environment-name>We'll test the remote server with Visual Studio Code, so configure it as an allowed client application to request access tokens from Microsoft Entra:
azd env set PRE_AUTHORIZED_CLIENT_IDS aebc6443-996d-45c2-90f0-388ff96faa56Run this azd command to provision the function app, with any required Azure resources, and deploy your code:
azd upYou can opt-in to a VNet being used in the sample. To do so, do this before azd up
azd env set VNET_ENABLED trueConnect to the remote MCP server after deployment finishes. For GitHub Copilot within VS Code, use https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp for the URL. Note a mcp.json is already included in this repo and will be picked up by VS Code, so just click Start above remote-mcp-function to be prompted for functionapp-name (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.
{
"inputs": [
{
"type": "promptString",
"id": "functionapp-name",
"description": "Azure Functions App Name"
}
],
"servers": {
"remote-mcp-function": {
"type": "http",
"url": "https://${input:functionapp-name}.azurewebsites.net/runtime/webhooks/mcp",
},
"local-mcp-function": {
"type": "http",
"url": "http://localhost:7071/runtime/webhooks/mcp"
}
}
}You can run the azd up command as many times as you need to both provision your Azure resources and deploy code updates to your function app.
Note
Deployed code files are always overwritten by the latest deployment package.
When you're done working with your function app and related resources, you can use this command to delete the function app and its related resources from Azure and avoid incurring any further costs:
azd downThe 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
