Skip to content

feat(mcp): add built-in image generation MCP server#1243

Merged
piorpua merged 8 commits intoiOfficeAI:mainfrom
Castor6:feat/image-generation-mcp
Mar 19, 2026
Merged

feat(mcp): add built-in image generation MCP server#1243
piorpua merged 8 commits intoiOfficeAI:mainfrom
Castor6:feat/image-generation-mcp

Conversation

@Castor6
Copy link
Copy Markdown
Contributor

@Castor6 Castor6 commented Mar 10, 2026

Summary

  • Replace the Gemini-specific image generation tool with a unified built-in MCP server that works across all agent types (Claude Code, Codex, Gemini)
  • Extract shared image generation logic into a reusable core module (imageGenCore.ts) and expose it as a stdio MCP server
  • Auto-inject the built-in MCP server into ACP sessions based on agent MCP capabilities
  • Move the image generation toggle back to the Tools panel and hide the built-in MCP entry from the MCP management list

Changes

  • src/process/builtinMcp/imageGenServer.ts (new): Standalone stdio MCP server that reads provider config from environment variables and exposes an aionui_image_generation tool for text-to-image generation and image editing
  • src/process/builtinMcp/imageGenCore.ts (new): Shared image generation logic (URI processing, base64 encoding, API call, image saving) used by both the MCP server and the legacy Gemini tool
  • src/process/builtinMcp/constants.ts (new): Constants and helpers for identifying the built-in image generation server (BUILTIN_IMAGE_GEN_ID, isBuiltinImageGenName, isBuiltinImageGenTransport)
  • src/agent/acp/mcpSessionConfig.ts (new): Build ACP session MCP server configs from storage; parse MCP capabilities from ACP session responses; filter built-in servers by agent capability support (stdio/http/sse)
  • src/agent/acp/index.ts: Inject built-in MCP servers into ACP session/create and session/load requests based on parsed MCP capabilities
  • src/agent/acp/AcpConnection.ts: Pass MCP session servers through to session create/load payloads
  • src/agent/gemini/cli/tools/img-gen.ts: Refactor to delegate to shared executeImageGeneration from imageGenCore.ts, removing ~400 lines of duplicated logic
  • src/process/initStorage.ts: Auto-provision the built-in image generation MCP server entry on first launch; migrate legacy server names to the new canonical name
  • src/process/services/mcpServices/agents/ClaudeMcpAgent.ts: Filter out the built-in image gen server from user MCP list (injected separately via ACP session config)
  • src/process/services/mcpServices/agents/CodexMcpAgent.ts: Same filtering for Codex MCP agent
  • src/process/task/GeminiAgentManager.ts: Integrate built-in MCP toggle check for Gemini image generation availability
  • src/common/storage.ts: Add builtin flag to IMcpServer type
  • src/renderer/components/SettingsModal/contents/ToolsModalContent.tsx: Add image generation on/off toggle that enables/disables the built-in MCP server; sync model config to MCP server env vars; hide built-in server from MCP list
  • src/renderer/pages/settings/McpManagement/index.tsx: Filter built-in image gen server from the visible MCP server list
  • src/renderer/pages/settings/McpManagement/McpServerHeader.tsx: Show "Built-in" badge for built-in MCP servers
  • src/renderer/hooks/useConfigModelListWithImage.ts: Expand image model name matching to include imagine pattern
  • electron.vite.config.ts: Add builtin-mcp-image-gen entry point for the standalone MCP server bundle
  • tests/unit/acpBuiltinMcp.test.ts (new): Tests for mcpSessionConfig — capability parsing, server filtering, transport mapping
  • tests/unit/claudeMcpAgent.test.ts (new): Tests for built-in server filtering in Claude MCP agent
  • tests/unit/codexMcpAgent.test.ts (new): Tests for built-in server filtering in Codex MCP agent

Test plan

  • Verify image generation toggle appears in Settings > Tools panel
  • Verify toggling image generation on/off enables/disables the built-in MCP server for all agents
  • Verify the built-in MCP server entry is hidden from the MCP management list
  • Verify image generation works with Claude Code via ACP session MCP injection
  • Verify image generation works with Codex via ACP session MCP injection
  • Verify image generation still works with Gemini (legacy path via shared core)
  • Verify changing the image generation model in Tools panel syncs env vars to the MCP server
  • Verify first-launch auto-provisioning creates the built-in MCP server entry
  • Run bun run test — all unit tests pass including new acpBuiltinMcp, claudeMcpAgent, codexMcpAgent tests

@IceyLiu
Copy link
Copy Markdown
Collaborator

IceyLiu commented Mar 10, 2026

@Castor6 卷得很嗷

@IceyLiu
Copy link
Copy Markdown
Collaborator

IceyLiu commented Mar 12, 2026

