chittycan
Version:
Your completely autonomous network that grows with you - DNA ownership platform with encrypted vaults, PDX portability, and ChittyFoundation governance
179 lines • 6.23 kB
JavaScript
/**
* MCP (Model Context Protocol) Server Management
*
* Manages local and remote MCP servers configured in ChittyCan.
*/
import { spawn } from "child_process";
import { loadConfig } from "../lib/config.js";
// Track running MCP servers
const runningServers = new Map();
/**
* List configured MCP servers
*/
export function listMcpServers() {
const config = loadConfig();
const mcpRemotes = Object.entries(config.remotes)
.filter(([_, remote]) => remote.type === "mcp-server");
if (mcpRemotes.length === 0) {
console.log("No MCP servers configured.");
console.log("Run 'can config' to add MCP server remotes.");
return;
}
console.log("\nConfigured MCP Servers:");
console.log("=======================\n");
for (const [name, remote] of mcpRemotes) {
const status = runningServers.has(name) ? "🟢 Running" : "⚪ Stopped";
console.log(`${status} ${name}`);
console.log(` Command: ${remote.command} ${remote.args.join(" ")}`);
if (remote.description) {
console.log(` Description: ${remote.description}`);
}
if (remote.env && Object.keys(remote.env).length > 0) {
console.log(` Environment: ${Object.keys(remote.env).join(", ")}`);
}
console.log();
}
}
/**
* Start an MCP server
*/
export function startMcpServer(name) {
const config = loadConfig();
const remote = config.remotes[name];
if (!remote) {
console.error(`Remote '${name}' not found.`);
console.log("Run 'can remote list' to see configured remotes.");
process.exit(1);
}
if (remote.type !== "mcp-server") {
console.error(`Remote '${name}' is not an MCP server (type: ${remote.type})`);
process.exit(1);
}
if (runningServers.has(name)) {
console.log(`MCP server '${name}' is already running.`);
return;
}
const mcpRemote = remote;
console.log(`Starting MCP server: ${name}`);
console.log(`Command: ${mcpRemote.command} ${mcpRemote.args.join(" ")}`);
try {
const child = spawn(mcpRemote.command, mcpRemote.args, {
env: { ...process.env, ...mcpRemote.env },
stdio: ["ignore", "pipe", "pipe"],
detached: true
});
child.stdout?.on("data", (data) => {
console.log(`[${name}] ${data.toString().trim()}`);
});
child.stderr?.on("data", (data) => {
console.error(`[${name}] ${data.toString().trim()}`);
});
child.on("error", (err) => {
console.error(`Failed to start ${name}:`, err.message);
runningServers.delete(name);
});
child.on("exit", (code) => {
console.log(`MCP server ${name} exited with code ${code}`);
runningServers.delete(name);
});
runningServers.set(name, child);
child.unref(); // Allow parent to exit
console.log(`✓ MCP server '${name}' started (PID: ${child.pid})`);
console.log("Logs will appear above. Press Ctrl+C to stop following logs.");
}
catch (error) {
console.error(`Failed to start MCP server:`, error.message);
process.exit(1);
}
}
/**
* Stop an MCP server
*/
export function stopMcpServer(name) {
const child = runningServers.get(name);
if (!child) {
console.log(`MCP server '${name}' is not running.`);
return;
}
console.log(`Stopping MCP server: ${name}`);
try {
child.kill("SIGTERM");
runningServers.delete(name);
console.log(`✓ MCP server '${name}' stopped`);
}
catch (error) {
console.error(`Failed to stop MCP server:`, error.message);
process.exit(1);
}
}
/**
* Check MCP server status
*/
export function mcpServerStatus(name) {
const config = loadConfig();
const remote = config.remotes[name];
if (!remote) {
console.error(`Remote '${name}' not found.`);
process.exit(1);
}
if (remote.type !== "mcp-server") {
console.error(`Remote '${name}' is not an MCP server`);
process.exit(1);
}
const isRunning = runningServers.has(name);
const status = isRunning ? "🟢 Running" : "⚪ Stopped";
console.log(`\nMCP Server: ${name}`);
console.log(`Status: ${status}`);
const mcpRemote = remote;
console.log(`Command: ${mcpRemote.command} ${mcpRemote.args.join(" ")}`);
if (isRunning) {
const child = runningServers.get(name);
console.log(`PID: ${child.pid}`);
}
if (mcpRemote.description) {
console.log(`Description: ${mcpRemote.description}`);
}
console.log();
}
/**
* List tools available from an MCP server
*/
export async function listMcpTools(name) {
const config = loadConfig();
const remote = config.remotes[name];
if (!remote || remote.type !== "mcp-server") {
console.error(`MCP server '${name}' not found.`);
process.exit(1);
}
console.log(`\nFetching tools from MCP server: ${name}`);
console.log("(This requires the server to be running)\n");
// For now, show placeholder
// TODO: Implement MCP protocol client to query tools
console.log("Tool listing requires MCP protocol implementation.");
console.log("This feature is coming in v0.5.0.");
console.log("\nMeanwhile, check server documentation for available tools.");
}
/**
* Test connection to MCP server
*/
export async function testMcpConnection(name) {
console.log(`Testing connection to MCP server: ${name}`);
console.log("(This will attempt to start the server and verify it responds)\n");
// Start if not running
if (!runningServers.has(name)) {
startMcpServer(name);
// Wait a moment for startup
await new Promise(resolve => setTimeout(resolve, 2000));
}
// Check if server is responding
if (runningServers.has(name)) {
console.log("✓ Server is running");
// TODO: Implement actual protocol handshake test
console.log("Protocol handshake test coming in v0.5.0");
}
else {
console.error("✗ Server failed to start");
process.exit(1);
}
}
//# sourceMappingURL=mcp.js.map