自定义MCP服务器实现

一个用于学习和实验的自定义MCP服务器实现,支持搜索、页面内容提取和服务器信息查询等功能。

  • by freakynit
  • JavaScript
  • 本地部署
  • 2 星标
  • 更新于
在 GitHub 查看 freakynit/mcp-like-server

使用说明

这是为理解这些工具背后的核心概念而创建的类似MCP服务器的自定义实现。这是在不到3个小时的时间内完成的,所以,没有关注代码质量或其他任何事情。这纯粹是为了学习(和实验)。

聊天运行示例

日志级别设置为info,而不是debug,因此不会显示背景消息。

搜索最新的美国政治新闻

> custom-mcp-server-implementation@1.0.0 example_chat
> node src/example_chat.js

Query (exit/quit/bye to exit)...search for latest US politics news
info: [Final Answer]
[{"type":"ORGANIC","link":"https://www.cnn.com/politics","description":"The IRS is drafting plans to cut as much as half of its 90,000-person workforce, sources say � Trump says US has apprehended 'top terrorist' responsible for�...","title":"Politics - CNN"},{"type":"ORGANIC","link":"https://www.nbcnews.com/politics","description":"Find the latest political news stories, photos, and videos on NBCNews.com. Read breaking headlines covering Congress, Democrats, Republicans, and more. �  �  � ","title":"Politics: Latest news and headlines | NBC News"},{"type":"ORGANIC","link":"https://www.nytimes.com/international/section/politics","description":"Breaking news and analysis on U.S. politics, including the latest coverage of the White House, Congress, the Supreme Court and more. �  �  � ","title":"U.S. Politics - The New York Times International"},{"type":"ORGANIC","link":"https://www.theguardian.com/us-news/us-politics","description":"President Donald Trump addresses a joint session of Congress at the US Capitol. Trump touts renewal of rightwing policies in lengthy speech as Democrats jeer,�...","title":"US politics | The Guardian"},{"type":"ORGANIC","link":"https://www.politico.com/politics","description":"Protesters rally in support of Ukraine outside the U.S. Capitol. Trump: Zelenskyy says Ukraine is ready for peace. By DASHA BURNS. 03/�... �  � ","title":"Latest and breaking political news today - POLITICO"},{"type":"ORGANIC","link":"https://abcnews.go.com/Politics","description":"Trump orders a 'pause' on military aid to Ukraine. The move followed a fiery Oval Office meetup of the U.S. and Ukraine leaders. March 03. �  � ","title":"Breaking Political News, Video & Analysis-ABC News"},{"type":"ORGANIC","link":"https://www.bbc.com/news/topics/cwnpxwzd269t","description":"7 hours ago � The president declares \"America is back\" as he soaks up Republican ovations and spars with Democrats. Just now. World.","title":"US politics - BBC News"},{"type":"ORGANIC","link":"https://www.independent.co.uk/news/world/americas/us-politics","description":"US politics � Truth behind the US economy and where it's heading after Trump tariffs � Stock markets drop as Trump's tariffs with Mexico and Canada begin. �  � ","title":"US Politics | Latest news, comment and analysis - The Independent"},{"type":"ORGANIC","link":"https://www.politico.com/","description":"Democrats Are Serious About a Shutdown � Republicans squirm as Trump's tariffs come for their states � Zelenskyy says it is 'time to make things right' after Oval�... �  �  � ","title":"Politics, Policy, Political News - POLITICO"}]
Query (exit/quit/bye to exit)...

服务器CPU计数

> custom-mcp-server-implementation@1.0.0 example_chat
> node src/example_chat.js

Query (exit/quit/bye to exit)...tell me server cpu count
info: [Final Answer]
8
Query (exit/quit/bye to exit)...

没有工具调用,然后是页面抓取工具调用

> custom-mcp-server-implementation@1.0.0 example_chat
> node src/example_chat.js

Query (exit/quit/bye to exit)...how are you doing today?
info: [Final Answer]
I'm just a computer program, so I don't have feelings, but I'm here and ready to help you with whatever you need! How can I assist you today?
Query (exit/quit/bye to exit)...extract this page content: https://example.com/
info: [Final Answer]
# Example Domain

This domain is for use in illustrative examples in documents. You may use this
domain in literature without prior coordination or asking for permission.

