import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
ListPromptsRequestSchema,
GetPromptRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import chalk from 'chalk';
import { type ServerConfig, type ToolName, TOOLS } from './types.js';
import { handleError } from './errors.js';
import { toolDefinitions, promptDefinitions } from './tools/definitions.js';
import { toolHandlers } from './tools/handlers.js';
export class CodexMcpServer {
private readonly server: Server;
private readonly config: ServerConfig;
constructor(config: ServerConfig) {
this.config = config;
this.server = new Server(
{
name: config.name,
version: config.version,
},
{
capabilities: {
tools: {},
prompts: {},
},
}
);
this.setupHandlers();
}
private setupHandlers(): void {
// List tools handler
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: toolDefinitions };
});
// Call tool handler
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
if (!this.isValidToolName(name)) {
throw new Error(`Unknown tool: ${name}`);
}
const handler = toolHandlers[name];
return await handler.execute(args);
} catch (error) {
return {
content: [
{
type: 'text',
text: handleError(error, `tool "${name}"`),
},
],
isError: true,
};
}
});
// List prompts handler (for slash commands)
this.server.setRequestHandler(ListPromptsRequestSchema, async () => {
return { prompts: promptDefinitions };
});
// Get prompt handler
this.server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const promptDef = promptDefinitions.find(p => p.name === name);
if (!promptDef) {
throw new Error(`Unknown prompt: ${name}`);
}
// Build the prompt message based on arguments
let promptText = '';
if (name === 'codex') {
promptText = (args as any)?.prompt || 'Please review this code';
} else if (name === 'ping') {
promptText = (args as any)?.message || 'ping';
} else if (name === 'help') {
promptText = 'Show Codex CLI help';
} else if (name === 'listSessions') {
promptText = 'List all active Codex sessions';
}
return {
messages: [{
role: 'user' as const,
content: {
type: 'text' as const,
text: promptText
}
}]
};
});
}
private isValidToolName(name: string): name is ToolName {
return Object.values(TOOLS).includes(name as ToolName);
}
async start(): Promise<void> {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error(chalk.green(`${this.config.name} started successfully`));
}
}