载入中...
搜索中...
未找到
Mcp.cpp
浏览该文件的文档.
1#include "cmdline.h"
2
3#include "common/Capability.h"
4#include "common/EditorHost.h"
5#include "common/Module.h"
6#include "common/Runtime.h"
7#include "common/config.h"
10
11#if !defined(EVENGINE_ANDROID) && !defined(EVENGINE_IOS) && !defined(EVENGINE_WEBGPU)
12#include "devtools/DevTool.hpp"
14#endif
15
16#include <simplesquirrel/simplesquirrel.hpp>
17#include <CLI11.hpp>
18#include <squirrel.h>
19
20#include <chrono>
21#include <cstdarg>
22#include <cstdio>
23#include <filesystem>
24#include <iostream>
25#include <string>
26#include <thread>
27
28namespace eve::cmd {
29namespace {
30
32void hostPrintFunc(HSQUIRRELVM, const SQChar* s, ...) {
33 char buf[4096];
34 va_list args;
35 va_start(args, s);
36 vsnprintf(buf, sizeof(buf), s, args);
37 va_end(args);
38 fputs(buf, stderr);
39}
40
41} // namespace
42
43struct McpArgs : Handler {
44 std::string root_path;
45 int port = 0;
46
47 void setup(CLI::App& app, std::shared_ptr<CLI::Formatter> formatter) override {
48 auto mcp = app.add_subcommand(
49 "mcp", "Run a headless MCP host for AI agents (stdio by default; --port for TCP)");
50 mcp->allow_extras()->formatter(formatter);
51 mcp->add_option("--port", port,
52 "Listen on this TCP port instead of stdio (Codex uses stdio)");
53 mcp->add_option("-r,--root", root_path, "Project root directory (default: current dir)");
54 }
55
56 int parse(CLI::App& app, Cmdline& cmd) override {
57 auto mcp = app.get_subcommand("mcp");
58 if (mcp->parsed()) {
59 std::string current_path = cmd.get_remaining(mcp);
60 if (!root_path.empty()) current_path = root_path;
61 return cmd.McpHost(current_path, port);
62 }
63 return -1;
64 }
65};
66
68
69int Cmdline::McpHost(std::string path, int port) {
70#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS) || defined(EVENGINE_WEBGPU)
71 (void)path;
72 (void)port;
73 std::cerr << "eve mcp: headless MCP host is desktop-only" << std::endl;
74 return 4;
75#else
76 try {
77 std::string gameDir = path;
78 {
79 std::error_code ec;
80 if (path.empty() || path == ".")
81 gameDir = std::filesystem::current_path(ec).string();
82 else
83 gameDir = std::filesystem::absolute(path, ec).string();
84 if (ec) gameDir = path;
85 }
86 if (!gameDir.empty() && gameDir != ".") {
87 std::error_code ec;
88 std::filesystem::current_path(gameDir, ec);
89 if (ec) {
90 std::cerr << "Cannot chdir to project path '" << gameDir << "': " << ec.message()
91 << std::endl;
92 return 2;
93 }
94 }
95
96 Runtime runtime(2048, ssq::Libs::ALL);
97 runtime.initialize();
98 runtime.vm().setPrintFunc(&hostPrintFunc, &hostPrintFunc);
99
100 // Mount the project for PhysFS so script file APIs / editors/ resolve.
101 // Optional for the headless host: keep running when the environment
102 // cannot initialize PhysFS (sandboxed/CI shells), scripts just lose
103 // the PhysFS-backed file API.
104 try {
105 auto* fs = eve::filesystem::Filesystem::create();
106 if (fs) {
107 std::error_code ec;
108 auto cwd = std::filesystem::current_path(ec);
109 if (!ec) {
110 try {
111 fs->setSource(cwd.string());
112 } catch (...) {
113 }
114 }
115 }
117 } catch (const std::exception& e) {
118 std::cerr << "eve mcp: filesystem mount skipped (" << e.what() << ")" << std::endl;
119 }
120
121 // DevTools: attach the VM so eve_eval / eve_run_script / snapshot work;
122 // expose eve.dev. No statement-level local sampling (host is a service).
123 auto& dt = eve::dev::DevTool::instance();
124 dt.attach(runtime.vm(), /*sampleLocals=*/false);
125 dt.exposeScriptApi(runtime.vm());
126
127 // Editor host: JSON Views <-> Squirrel ViewModel binding + window/render.
129 if (host) {
130 host->start(runtime.vm(), gameDir, /*allowWindow=*/true);
131 host->exposeScriptApi(runtime.vm());
132 }
133
134 auto& mcp = dt.mcp();
135 mcp.setGameRoot(gameDir);
136
137 if (port > 0) {
138 const int bound = dt.startMcp(static_cast<uint16_t>(port));
139 if (bound <= 0) {
140 std::cerr << "Failed to start MCP server on port " << port << std::endl;
141 return 3;
142 }
143 std::cerr << "MCP listening on 127.0.0.1:" << bound
144 << " (newline JSON-RPC; use tools/eve-mcp for Cursor stdio)" << std::endl;
145 } else {
146 if (!mcp.listenStdio()) {
147 std::cerr << "Failed to start stdio MCP transport" << std::endl;
148 return 3;
149 }
150 std::cerr << "MCP stdio transport ready (headless editor host; project=" << gameDir
151 << ")" << std::endl;
152 }
153
154 while (!host->exitRequested()) {
155 dt.poll(); // drains MCP requests on the main thread
156 host->frame();
157 if (mcp.transport() == eve::dev::McpServer::Transport::Stdio &&
158 mcp.stdinClosed()) {
159 std::cerr << "MCP stdio closed; host exiting" << std::endl;
160 break;
161 }
162 std::this_thread::sleep_for(std::chrono::milliseconds(2));
163 }
164
165 host->stop();
166 dt.detach();
167 return 0;
168 } catch (const std::exception& e) {
169 std::cerr << "eve mcp failed: " << e.what() << std::endl;
170 return 3;
171 }
172#endif
173}
174
175} // namespace eve::cmd
struct SQVM * HSQUIRRELVM
uint32_t s
Definition Weather.cpp:28
void initialize()
Exposes registered engine modules into the script root table. Safe to call more than once.
Definition Runtime.cpp:155
ssq::VM & vm() noexcept
Underlying SimpleSquirrel VM (requires a live, initialized runtime).
Definition Runtime.cpp:195
命令行模块(eve.cmd):run / build / package / test / zip / dev-server 等子命令入口。
Definition cmdline.h:30
static std::string get_remaining(CLI::App *sub, std::string default_path=".")
子命令剩余位置参数。
Definition cmdline.cpp:79
int McpHost(std::string path, int port)
无头 MCP 宿主:stdio(默认)或 TCP,供 AI 代理驱动。
Definition Mcp.cpp:69
static DevTool & instance()
Definition DevTool.cpp:118
#define CMD_REG(name)
Definition cmdline.h:92
I * query()
Definition Capability.h:77
void installScriptFileApi(ssq::VM &vm)
用 PhysFS 版本覆盖 Squirrel 的 file / dofile / loadfile 全局, 使脚本与资源从已挂载的游戏源(真实目录或内存挂载的 .eve 归档)解析; PhysFS 中不...
Definition FileApi.cpp:172
One eve <subcommand> handler: CLI setup + argument parsing.
Definition cmdline.h:21
int parse(CLI::App &app, Cmdline &cmd) override
Parses arguments and runs the command; returns the exit code.
Definition Mcp.cpp:56
void setup(CLI::App &app, std::shared_ptr< CLI::Formatter > formatter) override
Registers options on the CLI11 sub-app.
Definition Mcp.cpp:47
std::string root_path
Definition Mcp.cpp:44