sap-extension-creator · git:20260503.745c34d · 2026-05-03 · sha256 b7a857147a87d025

sap-extension-creator git:20260503.745c34dA

Immutable. This exact content never changes and is served at /api/v1/blob/b7a857147a87d025.

---
name: sap-extension-creator
description: Create Super Agent Party (SAP) extensions. This skill should be used when users want to create, build, or scaffold a new extension for Super Agent Party - including static HTML extensions (pure frontend) and Node.js backend extensions. Triggers on requests like "create a new SAP extension", "build an extension for Super Agent Party", "scaffold a plugin", "make a chat UI extension", or when working with sap extension projects.
---

# SAP Extension Creator

## Overview

Create Super Agent Party extensions—self-contained packages that extend the platform with custom chat UI and tools. Two modes are supported:

- **Static extension**: Pure HTML/CSS/JS frontend, no backend
- **Node.js extension**: Full-stack with Express backend, auto-managed by SAP

## Quick Decision Tree

```
User wants to create an extension?
├─ Only needs UI (chat, display, simple interactions)? → Static Extension
├─ Needs backend logic (API calls, DB, file processing)? → Node.js Extension
└─ Needs to register custom tools for AI agent? → Node.js Extension (with MCP)
```

## Core Files Every Extension Needs

| File | Required | Purpose |
|------|----------|---------|
| `package.json` | ✅ | Metadata, dependencies, tool declarations |
| `index.html` | ✅ | Main UI (full HTML page) |
| `index.js` | Node only | Node.js entry point |
| `node_modules/` | Node only | Auto-installed by SAP via `npm install` |

## Workflow

### Step 1: Gather Requirements

Ask the user:

1. **Extension name?** (hyphen-case, e.g., `my-weather-widget`)
2. **Description?** (one sentence)
3. **Static or Node.js?**
4. **For Node.js: what npm dependencies?**
5. **Should it register custom tools for the AI?** (e.g., close_extension, fetch_data, etc.)
6. **GitHub repository URL?** (optional, for updates)
7. **Transparent window?** (frameless, always-on-top — for mini widgets like music controllers)
8. **Default window size?** (width/height in pixels)

### Step 2: Scaffold the Extension

Use the templates in `assets/` as starting points:

- **Static**: Copy `assets/static-template/`
- **Node.js**: Copy `assets/node-template/`

