first commit

This commit is contained in:
2026-02-23 12:06:13 -05:00
commit 53af0d44cd
33 changed files with 6483 additions and 0 deletions

116
src/mcp.ts Normal file
View File

@@ -0,0 +1,116 @@
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import type { CodexOptions } from "@openai/codex-sdk";
import {
createMcpHandlerShell,
listMcpHandlers,
registerMcpHandler,
resolveServerWithHandler,
type McpHandlerBusinessLogic,
type McpHandlerBusinessLogicInput,
type McpHandlerInput,
type McpHandlerResult,
type McpHandlerShellOptions,
type McpHandlerUtils,
type McpServerHandler,
} from "./mcp/handlers.js";
import type {
LoadedMcpConfig,
McpLoadContext,
SharedMcpConfigFile,
} from "./mcp/types.js";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function readConfigFile(pathFromEnv: string | undefined): {
config?: SharedMcpConfigFile;
sourcePath?: string;
} {
const explicitPath = pathFromEnv?.trim();
const candidatePath = explicitPath || "./mcp.config.json";
const resolvedPath = resolve(process.cwd(), candidatePath);
if (!existsSync(resolvedPath)) {
if (explicitPath) {
throw new Error(`MCP config file not found: ${resolvedPath}`);
}
return {};
}
const rawText = readFileSync(resolvedPath, "utf8");
const parsed = JSON.parse(rawText) as unknown;
if (!isRecord(parsed)) {
throw new Error(`MCP config file must contain a JSON object: ${resolvedPath}`);
}
return { config: parsed as SharedMcpConfigFile, sourcePath: resolvedPath };
}
export function loadMcpConfigFromEnv(context: McpLoadContext = {}): LoadedMcpConfig {
const { config, sourcePath } = readConfigFile(process.env.MCP_CONFIG_PATH);
if (!config) {
return {};
}
const codexServers: NonNullable<CodexOptions["config"]> = {};
const claudeServers: NonNullable<LoadedMcpConfig["claudeMcpServers"]> = {};
const resolvedHandlers: Record<string, string> = {};
for (const [serverName, server] of Object.entries(config.servers ?? {})) {
const resolved = resolveServerWithHandler({
serverName,
server,
context,
fullConfig: config,
});
resolvedHandlers[serverName] = resolved.handlerId;
if (resolved.enabled === false) {
continue;
}
if (resolved.codex) {
codexServers[serverName] = resolved.codex;
}
if (resolved.claude) {
claudeServers[serverName] = resolved.claude;
}
}
const codexWithOverrides = {
...codexServers,
...(config.codex?.mcp_servers ?? {}),
};
const claudeWithOverrides = {
...claudeServers,
...(config.claude?.mcpServers ?? {}),
};
const codexConfig =
Object.keys(codexWithOverrides).length > 0
? ({ mcp_servers: codexWithOverrides } as NonNullable<CodexOptions["config"]>)
: undefined;
return {
...(codexConfig ? { codexConfig } : {}),
...(Object.keys(claudeWithOverrides).length > 0
? { claudeMcpServers: claudeWithOverrides }
: {}),
...(sourcePath ? { sourcePath } : {}),
...(Object.keys(resolvedHandlers).length > 0 ? { resolvedHandlers } : {}),
};
}
export { createMcpHandlerShell, listMcpHandlers, registerMcpHandler };
export type {
LoadedMcpConfig,
McpHandlerBusinessLogic,
McpHandlerBusinessLogicInput,
McpHandlerInput,
McpHandlerResult,
McpHandlerShellOptions,
McpHandlerUtils,
McpLoadContext,
McpServerHandler,
};