- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery - Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers - Add mcpd auth (login/logout) and MCP proxy endpoints - Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag - Add tiered health monitoring, shell completions, e2e integration tests - 57 test files, 597 tests passing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
78 lines
2.7 KiB
TypeScript
78 lines
2.7 KiB
TypeScript
import { Command } from 'commander';
|
|
import http from 'node:http';
|
|
import { loadConfig } from '../config/index.js';
|
|
import type { ConfigLoaderDeps } from '../config/index.js';
|
|
import { loadCredentials } from '../auth/index.js';
|
|
import type { CredentialsDeps } from '../auth/index.js';
|
|
import { formatJson, formatYaml } from '../formatters/index.js';
|
|
import { APP_VERSION } from '@mcpctl/shared';
|
|
|
|
export interface StatusCommandDeps {
|
|
configDeps: Partial<ConfigLoaderDeps>;
|
|
credentialsDeps: Partial<CredentialsDeps>;
|
|
log: (...args: string[]) => void;
|
|
checkHealth: (url: string) => Promise<boolean>;
|
|
}
|
|
|
|
function defaultCheckHealth(url: string): Promise<boolean> {
|
|
return new Promise((resolve) => {
|
|
const req = http.get(`${url}/health`, { timeout: 3000 }, (res) => {
|
|
resolve(res.statusCode !== undefined && res.statusCode >= 200 && res.statusCode < 400);
|
|
res.resume();
|
|
});
|
|
req.on('error', () => resolve(false));
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
resolve(false);
|
|
});
|
|
});
|
|
}
|
|
|
|
const defaultDeps: StatusCommandDeps = {
|
|
configDeps: {},
|
|
credentialsDeps: {},
|
|
log: (...args) => console.log(...args),
|
|
checkHealth: defaultCheckHealth,
|
|
};
|
|
|
|
export function createStatusCommand(deps?: Partial<StatusCommandDeps>): Command {
|
|
const { configDeps, credentialsDeps, log, checkHealth } = { ...defaultDeps, ...deps };
|
|
|
|
return new Command('status')
|
|
.description('Show mcpctl status and connectivity')
|
|
.option('-o, --output <format>', 'output format (table, json, yaml)', 'table')
|
|
.action(async (opts: { output: string }) => {
|
|
const config = loadConfig(configDeps);
|
|
const creds = loadCredentials(credentialsDeps);
|
|
|
|
const [mcplocalReachable, mcpdReachable] = await Promise.all([
|
|
checkHealth(config.mcplocalUrl),
|
|
checkHealth(config.mcpdUrl),
|
|
]);
|
|
|
|
const status = {
|
|
version: APP_VERSION,
|
|
mcplocalUrl: config.mcplocalUrl,
|
|
mcplocalReachable,
|
|
mcpdUrl: config.mcpdUrl,
|
|
mcpdReachable,
|
|
auth: creds ? { user: creds.user } : null,
|
|
registries: config.registries,
|
|
outputFormat: config.outputFormat,
|
|
};
|
|
|
|
if (opts.output === 'json') {
|
|
log(formatJson(status));
|
|
} else if (opts.output === 'yaml') {
|
|
log(formatYaml(status));
|
|
} else {
|
|
log(`mcpctl v${status.version}`);
|
|
log(`mcplocal: ${status.mcplocalUrl} (${mcplocalReachable ? 'connected' : 'unreachable'})`);
|
|
log(`mcpd: ${status.mcpdUrl} (${mcpdReachable ? 'connected' : 'unreachable'})`);
|
|
log(`Auth: ${creds ? `logged in as ${creds.user}` : 'not logged in'}`);
|
|
log(`Registries: ${status.registries.join(', ')}`);
|
|
log(`Output: ${status.outputFormat}`);
|
|
}
|
|
});
|
|
}
|