<p align="center">
  <img src="./icon.svg" alt="Popup MCP Logo" width="200"/>
</p>

<br/>

# Popup MCP

> 让 AI Agent 能够“看见”你 —— 一个赋予 LLM 弹窗交互能力的 MCP Server。

`popup-mcp` 是一个基于 **Tauri + Vue** 构建的 MCP (Model Context Protocol) Server。它弥补了 Agent 在本地运行时缺乏即时用户反馈机制的短板，以原生窗口的形式提供类似于 Claude Code 的交互体验。

当你的 Agent 需要人类确认敏感操作、从用户处获取具体参数时，`popup-mcp` 能够优雅地唤起弹窗并等待用户输入。

---

## ✨ 核心特性

- **交互式问卷 (`ask_user_question`)**：
  - 支持单选、多选列表。
  - 支持开放式文本输入。
  - 支持取消操作，并允许用户在提交时附带额外上下文信息。
- **安全确认 (`confirm`)**：
  - 适用于敏感操作（如文件删除、代码执行）前的二次确认。
  - 支持自定义确认/取消文案及默认选项。
  - 危险操作警示样式。
- **原生体验**：
  - 支持键盘快捷键操作，效率至上。
  - 跨平台支持（Windows/macOS/Linux）。
  - 界面语言支持中/英切换。

## 📸 效果展示

### 1. 交互式问卷 (Questionnaire)

Agent 可以通过问卷收集用户的意图或配置信息。

|     场景     |                     截图                      |
| :----------: | :-------------------------------------------: |
| **基础提问** | ![问卷窗口1](./assets/ask_user_question1.png) |
| **多选模式** | ![问卷窗口2](./assets/ask_user_question2.png) |
| **提交界面** | ![问卷窗口3](./assets/ask_user_question3.png) |
| **取消窗口** | ![问卷窗口4](./assets/ask_user_question4.png) |

### 2. 操作确认 (Confirmation)

![确认窗口](./assets/confirm.png)

---

## 🚀 快速开始

### 系统要求

- **操作系统**:
  - Windows 11 x64 (其他 Windows 版本未充分验证)
  - macOS x64/arm64 (实验性)
  - Linux x64 (实验性)
- **网络**: 首次下载二进制文件需要访问 GitHub Releases。

> 说明：CI 会在 macOS/Linux/Windows 上构建发布产物，但不代表已在对应系统完成完整运行验证。

### 安装（二进制）

1. 从 GitHub Releases 下载与你平台匹配的发布包（推荐下载压缩包）：
   - Windows: `popup-mcp-v<版本>-windows-x86_64.zip`
   - macOS: `popup-mcp-v<版本>-macos-x64.tar.gz` / `popup-mcp-v<版本>-macos-arm64.tar.gz`
   - Linux: `popup-mcp-v<版本>-linux-x86_64.tar.gz`
2. 解压后得到可执行文件：
   - Windows（PowerShell）：`Expand-Archive popup-mcp-v<版本>-windows-x86_64.zip -DestinationPath .`
   - macOS/Linux：`tar -xzf popup-mcp-v<版本>-macos-x64.tar.gz`（按需替换为对应文件名）
3. 将可执行文件放到你希望的位置（任意目录均可），并记住它的绝对路径。
4. macOS/Linux 如遇到权限问题，赋予可执行权限：
   - `chmod +x popup-mcp`

> 说明：Release 同时提供“裸二进制”（例如 `popup-mcp-v<版本>-macos-x64` / `popup-mcp-v<版本>-windows-x86_64.exe`）和压缩包；无需放入固定目录，直接在 MCP 配置里把 `command` 设置为该可执行文件的完整路径即可。

### MCP 客户端配置

请将以下配置添加到你使用的 MCP Client (如 Claude Desktop, Codex CLI 等) 的配置文件中。

#### 1. 通用配置 (Claude Desktop 等)

```json
{
  "mcpServers": {
    "popup": {
      "command": "C:\\\\path\\\\to\\\\popup-mcp.exe",
      "args": []
    }
  }
}
```

> macOS/Linux 示例：`"command": "/abs/path/to/popup-mcp"`。

#### 2. Codex CLI 配置

```toml
[mcp_servers.popup-mcp]
command = "C:\\path\\to\\popup-mcp.exe"
type = "stdio"
args = []
# 建议设置较长的超时时间，防止用户思考或输入时连接断开
tool_timeout_sec = 3600
```

> macOS/Linux 示例：`command = "/abs/path/to/popup-mcp"`。

#### 3. WSL 环境配置

由于 WSL 无法直接调用 Windows 的 GUI 窗口，需要通过 `mcp-proxy` 进行转发。

1.  **在 Windows 端 (PowerShell)** 运行代理服务：

    ```powershell
    npx -y mcp-proxy --port 3333 "C:\\path\\to\\popup-mcp.exe" -- --header-text Codex
    ```

    > 提示：当 `mcp-proxy` 退出（例如 Ctrl+C）或连接断开时，`popup-mcp` 会随 stdio 连接结束而自动退出，避免残留后台进程。