1、问题1 Codex会调用失败
935a9571218a63cefacb9b045dc817b4

2、我需要把Agent注册结果回显
image

@Castor6

@IceyLiu
Copy link
Copy Markdown
Collaborator

IceyLiu commented Mar 19, 2026

@piorpua 冲突解决后可合入

…on-mcp

# Conflicts:
#	src/agent/acp/AcpConnection.ts
#	src/agent/acp/index.ts
#	src/agent/gemini/cli/tools/img-gen.ts
#	src/common/ApiKeyManager.ts
#	src/process/services/mcpServices/agents/CodexMcpAgent.ts
#	src/renderer/components/SettingsModal/contents/ToolsModalContent.tsx
#	src/renderer/pages/settings/McpManagement/McpServerHeader.tsx
#	src/renderer/pages/settings/McpManagement/index.tsx
@Castor6
Copy link
Copy Markdown
Contributor Author

Castor6 commented Mar 19, 2026

@piorpua 佬冲突已解决,麻烦看看哈

@sentry
Copy link
Copy Markdown

sentry bot commented Mar 19, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/agent/acp/AcpConnection.ts 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@piorpua
Copy link
Copy Markdown
Contributor

piorpua commented Mar 19, 2026

Code Review:feat(mcp): add built-in image generation MCP server (#1243)

变更概述

本 PR 将 Gemini 专有的图片生成工具重构为统一的内置 MCP 服务器,提取共享核心逻辑到 imageGenCore.ts,通过 ACP session/new 注入机制支持 Claude Code、Codex、Gemini 三类 Agent。同时将图片生成开关迁移回 Tools 面板,并从 MCP 管理列表中隐藏内置 MCP 条目。


方案评估

结论:✅ 方案合理

整体方案正确解决了图片生成工具跨 Agent 统一的问题,利用 MCP 标准协议实现了解耦。拆分 imageGenCore.ts 避免了重复逻辑,ACP capabilities 感知注入的设计也较为优雅。方案与项目架构整体一致,通过 env var 传递 provider 配置给 stdio 进程是 MCP 标准实践。主要的设计盲点是 imageGenCore.ts 放在 @process/ 目录下却被 worker 直接引用,以及核心逻辑缺乏单元测试。


问题清单

🔴 CRITICAL — initStorage.ts 直接变异 existing 对象,违反不可变性原则

文件src/process/initStorage.ts,第 88–113 行

问题代码

if (needsNameMigration) {
  existing.name = BUILTIN_IMAGE_GEN_NAME;  // 变异!
  changed = true;
}
if (existing.transport.type === 'stdio' && existing.transport.command === 'node') {
  if (currentArgs[0] !== scriptPath || needsNameMigration) {
    existing.transport.args = [scriptPath];      // 变异!
    existing.originalJson = buildOriginalJson(...);  // 变异!
    existing.updatedAt = now;                    // 变异!
    changed = true;
  }
}
if (shouldEnable && !existing.enabled) {
  existing.enabled = true;   // 变异!
  existing.transport = { ... };  // 变异!
  existing.updatedAt = now;  // 变异!
  changed = true;
}

问题说明:多处直接在 mcpServers[existingIdx] 引用上原地修改字段,违反项目 CRITICAL 不可变性原则。

修复建议

if (existingIdx >= 0) {
  const existing = mcpServers[existingIdx];
  const displayName = needsNameMigration ? BUILTIN_IMAGE_GEN_NAME : existing.name;
  const needsPathUpdate = existing.transport.type === 'stdio' &&
    existing.transport.command === 'node' &&
    ((existing.transport.args || [])[0] !== scriptPath || needsNameMigration);

  const needsMigration = shouldEnable && !existing.enabled;

  if (needsPathUpdate || needsMigration) {
    const updatedTransport = {
      ...existing.transport,
      ...(needsPathUpdate && { args: [scriptPath] }),
      ...(needsMigration && {
        env: { ...(existing.transport as { env?: Record<string, string> }).env, ...buildEnvFromConfig(oldConfig) },
      }),
    };

    mcpServers[existingIdx] = {
      ...existing,
      name: displayName,
      transport: updatedTransport,
      originalJson: needsPathUpdate ? buildOriginalJson(scriptPath, updatedTransport.env || {}) : existing.originalJson,
      enabled: needsMigration ? true : existing.enabled,
      updatedAt: now,
    };
    changed = true;
  }
}

🔴 CRITICAL — img-gen.ts 在方法内部动态 require('fs')

文件src/agent/gemini/cli/tools/img-gen.ts,第 ~130 行

问题代码

const fs = require('fs');
for (let i = 0; i < imageUris.length; i++) {

问题说明:重构时删除了顶部的 import * as fs from 'fs',却在方法内部用动态 require。这会绕过 TypeScript 类型检查,在每次调用时触发同步的模块查找(尽管 Node.js 有缓存),且破坏 ESM 兼容性。

修复建议:恢复顶层静态 import:

// 文件顶部
import * as fs from 'fs';

然后删除方法内的 const fs = require('fs'); 这一行。


🟠 HIGH — imageGenCore.ts 318 行核心逻辑无任何单元测试

文件src/process/builtinMcp/imageGenCore.ts

问题说明imageGenCore.ts 是两个执行路径(MCP 服务器 + Gemini 工具)共享的核心模块,包含 safeJsonParseprocessImageUrisaveGeneratedImageexecuteImageGeneration 等多个边界复杂的函数。PR 新增了 acpBuiltinMcp.test.tsclaudeMcpAgent.test.tscodexMcpAgent.test.ts 三个测试文件,但均未覆盖 imageGenCore.ts。关键场景缺失:

  • processImageUri 对相对路径/绝对路径/HTTP URL 的处理
  • safeJsonParse 的 jsonrepair fallback 分支
  • executeImageGeneration 无图片响应时的 warning 分支
  • saveGeneratedImage 写文件失败时的错误处理

此外,imageGenCore.ts 未被加入 vitest.config.tscoverage.include,覆盖率门控对该文件失效。

修复建议:新建 tests/unit/imageGenCore.test.ts,至少覆盖:

describe('safeJsonParse', () => {
  it('returns fallback for non-JSON string')
  it('repairs and parses malformed JSON')
})

describe('processImageUri', () => {
  it('returns image_url object for http URL directly')
  it('resolves relative path against workspaceDir')
  it('throws for non-image file extension')
  it('throws with search paths when file not found')
})

describe('executeImageGeneration', () => {
  it('returns cancelled result when signal is already aborted')
  it('returns warning when API returns no images')
})

同时将 src/process/builtinMcp/imageGenCore.ts 加入 vitest.config.tscoverage.include


🟡 MEDIUM — 架构越界:agent worker 导入 @process/builtinMcp/imageGenCore

文件src/agent/gemini/cli/tools/img-gen.ts,第 18 行

问题代码

import { executeImageGeneration, safeJsonParse, isImageFile, isHttpUrl } from '@/process/builtinMcp/imageGenCore';

问题说明src/agent/gemini/ 代码在 worker 进程中运行,src/process/ 按项目约定是主进程目录。imageGenCore.ts 本身不使用 Electron API(当前安全),但放在 @process/ 下的模块可能随时被添加主进程依赖。若未来有人在 imageGenCore.ts 中引入 Electron API,worker 将悄然崩溃。

修复建议:将 imageGenCore.ts 移至 src/common/imageGenCore.ts(纯 Node.js 逻辑,适合多进程共享),在 img-gen.ts 中改为从 @/common/imageGenCore 导入。


🔵 LOW — BUILTIN_IMAGE_GEN_ID 在渲染层重复定义

文件src/renderer/components/SettingsModal/contents/ToolsModalContent.tsx:31src/renderer/pages/settings/McpManagement/index.tsx:23

问题代码

// ToolsModalContent.tsx
const BUILTIN_IMAGE_GEN_ID = 'builtin-image-gen';

// McpManagement/index.tsx
const BUILTIN_IMAGE_GEN_ID = 'builtin-image-gen';

问题说明:渲染层两处各自硬编码了相同的字符串常量,与 src/process/builtinMcp/constants.ts 中的 BUILTIN_IMAGE_GEN_ID 完全重复。若 ID 将来变更,三处需要同步修改。建议将该常量提升到 src/common/storage.tssrc/common/builtinMcpConstants.ts,渲染层和主进程均从该公共位置导入。


汇总

# 严重级别 文件 问题
1 🔴 CRITICAL src/process/initStorage.ts:88-113 直接变异 existing 对象,违反不可变性原则
2 🔴 CRITICAL src/agent/gemini/cli/tools/img-gen.ts:~130 方法内动态 require('fs'),应为顶层 import
3 🟠 HIGH src/process/builtinMcp/imageGenCore.ts 318 行核心逻辑无单元测试,未加入 coverage.include
4 🟡 MEDIUM src/agent/gemini/cli/tools/img-gen.ts:18 worker 导入 @process/ 代码,架构越界
5 🔵 LOW ToolsModalContent.tsx:31, McpManagement/index.tsx:23 BUILTIN_IMAGE_GEN_ID 重复定义,建议提取到公共位置

结论

需要修改 — 存在 2 个阻塞性问题(不可变性违反 + 动态 require),以及核心逻辑无测试覆盖的高优先级问题,需处理后合并。


本报告由本地 pr-review skill 生成,包含完整项目上下文,无截断限制。

@piorpua piorpua merged commit 1e07b0c into iOfficeAI:main Mar 19, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants