How ToDesktopBuild a 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

KindUse Case
tool_providerUtility tools, data processing, custom commands
agent_providerAI agents that can run sessions, send turns, handle approvals

Step 1: Create the manifest

Create a plugin.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:

# main.py
import sys
import json
 
 
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:

SlotDescription
sidebar.itemIcon + label in the main sidebar
page.mainFull-page view when sidebar item is clicked
lab.sidepanelTab in the lab detail sidebar
settings.sectionSection 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:

# 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:

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:

VariableDescription
BIOSIMULANT_PLUGIN_INSTALL_DIRPlugin installation directory
BIOSIMULANT_PLUGIN_DATA_DIRPersistent data directory for your plugin
BIOSIMULANT_DESKTOP_VERSIONDesktop app version

For complete working examples, see the Reference Examples page, which includes a tool_provider (Echo) and an agent_provider (Codex) with full source code.

Next Steps