diff --git a/runners/agent-runner/src/index.ts b/runners/agent-runner/src/index.ts index 29261b4..83eb598 100644 --- a/runners/agent-runner/src/index.ts +++ b/runners/agent-runner/src/index.ts @@ -60,6 +60,7 @@ import { createSanitizeBashHook, } from './runner-hooks.js'; import { buildClaudeSdkEnv } from './sdk-env.js'; +import { buildEjclawMcpServerConfig } from './mcp-config.js'; interface RunnerInput { prompt: string; @@ -299,23 +300,15 @@ async function runQuery( settingSources: ['project', 'user'], abortController: agentAbortController, mcpServers: { - ejclaw: { - command: 'node', - args: [mcpServerPath], - env: { - EJCLAW_CHAT_JID: runnerInput.chatJid, - EJCLAW_GROUP_FOLDER: runnerInput.groupFolder, - EJCLAW_IS_MAIN: runnerInput.isMain ? '1' : '0', - EJCLAW_AGENT_TYPE: process.env.EJCLAW_AGENT_TYPE || 'claude-code', - EJCLAW_ROOM_ROLE: runnerInput.roomRoleContext?.role || '', - ...(process.env.EJCLAW_IPC_DIR && { - EJCLAW_IPC_DIR: process.env.EJCLAW_IPC_DIR, - }), - ...(process.env.EJCLAW_HOST_IPC_DIR && { - EJCLAW_HOST_IPC_DIR: process.env.EJCLAW_HOST_IPC_DIR, - }), - }, - }, + ejclaw: buildEjclawMcpServerConfig(mcpServerPath, { + chatJid: runnerInput.chatJid, + groupFolder: runnerInput.groupFolder, + isMain: runnerInput.isMain, + agentType: process.env.EJCLAW_AGENT_TYPE || 'claude-code', + roomRole: runnerInput.roomRoleContext?.role || '', + ipcDir: process.env.EJCLAW_IPC_DIR, + hostIpcDir: process.env.EJCLAW_HOST_IPC_DIR, + }), }, hooks: { PreCompact: [ diff --git a/runners/agent-runner/src/mcp-config.ts b/runners/agent-runner/src/mcp-config.ts new file mode 100644 index 0000000..d9a2715 --- /dev/null +++ b/runners/agent-runner/src/mcp-config.ts @@ -0,0 +1,35 @@ +import type { McpStdioServerConfig } from '@anthropic-ai/claude-agent-sdk'; + +export interface EjclawMcpServerConfigInput { + chatJid: string; + groupFolder: string; + isMain: boolean; + agentType: string; + roomRole: string; + ipcDir?: string; + hostIpcDir?: string; +} + +export function buildEjclawMcpServerConfig( + mcpServerPath: string, + input: EjclawMcpServerConfigInput, +): McpStdioServerConfig { + return { + command: 'node', + args: [mcpServerPath], + alwaysLoad: true, + env: { + EJCLAW_CHAT_JID: input.chatJid, + EJCLAW_GROUP_FOLDER: input.groupFolder, + EJCLAW_IS_MAIN: input.isMain ? '1' : '0', + EJCLAW_AGENT_TYPE: input.agentType, + EJCLAW_ROOM_ROLE: input.roomRole, + ...(input.ipcDir && { + EJCLAW_IPC_DIR: input.ipcDir, + }), + ...(input.hostIpcDir && { + EJCLAW_HOST_IPC_DIR: input.hostIpcDir, + }), + }, + }; +} diff --git a/runners/agent-runner/test/mcp-config.test.ts b/runners/agent-runner/test/mcp-config.test.ts new file mode 100644 index 0000000..cc28ac4 --- /dev/null +++ b/runners/agent-runner/test/mcp-config.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from 'vitest'; + +import { buildEjclawMcpServerConfig } from '../src/mcp-config.js'; + +describe('ejclaw MCP config', () => { + it('always loads EJClaw MCP tools on turn one and preserves runtime env', () => { + expect( + buildEjclawMcpServerConfig('/runner/dist/ipc-mcp-stdio.js', { + chatJid: 'dc:room', + groupFolder: 'ejclaw', + isMain: true, + agentType: 'claude-code', + roomRole: 'reviewer', + ipcDir: '/tmp/ipc/task', + hostIpcDir: '/tmp/ipc/host', + }), + ).toEqual({ + command: 'node', + args: ['/runner/dist/ipc-mcp-stdio.js'], + alwaysLoad: true, + env: { + EJCLAW_CHAT_JID: 'dc:room', + EJCLAW_GROUP_FOLDER: 'ejclaw', + EJCLAW_IS_MAIN: '1', + EJCLAW_AGENT_TYPE: 'claude-code', + EJCLAW_ROOM_ROLE: 'reviewer', + EJCLAW_IPC_DIR: '/tmp/ipc/task', + EJCLAW_HOST_IPC_DIR: '/tmp/ipc/host', + }, + }); + }); +}); diff --git a/runners/shared/package.json b/runners/shared/package.json index e5c843c..b77f093 100644 --- a/runners/shared/package.json +++ b/runners/shared/package.json @@ -8,7 +8,7 @@ "exports": { ".": { "types": "./src/index.ts", - "default": "./src/index.ts" + "default": "./dist/index.js" } }, "scripts": { diff --git a/runners/shared/test/package-exports.test.ts b/runners/shared/test/package-exports.test.ts new file mode 100644 index 0000000..d517146 --- /dev/null +++ b/runners/shared/test/package-exports.test.ts @@ -0,0 +1,19 @@ +import fs from 'fs'; +import path from 'path'; +import { describe, expect, it } from 'vitest'; + +describe('runner shared package exports', () => { + it('points runtime imports at built JavaScript for node-launched MCP servers', () => { + const packageJson = JSON.parse( + fs.readFileSync( + path.resolve(import.meta.dirname, '../package.json'), + 'utf8', + ), + ) as { exports: { '.': { default: string; types: string } } }; + + expect(packageJson.exports['.']).toEqual({ + types: './src/index.ts', + default: './dist/index.js', + }); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index ec3bbe6..a8291d0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,6 +5,10 @@ export default defineConfig({ resolve: { alias: { 'bun:sqlite': path.resolve(__dirname, 'test/bun-sqlite-shim.ts'), + 'ejclaw-runners-shared': path.resolve( + __dirname, + 'runners/shared/src/index.ts', + ), }, }, test: {