自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MCP 開發(fā)實(shí)戰(zhàn):如何使用 MCP 真正加速 UE 項(xiàng)目開發(fā)

開發(fā) 架構(gòu)
目前各種MCP的文章和實(shí)際例子以及開源工具層出不窮,本文試圖用最簡單的方式解釋下MCP解決什么問題和MCP怎么寫的問題。

作者 | hanzo

用說人話的方式講解MCP。

目前各種MCP的文章和實(shí)際例子以及開源工具層出不窮,本文試圖用最簡單的方式解釋下MCP解決什么問題和MCP怎么寫的問題。

為啥要用MCP

MCP是一項(xiàng)專為LLM工具化操作設(shè)計(jì)的輕量化標(biāo)準(zhǔn)協(xié)議,其核心目標(biāo)是構(gòu)建LLM與異構(gòu)軟件系統(tǒng)間的通用指令交互框架。與傳統(tǒng)的單一功能調(diào)用機(jī)制不同,MCP通過三層架構(gòu)創(chuàng)新解決工具擴(kuò)展性問題:

(1) 協(xié)議定位

作為中間協(xié)議層,MCP抽象出獨(dú)立于具體LLM和業(yè)務(wù)系統(tǒng)的接口描述層,允許開發(fā)者在不同維度(功能權(quán)限、輸入格式、執(zhí)行環(huán)境)對工具接口進(jìn)行靈活管控,避免傳統(tǒng)方案中接口爆炸帶來的維護(hù)難題。

(2) 技術(shù)架構(gòu)

  • 接口描述層:采用聲明式DSL定義工具元數(shù)據(jù),包括功能語義、入?yún)chema、權(quán)限策略和執(zhí)行上下文
  • 代理控制層:內(nèi)置動態(tài)路由引擎和權(quán)限驗(yàn)證模塊,支持熱插拔式工具注冊與版本管理
  • 協(xié)議適配層:提供跨平臺SDK,自動生成OpenAPI/Swagger等標(biāo)準(zhǔn)接口文檔

(3) 核心優(yōu)勢

  • 雙向解耦:前端LLM無需感知具體工具實(shí)現(xiàn),后端系統(tǒng)可獨(dú)立迭代
  • 權(quán)限縱深:細(xì)粒度控制工具可見性(開發(fā)者/用戶/模型層級)
  • 執(zhí)行沙箱:支持Docker/WASM等多重運(yùn)行時(shí)隔離方案
  • 生態(tài)兼容:自帶LangChain/LLamaIndex等主流框架的適配器

綜合上述的專業(yè)表述,說人話就是,只要你的LLM有Prompt遵循能力,那么不管你是qwen,llama,DeepSeek還是claude ,都可以連接同樣的MCP Server并且讓你的LLM能夠真正的調(diào)用工具,因此大大加速了LLM工具使用的開發(fā)速度。

為什么最近MCP爆發(fā)了?

最近大量MCP的爆發(fā)依賴于LLM本身兩個(gè)能力的大幅度提升:1.結(jié)構(gòu)化輸出能力2.指令遵循能力。特別是claude3.7 sonnets之后的進(jìn)展,使得工具的使用成功率大幅提升。對于LLM本身的能力進(jìn)展來說,通過工具使用的方式積累真實(shí)世界的數(shù)據(jù),并且進(jìn)行后訓(xùn)練,也會成為LLM的垂直能力和LLM工作準(zhǔn)確率進(jìn)一步提升的關(guān)鍵。

MCP Server開發(fā)實(shí)戰(zhàn)

有了基礎(chǔ)概念之后,我們就可以直接開始一個(gè)MCP Server的開發(fā)了,目前MCP官方提供四種語言的開發(fā)SDK,包括Python,typescript,java和kotlin。我們以IEG最常用的typescript為例構(gòu)建工程。

在開始前我們先明確一些概念,通常,我們編寫的MCP是一個(gè)MCP Server,在Server中我們通常會定義一系列我們所需要的工具。使用各種LLM的客戶端只要能連接上Server,就可以使用我們的MCP的各種工具調(diào)用能力了。

在UE開發(fā)中,UE廢物一樣的文檔和天量的代碼經(jīng)常讓人頭大,那么能不能讓LLM幫我來分析代碼呢?結(jié)合Emacs常用的tree-sitter語法分析庫和MCP,我們就可以用LLM來做這件事。

(本工程基于github:github.com/ayeletstu... 進(jìn)行修改得來,由于原工程已經(jīng)無法配置運(yùn)行,我已經(jīng)將修改后的代碼傳至 git.woa.com/IEG-RED-...)

首先,我們和普通配置NodeJs工程一樣,在Package.json中添加相應(yīng)依賴:

"dependencies": {
    "@modelcontextprotocol/sdk": "0.6.0",
    "glob": "^8.1.0",
    "tree-sitter": "^0.20.1",
    "tree-sitter-cpp": "^0.20.0"
  },
  "devDependencies": {
    "@types/glob": "^8.1.0",
    "@types/jest": "^29.5.14",
    "@types/node": "^18.15.11",
    "jest": "^29.7.0",
    "ts-jest": "^29.2.5",
    "typescript": "^5.0.4"
  }

可以看到我們所需要的modelcontextprotocol sdk和tree-sitter等都可以直接從npm下載配置,我們按照常理執(zhí)行npm install等步驟。接下來和通常的NodeJS程序一樣,我們編寫index.ts文件,先導(dǎo)入mcp相關(guān)的接口:

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  ListToolsRequestSchema,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';

這里我們可以看到MCP的幾個(gè)關(guān)鍵概念:

  • Server:我們的MCP服務(wù)器,也就是處理一類任務(wù)的工具集合
  • stdioServertransport:MCP默認(rèn)用的通訊格式。
  • RequestSchema:使用MCP時(shí)需要提供的參數(shù),名字等等信息。

首先,我們需要定義一個(gè)Server class:

class UnrealAnalyzerServer {
  private server: Server;
  private analyzer: UnrealCodeAnalyzer;
 ......
  }

  public async start() {
    try {
      // Setup handlers first
      this.setupToolHandlers();
      
      // Connect to stdio transport
      const transport = new StdioServerTransport();
      await this.server.connect(transport);
      
      console.log('Unreal Analyzer Server started successfully');
    } catch (error) {
      console.error('Failed to initialize server:', error);
      process.exit(1);
    }
  }

這里定義了我們的server的一些最常用的初始化流程和工具定義過程,因?yàn)槲覀兪窍M肕CP來分析代碼,因此我們的CodeAnalyzer也屬于我們的Server Class Member。

要定義工具,我們首先需要結(jié)合ListToolsRequestSchema來綁定我們的tools,參考下面的代碼:

private setupToolHandlers() {
    this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        {
          ......
        {
          name: 'analyze_class',
          description: 'Get detailed information about a C++ class',
          inputSchema: {
            type: 'object',
            properties: {
              className: {
                type: 'string',
                description: 'Name of the class to analyze',
              },
            },
            required: ['className'],
          },
        },
  .....

我們定義工具的名字,和工具所需要的輸入,并將其綁定到server。這些信息會讓MCP識別到我們需要調(diào)用到什么工具,并且在調(diào)用工具時(shí),需要提供什么樣的參數(shù)。

有了工具的名字和參數(shù),MCP需要知道具體如何去執(zhí)行我們想要的操作,比如分析C++類,搜索代碼等等,這里就要用到callToolRequestSchema結(jié)構(gòu)體:

this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      // Only check for initialization for analysis tools
      const analysisTools = ['analyze_class', 'find_class_hierarchy', 'find_references', 'search_code', 'analyze_subsystem', 'query_api'];
      if (analysisTools.includes(request.params.name) && !this.analyzer.isInitialized() && 
          request.params.name !== 'set_unreal_path' && request.params.name !== 'set_custom_codebase') {
        throw new Error('No codebase initialized. Use set_unreal_path or set_custom_codebase first.');
      }

      switch (request.params.name) {
       .....
        case 'search_code':
          return this.handleSearchCode(request.params.arguments);
        case 'analyze_subsystem':
          return this.handleAnalyzeSubsystem(request.params.arguments);
        case 'query_api':
          return this.handleQueryApi(request.params.arguments);
        default:
          throw new Error(`Unknown tool: ${request.params.name}`);
      }
    });
  }

