ai-cli-hub
Version:
Unified MCP Server for AI CLI Tools (Gemini, Qwen, OpenCode)
259 lines (218 loc) • 7.54 kB
JavaScript
/**
* AI CLI Hub MCP Client Demo
* このスクリプトはMCPサーバーの各ツールをテストするためのクライアントです
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { spawn } from "child_process";
import { join } from "path";
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// MCPサーバーへの接続をシミュレートするテストクライアント
class MCPTestClient {
constructor(serverCommand, serverArgs) {
this.serverProcess = null;
this.serverCommand = serverCommand;
this.serverArgs = serverArgs;
}
async start() {
console.log("🚀 AI CLI Hub MCP Server Demo");
console.log("====================================");
// サーバープロセス起動
this.serverProcess = spawn(this.serverCommand, this.serverArgs, {
stdio: ['pipe', 'pipe', 'inherit'],
cwd: join(__dirname, '..')
});
console.log("✅ MCP Server started with PID:", this.serverProcess.pid);
// リストツール要求
await this.testListTools();
// 各ツールのテスト実行
await this.testFileOperations();
await this.testAuthTools();
await this.testGitOperations();
await this.testOpenCodeTools();
}
async sendMCPMessage(message) {
return new Promise((resolve, reject) => {
const msgString = JSON.stringify(message) + '\\n';
this.serverProcess.stdin.write(msgString);
let responseBuffer = '';
const timeout = setTimeout(() => {
reject(new Error('Request timeout'));
}, 5000);
const onData = (chunk) => {
responseBuffer += chunk.toString();
try {
const response = JSON.parse(responseBuffer);
clearTimeout(timeout);
this.serverProcess.stdout.off('data', onData);
resolve(response);
} catch (e) {
// まだ完全なJSONではない、継続
}
};
this.serverProcess.stdout.on('data', onData);
});
}
async testListTools() {
console.log("\\n📋 Testing: List Tools");
try {
const request = {
jsonrpc: "2.0",
id: 1,
method: "tools/list",
params: {}
};
const response = await this.sendMCPMessage(request);
console.log("✅ Available tools:", response.result?.tools?.length || 0);
if (response.result?.tools) {
response.result.tools.slice(0, 5).forEach(tool => {
console.log(` - ${tool.name}: ${tool.description || 'No description'}`);
});
if (response.result.tools.length > 5) {
console.log(` ... and ${response.result.tools.length - 5} more tools`);
}
}
} catch (error) {
console.error("❌ List tools failed:", error.message);
}
}
async testFileOperations() {
console.log("\\n📁 Testing: File Operations");
try {
// Hello World ファイル作成テスト
const writeRequest = {
jsonrpc: "2.0",
id: 2,
method: "tools/call",
params: {
name: "file_write",
arguments: {
path: "/tmp/hello-world-mcp.txt",
content: "Hello World from AI CLI Hub MCP Server!\\nCreated on: " + new Date().toISOString() + "\\n"
}
}
};
const writeResponse = await this.sendMCPMessage(writeRequest);
console.log("✅ File write:", writeResponse.result ? "Success" : "Failed");
// ファイル読み込みテスト
const readRequest = {
jsonrpc: "2.0",
id: 3,
method: "tools/call",
params: {
name: "file_read",
arguments: {
path: "/tmp/hello-world-mcp.txt"
}
}
};
const readResponse = await this.sendMCPMessage(readRequest);
console.log("✅ File read result:");
console.log(" Content:", JSON.parse(readResponse.result?.content?.[0]?.text || '{}').content?.substring(0, 50) + "...");
} catch (error) {
console.error("❌ File operations failed:", error.message);
}
}
async testAuthTools() {
console.log("\\n🔐 Testing: Authentication Tools");
try {
const statusRequest = {
jsonrpc: "2.0",
id: 4,
method: "tools/call",
params: {
name: "auth_check_status",
arguments: {}
}
};
const statusResponse = await this.sendMCPMessage(statusRequest);
const authData = JSON.parse(statusResponse.result?.content?.[0]?.text || '{}');
console.log("✅ Auth status check:");
console.log(" Providers:", Object.keys(authData.providers || {}));
console.log(" Authenticated:", authData.authenticatedCount || 0, "/", authData.totalCount || 0);
} catch (error) {
console.error("❌ Auth tools failed:", error.message);
}
}
async testGitOperations() {
console.log("\\n🌟 Testing: Git Operations");
try {
const gitStatusRequest = {
jsonrpc: "2.0",
id: 5,
method: "tools/call",
params: {
name: "git_status",
arguments: {
repository: "/Users/jun/ai-cli"
}
}
};
const gitResponse = await this.sendMCPMessage(gitStatusRequest);
const gitData = JSON.parse(gitResponse.result?.content?.[0]?.text || '{}');
console.log("✅ Git status:");
console.log(" Repository:", gitData.repository || "Unknown");
console.log(" Branch:", gitData.branch || "Unknown");
console.log(" Status:", gitData.hasChanges ? "Has changes" : "Clean");
} catch (error) {
console.error("❌ Git operations failed:", error.message);
}
}
async testOpenCodeTools() {
console.log("\\n🤖 Testing: OpenCode AI Tools");
try {
const chatRequest = {
jsonrpc: "2.0",
id: 6,
method: "tools/call",
params: {
name: "opencode_chat",
arguments: {
message: "Hello! Please respond with a simple 'Hello World from OpenCode' message.",
provider: "anthropic"
}
}
};
const chatResponse = await this.sendMCPMessage(chatRequest);
const chatData = JSON.parse(chatResponse.result?.content?.[0]?.text || '{}');
console.log("✅ OpenCode chat:");
console.log(" Response:", chatData.message || chatData.response || "No response");
} catch (error) {
console.error("❌ OpenCode tools failed:", error.message);
}
}
async stop() {
if (this.serverProcess) {
console.log("\\n🛑 Stopping MCP Server...");
this.serverProcess.kill('SIGTERM');
console.log("✅ Demo completed!");
}
}
}
// メイン実行
async function main() {
const client = new MCPTestClient('npm', ['run', 'dev']);
try {
await client.start();
// 5秒後に終了
setTimeout(async () => {
await client.stop();
process.exit(0);
}, 8000);
} catch (error) {
console.error("Demo failed:", error);
await client.stop();
process.exit(1);
}
}
// 実行制御
if (process.argv[2] === '--run') {
main().catch(console.error);
} else {
console.log("AI CLI Hub MCP Client Demo");
console.log("Usage: node test-mcp-client.js --run");
}