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 secured by design.
What is Azure Samples remote mcp functions typescript
Getting Started with Remote MCP Servers using Azure Functions (Node.js/TypeScript)
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 secured by design using keys and HTTPs, and allows more options for OAuth using EasyAuth and/or API Management as well as network isolation using VNET.
Watch the video overview
If you're looking for this sample in more languages check out the .NET/C# and Python versions.
*
Below is the architecture diagram for the Remote MCP Server using Azure Functions:
!Architecture Diagram
Prerequisites
- Node.js version 18 or higher
- Azure Functions Core Tools >=
4.0.7030
- Azure Developer CLI
- To use Visual Studio Code to run and debug locally:
- Visual Studio Code
- Azure Functions extension
- Docker to run Azurite, the Azure Storage Emulator (optional)
Prepare your local environment
An Azure Storage Emulator is needed for this particular sample because we will save and get snippets from blob storage.
-
Start Azurite
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 \ mcr.microsoft.com/azure-storage/azurite
Note if you use Azurite coming from VS Code extension you need to run
Azurite: Start
now or you will see errors.
Run your MCP Server locally from the terminal
-
Install dependencies
npm install
-
Build the project
npm run build
-
Start the Functions host locally:
func start
Note by default this will use the webhooks route:
/runtime/webhooks/mcp/sse
. Later we will use this in Azure to set the key on client/host calls:/runtime/webhooks/mcp/sse?code=<system_key>
Use the local MCP server from within a client/host
VS Code - Copilot Edits
-
Add MCP Server from command palette and add URL to your running Function app's SSE endpoint:
http://0.0.0.0:7071/runtime/webhooks/mcp/sse
-
Select HTTP (Server-Sent-Events) for the type of MCP server to add.
-
Enter the URL to your running function app's SSE endpoint
-
Enter the server ID. (This can be any name you want)
-
Choose if you want to run this in your User settings (available to all apps for you) or to your Workspace settings (available to this app, only)
-
List MCP Servers from command palette and start the server. The previous step may have already started your local server. If so, you can skip this step.
-
In Copilot chat agent mode enter a prompt to trigger the tool, e.g., select some code and enter this prompt
Say Hello
Save this snippet as snippet1
Retrieve snippet1 and apply to newFile.ts
-
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.exe
host process, and List MCP Servers from command palette and stop the local server.
MCP Inspector
-
In a new terminal window, install and run MCP Inspector
npx @modelcontextprotocol/inspector node build/index.js
-
If you stopped your function app previously, start the Functions host locally:
func start
-
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
SSE
-
Set the URL to your running Function app's SSE endpoint and Connect:
http://0.0.0.0:7071/runtime/webhooks/mcp/sse
-
List Tools. Click on a tool and Run Tool.
-
When you're done, press Ctrl+C in the terminal window to stop the
func.exe
host process, and press Ctrl+C in the terminal window to stop the@modelcontextprotocol/inspector
host process.
Deploy to Azure for Remote MCP
Optionally, you can opt-in to a VNet being used in the sample. (If you choose this, do this before azd up
)
azd env set VNET_ENABLED true
Run this azd command to provision the function app, with any required Azure resources, and deploy your code:
azd up
Note API Management can be used for improved security and policies over your MCP Server, and App Service built-in authentication can be used to set up your favorite OAuth provider including Entra.
Connect to your remote MCP server function app from a client
Your client will need a key in order to invoke the new hosted SSE endpoint, which will be of the form https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse
. The hosted function requires a system key by default which can be obtained from the portal or the CLI (az functionapp keys list --resource-group <resource_group> --name <function_app_name>
). Obtain the system key named mcp_extension
.
Connect to remote MCP server in MCP Inspector
For MCP Inspector, you can include the key in the URL:
https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse?code=<your-mcp-extension-system-key>
Connect to remote MCP server in VS Code - GitHub Copilot
For GitHub Copilot within VS Code, you should set the key as the x-functions-key
header in mcp.json
, and you would use https://<funcappname>.azurewebsites.net/runtime/webhooks/mcp/sse
for the URL. The following example is from the mcp.json
file included in this repository and uses an input to prompt you to provide the key when you start the server from VS Code. Your mcp.json
file looks like this:
{
"inputs": [
{
"type": "promptString",
"id": "functions-mcp-extension-system-key",
"description": "Azure Functions MCP Extension System Key",
"password": true
},
{
"type": "promptString",
"id": "functionapp-name",
"description": "Azure Functions App Name"
}
],
"servers": {
"remote-mcp-function": {
"type": "sse",
"url": "https://${input:functionapp-name}.azurewebsites.net/runtime/webhooks/mcp/sse",
"headers": {
"x-functions-key": "${input:functions-mcp-extension-system-key}"
}
},
"local-mcp-function": {
"type": "sse",
"url": "http://0.0.0.0:7071/runtime/webhooks/mcp/sse"
}
}
}
-
Click Start on the server
remote-mcp-function
, inside themcp.json
file: -
Enter the name of the function app that you created in the Azure Portal, when prompted by VS Code.
-
Enter the
Azure Functions MCP Extension System Key
into the prompt. You can copy this from the Azure portal for your function app by going to the Functions menu item, then App Keys, and copying themcp_extension
key from the System Keys. -
In Copilot chat agent mode enter a prompt to trigger the tool, e.g., select some code and enter this prompt
Say Hello
Save this snippet as snippet1
Retrieve snippet1 and apply to newFile.ts
Redeploy your code
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.
Clean up resources
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 down
Source Code
The function code for the getSnippet
and saveSnippet
endpoints are defined in the TypeScript files in the src
directory. The MCP function annotations expose these functions as MCP Server tools.
This shows the code for a few MCP server examples (get string, get object, save object):
// Hello function - responds with hello message
export async function mcpToolHello(context: InvocationContext): Promise<string> {
return "Hello 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.',
handler: mcpToolHello
});
// GetSnippet function - retrieves a snippet by name
export async function getSnippet(_message: unknown, context: InvocationContext): Promise<string> {
console.info('Getting snippet');
// Get snippet name from the tool arguments
const mcptoolargs = context.triggerMetadata.mcptoolargs as { snippetname?: string };
const snippetName = mcptoolargs?.snippetname;
console.info(`Snippet name: ${snippetName}`);
if (!snippetName) {
return "No snippet name provided";
}
// Get the content from blob binding - properly retrieving from extraInputs
const snippetContent = context.extraInputs.get(blobInputBinding);
if (!snippetContent) {
return `Snippet '${snippetName}' not found`;
}
console.info(`Retrieved snippet: ${snippetName}`);
return snippetContent as string;
}
// Register the GetSnippet tool
app.mcpTool('getsnippet', {
toolName: GET_SNIPPET_TOOL_NAME,
description: GET_SNIPPET_TOOL_DESCRIPTION,
toolProperties: [
{
propertyName: SNIPPET_NAME_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
}
],
extraInputs: [blobInputBinding],
handler: getSnippet
});
// SaveSnippet function - saves a snippet with a name
export async function saveSnippet(_message: 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;
}
// Register the SaveSnippet tool
app.mcpTool('savesnippet', {
toolName: SAVE_SNIPPET_TOOL_NAME,
description: SAVE_SNIPPET_TOOL_DESCRIPTION,
toolProperties: [
{
propertyName: SNIPPET_NAME_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_NAME_PROPERTY_DESCRIPTION,
},
{
propertyName: SNIPPET_PROPERTY_NAME,
propertyValue: PROPERTY_TYPE,
description: SNIPPET_PROPERTY_DESCRIPTION,
}
],
extraOutputs: [blobOutputBinding],
handler: saveSnippet
});
Note that the host.json
file also includes a reference to the experimental bundle, which is required for apps using this feature:
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental",
"version": "*4.*, 5.0.0)"
}
Next Steps
- Add [API Management* to your MCP server (auth, gateway, policies, more!)
- Add built-in auth to your MCP server
- Enable VNET using VNET_ENABLED=true flag
- Learn more about related MCP efforts from Microsoft
Leave a Comment
Frequently Asked Questions
What is MCP?
MCP (Model Context Protocol) is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications, providing a standardized way to connect AI models to different data sources and tools.
What are MCP Servers?
MCP Servers are lightweight programs that expose specific capabilities through the standardized Model Context Protocol. They act as bridges between LLMs like Claude and various data sources or services, allowing secure access to files, databases, APIs, and other resources.
How do MCP Servers work?
MCP Servers follow a client-server architecture where a host application (like Claude Desktop) connects to multiple servers. Each server provides specific functionality through standardized endpoints and protocols, enabling Claude to access data and perform actions through the standardized protocol.
Are MCP Servers secure?
Yes, MCP Servers are designed with security in mind. They run locally with explicit configuration and permissions, require user approval for actions, and include built-in security features to prevent unauthorized access and ensure data privacy.
Related MCP Servers
AWS Knowledge Base Retrieval
An MCP server implementation for retrieving information from the AWS Knowledge Base using the Bedrock Agent Runtime.
chrisdoc hevy mcp
sylphlab pdf reader mcp
An MCP server built with Node.js/TypeScript that allows AI agents to securely read PDF files (local or URL) and extract text, metadata, or page counts. Uses pdf-parse.
aashari mcp server atlassian bitbucket
Node.js/TypeScript MCP server for Atlassian Bitbucket. Enables AI systems (LLMs) to interact with workspaces, repositories, and pull requests via tools (list, get, comment, search). Connects AI directly to version control workflows through the standard MCP interface.
aashari mcp server atlassian confluence
Node.js/TypeScript MCP server for Atlassian Confluence. Provides tools enabling AI systems (LLMs) to list/get spaces & pages (content formatted as Markdown) and search via CQL. Connects AI seamlessly to Confluence knowledge bases using the standard MCP interface.
prisma prisma
Next-generation ORM for Node.js & TypeScript | PostgreSQL, MySQL, MariaDB, SQL Server, SQLite, MongoDB and CockroachDB
Zzzccs123 mcp sentry
mcp sentry for typescript sdk
zhuzhoulin dify mcp server
zhongmingyuan mcp my mac
zhixiaoqiang desktop image manager mcp
MCP 服务器,用于管理桌面图片、查看详情、压缩、移动等(完全让Trae实现)
Submit Your MCP Server
Share your MCP server with the community
Submit Now