MCP服务搭建入门

在前面的章节中,我们体验了调用百度地图的 MCP 服务。现在,我们要反向操作——自己动手写一个 MCP 服务端,让别人(或别的 AI)来调用我们的MCP服务!

为了让你最快理解 MCP 的运作机制,我们摒弃所有复杂的业务逻辑,用两个纯粹的数学计算工具来演示。

核心架构:什么是 MCP 服务端?

MCP 服务端本质上就是一个 Web 服务(我们用 Foxtable 的 HttpServer 来实现)。它向外提供两个核心接口:

tools/list(报菜单):告诉 AI“我有哪些工具,需要什么参数”。

tools/call(干活):接收 AI 传来的参数,执行计算,并返回结果。

为了让演示更直观,我们设定一个规则:两个工具计算出的最终结果,都强制加 100 再返回。这样你就能一眼看出,结果是 AI 算出来的,还是工具真正执行出来的。

搭建MCP服务最核心的逻辑就是工具的定义和执行,关于工具定义的入门知识,可以参考工具定义快速入门,不同的是MCP定义工具的JSON格式和之前稍有差异。

MCP服务端项目设计过程

1、在项目的AfterOpenProject事件开启Web服务:

HttpServer.Prefixes.Add("http://*:18688/") '端口自行决定,避免和其他服务冲突即可 ,默认为80
HttpServer.Start()

2、在项目的 HttpRequest 事件中,拦截 mcp 路径的请求并交给自定义函数处理:

'''Async
If
e.Path = "mcp" Then
    e.AsyncExecute =
True
   
Await Functions.ExecuteAsync("HandleMcp", e)
    e.Handled =
True
Else

    e.WriteString(
"Foxtable Private MCP Server is running.")
End
If

3、增加一个自定义函数HandleMcp,这是整个MCP服务的核心逻辑所在。

这个MCP服务提供了两个工具:

自定义函数HandleMcp的代码如下:

Dim e As RequestEventArgs = Args(0)
If
String.IsNullOrEmpty(e.PlainText) Then '如果请求内容为空(Trae这类智能体软件会持续发出空请求)则直接返回
   
Dim emptyErr As New JObject From {
        {
"jsonrpc", "2.0"},
        {
"id", Nothing},
        {
"error", New JObject From {{"code", - 32700}, {"message", "Empty request"}}}
    }
    e.WriteString(emptyErr.ToString(),
"application/json")
   
Return Nothing
End
If
Dim
req As JObject = JObject.Parse(e.PlainText)
Dim
method As String = If(req("method") IsNot Nothing, req("method").ToString(), "")
Dim
id As JToken = req("id")
Dim
resp As New JObject From {{"jsonrpc", "2.0"}, {"id", id}}
Try

   
Select Case method
       
Case "initialize" ' 响应客户端的握手请求
            resp(
"result") = New JObject From {
                {
"protocolVersion", "2025-11-25"},
                {
"capabilities", New JObject From {{"tools", New JObject()}}},
                {
"serverInfo", New JObject From {{"name", "foxCalculator"}, {"version", "1.0"}}}
            }
       
Case "notifications/initialized" '握手结束后,客户端发来准备就绪的通知,无需返回信息
            e.StatusCode = 202
            e.WriteString(
"", "application/json")
           
Return Nothing
       
Case "tools/list" ' 返回工具拉取结果
           
Dim tools As New JArray
           
' 【工具1】:最简单的双数相加
           
Dim t1 As New JObject From {
                {
"name", "add_two"},
                {
"description", "计算两个数字之和(加法),会在正确结果上加100"},
                {
"inputSchema", New JObject From {
                    {
"type", "object"},
                    {
"properties", New JObject From {
                        {
"a", New JObject From {{"type", "number"}, {"description", "第一个数字"}}},
                        {
"b", New JObject From {{"type", "number"}, {"description", "第二个数字"}}}
                    }},
                    {
"required", New JArray From {"a", "b"}}
                }}
            }
            tools.Add(t1)
           
' 【工具2】:数组运算(演示 Enum 和 Array)
           
Dim t2 As New JObject From {
                {
"name", "multi_calc"},
                {
"description", "对多个数字进行累加、累乘、累除、累减运算,会在正确结果上加100"},
                {
"inputSchema", New JObject From {
                    {
"type", "object"},
                    {
"properties", New JObject From {
                        {
"operation", New JObject From {
                            {
"type", "string"},
                            {
"enum", New JArray From {"add", "subtract", "multiply", "divide"}},
                            {
"description", "运算方式"}
                        }},
                        {
"values", New JObject From {
                            {
"type", "array"},
                            {
"items", New JObject From {{"type", "number"}}},
                            {
"description", "参与计算的数字数组"}
                        }}
                    }},
                    {
"required", New JArray From {"operation", "values"}}
                }}
            }
            tools.Add(t2)
            resp(
"result") = New JObject From {{"tools", tools}}
       
Case "tools/call"  ' 干活:解析参数并计算
           
Dim params As JObject = req("params")
           
Dim toolName As String = params("name").ToString()
           
Dim toolArgs As JObject = CType(params("arguments"), JObject)
           
Dim txt As String = ""
           
Dim isError As Boolean = False
           
Select Case toolName
               
Case "add_two"
                   
Dim a As Double = toolArgs("a").ToObject(Of Double)()
                   
Dim b As Double = toolArgs("b").ToObject(Of Double)()
                   
Dim result As Double = a + b + 100
                    txt =
"计算结果:" & result.ToString()
               
Case "multi_calc"
                   
Dim op As String = toolArgs("operation").ToString()
                   
Dim values As JArray = toolArgs("values")
                   
Dim result As Double = If(op = "multiply", 1, 0)
                   
For i As Integer = 0 To values.Count - 1
                       
Dim v As Double = values(i).ToObject(Of Double)()
                       
Select Case op
                           
Case "add" : result += v
                           
Case "subtract" : result -= v
                           
Case "multiply" : result *= v
                           
Case "divide" : If v <> 0 Then result /= v
                       
End Select
                   
Next
                    result = result + 100
                    txt =
"运算结果:" & result.ToString()
               
Case Else
                    txt =
"未知工具:" & toolName
                    isError =
True
           
End Select
            resp(
"result") = New JObject From {
                {
"isError", isError},
                {
"content", New JArray From {New JObject From {{"type", "text"}, {"text", txt}}}}
            }
   
End Select
Catch
ex As Exception ' 全局异常兜底
    resp =
New JObject From {
        {
"jsonrpc", "2.0"}, {"id", id},
        {
"error", New JObject From {{"code", - 32603}, {"message", ex.Message}}}
    }

End
Try
e.WriteString(resp.ToString(),
"application/json") '输出最终响应(必须显式指定 application/json)

客户端测试代码

服务端写好了,我们再次召唤出 AI 大脑(LLMCaller),让它来调用我们刚写的 MCP 服务端。

以下代码请在另一个项目的命令窗口运行,这个代码是通用的,如果要切换到其他MCP,只需修改url地址和系统提示词即可:

Dim deepseekKey As String = "sk-a734b83536da45e9aedc3e42605bfb3a"  '你的DeepSeek密钥
Dim
caller As New LLMCaller("https://api.deepseek.com/chat/completions", "deepseek-chat", deepseekKey)
caller.SystemPrompt =
"你是一个计算助手,请调用工具进行计算,不得质疑工具计算结果,无需多余解释。"
Dim
mcpUrl As String = "http://127.0.0.1:18688/mcp"
Dim
hc As New HttpClient(mcpUrl)
hc.SkipError =
True
hc.ContentType =
"application/json"
hc.Headers.Add(
"Accept", "application/json, text/event-stream")
' 1.
从我们的私有MCP拉取工具列表
hc.Content =
"{""jsonrpc"":""2.0"",""id"":1,""method"":""tools/list"",""params"":{}}"
Dim
listJson As JObject = JsonConvert.DeserializeObject(hc.GetData())
Dim
mcpTools As JArray = listJson("result")("tools")
caller.AddMCPTools(mcpTools) 
'注册MCP工具到LLMCaller
' 3.
设置工具执行器(转发给我们的私有 MCP)
caller.ToolExecutor =

Function
(ttoolName As String, targs As JObject) As String
   
Dim callReq As New JObject From {
        {
"jsonrpc", "2.0"}, {"id", Rand.NextString(10)}, {"method", "tools/call"},
        {
"params", New JObject From {{"name", ttoolName}, {"arguments", targs}}}
    }
    hc = hc.Clone
    hc.Content = callReq.ToString()
   
Dim respJson As JObject = JsonConvert.DeserializeObject(hc.GetData())
   
Return respJson("result")("content")(0)("text").ToString()
End
Function
' 3.
让 AI 发起提问
caller.UserMessage =
"请帮我算一下 5 加 3 等于多少?然后再计算 6 * 5 * ... * 1 等于多少"
If
caller.GetResult() Then
    MessageBoxA.Show(caller.Result)

Else

    MessageBoxA.Show(caller.Error)

End
If 

如果你运行成功,AI 的回答大概是 108 和 820。如果 AI 自己心算,答案会是 8 和 720。这多出来的 100,就是 MCP 服务端真实运行、AI 乖乖调用MCP工具的铁证!

将MCP服务连接到智能体软件

可以将我们的MCP服务连接到Trae和WorkBuddy等智能体软件。

以Trae为例,假定你已经在本机安装好Trae,连接方法为:

插件市场 → MCP → 添加 → 手动配置,配置内容为:

{
    "mcpServers": {
    "foxCalculator": {
        "url": "http://127.0.0.1:18688/mcp"
        }
    }
}

现在就可以在trae使用我们开发好的MCP服务了:


本页地址:http://www.foxtable.com/webhelp/topics/7237.htm