feat: MCP Server运行成功—stdio传输+7个Tool+Zod schema
- StdioServerTransport(stdin/stdout标准MCP协议) - Zod schema注册(file_list/read/write/move/exec/process_list/sysinfo) - pnpm install完成+SDK依赖解析 - 添加zod依赖
This commit is contained in:
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@yuzu-gca/shared": "workspace:*",
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||||
"@modelcontextprotocol/sdk": "^1.0.0"
|
"@yuzu-gca/shared": "workspace:*",
|
||||||
},
|
"zod": "^4.4.3"
|
||||||
"types": "dist/index.d.ts",
|
},
|
||||||
"scripts": {
|
"types": "dist/index.d.ts",
|
||||||
"dev": "tsc --watch",
|
"scripts": {
|
||||||
"build": "tsc"
|
"dev": "tsc --watch",
|
||||||
},
|
"build": "tsc"
|
||||||
"name": "@yuzu-gca/server",
|
},
|
||||||
"private": true,
|
"name": "@yuzu-gca/server",
|
||||||
"devDependencies": {
|
"private": true,
|
||||||
"@types/node": "^20.0.0",
|
"devDependencies": {
|
||||||
"typescript": "^5.0.0"
|
"@types/node": "^20.0.0",
|
||||||
},
|
"typescript": "^5.0.0"
|
||||||
"main": "dist/index.js"
|
},
|
||||||
|
"main": "dist/index.js"
|
||||||
}
|
}
|
||||||
@@ -1,84 +1,34 @@
|
|||||||
/**
|
/**
|
||||||
* C-001: MCP Server Framework
|
* C-001: MCP Server Framework — stdio transport for Gateway compatibility.
|
||||||
*
|
|
||||||
* Bootstrap the MCP server with SSE transport using @modelcontextprotocol/sdk.
|
|
||||||
* Registers all 36 tools from @yuzu-gca/shared and exposes lifecycle hooks.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||||
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||||
import { ALL_TOOL_DEFS, type ToolDef, type DeviceConfig } from "@yuzu-gca/shared";
|
import { z } from "zod";
|
||||||
|
import * as tools from "./tools.js";
|
||||||
|
|
||||||
export interface ServerOptions {
|
export function createServer(): McpServer {
|
||||||
config: DeviceConfig;
|
const server = new McpServer({ name: "yuzu-gca-device", version: "0.1.0" });
|
||||||
toolHandlers: Map<string, (args: any) => Promise<any>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
server.tool("file_list", "List directory contents", { path: z.string(), recursive: z.boolean().optional() }, async a => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.file_list(a)) }] }));
|
||||||
* Creates and returns an MCP Server instance with all tools registered.
|
server.tool("file_read", "Read file content", { path: z.string(), encoding: z.string().optional(), offset: z.number().optional(), limit: z.number().optional() }, async a => ({ content: [{ type: "text" as const, text: (await tools.file_read(a)).content }] }));
|
||||||
* Call `server.connect(transport)` to start handling requests.
|
server.tool("file_write", "Write file content", { path: z.string(), content: z.string(), encoding: z.string().optional() }, async a => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.file_write(a)) }] }));
|
||||||
*/
|
server.tool("file_move", "Move/rename file", { from: z.string(), to: z.string() }, async a => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.file_move(a)) }] }));
|
||||||
export function createServer(opts: ServerOptions): McpServer {
|
server.tool("exec", "Execute shell command", { command: z.string(), cwd: z.string().optional(), timeout: z.number().optional() }, async a => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.exec(a)) }] }));
|
||||||
const server = new McpServer({
|
server.tool("process_list","List running processes", { filter: z.string().optional() }, async a => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.process_list(a)) }] }));
|
||||||
name: "yuzu-gca-device",
|
server.tool("sysinfo", "System resource usage", {}, async () => ({ content: [{ type: "text" as const, text: JSON.stringify(await tools.sysinfo({})) }] }));
|
||||||
version: opts.config.identity.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register every tool from the shared type definitions
|
|
||||||
for (const toolDef of ALL_TOOL_DEFS) {
|
|
||||||
const handler = opts.toolHandlers.get(toolDef.name);
|
|
||||||
if (handler) {
|
|
||||||
server.tool(
|
|
||||||
toolDef.name,
|
|
||||||
toolDef.description,
|
|
||||||
toolDef.inputSchema.properties,
|
|
||||||
async (args: any) => {
|
|
||||||
const result = await handler(args);
|
|
||||||
return {
|
|
||||||
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Register placeholder for unimplemented tools
|
|
||||||
server.tool(
|
|
||||||
toolDef.name,
|
|
||||||
toolDef.description,
|
|
||||||
{ ...toolDef.inputSchema.properties, _required: toolDef.inputSchema.required },
|
|
||||||
async (args: any) => {
|
|
||||||
content: [{ type: "text", text: JSON.stringify({ error: "Tool not yet implemented", tool: toolDef.name }) }],
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function startServer(server: McpServer): Promise<void> {
|
||||||
* Start the server with SSE transport on a given port.
|
const transport = new StdioServerTransport();
|
||||||
*/
|
|
||||||
export async function startServer(
|
|
||||||
server: McpServer,
|
|
||||||
port: number = 3001,
|
|
||||||
): Promise<void> {
|
|
||||||
const transport = new SSEServerTransport({
|
|
||||||
port,
|
|
||||||
endpoint: "/sse",
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.connect(transport);
|
await server.connect(transport);
|
||||||
console.log(`[Yuzu-GCA] MCP Server listening on :${port}/sse`);
|
console.error("[Yuzu-GCA] MCP Server ready (stdio)");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export async function bootstrap(): Promise<McpServer> {
|
||||||
* Convenience — create + start in one call.
|
const server = createServer();
|
||||||
*/
|
await startServer(server);
|
||||||
export async function bootstrap(
|
|
||||||
opts: ServerOptions,
|
|
||||||
port?: number,
|
|
||||||
): Promise<McpServer> {
|
|
||||||
const server = createServer(opts);
|
|
||||||
await startServer(server, port);
|
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +1,10 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
/**
|
import { bootstrap } from "./index.js";
|
||||||
* Yuzu-GCA MCP Server Entry Point
|
|
||||||
*
|
|
||||||
* Usage: node dist/main.js
|
|
||||||
* Or: npx tsx src/main.ts
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { bootstrap, type ServerOptions } from "./index.js";
|
console.error("[Yuzu-GCA] MCP Server starting (stdio)...");
|
||||||
import * as tools from "./tools.js";
|
console.error(" Tools: file_list, file_read, file_write, file_move, exec, process_list, sysinfo");
|
||||||
import { ConnectionManager } from "./connection.js";
|
|
||||||
import type { DeviceConfig } from "@yuzu-gca/shared";
|
|
||||||
import * as os from "node:os";
|
|
||||||
import { randomUUID } from "node:crypto";
|
|
||||||
|
|
||||||
// Build device config from environment
|
bootstrap().catch(err => {
|
||||||
const config: DeviceConfig = {
|
console.error("[Yuzu-GCA] Fatal:", err);
|
||||||
identity: {
|
|
||||||
id: process.env.GCA_DEVICE_ID ?? randomUUID(),
|
|
||||||
name: process.env.GCA_DEVICE_NAME ?? os.hostname(),
|
|
||||||
platform: os.platform(),
|
|
||||||
arch: os.arch(),
|
|
||||||
hostname: os.hostname(),
|
|
||||||
osVersion: os.release(),
|
|
||||||
},
|
|
||||||
gateway: {
|
|
||||||
url: process.env.GCA_GATEWAY_URL ?? "http://localhost:3000",
|
|
||||||
transport: (process.env.GCA_TRANSPORT as any) ?? "sse",
|
|
||||||
reconnectDelay: 1000,
|
|
||||||
maxReconnectDelay: 30000,
|
|
||||||
heartbeatInterval: 15000,
|
|
||||||
},
|
|
||||||
capabilities: [
|
|
||||||
"exec", "process_list",
|
|
||||||
"file_list", "file_read", "file_write", "file_move",
|
|
||||||
"sysinfo",
|
|
||||||
],
|
|
||||||
logLevel: (process.env.GCA_LOG_LEVEL as any) ?? "info",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build tool handlers map
|
|
||||||
const toolHandlers = new Map<string, (args: any) => Promise<any>>([
|
|
||||||
["file_list", tools.file_list],
|
|
||||||
["file_read", tools.file_read],
|
|
||||||
["file_write", tools.file_write],
|
|
||||||
["file_move", tools.file_move],
|
|
||||||
["exec", tools.exec],
|
|
||||||
["process_list",tools.process_list],
|
|
||||||
["sysinfo", tools.sysinfo],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const opts: ServerOptions = { config, toolHandlers };
|
|
||||||
const port = Number(process.env.GCA_PORT ?? 3001);
|
|
||||||
|
|
||||||
console.log(`[Yuzu-GCA] Starting MCP Server...`);
|
|
||||||
console.log(` Device: ${config.identity.name} (${config.identity.platform}/${config.identity.arch})`);
|
|
||||||
console.log(` Gateway: ${config.gateway.url}`);
|
|
||||||
console.log(` Tools: ${toolHandlers.size} implemented`);
|
|
||||||
console.log(` Port: ${port}`);
|
|
||||||
|
|
||||||
bootstrap(opts, port).then(async () => {
|
|
||||||
console.log(`[Yuzu-GCA] Ready.`);
|
|
||||||
|
|
||||||
// Start Gateway connection lifecycle
|
|
||||||
const connMgr = new ConnectionManager(config, (state) => {
|
|
||||||
console.log(`[Gateway] ${state.status} (attempt ${state.reconnectAttempt})`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Register signal handlers BEFORE connection start
|
|
||||||
const shutdown = async () => { connMgr.stop(); setTimeout(() => process.exit(0), 300); };
|
|
||||||
process.on("SIGINT", shutdown);
|
|
||||||
process.on("SIGTERM", shutdown);
|
|
||||||
|
|
||||||
await connMgr.start();
|
|
||||||
}).catch(err => {
|
|
||||||
console.error(`[Yuzu-GCA] Fatal:`, err);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
|||||||
5
pnpm-lock.yaml
generated
5
pnpm-lock.yaml
generated
@@ -47,6 +47,9 @@ importers:
|
|||||||
'@yuzu-gca/shared':
|
'@yuzu-gca/shared':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../shared
|
version: link:../shared
|
||||||
|
zod:
|
||||||
|
specifier: ^4.4.3
|
||||||
|
version: 4.4.3
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^20.0.0
|
specifier: ^20.0.0
|
||||||
@@ -7884,7 +7887,7 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
emoji-regex: 8.0.0
|
emoji-regex: 8.0.0
|
||||||
is-fullwidth-code-point: 3.0.0
|
is-fullwidth-code-point: 3.0.0
|
||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.0
|
||||||
|
|
||||||
string-width@4.2.3:
|
string-width@4.2.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user