之前兩篇文章:MCP簡介和MCP能做什麼闡述了MCP的基本概念和原理。
本文將使用Visual Studio Code寫一個MCP服務端和MCP客户端,演示MCP的基本功能。
MCP版本迭代很快,能用把代碼順利跑起來並不是一件容易的事:)
準備運行環境
- Windows 10
- Visual studio code 1.104.3
- python 3.12.3
- mcp 1.16.0
- 創建 venv 虛擬環境
參見:在Visual Studio Code中配置venv
- 安裝 mcp
激活虛擬環境(windows):
.venv\Scripts\activate
安裝mcp
pip install mcp
MCP代碼倉庫推薦使用
uv工具管理包。由於我之前一直用pip管理包,覺得再用uv比較麻煩,所以沒有安裝它。
構建MCP服務端
下面的程序使用 FastMCP 構建了一個帶有 工具/tool、資源/resource和提示詞模板/prompt 的MCP服務端。
本次的服務使用 標準輸入輸出/Stdio 方式對客户端提供服務,這樣MCP客户端加載這個MCP服務端就能完成它們的通信,調試起來比較方便。當然,MCP服務端也可以使用HTTP(支持流式傳輸)的方式向遠端MCP客户端提供服務。
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo MCP Server")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
# Add a prompt
@mcp.prompt()
def greet_user(name: str, style: str = "friendly") -> str:
"""Generate a greeting prompt"""
styles = {
"friendly": "Please write a warm, friendly greeting",
"formal": "Please write a formal, professional greeting",
"casual": "Please write a casual, relaxed greeting",
}
return f"{styles.get(style, styles['friendly'])} for someone named {name}."
def main():
# Initialize and run the server
mcp.run(transport='stdio')
if __name__ == "__main__":
main()
由於沒有安裝
nv工具,所以這裏我們用mcp.run(transport='stdio')明確啓動mcp服務端,傳輸方式採用stdio。
構建MCP客户端
下面的MCP客户端將調用MCP服務端,測試 工具/tool、資源/resource和提示詞模板/prompt 。
import asyncio
import os
from pydantic import AnyUrl
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# 獲取當前文件所在目錄的絕對路徑
current_dir = os.path.dirname(os.path.abspath(__file__))
print(current_dir)
server_path = os.path.join(current_dir, "fastmcp_server.py")
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="python",
args=[server_path],
env=None,
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
# Initialize the connection
await session.initialize()
# List available tools
tools = await session.list_tools()
print(f"Available tools: {[t.name for t in tools.tools]}")
# Call a tool
result = await session.call_tool("add", arguments={"a": 5, "b": 3})
result_unstructured = result.content[0]
if isinstance(result_unstructured, types.TextContent):
print(f"Tool result: {result_unstructured.text}")
result_structured = result.structuredContent
print(f"Structured tool result: {result_structured}")
# List available resources
resources = await session.list_resources() # it doesn't work!
print(f"Available resources: {[r.uri for r in resources.resources]}")
# Read a resource
resource_content = await session.read_resource(AnyUrl("greeting://World"))
content_block = resource_content.contents[0]
if isinstance(content_block, types.TextResourceContents):
print(f"Resource content: {content_block.text}")
# List available prompts
prompts = await session.list_prompts()
print(f"Available prompts: {[p.name for p in prompts.prompts]}")
# Get a prompt
if prompts.prompts:
prompt = await session.get_prompt("greet_user", arguments={"name": "Alice", "style": "friendly"})
print(f"Prompt result: {prompt.messages[0].content}")
def main():
"""Entry point for the client script."""
asyncio.run(run())
if __name__ == "__main__":
main()
由於沒有使用nv,運行MCP服務端和MCP客户端要多寫一些代碼,而客户端看起來更復雜一點。
請注意:
- 使用MCP服務端的準確地址:使用絕對路徑是比較穩妥的方法,否則可能因為MCP客户端加載MCP服務端失敗。
- session.list_resources返回空:不知道為什麼無法返回MCP服務端的資源,而直接調用服務端的資源卻沒問題。
運行代碼
在 Visual Studio Code 中運行MCP客户端代碼即可,客户端會在啓動時加載MCP服務端。
Available tools: ['add']
Tool result: 8
Structured tool result: {'result': 8}
Available resources: []
Resource content: Hello, World!
Available prompts: ['greet_user']
Prompt result: type='text' text='Please write a warm, friendly greeting for someone named Alice.' annotations=None meta=None
總結
以上演示了MCP服務端和MCP客户端的基本功能。對於實際的項目工程來説,這只是一個小小的開始,但是在絕大部分情況下,這種簡單的實戰演練對“更深入的理解概念和技術”會大有幫助。
代碼
本文涉及的所有代碼以及相關資源都已經共享,參見:
- github
- gitee
為便於找到代碼,程序文件名稱最前面的編號與本系列文章的文檔編號相同。
🪐感謝您觀看,祝好運🪐