很明顯,callToolRequestSchema會將tools的名字和參數(shù)傳給工具真正的執(zhí)行者。在我們的Server內(nèi)部定義的工具函數(shù)中,會調(diào)用tree-sitter cpp庫 去進(jìn)行真正的分析,然后將結(jié)果返回給我們的LLM進(jìn)行總結(jié)。

來總結(jié)下, MCP的編寫本身是非常簡單的,我們需要實(shí)現(xiàn)的是定義工具的名字,參數(shù)(從LLM中自然語言的方式獲?。约坝么a描述的真正執(zhí)行工具的流程,并且將這些都綁定到我們的Server上,我們只需要關(guān)心我們在調(diào)用什么工具和我們需要什么數(shù)據(jù)就行了,至于給大模型的提示詞,多輪對話暫存,格式化輸出驗(yàn)證等需要考慮到問題,MCP的SDK都能幫我們搞定。

接下來我們用tsc編譯我們的Nodejs程序,我們的Server就做好了。

使用MCP

讀到這里細(xì)心的讀者肯定會發(fā)現(xiàn)。我們的LLM在哪里?這就是MCP更重要的一個(gè)好處,它的Server是LLM無關(guān)的,只要客戶端使用的LLM看得懂提示詞,那么它就能使用同一個(gè)MCP Server。

接下來我們配置客戶端來使用我們的MCP Server,目前很多軟件,包括Claude desktop,dify等都支持了MCP,這里我們選擇VSCode 的Cline插件作為客戶端(因?yàn)樗_源),安裝和配置Cline的過程在此不再贅述,打開Cline的Setting,點(diǎn)擊MCP Servers的按鈕,我們會在下方看到一個(gè)Configure MCP Server的按鈕,點(diǎn)擊我們就可以打開我們的MCP設(shè)置Json:

前文提到過,MCP支持多種語言開發(fā),包括Python,Typescript等,因此可以看到我們的MCP配置中也支持多種入口,一個(gè)MCP Server的入口,可以是Python腳本,可以是bat批處理,也可以是nodejs程序的入口,對于我們的server來說,我們要配置的是一個(gè)nodejs的入口程序,如下面的代碼:

"unreal-analyzer": {
      "command": "node",
      "args": [
        "C:/Users/admin/Documents/Cline/MCP/unreal-analyzer-mcp/build/index.js"
      ],
      "env": {},
      "disabled": false,
      "autoApprove": [],
      "timeout": 3600    }

我們將入口指向我們編譯好的JavaScript文件,保存好之后,Cline就會自動去執(zhí)行這個(gè)index.js,如果有運(yùn)行錯(cuò)誤,那么Cline的設(shè)置窗口中會報(bào)錯(cuò),當(dāng)出現(xiàn)下面的綠色按鈕時(shí),則證明我們的MCP Server連接成功了:

接下來,我們就可以用自然語言的方式快速分析UE代碼了,我們在Cline的對話框中切換到Act Mode(只有Actmode可以調(diào)用MCP Server),然后按照我們?nèi)粘:屯陆涣髡f話的口吻打字:先告訴他我們的UE代碼在哪里: 

直接說:我想分析下UMaterialExpressionPanner這個(gè)類,LLM會分析你的需求,自己去調(diào)用工具: 

同時(shí),LLM也會根據(jù)自己的思考去繼續(xù)調(diào)用工具,比如我的這個(gè)問題,它會繼續(xù)調(diào)用工具,去搜索代碼: 

