使用Azure函数(Python)开始使用远程MCP服务器
这是一个快速入门模板,可以使用Azure Functions和Python轻松构建自定义远程MCP服务器并将其部署到云端。您可以通过调试在本地计算机上克隆/还原/运行,以及 azd up 几分钟后就可以把它放在云端了。MCP服务器通过使用密钥和HTTPS进行设计来保护,并允许使用内置身份验证和/或 API管理 以及使用VNET进行网络隔离。
如果您正在寻找更多语言的此示例,请查看 .NET/C# 和 版本。
](https://codespaces.new/Azure-Samples/remote-mcp-functions-python)
以下是使用Azure Functions的远程MCP服务器的架构图:
先决条件
- python 3.11或更高版本
- Azure功能核心工具 >=
4.0.7030 - Azure开发者命令行界面
- 要使用Visual Studio代码在本地运行和调试,请执行以下操作:
- Visual Studio Code - Azure功能扩展
准备好当地环境
此特定示例需要Azure存储模拟器,因为我们将从blob存储中保存和获取代码段。
- 开始Azurite
docker run -p 10000:10000 -p 10001:10001 -p 10002:10002 \
mcr.microsoft.com/azure-storage/azurite备注 如果你使用来自VS Code扩展的Azurite,你需要运行 Azurite: Start 现在,否则你会看到错误。从终端本地运行MCP服务器
- 在新的终端窗口中切换到src文件夹:
cd src- 安装Python依赖项:
pip install -r requirements.txt备注 在执行以下操作之前创建虚拟环境是一种最佳做法 pip install 以避免依赖关系问题/冲突,或者如果您在CodeSpaces中运行。看 VS代码中的Python环境 了解更多信息。- 在本地启动Functions主机:
func start备注 默认情况下,这将使用webhooks路由:/runtime/webhooks/mcp稍后,我们将在Azure中使用它来设置客户端/主机调用的密钥:/runtime/webhooks/mcp?code=
连接到 *本地* 来自客户端/主机的MCP服务器
VS代码-副驾驶代理模式
- 添加MCP服务器 从命令面板中,将URL添加到正在运行的Function应用程序的MCP端点:
http://0.0.0.0:7071/runtime/webhooks/mcp- 列出MCP服务器 从命令面板启动服务器
- 在Copilot聊天代理模式下,输入提示以触发工具,例如,选择一些代码并输入此提示
Say Hello Save this snippet as snippet1 Retrieve snippet1 and apply to newFile.py- 当系统提示运行该工具时,点击同意 继续
- 完成后,在终端窗口中按Ctrl+C停止Functions主机进程。
MCP检查员
- 在一个 新终端窗口,安装并运行MCP检查器
npx @modelcontextprotocol/inspector- 按CTRL键单击可从应用程序显示的URL加载MCP Inspector web应用程序(例如。http://0.0.0.0:5173/#resources)
- 将运输类型设置为
Streamable HTTP
- 将URL设置为正在运行的Function应用程序的MCP端点,然后 连接:
http://0.0.0.0:7071/runtime/webhooks/mcp备注 此步骤在CodeSpaces中不起作用。请继续部署到远程MCP。
- 列出工具。单击工具,然后 运行工具.
验证Azurite中的本地blob存储
在本地测试代码段保存功能后,您可以验证blob是否正确存储在本地Azurite存储模拟器中。
使用Azure存储资源管理器
- 打开Azure存储资源管理器
- 在左侧面板中,展开 模拟器和附件 → 存储帐户 → (模拟器-默认端口)(按键)
- 引导到 Blob容器 → 片段
- 您应该看到此容器中所有保存的代码段都是blob文件
- 双击任何blob以查看其内容并验证代码段数据是否已正确保存
使用Azure CLI(替代方案)
如果您更喜欢使用命令行,还可以使用Azure CLI和存储模拟器验证blob:
# 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 --file --connection-string "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;"此验证步骤可确保您的MCP服务器与本地存储模拟器正确交互,并且blob存储功能在部署到Azure之前按预期工作。
部署到Azure以实现远程MCP
在根目录中,创建一个新的 雅匝德 环境。这将成为Azure资源的资源组:
azd env new 运行此azd命令为功能应用程序配置任何所需的Azure资源,并部署您的代码:
azd up您可以选择加入示例中使用的ExpressRoute。为此,请先执行此操作 azd up
azd env set VNET_ENABLED true另外, API管理 可用于提高MCP服务器的安全性和策略,以及 App Service内置身份验证 可用于设置您最喜欢的OAuth提供程序,包括Entra。
连接到您的 *远程* 来自客户端的MCP服务器功能应用程序
您的客户端需要一个密钥来调用新的托管MCP端点,该密钥的格式为 https://.azurewebsites.net/runtime/webhooks/mcp默认情况下,托管功能需要一个系统密钥,可以从 门户 或CLI(az functionapp keys list --resource-group --name ).获取名为的系统密钥 mcp_extension.
在MCP Inspector中连接到远程MCP服务器
对于MCP检查器,您可以在URL中包含密钥:
https://.azurewebsites.net/runtime/webhooks/mcp?code=在VS Code中连接到远程MCP服务器-GitHub Copilot
对于VS Code中的GitHub Copilot,您应该将密钥设置为 x-functions-key 标题在 mcp.json,你只需使用 https://.azurewebsites.net/runtime/webhooks/mcp 以下示例使用输入,并在从VS Code启动服务器时提示您提供密钥。备注 mcp.json 已包含在此仓库中,并将由VS Code获取。单击服务器上的“开始”,系统将提示输入以下值 functionapp-name (在您的/.alue/\*/env文件中)和 functions-mcp-extension-system-key 可以从上面的CLI命令或Function App门户中的API密钥中获取。
{
"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": "http",
"url": "https://${input:functionapp-name}.azurewebsites.net/runtime/webhooks/mcp",
"headers": {
"x-functions-key": "${input:functions-mcp-extension-system-key}"
}
},
"local-mcp-function": {
"type": "http",
"url": "http://0.0.0.0:7071/runtime/webhooks/mcp"
}
}
}对于MCP检查器,您可以在URL中包含密钥: https://.azurewebsites.net/runtime/webhooks/mcp?code=.
对于VS Code中的GitHub Copilot,您应该将密钥设置为 x-functions-key 标题在 mcp.json,你只需使用 https://.azurewebsites.net/runtime/webhooks/mcp 以下示例使用输入,当您从VS Code启动服务器时,将提示您提供密钥:
{
"inputs": [
{
"type": "promptString",
"id": "functions-mcp-extension-system-key",
"description": "Azure Functions MCP Extension System Key",
"password": true
}
],
"servers": {
"my-mcp-server": {
"type": "http",
"url": ".azurewebsites.net/runtime/webhooks/mcp",
"headers": {
"x-functions-key": "${input:functions-mcp-extension-system-key}"
}
}
}
}重新部署代码
你可以运行 azd up 根据需要多次执行命令,以配置Azure资源并将代码更新部署到功能应用程序。
\[!注意\] 部署的代码文件总是被最新的部署包覆盖。
清理资源
使用完功能应用程序和相关资源后,您可以使用此命令从Azure中删除功能应用程序及其相关资源,从而避免产生任何进一步的成本:
azd down有用的Azure命令
部署应用程序后,您可以使用以下命令来管理和监视应用程序:
# Get your function app name from the environment file
FUNCTION_APP_NAME=$(cat .azure/$(cat .azure/config.json | jq -r '.defaultEnvironment')/env.json | jq -r '.FUNCTION_APP_NAME')
echo $FUNCTION_APP_NAME
# Get resource group
RESOURCE_GROUP=$(cat .azure/$(cat .azure/config.json | jq -r '.defaultEnvironment')/env.json | jq -r '.AZURE_RESOURCE_GROUP')
echo $RESOURCE_GROUP
# View function app logs
az webapp log tail --name $FUNCTION_APP_NAME --resource-group $RESOURCE_GROUP
# Redeploy the application without provisioning new resources
azd deploy源代码
的功能代码 get_snippet 和 save_snippet 端点在Python文件中定义 src 目录。MCP函数装饰器将这些函数作为MCP服务器工具公开。
此示例使用中提供的新的一流MCP装饰器 azure-functions>=1.25.0b2,通过以下方式大大简化了代码:
- 直接从函数签名和类型提示推断工具属性
- 无需手动对工具属性进行JSON序列化
- 使用标准Python装饰器(
@app.mcp_tool(),@app.mcp_tool_property(),@app.blob_input(),@app.blob_output())
重要提示: 使用MCP装饰器时,您必须设置 PYTHON_ISOLATE_WORKER_DEPENDENCIES=1 在您的应用程序设置中(均在本地 local.settings.json 以及在您部署的Azure Function应用程序中)。
以下是function_app.py文件中的实际代码:
@app.mcp_tool()
def hello_mcp() -> str:
"""Hello world."""
return "Hello I am MCPTool!"
@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}")
return f"Snippet '{snippet}' saved successfully"请注意 host.json 文件还包括对 _预览_ 扩展包,现在使用此功能的应用程序需要它:
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Preview",
"version": "[4.32.0, 5.0.0)"
}天气应用程序示例
一个示例MCP应用程序,通过交互式UI显示天气信息。
什么是MCP应用程序?
MCP应用程序 让工具返回交互式界面而不是纯文本。当工具声明UI资源时,主机会在沙盒iframe中呈现它,用户可以在其中直接交互。
MCP应用程序=工具+UI资源
该架构依赖于两个MCP原语:
- 工具 UI元数据指向资源URI
- 资源 包含通过
ui://方案
Azure Functions使两者都易于构建。
天气应用程序的先决条件
- python 3.11或更高版本
- (用于构建UI)
- Azure功能核心工具v4
- MCP兼容主机(Claude Desktop、VS Code、ChatGPT等)
天气应用程序入门
1.构建UI
在运行功能应用程序之前,必须捆绑UI:
cd src/app
npm install
npm run build
cd ../这将创建一个捆绑 src/app/dist/index.html 函数所服务的文件。
2.运行功能应用程序
在 src 目录,运行:
func startMCP服务器将在 http://localhost:7071/runtime/webhooks/mcp.
3.从VS代码连接
打开 .vcode/mcp.json.查找被调用的服务器 _本地mcp功能_ 然后单击 开始 名字之上。服务器已经设置了正在运行的Function应用程序的MCP端点:
http://localhost:7071/runtime/webhooks/mcp4.提示代理人
问Copilot:“西雅图的天气怎么样?”
天气应用程序源代码
源代码位于 src/function_app.py关键概念是如何 工具通过元数据连接到资源.
带有UI元数据的工具
这 get_weather 工具使用 metadata 参数在 @app.mcp_tool() 声明它具有关联的UI:
# Required metadata
TOOL_METADATA = '{"ui": {"resourceUri": "ui://weather/index.html"}}'
@app.mcp_tool()
@app.mcp_tool_property(arg_name="location", description="City name to check weather for (e.g., Seattle, New York, Miami)")
def get_weather(location: str) -> Dict[str, Any]:
"""Returns current weather for a location via Open-Meteo."""
logging.info(f"Getting weather for location: {location}")
try:
result = weather_service.get_current_weather(location)
if "TemperatureC" in result:
logging.info(f"Weather fetched for {result['Location']}: {result['TemperatureC']}°C")
else:
logging.warning(f"Weather error for {result['Location']}: {result.get('Error', 'Unknown error')}")
return result
except Exception as e:
logging.error(f"Failed to get weather for {location}: {e}")
return {
"Location": location or "Unknown",
"Error": f"Unable to fetch weather: {str(e)}",
"Source": "api.open-meteo.com"
}这 resourceUri 指向 ui://weather/index.html-这个消息告诉MCP主机,当调用这个工具时,在那个资源URI上有一个可用的交互式UI。
为UI服务的资源
这 get_weather_widget 函数在匹配的URI处提供捆绑的HTML:
# Optional UI metadata
RESOURCE_METADATA = '{"ui": {"prefersBorder": true}}'
@app.mcp_resource_trigger(
arg_name="context",
uri=WEATHER_WIDGET_URI,
resource_name=WEATHER_WIDGET_NAME,
description=WEATHER_WIDGET_DESCRIPTION,
mime_type=WEATHER_WIDGET_MIME_TYPE,
metadata=RESOURCE_METADATA
)
def get_weather_widget(context) -> str:
"""Get the weather widget HTML content."""
logging.info("Getting weather widget")
try:
# Get the path to the widget HTML file
current_dir = Path(__file__).parent
file_path = current_dir / "app" / "dist" / "index.html"
if file_path.exists():
return file_path.read_text(encoding="utf-8")
else:
# Return a fallback HTML if file not found
return """
Weather Widget
Weather Widget
Widget content not found. Please ensure the app/dist/index.html file exists.
"""
except Exception as e:
logging.error(f"Error reading weather widget file: {e}")
return """
Weather Widget Error
Weather Widget
Error loading widget content.
"""它是如何协同工作的
- 用户问:“西雅图的天气怎么样?”
- 代理人致电
get_weather工具 - 工具返回天气数据(JSON) 和 主机看到
ui.resourceUri元数据 - 主机从以下位置获取UI资源
ui://weather/index.html - 主机在沙盒iframe中呈现HTML,并将工具结果作为上下文传递
- 用户看到的是交互式天气小部件,而不是纯文本
用户界面(TypeScript)
前端在 src/app/src/weather-app.ts 接收工具结果并呈现天气显示。它与Vite捆绑在一起 index.html 资源所服务的。
后续步骤
- 添加 API管理 到您的MCP服务器(身份验证、网关、策略等!)
- 添加 内置身份验证 到您的MCP服务器
- 使用VNET_ENABLED=true标志启用VNET
- 了解更多 微软的相关MCP工作
