免费包白嫖最新DeepSeek-V3驱动的MCP与SemanticKernel实战教程 - 打造智能应用的终极指南
如果您需要深入交流了解请加入我们一块交流
简单介绍
在进入教程之前我们可以先对MCP进行一个大概的了解,MCP是什么?为什么要用MCP?MCP和Function Calling有什么区别?
MCP是什么?
Model Context Protocol (MCP) 是一个开放协议,它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能。无论你是构建 AI 驱动的 IDE、改善 chat 交互,还是构建自定义的 AI 工作流,MCP 提供了一种标准化的方式,将 LLM 与它们所需的上下文连接起来。
- MCP Hosts: 想要通过MCP访问数据的程序,如Claude Desktop、ide或AI工具
- MCP Clients: 与服务器保持1:1连接的协议客户端
- MCP Servers: 轻量级程序,每个程序都通过标准化的模型上下文协议公开特定的功能
- Local Data Sources: MCP服务器可以安全访问的计算机文件、数据库和服务
- Remote Services: MCP服务器可以连接的internet上可用的外部系统(例如通过api)
为什么要用MCP?
- 扩展LLM的能力:让模型能够访问最新数据、专有信息和本地资源
- 数据私密性和安全性:数据可以保留在本地或受控环境中,减少敏感信息共享的风险
- 工具集成:使LLM能够调用和控制外部工具,扩展其功能范围
- 减少幻觉:通过提供准确的上下文信息,降低模型生成虚假内容的可能性
- 标准化:为开发者提供一致的接口,简化集成过程
- 可扩展性:支持从简单用例到复杂企业级应用的多种场景
- 跨语言的能力:通过标准的MCP协议我们可以使用不同语言提供的MCPServer的能力
MCP和Function Calling有什么区别?
我听过最多人问的就是MCP和Function Calling有什么区别?
特性 | MCP | Function Calling |
---|---|---|
基础关系 | 基于Function Calling构建并扩展 | 作为MCP的基础技术 |
设计范围 | 更广泛的协议,处理上下文获取和工具使用 | 主要聚焦于调用特定函数 |
架构 | 客户端-服务器架构,支持分布式系统 | 通常是API参数的直接定义和调用 |
数据处理 | 可以处理大型数据集和复杂上下文 | 主要处理结构化的函数参数和返回值 |
上下文管理 | 专门设计用于管理和提供上下文 | 上下文管理不是核心功能 |
标准化程度 | 开放协议,旨在跨不同系统和模型标准化 | 实现方式因平台而异,标准化程度较低 |
应用场景 | 适用于需要复杂上下文和工具集成的场景 | 适用于调用预定义函数执行特定任务 |
简而言之,MCP(Model Context Protocol)是在Function Calling基础上发展而来的更全面的协议,它不仅保留了Function Calling的核心功能,还扩展了上下文获取和管理能力,为LLM提供更丰富、更标准化的环境交互能力。
开始MCPClient基础教程
前提条件,您需要先将上一个教程的MCPServer跑起来一会才能进行当前步骤,因为MCPClient是需要MCPServer提供Tools,下面我默认您已经运行了MCPServer,
然后可以访问 https://account.coreshub.cn/signup?invite=ZmpMQlZxYVU= 获取免费的DeepSeek-V3模型和白嫖GPU服务器,通过上面连接注册以后访问
https://console.coreshub.cn/xb3/maas/global-keys/ 地址获取到APIKey
然后记住我们的APIKey,开始下面的旅途
创建属于您的MCPClient程序
创建一个McpClient的控制台项目
给项目添加依赖包
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.3" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.44.0" />
<PackageReference Include="ModelContextProtocol" Version="0.1.0-preview.4" />
</ItemGroup>
下面的教程我们将使用Microsoft.SemanticKernel
开发,然后依赖ModelContextProtocol
官方的包,然后我们需要用到Microsoft.Extensions.Hosting
开始我们的代码开发
扩展SemanticKernel的MCPClient
由于SemnaticKernel默认是不能直接使用MCP的功能,那么我们需要先对它进行扩展
创建几个基础类
JsonSchema.cs
/// <summary>
/// Represents a JSON schema for a tool's input arguments.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary>
internal class JsonSchema
{
/// <summary>
/// The type of the schema, should be "object".
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = "object";
/// <summary>
/// Map of property names to property definitions.
/// </summary>
[JsonPropertyName("properties")]
public Dictionary<string, JsonSchemaProperty>? Properties { get; set; }
/// <summary>
/// List of required property names.
/// </summary>
[JsonPropertyName("required")]
public List<string>? Required { get; set; }
}
JsonSchemaProperty.cs
/// <summary>
/// Represents a property in a JSON schema.
/// <see href="https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.json">See the schema for details</see>
/// </summary>
internal class JsonSchemaProperty
{
/// <summary>
/// The type of the property. Should be a JSON Schema type and is required.
/// </summary>
[JsonPropertyName("type")]
public string Type { get; set; } = string.Empty;
/// <summary>
/// A human-readable description of the property.
/// </summary>
[JsonPropertyName("description")]
public string? Description { get; set; } = string.Empty;
}
接下来创建MCP的扩展类,用于将MCPFunction进行转换成SemanticKernel Function
ModelContextProtocolExtensions.cs
/// <summary>
/// Extension methods for ModelContextProtocol
/// </summary>
internal static class ModelContextProtocolExtensions
{
/// <summary>
/// Map the tools exposed on this <see cref="IMcpClient"/> to a collection of <see cref="KernelFunction"/> instances for use with the Semantic Kernel.
/// <param name="mcpClient">The <see cref="IMcpClient"/>.</param>
/// <param name="cancellationToken">The optional <see cref="CancellationToken"/>.</param>
/// </summary>
internal static async Task<IReadOnlyList<KernelFunction>> MapToFunctionsAsync(this IMcpClient mcpClient, CancellationToken cancellationToken = default)
{
var functions = new List<KernelFunction>();
foreach (var tool in await mcpClient.ListToolsAsync(cancellationToken).ConfigureAwait(false))
{
functions.Add(tool.ToKernelFunction(mcpClient, cancellationToken));
}
return functions;
}
private static KernelFunction ToKernelFunction(this McpClientTool tool, IMcpClient mcpClient, CancellationToken cancellationToken)
{
async Task<string> InvokeToolAsync(Kernel kernel, KernelFunction function, KernelArguments arguments, CancellationToken ct)
{
try
{
// Convert arguments to dictionary format expected by ModelContextProtocol
Dictionary<string, object?> mcpArguments = [];
foreach (var arg in arguments)
{
if (arg.Value is not null)
{
mcpArguments[arg.Key] = function.ToArgumentValue(arg.Key, arg.Value);
}
}
// Call the tool through ModelContextProtocol
var result = await mcpClient.CallToolAsync(
tool.Name,
mcpArguments.AsReadOnly(),
cancellationToken: ct
).ConfigureAwait(false);
// Extract the text content from the result
return string.Join("\n", result.Content
.Where(c => c.Type == "text")
.Select(c => c.Text));
}
catch (Exception ex)
{
await Console.Error.WriteLineAsync($"Error invoking tool '{tool.Name}': {ex.Message}");
// Rethrowing to allow the kernel to handle the exception
throw;
}
}
return KernelFunctionFactory.CreateFromMethod(
method: InvokeToolAsync,
functionName: tool.Name,
description: tool.Description,
parameters: tool.ToParameters(),
returnParameter: ToReturnParameter()
);
}
private static object ToArgumentValue(this KernelFunction function, string name, object value)
{
var parameterType = function.Metadata.Parameters.FirstOrDefault(p => p.Name == name)?.ParameterType;
if (parameterType == null)
{
return value;
}
if (Nullable.GetUnderlyingType(parameterType) == typeof(int))
{
return Convert.ToInt32(value);
}
if (Nullable.GetUnderlyingType(parameterType) == typeof(double))
{
return Convert.ToDouble(value);
}
if (Nullable.GetUnderlyingType(parameterType) == typeof(bool))
{
return Convert.ToBoolean(value);
}
if (parameterType == typeof(List<string>))
{
return (value as IEnumerable<object>)?.ToList() ?? value;
}
if (parameterType == typeof(Dictionary<string, object>))
{
return (value as Dictionary<string, object>)?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? value;
}
return value;
}
private static List<KernelParameterMetadata>? ToParameters(this McpClientTool tool)
{
var inputSchema = JsonSerializer.Deserialize<JsonSchema>(tool.JsonSchema.GetRawText());
var properties = inputSchema?.Properties;
if (properties == null)
{
return null;
}
HashSet<string> requiredProperties = [.. inputSchema!.Required ?? []];
return properties.Select(kvp =>
new KernelParameterMetadata(kvp.Key)
{
Description = kvp.Value.Description,
ParameterType = ConvertParameterDataType(kvp.Value, requiredProperties.Contains(kvp.Key)),
IsRequired = requiredProperties.Contains(kvp.Key)
}).ToList();
}
private static KernelReturnParameterMetadata ToReturnParameter()
{
return new KernelReturnParameterMetadata
{
ParameterType = typeof(string),
};
}
private static Type ConvertParameterDataType(JsonSchemaProperty property, bool required)
{
var type = property.Type switch
{
"string" => typeof(string),
"integer" => typeof(int),
"number" => typeof(double),
"boolean" => typeof(bool),
"array" => typeof(List<string>),
"object" => typeof(Dictionary<string, object>),
_ => typeof(object)
};
return !required && type.IsValueType ? typeof(Nullable<>).MakeGenericType(type) : type;
}
}
然后创建SemanticKernel的扩展,对SKFunction进行扩展
KernelExtensions.cs
/// <summary>
/// Extension methods for KernelPlugin
/// </summary>
public static class KernelExtensions
{
private static readonly ConcurrentDictionary<string, IKernelBuilderPlugins> SseMap = new();
/// <summary>
/// Creates a Model Content Protocol plugin from an SSE server that contains the specified MCP functions and adds it into the plugin collection.
/// </summary>
/// <param name="endpoint"></param>
/// <param name="serverName"></param>
/// <param name="cancellationToken">The optional <see cref="CancellationToken"/>.</param>
/// <param name="plugins"></param>
/// <returns>A <see cref="KernelPlugin"/> containing the functions.</returns>
public static async Task<IKernelBuilderPlugins> AddMcpFunctionsFromSseServerAsync(this IKernelBuilderPlugins plugins,
string endpoint, string serverName, CancellationToken cancellationToken = default)
{
var key = ToSafePluginName(serverName);
if (SseMap.TryGetValue(key, out var sseKernelPlugin))
{
return sseKernelPlugin;
}
var mcpClient = await GetClientAsync(serverName, endpoint, null, null, cancellationToken).ConfigureAwait(false);
var functions = await mcpClient.MapToFunctionsAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
cancellationToken.Register(() => mcpClient.DisposeAsync().ConfigureAwait(false).GetAwaiter().GetResult());
sseKernelPlugin = plugins.AddFromFunctions(key, functions);
return SseMap[key] = sseKernelPlugin;
}
private static async Task<IMcpClient> GetClientAsync(string serverName, string? endpoint,
Dictionary<string, string>? transportOptions, ILoggerFactory? loggerFactory,
CancellationToken cancellationToken)
{
var transportType = !string.IsNullOrEmpty(endpoint) ? TransportTypes.Sse : TransportTypes.StdIo;
McpClientOptions options = new()
{
ClientInfo = new()
{
Name = $"{serverName} {transportType}Client",
Version = "1.0.0"
}
};
var config = new McpServerConfig
{
Id = serverName.ToLowerInvariant(),
Name = serverName,
Location = endpoint,
TransportType = transportType,
TransportOptions = transportOptions
};
return await McpClientFactory.CreateAsync(config, options,
loggerFactory: loggerFactory ?? NullLoggerFactory.Instance, cancellationToken: cancellationToken);
}
// A plugin name can contain only ASCII letters, digits, and underscores.
private static string ToSafePluginName(string serverName)
{
return Regex.Replace(serverName, @"[^\w]", "_");
}
}
实现连接MCPServer的Tools
现在我们就可以实现核心的代码了,打开我们的Program.cs
using McpClient;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using ChatMessageContent = Microsoft.SemanticKernel.ChatMessageContent;
#pragma warning disable SKEXP0010
var builder = Host.CreateEmptyApplicationBuilder(settings: null);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
var kernelBuilder = builder.Services.AddKernel()
.AddOpenAIChatCompletion("DeepSeek-V3", new Uri("https://openapi.coreshub.cn/v1"), "您使用的https://openapi.coreshub.cn/v1平台的APIKey");
await kernelBuilder.Plugins.AddMcpFunctionsFromSseServerAsync("http://您的MCPServerip:端口/sse", "token");
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("MCP Client Started!");
Console.ResetColor();
var app = builder.Build();
var kernel = app.Services.GetService<Kernel>();
var chatCompletion = app.Services.GetService<IChatCompletionService>();
PromptForInput();
while (Console.ReadLine() is string query && !"exit".Equals(query, StringComparison.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(query))
{
PromptForInput();
continue;
}
var history = new ChatHistory
{
new ChatMessageContent(AuthorRole.System, "下面如果需要计算俩个数的和,请使用我提供的工具。"),
new ChatMessageContent(AuthorRole.User, query)
};
await foreach (var message in chatCompletion?.GetStreamingChatMessageContentsAsync(history,
new OpenAIPromptExecutionSettings()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
}, kernel))
{
Console.Write(message.Content);
}
Console.WriteLine();
PromptForInput();
}
static void PromptForInput()
{
Console.WriteLine("Enter a command (or 'exit' to quit):");
Console.ForegroundColor = ConsoleColor.Cyan;
Console.Write("> ");
Console.ResetColor();
}
然后开始启动我们的MCPClient,在此之前需要先修改代码中的参数改成自己的,然后先启动MCPServer以后,在启动我们的MCPClient
执行以后提问1+1=?然后会得到一下的结果
在执行之前我们可以先在MCPServer中的算法函数Debug,这样就可以清晰的看到进入的流程了
通过上面的案例,恭喜您您已经熟练的掌握了MCPServer+MCPClient+SemanticKernel的入门级教程
当然如果您需要更加深入的了解AI相关的教程,您可以与我们连续加入我们的AI VIP得到更多的AI相关的教程和帮助。
免费包白嫖最新DeepSeek-V3驱动的MCP与SemanticKernel实战教程 - 打造智能应用的终极指南的更多相关文章
- GitHub+JSDelivr+PicGo+Typora免费白嫖高速稳定图床
0. 初衷1. 创建 GitHub 仓库2. 使用 jsDelivr 进行 CDN 加速3. 使用PicGo上传图片4. Typora 配置 PicGo 上传 0. 初衷 平时写文章,经常需要插入图片 ...
- 5分钟白嫖我常用的免费效率软件/工具!效率300% up!
Mac 免费效率软件/工具推荐 1. uTools(Windows/Mac) 还在为了翻译 English 而专门下载一个翻译软件吗? 还在为了格式某个 json 文本.时间戳转换而打开网址百度地址吗 ...
- 深度学习,机器学习神器,白嫖免费GPU
深度学习,机器学习神器,白嫖免费GPU! 最近在学习计算机视觉,自己的小本本没有那么高的算力,层级尝试过Google的Colab,以及移动云的GPU算力,都不算理想.如果数据集比较小,可以试试Cola ...
- 从零开始使用阿里云OSS服务(白嫖)
阿里云对象存储服务OSS使用记录 1 新人免费开通OSS服务 访问 阿里云官网,登录账号(个人新用户账号),首页搜索 对象存储OSS,点击下方的直达. 点击立即开通,此时会在一个新网页中完成开通 完成 ...
- math type白嫖教程
math type作为一款很方便的公式编辑器,缺陷就是要收费,只有30天的免费试用.这里有一个白嫖的方法,当你30天的期限过了之后,只需要删除HKEY_CURRENT_USER\Software\In ...
- 如何白嫖微软Azure12个月及避坑指南
Azure是微软提供的一个云服务平台.是全球除了AWS外最大的云服务提供商.Azure是微软除了windows之外另外一个王牌,微软错过了移动端,还好抓住了云服务.这里的Azure是Azure国际不是 ...
- Spring配置类深度剖析-总结篇(手绘流程图,可白嫖)
生命太短暂,不要去做一些根本没有人想要的东西.本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习 ...
- 白嫖码云Pages,两分钟的事,就能搭个百度能搜到的个人博客平台
为了攒点钱让女儿做个富二代(笑),我就没掏钱买服务器,白嫖 GitHub Pages 搭了一个博客平台.不过遗憾的是,GitHub Pages 只能被谷歌收录,无法被百度收录,这就白白损失了一大波流量 ...
- Xray高级版白嫖破解指南
啊,阿Sir,免费的还想白嫖?? 好啦好啦不开玩笑 Xray是从长亭洞鉴核心引擎中提取出的社区版漏洞扫描神器,支持主动.被动多种扫描方式,自备盲打平台.可以灵活定义 POC,功能丰富,调用简单,支持 ...
- 白嫖JetBrains正版全家桶!
使用自己的开源项目,是可以白嫖JetBrains正版全家桶的! 前言 之前在学Go的时候,想着要用什么编辑器,网上的大佬都讲,想省事直接用Goland,用VsCode配置会存在一些未知的使用体验问题, ...
随机推荐
- 深度剖析 GROUP BY 和 HAVING 子句:优化 SQL 查询的利器
title: 深度剖析 GROUP BY 和 HAVING 子句:优化 SQL 查询的利器 date: 2025/1/14 updated: 2025/1/14 author: cmdragon ex ...
- flutter中 ListView的使用
1.ListView的简单介绍 ListView是最常用的可以滚动组件之一, 它可以沿一个方向进行线性排列所有的子组件. 下面是ListView的属性值介绍: scrollDirection:列表的滚 ...
- dart 中在实例化 new 关键字可以省略不写
dart 中在实例化 new 关键字可以省略不写 class Person { String name; int age; String sex; Person(this.name, this.age ...
- dart变量类型详解
1==> 三个单引号的作用 String Str = ''' qijqowjdo 哈哈嘿嘿黑 '''; print(Str); 这样使用三个单引号,输出来换行:方便我们观看而已哈 2==> ...
- VPC终端节点的实现架构和原理
本文分享自天翼云开发者社区<VPC终端节点的实现架构和原理>,作者:云云生息 什么是VPC终端节点? 在传统的VPC架构中,为了使VPC内的资源能够与云服务提供商的各种服务进行通信,通常需 ...
- C#正则提取字符串中的数字
首先需要引入命名空间System.Text.RegularExpressions,具体实现如下所示: //提取纯数字,该方式会将所有数字提取出来并拼接在一起,如:"ABC#123@AS456 ...
- DBeaver连接SqlServer报“The server selected protocol version TLS10 is not accepted by client prefere”的错误
1.问题描述 DBeaver在连接SqlServer时,出现如下图所示的错误: The server selected protocol version TLS10 is not accepted b ...
- presto解析jsonArr转多行
一.假数据解析 SELECT r1.col.dataSourceId, r1.col.database, r1.col.dataTable FROM (SELECT explode(r.json) A ...
- 浏览器自动化与AI Agent结合项目browser-use初探
browser-use介绍 browser-use是将您的 AI 代理连接到浏览器的最简单方式.它通过提供一个强大且简单的接口来实现 AI 代理访问网站的自动化. GitHub地址:https://g ...
- 牛客题解 | 单组_spj判断YES与NO
题目 题目链接 解题思路 后台有spj代码,能对同学们的输出数据进行校验,符合条件即可通过. 附赠 spj 代码 #include <iostream> #include <fstr ...