2.  **在 WSL 端** 配置 MCP Client：
    ```toml
    [mcp_servers.popup-mcp]
    # 尝试连接宿主机代理
    url = "http://localhost:3333/mcp" # 部分客户端可能需要使用 "/sse" 后缀
    tool_timeout_sec = 3600
    ```

### ⚙️ 启动参数

你可以通过 `args` 传递以下参数来自定义行为：

| 参数                   | 类型         | 默认值     | 说明                                     |
| :--------------------- | :----------- | :--------- | :--------------------------------------- |
| `--ui-lang`            | `zh` \| `en` | 系统语言   | 强制指定界面语言                         |
| `--ui-timeout-seconds` | `number`     | 0 (无超时) | UI 等待用户操作的最大时长（秒）          |
| `--header-text`        | `string`     | 空字符串   | 覆盖顶部栏工具名；空或仅空格时回退默认值 |

### 💡 Prompt 最佳实践

虽然工具定义了参数结构，但 LLM 并不总是知道何时该调用此工具。建议在 System Prompt 中显式加入如下类似的指引：

```
## 交互工具与使用优先级

- 交互优先原则：除非需求已完全清晰且风险极低，一旦出现不确定性、重要权衡或多方案选择，应优先调用交互工具，而不是直接假设用户意图。

- `ask_user_question`
  - 用途：进行结构化提问，收集用户决策和反馈。
  - 场景：在设计模式中收集方案偏好，在规划模式中确认实施细节，在执行模式中澄清不确定需求或边界。
  - 要求：采用多轮提问，逐步收敛需求。
  - **必须使用场景：存在模糊需求和隐含假设，或需要向用户提问、确认**
  - **使用方式：循环提问，直到需求完全明确**
- `confirm_action`
  - 用途：获取用户明确的确认或拒绝。
  - 场景：要进行潜在高风险操作（如大规模重构、批量重命名/删除文件）之前，向用户进行确认。

```

在**多 Agent 并行调用**或多个工具共用同一 `popup-mcp` 实例的场景下，推荐在提示词中同时约定好 `topic` 参数的使用，用于区分窗口主题/来源：

- `ask-user-question` / `confirm-action` 的参数中都支持必填字段 `topic: string`，会显示在弹窗顶部标题栏中（例如 `Popup MCP • Code Review Agent`）。

这样，即使多个 Agent 同时使用本 MCP，用户也能从窗口顶栏一眼看出当前弹窗是由哪一个 Agent 发起的。

---

## 🛠️ 贡献指南

欢迎提交 Issue 和 PR！如果你想参与开发，请参考以下步骤。

### 环境准备

确保本地已安装：

- Node.js >= 18 & pnpm
- Rust (最新稳定版)
- Tauri CLI（本项目使用 `@tauri-apps/cli`，随 `pnpm install` 安装；通常无需额外全局安装）

> Linux 开发/构建时，可能还需要系统依赖（与 CI 一致），例如：`libwebkit2gtk-4.1-dev`、`libappindicator3-dev`、`librsvg2-dev`、`patchelf`、`libssl-dev`。

### 安装与构建

```bash
# 1. 安装前端依赖
pnpm install

# 2. 初始化 Rust 依赖 (Tauri 会自动处理)
# 此时可以检查 src-tauri 目录下的环境
```

### 项目结构概览

- `src/`: 前端源码 (Vue 3)
- `src-tauri/`: 后端源码 (Rust)
  - `src/mcp/`: **核心逻辑**，处理 MCP 协议与 Tauri Window 的通信
- `OVERVIEW.md`: 📜 **详细架构说明** (推荐阅读)

### 开发调试

启动开发模式后，支持热重载：

```bash
pnpm tauri dev
```

启动后，焦点可能会聚焦到被隐藏的窗口上。按 `Ctrl + Shift + I` (Windows/Linux) 或 `Cmd + Option + I` (macOS) 打开开发者工具。你可以直接在 Console 中执行以下代码模拟后端发往前端的 `ui:request` 事件，用于预览 UI 样式：

```javascript
const { window: tauriWin, event: tauriEvent } = window.__TAURI__;
const win = tauriWin.getCurrentWindow();

// 显示窗口
await win.show();

// 统一事件通道：ui:request
await tauriEvent.emit("ui:request", {
  id: "dev-question",
  kind: "ask-user-question",
  request: {
    topic: "开发调试",
    questions: [
      {
        question: "Detect environment?",
        header: "Env Check",
        multiSelect: false,
        options: [
          { label: "Production", description: "Live environment" },
          { label: "Development", description: "Local testing" },
        ],
      },
    ],
  },
});

await tauriEvent.emit("ui:request", {
  id: "dev-confirm",
  kind: "confirm-action",
  request: {
    title: "确认示例",
    message: "用来检查 confirm 卡片样式",
    confirmLabel: "OK",
    cancelLabel: "再看看",
    danger: false,
    defaultConfirm: true,
    topic: "开发调试",
  },
});
```

> 说明：上述方式仅用于前端样式调试；因为没有对应的后端等待队列，点击按钮后产生的结果不会返回到 MCP 调用方。

---

## 📄 许可证

MIT License
