<!-- Source: https://docs.biosimulant.com/how-to/desktop/build-desktop-plugin -->

# How to Build a Desktop Plugin

Extend the Biosimulant Desktop app with custom tools or AI agents. Plugins are standalone processes that communicate with the desktop app via JSON-RPC 2.0 over stdin/stdout.

## Plugin kinds

| Kind | Use Case |
|------|----------|
| `tool_provider` | Utility tools, data processing, custom commands |
| `agent_provider` | AI agents that can run sessions, send turns, handle approvals |

## Step 1: Create the manifest

Create a `plugin.json`:

```json
{
  "id": "com.example.my-tool",
  "name": "My Tool",
  "version": "0.1.0",
  "description": "A custom tool for Biosimulant Desktop",
  "kind": "tool_provider",
  "entrypoint": "python main.py",
  "permissions": [
    "workspace:read"
  ],
  "capabilities": [
    "tool:custom_action"
  ],
  "ui": {
    "contributions": [
      {
        "slot": "sidebar.item",
        "label": "My Tool",
        "icon": "wrench"
      }
    ]
  }
}
```

## Step 2: Implement the JSON-RPC protocol

Your plugin must handle two required methods:

```python
# main.py

def handle_request(request):
    method = request.get("method")
    req_id = request.get("id")

    if method == "plugin/health":
        return {"jsonrpc": "2.0", "id": req_id, "result": {"status": "ok"}}

    if method == "plugin/shutdown":
        return {"jsonrpc": "2.0", "id": req_id, "result": {"status": "ok"}}

    if method == "tool/custom_action":
        params = request.get("params", {})
        result = do_something(params)
        return {"jsonrpc": "2.0", "id": req_id, "result": result}

    return {
        "jsonrpc": "2.0",
        "id": req_id,
        "error": {"code": -32601, "message": f"Method not found: {method}"},
    }

def do_something(params):
    # Your tool logic here
    return {"message": "Done!", "data": params}

def main():
    for line in sys.stdin:
        line = line.strip()
        if not line:
            continue
        try:
            request = json.loads(line)
            response = handle_request(request)
            sys.stdout.write(json.dumps(response) + "\n")
            sys.stdout.flush()
        except json.JSONDecodeError:
            pass

if __name__ == "__main__":
    main()
```

## Step 3: Add UI contributions

Plugins can contribute to these UI slots:

| Slot | Description |
|------|-------------|
| `sidebar.item` | Icon + label in the main sidebar |
| `page.main` | Full-page view when sidebar item is clicked |
| `lab.sidepanel` | Tab in the lab detail sidebar |
| `settings.section` | Section in the settings page (planned) |

## Step 4: Test locally

1. Open Biosimulant Desktop
2. Go to **Settings > Plugins**
3. Click **Install from file**
4. Select your plugin directory (containing `plugin.json`)
5. The plugin appears in the sidebar

## Step 5: Package for distribution

Create a `.bsiplugin` archive:

```bash
# Build script
mkdir -p dist
tar -czf dist/my-tool.bsiplugin \
  plugin.json \
  main.py \
  requirements.txt
```

## Calling host methods

Your plugin can call back to the desktop app:

```python
def call_host(method, params=None):
    request = {
        "jsonrpc": "2.0",
        "id": next_id(),
        "method": method,
        "params": params or {},
    }
    sys.stdout.write(json.dumps(request) + "\n")
    sys.stdout.flush()
    # Read response from stdin
    response = json.loads(sys.stdin.readline())
    return response.get("result")

# Check connectivity
result = call_host("host/ping")

# Request user approval for an action
approval = call_host("host/request_approval", {
    "action": "delete_model",
    "description": "Delete model xyz from workspace"
})
```

## Environment variables

The host sets these when spawning your plugin:

| Variable | Description |
|----------|-------------|
| `BIOSIMULANT_PLUGIN_INSTALL_DIR` | Plugin installation directory |
| `BIOSIMULANT_PLUGIN_DATA_DIR` | Persistent data directory for your plugin |
| `BIOSIMULANT_DESKTOP_VERSION` | Desktop app version |

**Info:**

  For complete working examples, see the [Reference Examples](/examples/desktop-plugin-samples) page, which includes a tool_provider (Echo) and an agent_provider (Codex) with full source code.

## Next Steps

- [Plugin Manifest Reference](/references/desktop-plugins/manifest): full manifest schema
- [Runtime & JSON-RPC](/references/desktop-plugins/runtime): protocol details
- [UI Contributions](/references/desktop-plugins/ui-contributions): all available slots
- [Packaging & Distribution](/references/desktop-plugins/packaging): publishing to the registry
