你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
在本快速入门中,你将使用 Azure 开发人员 CLI 从模板项目创建自定义远程模型上下文协议 (azdMCP) 服务器。 MCP 服务器使用 Azure Functions MCP 服务器扩展为 AI 模型、代理和助手提供工具。 在本地运行项目并使用 GitHub Copilot 验证代码后,将其部署到 Azure Functions 中的新无服务器函数应用,该应用遵循当前安全且可缩放的部署最佳做法。
小窍门
Functions 还使你能够将现有的 MCP 服务器代码项目部署到 Flex Consumption 计划应用,而无需对代码项目进行更改。 有关详细信息,请参阅 快速入门:在 Azure Functions 上托管现有的 MCP 服务器。
由于新应用采用 Flex 消耗计划,该计划遵循即用即付计费模式,因此完成本快速入门会在你的 Azure 帐户中产生最多几美分的费用。
重要
尽管所有 Functions 语言都支持 创建自定义 MCP 服务器 ,但本快速入门方案目前仅包含 C#、Python 和 TypeScript 的示例。 若要完成本快速入门,请在文章顶部选择其中一种受支持的语言。
本文支持适用于 Azure Functions 的 Node.js 编程模型版本 4。
本文支持适用于 Azure Functions 的 Python 编程模型版本 2。
先决条件
-
Java 17 开发人员工具包
- 如果使用另一个支持的 Java 版本,则必须更新项目的 pom.xml 文件。
- 将
JAVA_HOME环境变量设置为正确版本的 Java 开发工具包(JDK)的安装位置。
- Apache Maven 3.8.x
包含以下扩展的 Visual Studio Code:
Azure Functions 扩展。 此扩展需要 Azure Functions Core Tools ,并在不可用时尝试安装它。
Azure CLI。 还可以在 Azure Cloud Shell 中运行 Azure CLI 命令。
拥有有效订阅的 Azure 帐户。 免费创建帐户。
初始化项目
azd init使用命令从模板创建本地 Azure Functions 代码项目。
- 在 Visual Studio Code 中,打开要在其中创建项目的文件夹或工作区。
在终端中,运行以下命令
azd init:azd init --template remote-mcp-functions-dotnet -e mcpserver-dotnet此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。
-e标志设置当前环境的名称。 在azd中,环境可维护应用的唯一部署上下文,你可以定义多个环境。 它也在 Azure 中你创建的资源组名称中使用。
在本地终端或命令提示符下运行以下命令
azd init:azd init --template remote-mcp-functions-java -e mcpserver-java此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。
-e标志设置当前环境的名称。 在azd中,环境可维护应用的唯一部署上下文,你可以定义多个环境。 它还用于在 Azure 中创建的资源的名称。
在本地终端或命令提示符下运行以下命令
azd init:azd init --template remote-mcp-functions-typescript -e mcpserver-ts此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。
-e标志设置当前环境的名称。 在azd中,环境可维护应用的唯一部署上下文,你可以定义多个环境。 它还用于在 Azure 中创建的资源的名称。
在本地终端或命令提示符下运行以下命令
azd init:azd init --template remote-mcp-functions-python -e mcpserver-python此命令从模板存储库中拉取项目文件,并在当前文件夹中初始化项目。
-e标志设置当前环境的名称。 在azd中,环境可维护应用的唯一部署上下文,你可以定义多个环境。 它还用于在 Azure 中创建的资源的名称。
启动存储模拟器
在本地运行代码项目时,使用 Azurite 模拟器模拟 Azure 存储帐户连接。
如果尚未 安装,请安装 Azurite。
按 F1。 在命令面板中,搜索并运行命令
Azurite: Start以启动本地存储模拟器。
在本地运行 MCP 服务器
Visual Studio Code 与 Azure Functions Core 工具 集成,使你能够使用 Azurite 模拟器在本地开发计算机上运行此项目。
若要在本地启动函数,请按 F5 或左侧活动栏中的 “运行和调试 ”图标。 “终端”面板将显示 Core Tools 的输出。 应用在 终端 面板中启动,可以看到在本地运行的函数的名称。
记下用于在 Visual Studio Code 中配置 GitHub Copilot 的本地 MCP 服务器终结点(例如
http://localhost:7071/runtime/webhooks/mcp)。
使用 GitHub Copilot 进行验证
若要验证代码,请在 Visual Studio Code 中将正在运行的项目添加为 GitHub Copilot 的 MCP 服务器:
按 F1。 在命令面板中,搜索并运行 MCP:添加服务器。
选择 HTTP(Server-Sent Events 事件) 作为传输类型。
输入在上一步中复制的 MCP 终结点的 URL。
使用生成的 服务器 ID 并选择 “工作区 ”,将 MCP 服务器连接保存到工作区设置。
打开命令面板并运行 MCP:列出服务器 并验证添加的服务器是否已列出并运行。
在 Copilot 聊天中,选择 代理 模式并运行以下提示:
Say Hello当系统提示运行该工具时,选择 “在此工作区中允许” ,这样就不必继续授予权限。 提示运行后返回
Hello World响应,且函数执行信息会被记录在日志中。现在,在项目文件之一中选择一些代码,然后运行以下提示:
Save this snippet as snippet1Copilot 存储代码片段并响应请求,并提供有关如何使用该工具检索代码片段
getSnippets的信息。 再一次,你可以查看日志中的函数执行,并验证saveSnippets函数是否运行。在 Copilot 聊天中,运行以下提示:
Retrieve snippet1 and apply to NewFileCopilot 检索代码片段,将其添加到一个名为
NewFile的文件中,并执行任何其他认为必要的操作,以使代码片段在项目中正确运行。 Functions 日志显示调用了getSnippets端点。完成测试后,按 Ctrl+C 停止 Functions 主机。
查看代码(可选)
可以查看定义 MCP 服务器工具的代码:
MCP 服务器工具的函数代码在 src 文件夹中定义。 该 McpToolTrigger 属性将函数公开为 MCP 服务器工具:
[Function(nameof(SayHello))]
public string SayHello(
[McpToolTrigger(HelloToolName, HelloToolDescription)] ToolInvocationContext context
)
{
logger.LogInformation("Saying hello");
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;
}
}
可以在 Azure Functions .NET MCP 服务器 GitHub 存储库中查看完整的项目模板。
MCP 服务器工具的函数代码在 src/main/java/com/function/ 文件夹中定义。
@McpToolTrigger 批注将函数公开为 MCP 服务器工具:
description = "The messages to be logged.",
isRequired = true,
isArray = true)
String messages,
final ExecutionContext functionExecutionContext
) {
functionExecutionContext.getLogger().info("Hello, World!");
functionExecutionContext.getLogger().info("Tool Name: " + mcpToolInvocationContext.getName());
functionExecutionContext.getLogger().info("Transport Type: " + mcpToolInvocationContext.getTransportType());
// Handle different transport types
if (mcpToolInvocationContext.isHttpStreamable()) {
functionExecutionContext.getLogger().info("Session ID: " + mcpToolInvocationContext.getSessionid());
} else if (mcpToolInvocationContext.isHttpSse()) {
if (mcpToolInvocationContext.getClientinfo() != null) {
functionExecutionContext.getLogger().info("Client: " +
mcpToolInvocationContext.getClientinfo().get("name").getAsString() + " v" +
// Write the snippet content to the output blob
outputBlob.setValue(snippet);
return "Successfully saved snippet '" + snippetName + "' with " + snippet.length() + " characters.";
}
/**
* Azure Function that handles retrieving a text snippet from Azure Blob Storage.
* <p>
* The function is triggered by an MCP Tool Trigger. The snippet name is provided
* as an MCP tool property, and the snippet content is read from the blob at the
* path derived from the snippet name.
*
* @param mcpToolInvocationContext The JSON input from the MCP tool trigger.
* @param snippetName The name of the snippet to retrieve, provided as an MCP tool property.
* @param inputBlob The Azure Blob input binding that fetches the snippet content.
* @param functionExecutionContext The execution context for logging.
*/
@FunctionName("GetSnippets")
@StorageAccount("AzureWebJobsStorage")
public String getSnippet(
@McpToolTrigger(
name = "getSnippets",
description = "Gets a text snippet from your snippets collection.")
String mcpToolInvocationContext,
@McpToolProperty(
name = SNIPPET_NAME_PROPERTY_NAME,
propertyType = "string",
description = "The name of the snippet.",
isRequired = true)
String snippetName,
@BlobInput(name = "inputBlob", path = BLOB_PATH)
String inputBlob,
final ExecutionContext functionExecutionContext
) {
// Log the entire incoming JSON for debugging
functionExecutionContext.getLogger().info(mcpToolInvocationContext);
// Log the snippet name and the fetched snippet content from the blob
可以在 Azure Functions Java MCP 服务器 GitHub 存储库中查看完整的项目模板。
MCP 服务器工具的函数代码在 src/function_app.py 文件中定义。 MCP 函数注释将这些函数公开为 MCP 服务器工具:
tool_properties_save_snippets_json = json.dumps([prop.to_dict() for prop in tool_properties_save_snippets_object])
tool_properties_get_snippets_json = json.dumps([prop.to_dict() for prop in tool_properties_get_snippets_object])
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="hello_mcp",
description="Hello world.",
toolProperties="[]",
)
def hello_mcp(context) -> None:
"""
@app.generic_trigger(
arg_name="context",
type="mcpToolTrigger",
toolName="save_snippet",
description="Save a snippet with a name.",
toolProperties=tool_properties_save_snippets_json,
)
@app.generic_output_binding(arg_name="file", type="blob", connection="AzureWebJobsStorage", path=_BLOB_PATH)
def save_snippet(file: func.Out[str], context) -> str:
content = json.loads(context)
snippet_name_from_args = content["arguments"][_SNIPPET_NAME_PROPERTY_NAME]
snippet_content_from_args = content["arguments"][_SNIPPET_PROPERTY_NAME]
if not snippet_name_from_args:
return "No snippet name provided"
if not snippet_content_from_args:
return "No snippet content provided"
file.set(snippet_content_from_args)
logging.info(f"Saved snippet: {snippet_content_from_args}")
return f"Snippet '{snippet_content_from_args}' saved successfully"
可以在 Azure Functions Python MCP 服务器 GitHub 存储库中查看完整的项目模板。
MCP 服务器工具的函数代码在 src 文件夹中定义。 MCP 函数注册将这些函数公开为 MCP 服务器工具:
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;
}
可以在 Azure Functions TypeScript MCP 服务器 GitHub 存储库中查看完整的项目模板。
在本地验证 MCP 服务器工具后,可以将项目发布到 Azure。
部署到 Azure 云
此项目配置为使用 azd up 命令将此项目部署到 Azure 中 Flex 消耗计划中的新函数应用。 该项目包括一组 Bicep 文件,azd 使用这些文件可以按照最佳做法为 Flex 消耗计划创建安全部署。
在 Visual Studio Code 中,按 F1 打开命令面板。 搜索并运行命令
Azure Developer CLI (azd): Package, Provison and Deploy (up)。 然后,使用 Azure 帐户登录。如果尚未登录,请使用 Azure 帐户进行身份验证。
出现提示时,请提供以下所需的部署参数:
参数 Description Azure 订阅 要在其中创建资源的订阅。 Azure 位置 要在其中创建包含新 Azure 资源的资源组的 Azure 区域。 仅显示当前支持 Flex 消耗计划的区域。 命令成功完成后,你会看到指向所创建资源的链接。
连接到远程 MCP 服务器
MCP 服务器现在在 Azure 中运行。 访问这些工具时,需要在请求中包含系统密钥。 此密钥为访问远程 MCP 服务器的客户端提供一定程度的访问控制。 获取此密钥后,可以将 GitHub Copilot 连接到远程服务器。
运行此脚本,该脚本使用
azdAzure CLI 输出访问工具所需的 MCP 服务器 URL 和系统密钥(mcp_extension):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 "MCP Server URL: %s\n" "https://$SERVICE_API_NAME.azurewebsites.net/runtime/webhooks/mcp" printf "MCP Server key: %s\n" "$MCP_EXTENSION_KEY"在 Visual Studio Code 中,按 F1 打开命令面板,搜索并运行打开配置文件的
MCP: Open Workspace Folder MCP Configuraton命令mcp.json。在
mcp.json配置中,找到前面添加的命名 MCP 服务器,将url值更改为远程 MCP 服务器 URL,并添加包含headers.x-functions-key复制的 MCP 服务器访问密钥的元素,如以下示例所示:{ "servers": { "remote-mcp-function": { "type": "http", "url": "https://contoso.azurewebsites.net/runtime/webhooks/mcp", "headers": { "x-functions-key": "A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u..." } } } }选择打开的服务器名称上方的
mcp.json”按钮以重启远程 MCP 服务器,这次使用已部署的应用。
验证部署
现在可以让 GitHub Copilot 像在本地一样使用远程 MCP 工具,但现在代码在 Azure 中安全运行。 为确保一切正常工作,请重复执行您之前使用的相同命令。
清理资源
完成 MCP 服务器和相关资源的操作后,使用此命令从 Azure 中删除函数应用及其相关资源,以避免产生额外费用。
azd down --no-prompt
注释
此--no-prompt选项指示azd在无需您确认的情况下删除您的资源组。 此命令不会影响本地代码项目。