Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
In this quickstart, you create a custom remote Model Context Protocol (MCP) server from a template project by using the Azure Developer CLI (azd). This MCP server uses the Azure Functions MCP server extension to provide tools for AI models, agents, and assistants. You can also use the MCP server extension to create interactive MCP Apps.
After running the project locally and verifying your code by using GitHub Copilot, you deploy it to a new serverless function app in Azure Functions that follows current best practices for secure and scalable deployments.
Because the new app runs on the Flex Consumption plan, which follows a pay-for-what-you-use billing model, completing this quickstart incurs a small cost of a few USD cents or less in your Azure account.
Important
While creating custom MCP servers is supported for all Functions languages, this quickstart scenario currently only has examples for C#, Java, Python, and TypeScript. To complete this quickstart, select one of these supported languages at the top of the article.
This article supports version 4 of the Node.js programming model for Azure Functions.
This article supports version 2 of the Python programming model for Azure Functions.
Prerequisites
- Java 17 Developer Kit
- If you use another supported version of Java, update the project's
pom.xmlfile. - Set the
JAVA_HOMEenvironment variable to the install location of the correct version of the Java Development Kit (JDK).
- If you use another supported version of Java, update the project's
- Apache Maven 3.8.x
Visual Studio Code with these extensions:
Azure Functions extension. This extension requires Azure Functions Core Tools and attempts to install it when not available.
Azure CLI. You can also run Azure CLI commands in Azure Cloud Shell.
An Azure account with an active subscription. Create an account for free.
Initialize the project
Use the azd init command to create a local Azure Functions code project from a template.
- In Visual Studio Code, open a folder or workspace where you want to create your project.
In the Terminal, run this
azd initcommand:azd init --template remote-mcp-functions-dotnet -e mcpserver-dotnetThis command pulls the project files from the template repository and initializes the project in the current folder. The
-eflag sets a name for the current environment. Inazd, the environment maintains a unique deployment context for your app, and you can define more than one. It's also used in the name of the resource group you create in Azure.
In your local terminal or command prompt, run this
azd initcommand:azd init --template remote-mcp-functions-java -e mcpserver-javaThis command pulls the project files from the template repository and initializes the project in the current folder. The
-eflag sets a name for the current environment. Inazd, the environment maintains a unique deployment context for your app, and you can define more than one. It's also used in names of the resources you create in Azure.
In your local terminal or command prompt, run this
azd initcommand:azd init --template remote-mcp-functions-typescript -e mcpserver-tsThis command pulls the project files from the template repository and initializes the project in the current folder. The
-eflag sets a name for the current environment. Inazd, the environment maintains a unique deployment context for your app, and you can define more than one. It's also used in names of the resources you create in Azure.
In your local terminal or command prompt, run this
azd initcommand:azd init --template remote-mcp-functions-python -e mcpserver-pythonThis command pulls the project files from the template repository and initializes the project in the current folder. The
-eflag sets a name for the current environment. Inazd, the environment maintains a unique deployment context for your app, and you can define more than one. It's also used in names of the resources you create in Azure.
Start the storage emulator
Use the Azurite emulator to simulate an Azure Storage account connection when running your code project locally.
If you haven't already, install Azurite.
Press F1. In the command palette, search for and run the command
Azurite: Startto start the local storage emulator.
Run your MCP server locally
In a terminal window, go to the FunctionsMcpTool project folder:
cd src/FunctionsMcpTool
Visual Studio Code integrates with Azure Functions Core tools to let you run this project on your local development computer. To start your Functions app locally, press F5 or select the Run and Debug icon in the left-hand side Activity bar.
The Terminal panel displays the output from Core Tools. Your app starts in the Terminal panel, and you can see the names of the functions running locally.
Verify by using GitHub Copilot
The project template includes a .vscode/mcp.json file that already defines a local-mcp-function server pointing to your local MCP endpoint. Use this configuration to verify your code by using GitHub Copilot in Visual Studio Code:
Open the
.vscode/mcp.jsonfile and select the Start button above thelocal-mcp-functionconfiguration.In the Copilot Chat window, make sure that the Agent mode is selected, select the Configure tools icon, and verify that
MCP Server:local-mcp-functionis enabled in the chat.Run this prompt:
Say HelloWhen prompted to run the tool, select Allow in this Workspace so you don't have to keep granting permission. The prompt runs and returns a
Hello Worldresponse and function execution information is written to the logs.Now, select some code in one of your project files and run this prompt:
Save this snippet as snippet1Copilot stores the snippet and responds to your request with information about how to retrieve the snippet by using the
getSnippetstool. Again, you can review the function execution in the logs and verify that thesaveSnippetsfunction ran.In Copilot chat, run this prompt:
Retrieve snippet1 and apply to NewFileCopilot retrieves the snippets, adds it to a file called
NewFile, and does whatever else it thinks is needed to make the code snippet work in your project. The Functions logs show that thegetSnippetsendpoint was called.When you're done testing, press Ctrl+C to stop the Functions host.
Review the code (optional)
You can review the code that defines the MCP server tools:
The function code for the MCP server tools is defined in the src folder. The McpToolTrigger attribute exposes the functions as MCP Server tools:
[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!";
}
[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, SnippetNamePropertyDescription, true)]
string name,
[McpToolProperty(SnippetPropertyName, SnippetPropertyDescription, true)]
string snippet
)
{
return snippet;
}
}
You can view the complete project template in the Azure Functions .NET MCP Server GitHub repository.
The function code for the MCP server tools is defined in the src/main/java/com/function/ folder. The @McpToolTrigger annotation exposes the functions as MCP Server tools:
<!- :::code language="java" source="~/functions-scenarios-custom-mcp-java/src/main/java/com/function/HelloWorld.java" range="35-51" ::: >
<!- :::code language="java" source="~/functions-scenarios-custom-mcp-java/src/main/java/com/function/Snippets.java" range="80-118" ::: >
You can view the complete project template in the Azure Functions Java MCP Server GitHub repository.
The function code for the MCP server tools is defined in the src/function_app.py file. The MCP function annotations expose these functions as MCP Server tools:
@app.mcp_tool()
def hello_mcp() -> str:
"""Hello world."""
@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.blob_input(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def get_snippet(file: func.InputStream, snippetname: str) -> str:
"""Retrieve a snippet by name from Azure Blob Storage."""
snippet_content = file.read().decode("utf-8")
logging.info(f"Retrieved snippet: {snippet_content}")
return snippet_content
@app.mcp_tool()
@app.mcp_tool_property(arg_name="snippetname", description="The name of the snippet.")
@app.mcp_tool_property(arg_name="snippet", description="The content of the snippet.")
@app.blob_output(arg_name="file", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], snippetname: str, snippet: str) -> str:
"""Save a snippet with a name to Azure Blob Storage."""
if not snippetname:
return "No snippet name provided"
if not snippet:
return "No snippet content provided"
file.set(snippet)
logging.info(f"Saved snippet: {snippet}")
You can view the complete project template in the Azure Functions Python MCP Server GitHub repository.
The function code for the MCP server tools is defined in the src folder. The MCP function registration exposes these functions as MCP Server tools:
export async function mcpToolHello(_toolArguments:unknown, context: InvocationContext): Promise<string> {
console.log(_toolArguments);
// Get name from the tool arguments
const mcptoolargs = context.triggerMetadata.mcptoolargs as {
name?: string;
};
const name = mcptoolargs?.name;
console.info(`Hello ${name}, I am MCP Tool!`);
return `Hello ${name || 'World'}, I am MCP Tool!`;
}
// Register the hello tool
app.mcpTool('hello', {
toolName: 'hello',
description: 'Simple hello world MCP Tool that responses with a hello message.',
toolProperties:{
name: arg.string().describe('Required property to identify the caller.').optional()
},
handler: mcpToolHello
});
// SaveSnippet function - saves a snippet with a name
export async function saveSnippet(
_toolArguments: unknown,
context: InvocationContext
): Promise<string> {
console.info("Saving snippet");
// Get snippet name and content from the tool arguments
const mcptoolargs = context.triggerMetadata.mcptoolargs as {
snippetname?: string;
snippet?: string;
};
const snippetName = mcptoolargs?.snippetname;
const snippet = mcptoolargs?.snippet;
if (!snippetName) {
return "No snippet name provided";
}
if (!snippet) {
return "No snippet content provided";
}
// Save the snippet to blob storage using the output binding
context.extraOutputs.set(blobOutputBinding, snippet);
console.info(`Saved snippet: ${snippetName}`);
return snippet;
}
You can view the complete project template in the Azure Functions TypeScript MCP Server GitHub repository.
After verifying the MCP server tools locally, you can publish the project to Azure.
Deploy to Azure
This project is configured to use azd to deploy this project to a new function app in a Flex Consumption plan in Azure. The project includes a set of Bicep files that azd uses to create a secure deployment to a Flex Consumption plan that follows best practices.
In Visual Studio Code, press F1 to open the command palette. Search for and run the command
Azure Developer CLI (azd): Package, Provision and Deploy (up). Then, sign in by using your Azure account.When prompted, select these required deployment parameters:
Parameter Description Azure subscription Subscription in which your resources are created. Azure location Azure region in which to create the resource group that contains the new Azure resources. Only regions that currently support the Flex Consumption plan are shown. vnetEnabled Falseto skip creating virtual network resources, which simplifies the deployment.After the command completes successfully, you see links to the resources you created.
Connect to your remote MCP server
Your MCP server is now running in Azure. The project template includes a remote-mcp-function entry in .vscode/mcp.json that's already configured to connect to your remote server. When you start this server, VS Code prompts you for the function app name and system key needed to access the remote MCP endpoint.
Run this script that uses
azdand the Azure CLI to print out both the function app name and the system key (mcp_extension) required to access the tools:eval $(azd env get-values --output dotenv) MCP_EXTENSION_KEY=$(az functionapp keys list --resource-group $AZURE_RESOURCE_GROUP \ --name $AZURE_FUNCTION_NAME --query "systemKeys.mcp_extension" -o tsv) printf "Function app name: %s\n" "$SERVICE_API_NAME" printf "MCP Server key: %s\n" "$MCP_EXTENSION_KEY"In
.vscode/mcp.json, select Start above theremote-mcp-functionconfiguration.When prompted, enter the function app name and system key values from the previous step.
Verify your deployment
You can now have GitHub Copilot use your remote MCP tools just as you did locally, but now the code runs securely in Azure. Replay the same commands you used earlier to ensure everything works correctly.
Clean up resources
When you're done working with your MCP server and related resources, use this command to delete the function app and its related resources from Azure to avoid incurring further costs:
azd down