@happycastle/oh-my-openclaw 0.13.2 → 0.13.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ type McpServerEntry = {
2
+ url: string;
3
+ description: string;
4
+ };
5
+ export declare const OMOC_MCP_SERVERS: Record<string, McpServerEntry>;
6
+ type McporterConfig = {
7
+ mcpServers: Record<string, {
8
+ url?: string;
9
+ baseUrl?: string;
10
+ type?: string;
11
+ [key: string]: unknown;
12
+ }>;
13
+ [key: string]: unknown;
14
+ };
15
+ /**
16
+ * Resolve mcporter config path.
17
+ * Priority: ~/.openclaw/workspace/config/mcporter.json > ~/.config/mcporter/mcporter.json
18
+ */
19
+ export declare function resolveMcporterConfigPath(): string;
20
+ export declare function readMcporterConfig(configPath: string): McporterConfig;
21
+ export declare function writeMcporterConfig(configPath: string, config: McporterConfig): void;
22
+ export interface McporterMergeResult {
23
+ added: string[];
24
+ skipped: string[];
25
+ }
26
+ export declare function mergeMcpServers(existing: McporterConfig, servers: Record<string, McpServerEntry>): {
27
+ config: McporterConfig;
28
+ result: McporterMergeResult;
29
+ };
30
+ type Logger = {
31
+ info: (msg: string) => void;
32
+ warn: (msg: string) => void;
33
+ error: (msg: string) => void;
34
+ };
35
+ export interface McporterSetupOptions {
36
+ configPath?: string;
37
+ dryRun?: boolean;
38
+ logger: Logger;
39
+ }
40
+ export declare function runMcporterSetup(options: McporterSetupOptions): McporterMergeResult;
41
+ export {};
@@ -0,0 +1,102 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ export const OMOC_MCP_SERVERS = {
4
+ 'web-search-prime': {
5
+ url: 'https://api.z.ai/api/mcp/web_search_prime/mcp',
6
+ description: 'Keyword-based web search (news, blogs, general)',
7
+ },
8
+ 'web-reader': {
9
+ url: 'https://api.z.ai/api/mcp/web_reader/mcp',
10
+ description: 'Clean full-page content extraction',
11
+ },
12
+ exa: {
13
+ url: 'https://mcp.exa.ai/mcp?tools=web_search_exa',
14
+ description: 'Semantic web search (Exa)',
15
+ },
16
+ context7: {
17
+ url: 'https://mcp.context7.com/mcp',
18
+ description: 'Library/framework documentation search',
19
+ },
20
+ grep_app: {
21
+ url: 'https://mcp.grep.app',
22
+ description: 'Open-source code search on GitHub',
23
+ },
24
+ zread: {
25
+ url: 'https://api.z.ai/api/mcp/zread/mcp',
26
+ description: 'Direct GitHub repository exploration',
27
+ },
28
+ };
29
+ /**
30
+ * Resolve mcporter config path.
31
+ * Priority: ~/.openclaw/workspace/config/mcporter.json > ~/.config/mcporter/mcporter.json
32
+ */
33
+ export function resolveMcporterConfigPath() {
34
+ const homeDir = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '';
35
+ const openclawPath = path.join(homeDir, '.openclaw', 'workspace', 'config', 'mcporter.json');
36
+ if (fs.existsSync(openclawPath)) {
37
+ return openclawPath;
38
+ }
39
+ const mcporterHomePath = path.join(homeDir, '.config', 'mcporter', 'mcporter.json');
40
+ if (fs.existsSync(mcporterHomePath)) {
41
+ return mcporterHomePath;
42
+ }
43
+ return openclawPath;
44
+ }
45
+ export function readMcporterConfig(configPath) {
46
+ if (!fs.existsSync(configPath)) {
47
+ return { mcpServers: {} };
48
+ }
49
+ const raw = fs.readFileSync(configPath, 'utf-8');
50
+ const parsed = JSON.parse(raw);
51
+ if (!parsed.mcpServers || typeof parsed.mcpServers !== 'object') {
52
+ parsed.mcpServers = {};
53
+ }
54
+ return parsed;
55
+ }
56
+ export function writeMcporterConfig(configPath, config) {
57
+ const dir = path.dirname(configPath);
58
+ if (!fs.existsSync(dir)) {
59
+ fs.mkdirSync(dir, { recursive: true });
60
+ }
61
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
62
+ }
63
+ export function mergeMcpServers(existing, servers) {
64
+ const result = { added: [], skipped: [] };
65
+ const merged = { ...existing, mcpServers: { ...existing.mcpServers } };
66
+ for (const [name, entry] of Object.entries(servers)) {
67
+ if (merged.mcpServers[name]) {
68
+ result.skipped.push(name);
69
+ }
70
+ else {
71
+ merged.mcpServers[name] = { url: entry.url };
72
+ result.added.push(name);
73
+ }
74
+ }
75
+ return { config: merged, result };
76
+ }
77
+ export function runMcporterSetup(options) {
78
+ const { logger, dryRun = false } = options;
79
+ const configPath = options.configPath ?? resolveMcporterConfigPath();
80
+ logger.info(`mcporter config: ${configPath}`);
81
+ const existing = readMcporterConfig(configPath);
82
+ const { config: merged, result } = mergeMcpServers(existing, OMOC_MCP_SERVERS);
83
+ if (result.added.length === 0) {
84
+ logger.info('No changes needed — all MCP servers already configured.');
85
+ return result;
86
+ }
87
+ if (dryRun) {
88
+ logger.info(`[dry-run] Would add ${result.added.length} MCP server(s): ${result.added.join(', ')}`);
89
+ return result;
90
+ }
91
+ if (fs.existsSync(configPath)) {
92
+ const backupPath = configPath + '.bak';
93
+ fs.copyFileSync(configPath, backupPath);
94
+ logger.info(`Backup created: ${backupPath}`);
95
+ }
96
+ writeMcporterConfig(configPath, merged);
97
+ logger.info(`Added ${result.added.length} MCP server(s): ${result.added.join(', ')}`);
98
+ if (result.skipped.length > 0) {
99
+ logger.info(`Skipped ${result.skipped.length} existing server(s): ${result.skipped.join(', ')}`);
100
+ }
101
+ return result;
102
+ }
@@ -27,6 +27,8 @@ export interface MergeResult {
27
27
  added: string[];
28
28
  skipped: string[];
29
29
  updated: string[];
30
+ mcporterAdded?: string[];
31
+ mcporterSkipped?: string[];
30
32
  }
