@rubys/fly-explorer
Version:
A comprehensive web-based dashboard for managing Fly.io infrastructure using Model Context Protocol (MCP) integration with flyctl
101 lines • 3.69 kB
JavaScript
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { z } from 'zod';
import { execSync } from 'child_process';
import * as os from 'os';
export class FlyctlMCPClient {
client;
transport;
// Callback properties for notifications
onProgress;
onLogMessage;
onUnhandledNotification;
constructor() {
this.client = new Client({
name: 'flyctl-mcp-client',
version: '1.0.0',
}, {
capabilities: {}
});
}
async connect(flyctlPath) {
// Use flyctl from PATH or provided path
let command = flyctlPath || 'flyctl';
// If flyctl is not in PATH, check common installation locations
if (!flyctlPath) {
try {
execSync('flyctl version', { stdio: 'ignore' });
}
catch {
// Try common installation paths
const homeDir = os.homedir();
const platform = process.platform;
const possiblePaths = [
`${homeDir}/.fly/bin/flyctl`, // Unix-like default
`${homeDir}\\.fly\\bin\\flyctl.exe`, // Windows default
'/usr/local/bin/flyctl', // Homebrew or manual install
'/opt/homebrew/bin/flyctl', // Homebrew on Apple Silicon
];
for (const path of possiblePaths) {
try {
execSync(`"${path}" version`, { stdio: 'ignore' });
command = path;
console.log(`Found flyctl at: ${path}`);
break;
}
catch {
// Continue checking other paths
}
}
}
}
console.log(`Spawning flyctl MCP server: ${command}`);
this.transport = new StdioClientTransport({
command,
args: ['mcp', 'server'],
env: process.env
});
await this.client.connect(this.transport);
// Set up notification handlers after connecting
this.setupNotificationHandlers();
console.log('Connected to flyctl MCP server');
}
setupNotificationHandlers() {
// Define the progress notification schema
const ProgressNotificationSchema = z.object({
method: z.literal('notifications/progress'),
params: z.object({
progressToken: z.string(),
progress: z.number().optional(),
total: z.number().optional(),
message: z.string().optional()
})
});
// Handle progress notifications
this.client.setNotificationHandler(ProgressNotificationSchema, (notification) => {
this.onProgress?.(notification.params);
});
// Set up fallback notification handler for unknown notification types
this.client.fallbackNotificationHandler = async (notification) => {
this.onUnhandledNotification?.(notification);
};
}
async listTools() {
const allTools = [];
let cursor;
do {
const response = await this.client.listTools({ cursor });
allTools.push(...response.tools);
cursor = response.nextCursor;
} while (cursor);
return allTools;
}
async close() {
await this.client.close();
}
// Expose the client for tool calls
get rawClient() {
return this.client;
}
}
//# sourceMappingURL=flyctl-client.js.map