mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-06 06:41:56 +00:00
feat: add MCP config and system prompt support for Codex local mode
Implements MCP server configuration and developer instructions support in Codex local mode, bringing it to parity with remote mode. Key additions: - buildMcpServerConfigArgs() and buildDeveloperInstructionsArg() utilities to construct -c config arguments for passing MCP servers and instructions to the Codex CLI at runtime - TITLE_INSTRUCTION for Codex to call hapi__change_title to update chat session titles dynamically - Codex local mode now starts hapi MCP bridge server and passes both MCP configuration and developer instructions to Claude, enabling full feature parity with remote mode Files changed: - New: codexMcpConfig.ts (utilities), systemPrompt.ts (prompt definition), codexMcpConfig.test.ts (comprehensive tests) - Updated: codexLocal.ts (accepts mcpServers, builds config args), codexLocalLauncher.ts (starts hapi server, passes config), codexStartConfig.ts (imports TITLE_INSTRUCTION)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildMcpServerConfigArgs, buildDeveloperInstructionsArg } from './codexMcpConfig';
|
||||
|
||||
describe('codexMcpConfig', () => {
|
||||
describe('buildMcpServerConfigArgs', () => {
|
||||
it('builds config args for a single MCP server', () => {
|
||||
const mcpServers = {
|
||||
hapi: {
|
||||
command: 'hapi',
|
||||
args: ['mcp', '--url', 'http://localhost:3000']
|
||||
}
|
||||
};
|
||||
|
||||
const args = buildMcpServerConfigArgs(mcpServers);
|
||||
|
||||
expect(args).toEqual([
|
||||
'-c', 'mcp_servers.hapi.command="hapi"',
|
||||
'-c', 'mcp_servers.hapi.args=["mcp","--url","http://localhost:3000"]'
|
||||
]);
|
||||
});
|
||||
|
||||
it('builds config args for multiple MCP servers', () => {
|
||||
const mcpServers = {
|
||||
hapi: { command: 'hapi', args: ['mcp'] },
|
||||
other: { command: 'node', args: ['server.js'] }
|
||||
};
|
||||
|
||||
const args = buildMcpServerConfigArgs(mcpServers);
|
||||
|
||||
expect(args).toContain('-c');
|
||||
expect(args).toContain('mcp_servers.hapi.command="hapi"');
|
||||
expect(args).toContain('mcp_servers.other.command="node"');
|
||||
});
|
||||
|
||||
it('handles empty args array', () => {
|
||||
const mcpServers = {
|
||||
simple: { command: 'simple-server', args: [] }
|
||||
};
|
||||
|
||||
const args = buildMcpServerConfigArgs(mcpServers);
|
||||
|
||||
expect(args).toContain('mcp_servers.simple.args=[]');
|
||||
});
|
||||
|
||||
it('escapes special characters in command', () => {
|
||||
const mcpServers = {
|
||||
test: { command: 'path/to/server', args: [] }
|
||||
};
|
||||
|
||||
const args = buildMcpServerConfigArgs(mcpServers);
|
||||
|
||||
expect(args).toContain('mcp_servers.test.command="path/to/server"');
|
||||
});
|
||||
});
|
||||
|
||||
describe('buildDeveloperInstructionsArg', () => {
|
||||
it('builds developer instructions arg', () => {
|
||||
const instructions = 'Call functions.hapi__change_title to set title.';
|
||||
|
||||
const args = buildDeveloperInstructionsArg(instructions);
|
||||
|
||||
expect(args).toEqual([
|
||||
'-c',
|
||||
'developer_instructions="Call functions.hapi__change_title to set title."'
|
||||
]);
|
||||
});
|
||||
|
||||
it('escapes double quotes', () => {
|
||||
const instructions = 'Use "quotes" in text.';
|
||||
|
||||
const args = buildDeveloperInstructionsArg(instructions);
|
||||
|
||||
expect(args[1]).toContain('\\"quotes\\"');
|
||||
});
|
||||
|
||||
it('escapes newlines', () => {
|
||||
const instructions = 'Line 1\nLine 2';
|
||||
|
||||
const args = buildDeveloperInstructionsArg(instructions);
|
||||
|
||||
expect(args[1]).toContain('\\n');
|
||||
expect(args[1]).not.toContain('\n');
|
||||
});
|
||||
|
||||
it('escapes backslashes', () => {
|
||||
const instructions = 'Path: C:\\Users\\test';
|
||||
|
||||
const args = buildDeveloperInstructionsArg(instructions);
|
||||
|
||||
expect(args[1]).toContain('\\\\');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Utilities for building Codex CLI config arguments (-c) for MCP servers
|
||||
* and developer instructions.
|
||||
*
|
||||
* Codex CLI accepts -c / --config flags with TOML-formatted key=value pairs.
|
||||
* This module generates the appropriate arguments for passing MCP server
|
||||
* configuration and developer instructions at runtime.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Escape a string value for use in a TOML string literal.
|
||||
* Handles double quotes, backslashes, and newlines.
|
||||
*/
|
||||
function escapeTomlString(value: string): string {
|
||||
return value
|
||||
.replace(/\\/g, '\\\\')
|
||||
.replace(/"/g, '\\"')
|
||||
.replace(/\n/g, '\\n')
|
||||
.replace(/\r/g, '\\r')
|
||||
.replace(/\t/g, '\\t');
|
||||
}
|
||||
|
||||
/**
|
||||
* Build -c arguments for MCP server configuration.
|
||||
*
|
||||
* Generates arguments like:
|
||||
* -c 'mcp_servers.hapi.command="hapi"'
|
||||
* -c 'mcp_servers.hapi.args=["mcp", "--url", "http://..."]'
|
||||
*
|
||||
* @param mcpServers - Map of server name to server config
|
||||
* @returns Array of CLI arguments to pass to codex
|
||||
*/
|
||||
export function buildMcpServerConfigArgs(
|
||||
mcpServers: Record<string, { command: string; args: string[] }>
|
||||
): string[] {
|
||||
const configArgs: string[] = [];
|
||||
|
||||
for (const [name, server] of Object.entries(mcpServers)) {
|
||||
// -c 'mcp_servers.<name>.command="<command>"'
|
||||
configArgs.push('-c', `mcp_servers.${name}.command="${escapeTomlString(server.command)}"`);
|
||||
|
||||
// -c 'mcp_servers.<name>.args=["arg1", "arg2"]'
|
||||
// JSON.stringify produces valid TOML array syntax for simple string arrays
|
||||
const argsToml = JSON.stringify(server.args);
|
||||
configArgs.push('-c', `mcp_servers.${name}.args=${argsToml}`);
|
||||
}
|
||||
|
||||
return configArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build -c argument for developer instructions.
|
||||
*
|
||||
* Generates argument like:
|
||||
* -c 'developer_instructions="<escaped instructions>"'
|
||||
*
|
||||
* @param instructions - Developer instructions text
|
||||
* @returns Array of CLI arguments to pass to codex
|
||||
*/
|
||||
export function buildDeveloperInstructionsArg(instructions: string): string[] {
|
||||
const escaped = escapeTomlString(instructions);
|
||||
return ['-c', `developer_instructions="${escaped}"`];
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
import { trimIdent } from '@/utils/trimIdent';
|
||||
import type { CodexSessionConfig } from '../types';
|
||||
import type { EnhancedMode } from '../loop';
|
||||
import type { CodexCliOverrides } from './codexCliOverrides';
|
||||
|
||||
const TITLE_INSTRUCTION = trimIdent(`Based on this message, call functions.hapi__change_title to change chat session title that would represent the current task. If chat idea would change dramatically - call this function again to update the title.`);
|
||||
import { TITLE_INSTRUCTION } from './systemPrompt';
|
||||
|
||||
function resolveApprovalPolicy(mode: EnhancedMode): CodexSessionConfig['approval-policy'] {
|
||||
switch (mode.permissionMode) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Codex-specific system prompt for local mode.
|
||||
*
|
||||
* This prompt instructs Codex to call the hapi__change_title function
|
||||
* to set appropriate chat session titles.
|
||||
*/
|
||||
|
||||
import { trimIdent } from '@/utils/trimIdent';
|
||||
|
||||
/**
|
||||
* Title instruction for Codex to call the hapi MCP tool.
|
||||
* Note: Codex exposes MCP tools under the `functions.` namespace,
|
||||
* so the tool is called as `functions.hapi__change_title`.
|
||||
*/
|
||||
export const TITLE_INSTRUCTION = trimIdent(`
|
||||
Based on this message, call functions.hapi__change_title to change chat session title that would represent the current task. If chat idea would change dramatically - call this function again to update the title.
|
||||
`);
|
||||
|
||||
/**
|
||||
* The system prompt to inject via developer_instructions in local mode.
|
||||
*/
|
||||
export const codexSystemPrompt = TITLE_INSTRUCTION;
|
||||
Reference in New Issue
Block a user