當(dāng)他發(fā)現(xiàn)代碼非常多的時(shí)候,它會考慮到:OK,我可能需要過濾一下代碼,于是它會調(diào)用SearchWithContext的工具:

來個(gè)稍微復(fù)雜點(diǎn)的任務(wù),讓它幫我找找lumen里AO相關(guān)的類:

通過這個(gè)很簡單的例子工程,我們可以總結(jié)出MCP的特點(diǎn),MCP通過工程化的方法和統(tǒng)一的協(xié)議,給LLM裝上了使用工具的手,這樣我們的AI就可以真正的替我們干活。

真正的UnrealMCP實(shí)現(xiàn)

理解了MCP的工作邏輯和原理之后, 再開發(fā)垂直領(lǐng)域的MCP工具就會相對簡單。接下來我們來分析下真正的能干活的UnrealMCP(github.com/kvick-gam...)是怎么工作的。同時(shí)也展示下Python SDK下的MCP工作流。

UnrealMCP由兩部分組成,一部分是MCP Server的Python代碼,一部分是UE5的插件,其中UE5的插件主要負(fù)責(zé)對接我們操作UE需要的一些C++邏輯。這部分的安裝邏輯和通常的UE插件完全一樣。

而MCP本身的部分,我們希望能夠在Cline插件中調(diào)用,由于這個(gè)UnrealMCP只能在Claude中工作,而Claude在國內(nèi)使用非常麻煩,因此在Cline中配置本MCP時(shí),我們需要對倉庫上說明的配置文件稍微進(jìn)行些修改。 在運(yùn)行安裝python, 啟動venv等工作之后,我們在配置Cline MCP的json時(shí),需要按照如下代碼配置:

"unreal": {
      "command": "cmd.exe",
      "args": [
        "/c", 
        "F:\\UnrealEngine\\Engine\\Plugins\\UnrealMCP\\MCP\\run_unreal_mcp.bat"
      ],
      "env": {
        "PYTHONPATH": "F:\\UnrealEngine\\Engine\\Plugins\\UnrealMCP\\MCP\\python_modules",
        "PATH": "${PATH};F:\\UnrealEngine\\Engine\\Plugins\\UnrealMCP\\MCP\\python_env\\Scripts"
      },
      "disabled": false,
      "autoApprove": [],
      "cwd": "F:\\UnrealEngine\\Engine\\Plugins\\UnrealMCP\\MCP"
    },

這樣就可以在Cline中使用UnrealMCP了。

使用上,UnrealMCP也是非常簡單的,打開UE,啟動UnrealMCP插件之后,我們告訴LLM我們的需求:在場景中創(chuàng)建一個(gè)迷宮關(guān)卡: 

MCP會將相關(guān)信息包裝成Python調(diào)用腳本,發(fā)給UnrealMCP Server:

我們就可以得到最終結(jié)果:

和之前的例子一樣,開發(fā)UnrealMCP還是遵循定義工具名字,參數(shù),定義行為的這幾個(gè)步驟,在UnrealMCP中,它采用了:

  • Python MCP 服務(wù)
  • Python-C++ 橋接層
  • C++ Unreal Engine 插件

的三層架構(gòu)來實(shí)現(xiàn)(因?yàn)閁E的EditorPython并不是很完善,所以需要通過C++插件來實(shí)現(xiàn)命令的解析和工作)。以最簡單的CreateObject為例子: 首先,依然是注冊工具和參數(shù),當(dāng)然,使用Python SDK,這個(gè)過程會更加直接簡單,通過Python的注解語法來進(jìn)行:

@mcp.tool()
def create_object(ctx: Context, type: str, location: list = None, label: str = None) -> str:
    params = {"type": type}
    if location:
        params["location"] = location
    if label:
        params["label"] = label
    response = send_command("create_object", params)