31
33
  export declare function mergeAgentConfigs(existing: Array<{
32
34
  id: string;
@@ -41,6 +43,7 @@ export declare function mergeAgentConfigs(existing: Array<{
41
43
  export declare function applyProviderToConfigs(configs: OmocAgentConfig[], provider: string): OmocAgentConfig[];
42
44
  export declare function runInteractiveSetup(logger: Logger): Promise<{
43
45
  provider: string;
46
+ setupMcporter: boolean;
44
47
  }>;
45
48
  export interface SetupOptions {
46
49
  configPath?: string;
@@ -48,6 +51,8 @@ export interface SetupOptions {
48
51
  force?: boolean;
49
52
  dryRun?: boolean;
50
53
  provider?: string;
54
+ setupMcporter?: boolean;
55
+ mcporterConfigPath?: string;
51
56
  interactive?: boolean;
52
57
  logger: Logger;
53
58
  }
package/dist/cli/setup.js CHANGED
@@ -4,6 +4,7 @@ import * as readline from 'node:readline';
4
4
  import JSON5 from 'json5';
5
5
  import { OMOC_AGENT_CONFIGS } from '../agents/agent-configs.js';
6
6
  import { PROVIDER_PRESETS, PROVIDER_LABELS, AGENT_TIER_MAP, MODEL_TIERS, applyProviderPreset, getProviderNames, buildCustomPreset, registerCustomPreset, } from './model-presets.js';
7
+ import { OMOC_MCP_SERVERS, runMcporterSetup } from './mcporter-setup.js';
7
8
  const CONFIG_FILENAMES = [
8
9
  'openclaw.json5',
9
10
  'openclaw.json',
@@ -148,8 +149,7 @@ function printPreview(logger, provider) {
148
149
  }
149
150
  async function runCustomProviderFlow(rl, logger) {
150
151
  logger.info('');
151
- logger.info(' Enter model IDs for each tier.');
152
- logger.info(' Format: provider/model (e.g., cliproxy/claude-opus-4-6, z.ai/gpt-5.3-codex)');
152
+ logger.info('Step 1/3: Select your AI provider');
153
153
  logger.info('');
154
154
  const tierModels = {};
155
155
  for (const tier of MODEL_TIERS) {
@@ -211,17 +211,29 @@ export async function runInteractiveSetup(logger) {
211
211
  logger.info('');
212
212
  logger.info(` ✓ Selected: ${PROVIDER_LABELS[provider] ?? 'Custom'}`);
213
213
  logger.info('');
214
- logger.info('Step 2/2: Model configuration preview');
214
+ logger.info('Step 2/3: Model configuration preview');
215
215
  logger.info('');
216
216
  printPreview(logger, provider);
217
217
  logger.info('');
218
218
  const confirm = await askQuestion(rl, ' Apply this configuration? (Y/n): ');
219
219
  if (confirm.toLowerCase() === 'n' || confirm.toLowerCase() === 'no') {
220
220
  logger.info(' Setup cancelled.');
221
- return { provider: '' };
221
+ return { provider: '', setupMcporter: false };
222
222
  }
223
223
  logger.info('');
224
- return { provider };
224
+ logger.info('Step 3/3: Web search MCP servers');
225
+ logger.info('');
226
+ logger.info(' OmOC agents use mcporter MCP servers for web search,');
227
+ logger.info(' documentation lookup, and code search:');
228
+ logger.info('');
229
+ for (const [name, entry] of Object.entries(OMOC_MCP_SERVERS)) {
230
+ logger.info(` ${name}: ${entry.description}`);
231
+ }
232
+ logger.info('');
233
+ const mcpConfirm = await askQuestion(rl, ' Set up these MCP servers? (Y/n): ');
234
+ const setupMcporter = mcpConfirm.toLowerCase() !== 'n' && mcpConfirm.toLowerCase() !== 'no';
235
+ logger.info('');
236
+ return { provider, setupMcporter };
225
237
  }
226
238
  finally {
227
239
  rl.close();
@@ -281,6 +293,17 @@ export function runSetup(options) {
281
293
  if (result.added.length === 0 && result.updated.length === 0) {
282
294
  logger.info('No changes needed — all OmOC agents already present.');
283
295
  }
296
+ if (options.setupMcporter) {
297
+ logger.info('');
298
+ logger.info('Setting up mcporter MCP servers...');
299
+ const mcpResult = runMcporterSetup({
300
+ configPath: options.mcporterConfigPath,
301
+ dryRun,
302
+ logger,
303
+ });
304
+ result.mcporterAdded = mcpResult.added;
305
+ result.mcporterSkipped = mcpResult.skipped;
306
+ }
284
307
  return result;
285
308
  }
286
309
  export function registerSetupCli(ctx) {
@@ -299,11 +322,13 @@ export function registerSetupCli(ctx) {
299
322
  const valid = getProviderNames().join(', ');
300
323
  throw new Error(`Unknown provider "${provider}". Valid: ${valid}`);
301
324
  }
325
+ let setupMcporter = false;
302
326
  if (!provider && process.stdin.isTTY) {
303
327
  const result = await runInteractiveSetup(ctx.logger);
304
328
  if (!result.provider)
305
329
  return;
306
330
  provider = result.provider;
331
+ setupMcporter = result.setupMcporter;
307
332
  }
308
333
  runSetup({
309
334
  configPath: opts.config,
@@ -311,6 +336,7 @@ export function registerSetupCli(ctx) {
311
336
  force: provider ? true : opts.force,
312
337
  dryRun: opts.dryRun,
313
338
  provider,
339
+ setupMcporter,
314
340
  logger: ctx.logger,
315
341
  });
316
342
  ctx.logger.info('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happycastle/oh-my-openclaw",
3
- "version": "0.13.2",
3
+ "version": "0.13.3",
4
4
  "description": "Oh-My-OpenClaw plugin — multi-agent orchestration, todo enforcer, ralph loop, and custom tools for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",