| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- import fsPromises from 'fs/promises';
- import os from 'os';
- import path from 'path';
- const DEFAULT_MCP_CONFIG = {
- mcpServers: {},
- };
- function pilotHome() {
- return process.env.PILOT_HOME || path.join(os.homedir(), '.pilotdeck');
- }
- export function getGlobalMcpConfigPath() {
- return path.join(pilotHome(), 'mcp.json');
- }
- export function getProjectMcpConfigPath(projectPath) {
- return path.join(projectPath || process.cwd(), '.pilotdeck', 'mcp.json');
- }
- export function normalizeMcpConfig(input) {
- if (!input || typeof input !== 'object' || Array.isArray(input)) {
- throw new Error('MCP config root must be an object.');
- }
- if (input.mcpServers === undefined) {
- return { ...input, mcpServers: {} };
- }
- if (!input.mcpServers || typeof input.mcpServers !== 'object' || Array.isArray(input.mcpServers)) {
- throw new Error('mcpServers must be an object.');
- }
- for (const [name, server] of Object.entries(input.mcpServers)) {
- if (!name || !server || typeof server !== 'object' || Array.isArray(server)) {
- throw new Error(`Server "${name}" must be an object.`);
- }
- if (typeof server.command !== 'string' && typeof server.url !== 'string' && typeof server.httpUrl !== 'string') {
- throw new Error(`Server "${name}" must define command, url, or httpUrl.`);
- }
- if (server.args !== undefined && (!Array.isArray(server.args) || !server.args.every((arg) => typeof arg === 'string'))) {
- throw new Error(`Server "${name}" args must be an array of strings.`);
- }
- if (server.env !== undefined && !isStringRecord(server.env)) {
- throw new Error(`Server "${name}" env must be an object with string values.`);
- }
- if (server.headers !== undefined && !isStringRecord(server.headers)) {
- throw new Error(`Server "${name}" headers must be an object with string values.`);
- }
- }
- return input;
- }
- export async function readMcpConfigFile(scope, projectPath) {
- const filePath = scope === 'project' ? getProjectMcpConfigPath(projectPath) : getGlobalMcpConfigPath();
- try {
- const raw = await fsPromises.readFile(filePath, 'utf8');
- const parsed = normalizeMcpConfig(JSON.parse(raw));
- return {
- exists: true,
- path: filePath,
- raw: JSON.stringify(parsed, null, 2),
- config: parsed,
- };
- } catch (error) {
- if (error?.code === 'ENOENT') {
- return {
- exists: false,
- path: filePath,
- raw: JSON.stringify(DEFAULT_MCP_CONFIG, null, 2),
- config: DEFAULT_MCP_CONFIG,
- };
- }
- throw error;
- }
- }
- export async function writeMcpConfigFile(scope, raw, projectPath) {
- const parsed = normalizeMcpConfig(JSON.parse(raw));
- const filePath = scope === 'project' ? getProjectMcpConfigPath(projectPath) : getGlobalMcpConfigPath();
- await fsPromises.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
- await fsPromises.writeFile(filePath, JSON.stringify(parsed, null, 2) + '\n', { mode: 0o600 });
- return readMcpConfigFile(scope, projectPath);
- }
- export async function listMcpConfigFiles(projectPath) {
- const [globalConfig, projectConfig] = await Promise.all([
- readMcpConfigFile('global', projectPath),
- readMcpConfigFile('project', projectPath),
- ]);
- return {
- global: globalConfig,
- project: projectConfig,
- };
- }
- function isStringRecord(value) {
- return value && typeof value === 'object' && !Array.isArray(value) && Object.values(value).every((entry) => typeof entry === 'string');
- }
|