src / mcp / config.ts
src / mcp / config.ts
import { requiredEnv, optionalEnv } from "./core-bundle.mjs";
/**
* Mirrors generate-image/src/config.ts's globalConfigSchematics fields — NOT
* draw-things-chat-core/src/config.ts (a different, orchestrator-only plugin
* config with overlapping field names). `unloadAgentModelDuringRender` is
* intentionally absent: it doesn't exist in generate-image's own config and
* unloadAgentModel() is never called from core/tools.ts.
*/
export type McpConfig = {
chatWorkingDirectories: string;
previewInChat: boolean;
drawThingsHost: string;
drawThingsHttpPort: number;
drawThingsGrpcPort: number;
embedPngMetadata: boolean;
/** Raw value, may be "none" (disables custom configs) or empty. */
customConfigsPath: string;
};
function parseBoolean(value: string | undefined, fallback: boolean): boolean {
if (value === undefined) return fallback;
return /^(1|true|yes)$/i.test(value.trim());
}
function parsePort(name: string, value: string | undefined, fallback: number): number {
if (value === undefined) return fallback;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) {
throw new Error(`${name} must be a positive integer, got "${value}".`);
}
return parsed;
}
export function readMcpConfig(): McpConfig {
return {
chatWorkingDirectories: requiredEnv("CHAT_WORKING_DIRECTORIES"),
previewInChat: parseBoolean(optionalEnv("GENERATE_IMAGE_PREVIEW_IN_CHAT"), false),
drawThingsHost: optionalEnv("GENERATE_IMAGE_DRAW_THINGS_HOST") ?? "127.0.0.1",
drawThingsHttpPort: parsePort("GENERATE_IMAGE_DRAW_THINGS_HTTP_PORT", optionalEnv("GENERATE_IMAGE_DRAW_THINGS_HTTP_PORT"), 7860),
drawThingsGrpcPort: parsePort("GENERATE_IMAGE_DRAW_THINGS_GRPC_PORT", optionalEnv("GENERATE_IMAGE_DRAW_THINGS_GRPC_PORT"), 7859),
embedPngMetadata: parseBoolean(optionalEnv("GENERATE_IMAGE_EMBED_PNG_METADATA"), true),
customConfigsPath:
optionalEnv("GENERATE_IMAGE_CUSTOM_CONFIGS_PATH") ??
"~/Library/Containers/com.liuliu.draw-things/Data/Documents/Models/custom_configs.json",
};
}
/** Sets the process.env values core/tools.ts reads directly (see getCurrentConnectionSettings, EMBED_PNG_METADATA check). */
export function applyMcpConfig(config: McpConfig): void {
process.env.PREVIEW_IN_CHAT = config.previewInChat ? "true" : "false";
process.env.DRAW_THINGS_HOST = config.drawThingsHost;
process.env.DRAW_THINGS_HTTP_PORT = String(config.drawThingsHttpPort);
process.env.DRAW_THINGS_GRPC_PORT = String(config.drawThingsGrpcPort);
process.env.EMBED_PNG_METADATA = config.embedPngMetadata ? "true" : "false";
}
/**
* services/customConfigsLoader.ts caches the resolved path in module state, not
* an env var, and must be primed exactly once at startup (see its own comment
* "Called ONCE during main() startup"). Mirrors toolsProvider.ts's behavior.
*/
export async function initCustomConfigs(customConfigsPath: string): Promise<void> {
const trimmed = customConfigsPath.trim();
if (!trimmed || trimmed.toLowerCase() === "none") return;
const { checkCustomConfigs, loadCustomConfigs } = await import("../services/customConfigsLoader.js");
const status = await checkCustomConfigs(trimmed);
if (status.available) {
await loadCustomConfigs();
}
}
import { requiredEnv, optionalEnv } from "./core-bundle.mjs";
/**
* Mirrors generate-image/src/config.ts's globalConfigSchematics fields — NOT
* draw-things-chat-core/src/config.ts (a different, orchestrator-only plugin
* config with overlapping field names). `unloadAgentModelDuringRender` is
* intentionally absent: it doesn't exist in generate-image's own config and
* unloadAgentModel() is never called from core/tools.ts.
*/
export type McpConfig = {
chatWorkingDirectories: string;
previewInChat: boolean;
drawThingsHost: string;
drawThingsHttpPort: number;
drawThingsGrpcPort: number;
embedPngMetadata: boolean;
/** Raw value, may be "none" (disables custom configs) or empty. */
customConfigsPath: string;
};
function parseBoolean(value: string | undefined, fallback: boolean): boolean {
if (value === undefined) return fallback;
return /^(1|true|yes)$/i.test(value.trim());
}
function parsePort(name: string, value: string | undefined, fallback: number): number {
if (value === undefined) return fallback;
const parsed = Number.parseInt(value, 10);
if (!Number.isFinite(parsed) || parsed <= 0) {
throw new Error(`${name} must be a positive integer, got "${value}".`);
}
return parsed;
}
export function readMcpConfig(): McpConfig {
return {
chatWorkingDirectories: requiredEnv("CHAT_WORKING_DIRECTORIES"),
previewInChat: parseBoolean(optionalEnv("GENERATE_IMAGE_PREVIEW_IN_CHAT"), false),
drawThingsHost: optionalEnv("GENERATE_IMAGE_DRAW_THINGS_HOST") ?? "127.0.0.1",
drawThingsHttpPort: parsePort("GENERATE_IMAGE_DRAW_THINGS_HTTP_PORT", optionalEnv("GENERATE_IMAGE_DRAW_THINGS_HTTP_PORT"), 7860),
drawThingsGrpcPort: parsePort("GENERATE_IMAGE_DRAW_THINGS_GRPC_PORT", optionalEnv("GENERATE_IMAGE_DRAW_THINGS_GRPC_PORT"), 7859),
embedPngMetadata: parseBoolean(optionalEnv("GENERATE_IMAGE_EMBED_PNG_METADATA"), true),
customConfigsPath:
optionalEnv("GENERATE_IMAGE_CUSTOM_CONFIGS_PATH") ??
"~/Library/Containers/com.liuliu.draw-things/Data/Documents/Models/custom_configs.json",
};
}
/** Sets the process.env values core/tools.ts reads directly (see getCurrentConnectionSettings, EMBED_PNG_METADATA check). */
export function applyMcpConfig(config: McpConfig): void {
process.env.PREVIEW_IN_CHAT = config.previewInChat ? "true" : "false";
process.env.DRAW_THINGS_HOST = config.drawThingsHost;
process.env.DRAW_THINGS_HTTP_PORT = String(config.drawThingsHttpPort);
process.env.DRAW_THINGS_GRPC_PORT = String(config.drawThingsGrpcPort);
process.env.EMBED_PNG_METADATA = config.embedPngMetadata ? "true" : "false";
}
/**
* services/customConfigsLoader.ts caches the resolved path in module state, not
* an env var, and must be primed exactly once at startup (see its own comment
* "Called ONCE during main() startup"). Mirrors toolsProvider.ts's behavior.
*/
export async function initCustomConfigs(customConfigsPath: string): Promise<void> {
const trimmed = customConfigsPath.trim();
if (!trimmed || trimmed.toLowerCase() === "none") return;
const { checkCustomConfigs, loadCustomConfigs } = await import("../services/customConfigsLoader.js");
const status = await checkCustomConfigs(trimmed);
if (status.available) {
await loadCustomConfigs();
}
}