[More information...](https://www.iana.org/domains/example)
Query (exit/quit/bye to exit)...quit

安装和运行

  1. 设置qdrant矢量存储。请参阅下文(将Qdrant设置为矢量存储)
  2. 克隆仓库
  3. 复制 .env.example 向 .env 并填写所需的值。 FIRECRAWL_KEY 只有当你想使用页面抓取工具时才需要它。还需要其他人。
  4. 集 CHAT_LOG_LEVEL 向 debug 暂时。
  5. 启动服务器(确保 qdrant 正在默认运行所有内容): npm run server.
  6. 在另一个窗口中,启动示例聊天循环,该循环透明地使用此类似MCP的服务器进行工具调用: npm run example_chat.
  7. 首先尝试使用不需要调用任何工具的简单消息: how are you doing.
  8. 现在,让我们测试一下工具调用。中提供了一些工具 functions 目录。看看他们。
  9. 类型: search for latest US politics news。控制台上会出现很多消息。这些是调试消息。最终结果前缀为 Final Answer。您还可以将日志级别设置为 info 只看到最终的答案。
  10. 另一个: tell me server cpu count.
  11. FireCrawl测试(注册免费密钥): extract this page content: https://example.com/.
  12. 令人困惑的一个: Add these numbers: 10, 20。为什么这令人困惑?因为对于这种简单的添加,LLM不需要依赖于工具调用。因此,这并没有使用任何工具调用。
  13. 让我们强制工具调用这个: Add these numbers: 10, 20 using tool call这一次,我们的工具: functions/add_numbers.js 被称为。启用 debug 随时记录以查看幕后消息。

将Qdrant设置为矢量存储

docker run -p 6333:6333 -p 6334:6334 \
    -v $(pwd)/qdrant_storage:/qdrant/storage \
    qdrant/qdrant

用户界面位于:http://localhost:6333/dashboard

了解幕后发生的事情

  1. 广义上讲,MCP公开了两个端点:第一个端点用于查询已注册的函数/工具,第二个端点用于使用参数执行所选函数。
  2. LLM执行此功能选择。
  3. 如果你看看我们的 聊天代码,您将看到,在开始时,我们使用变量定义了两个这样的函数/工具: toolsServerTools.
  4. 这些正好对应于两个 服务器 端点: /api/functions/search 和 /api/functions/execute.
  5. 聊天循环基本上将这两个工具调用定义添加到每个用户聊天消息中。
  6. 然后,它依赖于LLM决定当前问题是否可以本机回答(不依赖于任何外部工具),或者它需要工具的帮助来正确回答用户的问题。
  7. 重要的是要记住:截至现在(第一条用户聊天消息),它确实 非 对已注册的工具/功能有任何了解。它只是知道有一个 tool 它可以用来 search 对于函数(或多个工具),其中一个可能能够回答用户的问题。
  8. 这是第一个注册的工具 toolsServerTools 开始发挥作用。
  9. 现在,LLM在其响应中返回工具调用。这被example_chat循环捕获:
    let content = result?.choices?.[0]?.message?.content;
    let toolCalls = result?.choices?.[0]?.message?.tool_calls;
    
    if(content) {
        logger.info(`[Final Answer]\n${typeof content === 'object' ? JSON.stringify(content) : content}`);
    } else {
        // ... tool call caught here    
    }
    
  10. 现在, handleToolCall 方法在提取函数/工具名称及其对应参数后被调用。由于这是第一次工具调用,因此这实际上是 searchFunctions 对应于我们的工具调用 /api/functions/search 终点。
  11. 我们使用LLM给出的参数调用此端点,作为回报,接收最适用的函数(目前为前3个)。然后将这些添加到现有 toolsServerTools 变量:
    for(const availableTool of toolCallResult?.result) {
        toolsServerTools.push({
            "type": "function",
            "function": availableTool?.definition
        });
    }
    
  12. 现在,原始消息再次发送,但这次,我们的LLM already 可以访问最可能的功能 finally,回答用户的问题(因为我们添加了如上所示的搜索功能)。
  13. 现在,LLM以最终的工具调用和相应的参数进行响应。我们打电话 handleToolCall 方法再一次,但这一次,最后 else case与正确的注册函数匹配,并将相应的参数传递给它。
  14. 此工具由我们的服务器在 /api/functions/execute 终点。
  15. 此函数/工具/服务器api调用的结果是显示给用户的最终答案。
  16. 请注意,此聊天循环不会保留聊天历史记录。这只是一次聊天。

各种输入的示例流程

  1. how are you doing =>用户->llm->完成。
  2. search for latest US politics news =>用户->llm->工具调用(searchFunctions)=>[前3个匹配函数]=>将它们添加到工具调用定义中=>再次调用LLM=>工具调用(googleSearch)=>最终结果=>用户。
  3. extract this page content: https://example.com/ =>流程与上述类似,但第二个工具调用是 extractPageContent.