我們需要告訴UE我要?jiǎng)?chuàng)建的location和物體類型,接下來,用Python的socket通信封裝一下LLM產(chǎn)生的數(shù)據(jù),傳給UE:

def send_command(command_type, params=None, timeout=DEFAULT_TIMEOUT):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect(("localhost", DEFAULT_PORT))
        command = {"type": command_type, "params": params or {}}
        s.sendall(json.dumps(command).encode('utf-8'))
        response_data = receive_response(s)
        return json.loads(response_data.decode('utf-8'))

最后UE在C++插件中進(jìn)行接受消息,去調(diào)用NewActor函數(shù):

TSharedPtr<FJsonObject> FMCPCreateObjectHandler::Execute(const TSharedPtr<FJsonObject> &Params, FSocket *ClientSocket)
{
    // 從參數(shù)中提取數(shù)據(jù)
    FString Type;
    Params->TryGetStringField(FStringView(TEXT("type")), Type);
    
    // 執(zhí)行 Unreal Engine 操作
    AStaticMeshActor *NewActor = World->SpawnActor<AStaticMeshActor>(...);
    
    // 返回響應(yīng)
    TSharedPtr<FJsonObject> ResultObj = MakeShared<FJsonObject>();
    ResultObj->SetStringField("name", NewActor->GetName());
    return CreateSuccessResponse(ResultObj);
}

雖然整體流程復(fù)雜了些,但是可以看到,它依然遵循了MCP的設(shè)計(jì)范式,既通過Tools來擴(kuò)展能力,告訴LLM,你可以去干什么事,然后用各種方法,將LLM分析自然語言后得出的指令轉(zhuǎn)為工具調(diào)用,去做真正的工作。

MCP的局限性

MCP雖然非常簡潔明了,大大方便了LLM Tool use的開發(fā)成本,但是從本質(zhì)上來說,MCP只是解決了工具使用的可能性這一個(gè)主題,要想讓AI真正干活,可以說MCP只是干活的那只手,我們同樣需要大腦(規(guī)劃Agent),記憶力(數(shù)據(jù)庫,記事本)來共同輔助完成自動化的工作。

此外,對于真正的專業(yè)軟件來說,每一個(gè)接口/功能對應(yīng)MCP可能也是一個(gè)工程量不小的工作,MCP結(jié)合真正靠譜的Agent編程框架才有可能完成真正復(fù)雜的任務(wù)。

總結(jié)

本文通過兩個(gè)案例,展示了MCP的整體開發(fā)邏輯和能力。通過MCP,大模型可以真正干活。但同時(shí),MCP不應(yīng)該被過度神話,它只是解決了工具調(diào)用這一系列的問題。要想讓大模型徹底重塑日常的游戲開發(fā)工作流,還需要在流程,記憶力,以及模型本身的后訓(xùn)練上持續(xù)工作。

責(zé)任編輯:趙寧寧 來源: 騰訊技術(shù)工程
相關(guān)推薦

2025-04-01 08:38:25

模型上下文協(xié)議MCPLLM

2025-03-13 03:00:00

DockerAgentic工具

2013-07-04 13:19:24

Java開發(fā)速度

2025-04-22 09:17:41

2025-03-31 00:00:00

MCPAPI服務(wù)器通信

2025-04-03 07:06:35

2025-04-18 00:00:00

MCPSSEHTTP

2025-04-01 08:45:56

2025-04-17 08:42:38

2013-07-03 09:52:13

熱部署熱替換

2025-03-28 09:33:11

2015-07-16 15:20:58

DockerDjango

2025-04-11 09:43:57

2016-04-09 17:29:33

銳捷網(wǎng)絡(luò)云營銷

2025-04-25 00:00:00

2025-03-26 03:01:00

2025-04-15 08:54:22

2009-07-29 17:36:55

ibmdwJava

2025-04-27 02:25:00

DeepSeekCursor程序

2009-08-21 15:40:26

Linux KerneLinux開發(fā)加速
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號