mirror of
https://github.com/aljazceru/dvmcp.git
synced 2025-12-17 05:14:24 +01:00
145 lines
3.9 KiB
TypeScript
145 lines
3.9 KiB
TypeScript
import { parse } from 'yaml';
|
|
import { join } from 'path';
|
|
import { existsSync, readFileSync } from 'fs';
|
|
import { HEX_KEYS_REGEX } from '@dvmcp/commons/constants';
|
|
import type { Config, MCPServerConfig } from './types';
|
|
|
|
const CONFIG_PATH = join(process.cwd(), 'config.dvmcp.yml');
|
|
|
|
const TEST_CONFIG: Config = {
|
|
nostr: {
|
|
privateKey:
|
|
'd4d4d7aae7857054596c4c0976b22a73acac3a10d30bf56db35ee038bbf0dd44',
|
|
relayUrls: ['ws://localhost:3334'],
|
|
},
|
|
mcp: {
|
|
name: 'Test DVM MCP Bridge',
|
|
about: 'Test MCP-enabled DVM',
|
|
clientName: 'Test Client',
|
|
clientVersion: '1.0.0',
|
|
servers: [],
|
|
},
|
|
whitelist: {
|
|
allowedPubkeys: new Set(),
|
|
},
|
|
};
|
|
|
|
function validateRequiredField(value: any, fieldName: string): string {
|
|
if (!value) {
|
|
throw new Error(`Missing required config field: ${fieldName}`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function getConfigValue(
|
|
value: string | undefined,
|
|
defaultValue: string
|
|
): string {
|
|
return value || defaultValue;
|
|
}
|
|
|
|
function validateRelayUrls(urls: any): string[] {
|
|
if (!Array.isArray(urls) || urls.length === 0) {
|
|
throw new Error(
|
|
'At least one relay URL must be provided in nostr.relayUrls'
|
|
);
|
|
}
|
|
return urls.map((url: string) => {
|
|
try {
|
|
const trimmedUrl = url.trim();
|
|
new URL(trimmedUrl);
|
|
if (!trimmedUrl.startsWith('ws://') && !trimmedUrl.startsWith('wss://')) {
|
|
throw new Error(
|
|
`Relay URL must start with ws:// or wss://: ${trimmedUrl}`
|
|
);
|
|
}
|
|
return trimmedUrl;
|
|
} catch (error) {
|
|
throw new Error(`Invalid relay URL: ${url}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
function validateMCPServers(servers: any): MCPServerConfig[] {
|
|
if (!Array.isArray(servers) || servers.length === 0) {
|
|
throw new Error(
|
|
'At least one MCP server must be configured in mcp.servers'
|
|
);
|
|
}
|
|
return servers.map((server: any, index: number) => {
|
|
if (!server.name || !server.command || !Array.isArray(server.args)) {
|
|
throw new Error(
|
|
`Invalid MCP server configuration at index ${index}. Required fields: name, command, args[]`
|
|
);
|
|
}
|
|
return {
|
|
name: server.name,
|
|
command: server.command,
|
|
args: server.args,
|
|
};
|
|
});
|
|
}
|
|
|
|
function loadConfig(): Config {
|
|
if (process.env.NODE_ENV === 'test') {
|
|
return TEST_CONFIG;
|
|
}
|
|
|
|
if (!existsSync(CONFIG_PATH)) {
|
|
throw new Error(
|
|
'No config.dvmcp.yml file found. Please create one based on config.example.yml'
|
|
);
|
|
}
|
|
|
|
try {
|
|
const configFile = readFileSync(CONFIG_PATH, 'utf8');
|
|
const rawConfig = parse(configFile);
|
|
|
|
const config: Config = {
|
|
nostr: {
|
|
privateKey: validateRequiredField(
|
|
rawConfig.nostr?.privateKey,
|
|
'nostr.privateKey'
|
|
),
|
|
relayUrls: validateRelayUrls(rawConfig.nostr?.relayUrls),
|
|
},
|
|
mcp: {
|
|
name: getConfigValue(rawConfig.mcp?.name, 'DVM MCP Bridge'),
|
|
about: getConfigValue(
|
|
rawConfig.mcp?.about,
|
|
'MCP-enabled DVM providing AI and computational tools'
|
|
),
|
|
clientName: validateRequiredField(
|
|
rawConfig.mcp?.clientName,
|
|
'mcp.clientName'
|
|
),
|
|
clientVersion: validateRequiredField(
|
|
rawConfig.mcp?.clientVersion,
|
|
'mcp.clientVersion'
|
|
),
|
|
picture: rawConfig.mcp?.picture,
|
|
website: rawConfig.mcp?.website,
|
|
banner: rawConfig.mcp?.banner,
|
|
servers: validateMCPServers(rawConfig.mcp?.servers),
|
|
},
|
|
whitelist: {
|
|
allowedPubkeys: rawConfig.whitelist?.allowedPubkeys
|
|
? new Set(
|
|
rawConfig.whitelist.allowedPubkeys.map((pk: string) => pk.trim())
|
|
)
|
|
: undefined,
|
|
},
|
|
};
|
|
|
|
if (!HEX_KEYS_REGEX.test(config.nostr.privateKey)) {
|
|
throw new Error('privateKey must be a 32-byte hex string');
|
|
}
|
|
|
|
return config;
|
|
} catch (error) {
|
|
throw new Error(`Failed to load config: ${error}`);
|
|
}
|
|
}
|
|
|
|
export const CONFIG = loadConfig();
|