Create the extension directory under the workspace (user will later install it into SAP's `extensions/` folder).

### Step 3: Write package.json

See `references/package-json-spec.md` for the complete field reference. Minimum:

```json
{
  "name": "my-extension",
  "version": "1.0.0",
  "description": "What it does",
  "author": "your-name",
  "repository": "https://github.com/user/repo",
  "backupRepository": "https://gitee.com/user/repo",
  "category": "Tools"
}
```

For Node.js extensions, also include:
```json
{
  "main": "index.js",
  "nodePort": 0,
  "dependencies": { "express": "^5.1.0" }
}
```

For transparent/frameless widgets (e.g., mini music controllers, floating panels):
```json
{
  "transparent": true,
  "width": 280,
  "height": 80
}
```

When `transparent: true`, SAP creates a frameless, transparent, always-on-top window (see main.js `open-extension-window` handler). Use this for compact overlay widgets.

### Step 4: Write index.html

The HTML page is rendered inside the SAP window (directly or in an iframe). Key patterns:

- **Dark theme**: Use CSS variables matching SAP's dark theme (see template)
- **WebSocket connection**: Connect to `ws://host/ws` for messaging
- **Get extension ID**: Parse `window.location.pathname` for `/extensions/{ext_id}/`
- **Message rendering**: Listen for `messages_update` and `broadcast_messages` events
- **Send user input**: Send `set_user_input` then `trigger_send_message`

### Step 5: Write index.js (Node.js only)

See `references/node-entry-spec.md` for the full protocol. The entry point:

1. Receives a port number via `process.argv[2]`
2. Starts an Express server on that port at `127.0.0.1`
3. Serves static files from its own directory
4. Exposes a `/health` endpoint for readiness checks
5. SAP reverse-proxies requests to the extension

### Step 6: Implement Tool Registration (optional, Node.js only)

Extensions can register tools that the AI agent can call. Register via WebSocket:

```js
ws.send(JSON.stringify({
    type: 'register_node_extension_mcp',
    data: {
        ext_id: extId,
        tools: [{
            name: `${extId}_my_tool`,
            description: 'What this tool does',
            parameters: {
                type: 'object',
                properties: {
                    param1: { type: 'string', description: '...' }
                },
                required: ['param1']
            }
        }]
    }
}));
```

Handle incoming tool calls:
```js
if (d.type === 'call_mcp_tool') {
    // Execute tool, then:
    ws.send(JSON.stringify({
        type: 'mcp_tool_result',
        data: { call_id: d.data.call_id, result: 'output' }
    }));
}
```

## Responsive Design

Every extension should work well across different window sizes since SAP extensions can be resized or opened at different dimensions. Critical patterns:

### Viewport Meta (REQUIRED)

```html
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
```

### CSS Media Queries

Use breakpoints to adapt layout at small sizes:

```css
/* Tablet / medium windows */
@media (max-width: 900px) {
  /* stack layouts vertically, reduce padding */
}

/* Phone / very small windows */
@media (max-width: 600px) {
  /* hide secondary elements, compact controls */
}
```

Key responsive practices:
- Use `vw` units for widths as fallback (e.g., `width: 65vw; max-width: 360px`)
- Use `flex` layouts that naturally wrap
- Hide non-essential elements on small screens (`display: none`)
- Reduce font sizes and padding at breakpoints

## Transparent Window / Compact Mode

When `transparent: true` is set in package.json, SAP creates a frameless transparent window. The extension must implement **compact mode** to work correctly.

### How SAP Creates Transparent Windows

From `main.js`, when `extension.transparent` is true:

```js
{
  frame: false,
  transparent: true,
  alwaysOnTop: true,
  skipTaskbar: false,
  hasShadow: false,
  backgroundColor: 'rgba(0, 0, 0, 0)',
}
```

### Compact Mode CSS (REQUIRED for transparent extensions)

The template includes compact mode that activates when the window height is small (< 200px). Key patterns:

```css
/* 1. Transparent backgrounds */
body.compact { background: transparent !important; }
html.compact { background: transparent !important; }

/* 2. Drag regions — make header/footer draggable for frameless windows */
body.compact header,
body.compact #inputBar {
  -webkit-app-region: drag;
}

/* 3. Interactive elements MUST opt-out of drag */
body.compact button,
body.compact input,
body.compact textarea,
body.compact .compact-close-btn {
  -webkit-app-region: no-drag;
}

/* 4. Compact close button (red circle, top-right) */
.compact-close-btn { display: none; }
body.compact .compact-close-btn {
  display: flex;
  position: absolute;
  top: 5px; right: 5px;
  width: 20px; height: 20px;
  background: rgb(255, 57, 57);
  border: none; border-radius: 50%;
  color: #fff;
  align-items: center; justify-content: center;
  font-size: 10px; cursor: pointer;
  transition: 0.2s;
  z-index: 100;
  -webkit-app-region: no-drag;
}
body.compact .compact-close-btn:hover { background: #ec4141; }

/* 5. Compact layout — hide/show elements */
body.compact header { padding: 0 12px; height: 44px; }
body.compact header span { display: none; }
```

### Compact Mode Detection (REQUIRED)

```js
function checkCompactMode() {
  if (window.innerHeight < 200) {
    document.documentElement.classList.add('compact');
    document.body.classList.add('compact');
  } else {
    document.documentElement.classList.remove('compact');
    document.body.classList.remove('compact');
  }
}

function closeWindow() { window.close(); }

checkCompactMode();
window.addEventListener('resize', checkCompactMode);
```

### Placing the Close Button

The close button HTML must be placed in the body (not inside other containers), typically right after `<body>`:

```html
<body>
  <button class="compact-close-btn" onclick="closeWindow()" title="关闭窗口">
    <i class="fa-solid fa-xmark"></i>
  </button>
  <!-- rest of content -->
</body>
```

## Using iframes for Custom Schemes

If your extension needs to invoke custom protocol URLs (e.g., `lxmusic://`, `myapp://`), use a hidden iframe technique:

```js
function invokeScheme(url) {
  let iframe = document.getElementById('scheme-invoker');
  if (!iframe) {
    iframe = document.createElement('iframe');
    iframe.id = 'scheme-invoker';
    iframe.style.display = 'none';
    document.body.appendChild(iframe);
  }
  iframe.src = url;
}
```

This avoids `window.open()` popup blockers and works reliably inside Electron.

## WebSocket Protocol Reference

| Message Type | Direction | Purpose |
|---|---|---|
| `get_messages` | → SAP | Request current message history |
| `messages_update` | ← SAP | Message list updated |
| `broadcast_messages` | ← SAP | Broadcast message update |
| `set_user_input` | → SAP | Update user input text |
| `trigger_send_message` | → SAP | Send current input as user message |
| `trigger_clear_message` | → SAP | Clear all messages |
| `register_node_extension_mcp` | → SAP | Register MCP tools |
| `unregister_node_extension_mcp` | → SAP | Unregister on page close |
| `mcp_registered` | ← SAP | Confirmation of registration |
| `call_mcp_tool` | ← SAP | AI agent calls a registered tool |
| `mcp_tool_result` | → SAP | Return tool execution result |
| `trigger_close_extension` | → SAP | Request extension window close |

## Important Notes

- **Extension ID format**: `{owner}_{repo}` (e.g., `heshengtao_sap-example`)
- **nodePort: 0** means auto-assign a free port
- **Always register `beforeunload` handler** to send `unregister_node_extension_mcp`
- **Font Awesome** is available at `../../fontawesome/css/all.min.css` (relative from extension path)
- **SAP dark theme colors**: bg `#1e1f20`, assistant bubble `#25262a`, accent `#00c2a8`
- **Transparent windows**: Always implement compact mode. Without `-webkit-app-region: drag`, frameless windows cannot be moved. Without `-webkit-app-region: no-drag` on interactive elements, buttons become unclickable.
- **Close button**: For transparent/frameless windows, the extension MUST provide its own close button since there's no native title bar.

## Resources

### assets/
- `assets/static-template/` — Complete starter template for static extensions
- `assets/node-template/` — Complete starter template for Node.js extensions

### references/
- `references/package-json-spec.md` — Complete package.json field reference
- `references/node-entry-spec.md` — Node.js entry point and lifecycle specification