# Daslab CLI and MCP

Daslab exposes your worlds and scenes as composable tools. Drive them from Claude
and Claude Cowork, Claude Code, Cursor, the shell, or any MCP client.

Each scene curates a tool surface (Gmail, GitHub, Google Drive, Postgres,
Tavily, Firecrawl, ...) with credentials and context already wired in.
The CLI is a thin MCP client on top of that surface; MCP clients can also
connect directly without installing the CLI.

## Claude / Claude Cowork (custom connector)

Claude and Claude Cowork use Claude's custom connector UI, not the
`claude mcp add` command. Paste this remote MCP URL:

```text
https://daslab.run/mcp
```

For Pro or Max plans:

1. Open Claude.
2. Go to `Customize -> Connectors`.
3. Click `+`, then `Add custom connector`.
4. Paste `https://daslab.run/mcp`.
5. Leave advanced OAuth client fields blank unless your workspace has a
   dedicated OAuth client.
6. Click `Add`, then `Connect`, sign into Daslab, and approve access.
7. In a conversation, enable Daslab from `+ -> Connectors`.

For Team or Enterprise plans, an Owner or Primary Owner first adds the connector
from `Organization settings -> Connectors -> Add -> Custom -> Web`. Members then
go to `Customize -> Connectors`, click `Connect`, sign into Daslab, and enable
the connector per conversation.

Daslab supports the hosted MCP OAuth flow for this path. Claude discovers OAuth
from `/mcp`, opens Daslab login/consent, and stores the resulting connector token
inside Claude. API-key headers are for Claude Code and other static-header MCP
clients.

## Claude Code (native MCP, no CLI required)

Create a Daslab API key from [Account → Developer Access](https://daslab.run/account#developer-access),
then add Daslab to Claude Code:

```bash
claude mcp add --scope user --transport http daslab https://daslab.run/mcp \
  --header "Authorization: Bearer dk_xxxxxxxxxxxx"

claude mcp list
```

`https://daslab.run/mcp` is the default: it starts in your home world and
can switch to any world or scene you can access.

To pin Claude Code to one readable world/scene URL, use the same URL
with `/mcp` in front:

```bash
claude mcp add --scope user --transport http daslab-acme https://daslab.run/mcp/acme/q4-launch \
  --header "Authorization: Bearer dk_xxxxxxxxxxxx"
```

Compatibility ID URLs still work when you need a permanent machine target:

```text
https://daslab.run/mcp/scene/scn_xxxxxxxxxxxx
```

## Install (macOS / Linux)

```bash
curl -fsSL https://releases.daslab.dev/install.sh | sh
```

Installs to `~/.local/bin/daslab`. Add to PATH if it isn't already.

## Authenticate the CLI

Two options. API key is recommended for LLM-driven use and MCP clients
that send static headers.

```bash
# Browser flow — opens a tab, redirects back, persists token
daslab login

# Or env var (idempotent, scriptable)
export DASLAB_API_KEY=dk_xxxxxxxxxxxx
```

`export` only lasts the current shell — add the line to `~/.zshrc`
or `~/.bashrc` to persist. Get an API key from
[Account → Developer Access](https://daslab.run/account#developer-access).

## Claude Code via the CLI

```bash
daslab install claude                         # recommended: https://daslab.run/mcp + /daslab
daslab install claude --world                 # pin your home world
daslab install claude --scene-id scn_x        # pin a specific scene id

# old alias still works
daslab claude-code-install
```

This writes a `daslab` MCP entry into `~/.claude.json` and installs a
Claude Code `/daslab` skill under `~/.claude/skills/daslab/`. Restart
Claude Code. Then in any conversation, every Daslab tool (140+ in a typical
scene) is callable natively — no `daslab` CLI invocation needed.

## Discover scenes and assets

The fastest way to orient an LLM (or yourself) in a fresh terminal.
All three calls work whether you run them in bash or Claude Code drives
them over MCP.

```bash
# 1. List the scenes you can access in your home world
daslab --world call daslab_list_scenes

# 2. Drill into one — list its assets (sheets, widgets, accounts, ...)
SCENE=29517950-fa0f-41ac-9dd5-0ef88aceb43d        # paste an id from step 1
daslab --scene-id $SCENE call daslab_get_scene_assets scene_id=$SCENE

# 3. See every tool that scene exposes (providers + builtins, typically 100+)
daslab --scene-id $SCENE tools
```

`--scene-id` and `--world` are global flags. They MUST appear before the
subcommand (or before the tool name in `daslab call`). Anything after the
tool name is parsed as a positional `key=value` argument by design — that
keeps `daslab call <tool> k=v k=v` working as the LLM-friendly form.

You're a member of multiple worlds — each one has its own scenes,
accounts, credentials, and tools. `--world` targets your home world in
the CLI; for an exact target, pass `--scene-id <id>`.

## Switch servers (nodes)

Each Daslab server (prod, a self-hosted box, localhost) is a separate node
with its own worlds + auth. The active node scopes everything below it:
`Server ▸ World ▸ Scene`.

```bash
daslab server                                      # list nodes (* active, ✓ logged in)
daslab server add http://89.167.27.66:3000 --name hetzner   # register a node
daslab server use hetzner                          # switch node — world/scene rebind to it
daslab login                                       # authenticate the active node
daslab server remove hetzner
```

(`DASLAB_API_URL` still overrides the server for a single invocation.)

## Switch worlds and scenes

Two ways: persistent (writes config) or one-shot per call.

```bash
# Persistent — sets the default for every subsequent invocation
daslab world use "Blackbelt Labs"          # switch the parent world
daslab world                               # interactive picker if name omitted
daslab scenes use Travel                   # switch the focused scene
daslab scenes                              # interactive picker

# One-shot — no config change, override for this call only.
# Global flags MUST come before the subcommand (or before the tool name
# in `call`) — see the note in "Discover scenes and assets" above.
daslab --scene-id <id> tools
daslab --scene-id <id> call <tool> key=value
daslab --world call <tool> key=value
```

Inside Claude Code (native MCP), the equivalent is which MCP endpoint
was wired up:
`https://daslab.run/mcp` can move across the worlds you can access;
`https://daslab.run/mcp/acme/q4-launch` pins a readable world/scene URL;
`https://daslab.run/mcp/scene/<id>` pins one scene by stable id.

## Core commands

```
daslab tools                       List tools in the current scope
daslab call <tool> key=value ...   Invoke one tool directly
daslab ask "..."                   Run a full agent turn
daslab chat                        Interactive REPL
daslab server                      Switch the active server (node)
daslab world / daslab scenes       Switch the persistent world / scene
daslab docs                        Print this document
```

Three scoping flags work the same on `tools`, `call`, `install claude`:

```
(default)                Current scene from config
--world                  Your home world (root scene + tools)
--scene-id <uuid|slug>   A specific scene by id
```

## `daslab tools`

```bash
daslab tools                         # pretty list, current scene
daslab tools --world                 # everything in your home world
daslab tools --scene-id scn_xxx      # specific scene
daslab tools --json                  # full input schemas for each tool
```

The JSON output is the MCP `tools/list` response shape:
`{ tools: [{ name, description, input_schema }] }`. Stable for parsing.

## `daslab call`

Invoke any tool from the catalog directly — no LLM in the loop.

```bash
# key=value form. Values auto-parse as JSON when shaped like JSON.
daslab call daslab_list_scenes
daslab call daslab_get_scene_assets scene_id=scn_xxx
daslab call tavily_search query="latest claude code release" max_results=5

# Raw JSON when args are complex (arrays, nested objects)
daslab call daslab_create_scene --json '{
  "name": "Q1 Investor Update",
  "parent_id": "scn_parent",
  "description": "Financials + market intelligence for the Q1 deck"
}'

# Print tool's text output unchanged (no JSON pretty-printing)
daslab call <tool> --raw
```

Output: by default, JSON-pretty if the tool's text output parses as JSON;
otherwise verbatim text. Stable for piping to `jq`, redirecting, etc.

## Most-used tools (in any scene)

```
daslab_list_scenes              List scenes in the current org
daslab_get_scene_assets         List assets in a scene  (arg: scene_id)
daslab_list_providers           Discover what providers you can add
daslab_list_asset_types         Per-provider, what types are addable
daslab_browse_assets            Browse a provider's catalog before adding

daslab_create_scene             Create a new scene
daslab_update_scene             Rename, set description
daslab_add_asset                Attach an asset (provider + type + ref)
daslab_remove_asset             Detach an asset

daslab_write_webview            Create a webview widget (HTML)
daslab_edit_webview             Edit an existing webview
daslab_write_widget             Create a structured widget
daslab_render                   Render a widget to an image
```

Plus the tool catalog from every provider connected to the scene:
`gmail_*`, `gdrive_*`, `github_*`, `postgres_*`, `tavily_*`, etc.

## Example end-to-end

```bash
# 1. Find the scene you want to extend
daslab --world call daslab_list_scenes

SCENE=29517950-fa0f-41ac-9dd5-0ef88aceb43d

# 2. See what's in it
daslab --scene-id $SCENE call daslab_get_scene_assets scene_id=$SCENE

# 3. Browse a provider's catalog before attaching
daslab --scene-id $SCENE call daslab_browse_assets provider=google_drive type=folder

# 4. Add the chosen asset
daslab --scene-id $SCENE call daslab_add_asset \
  --json '{"provider":"google_drive","type":"folder","external_id":"folderXYZ","name":"✈️ More Travel Receipts"}'

# 5. Render a widget summary
daslab --scene-id $SCENE call daslab_render widget_id=wid_xxx
```

The scene you built is now live on iOS / web — no separate publish step.

## Run an agent turn

When you want the engine to plan + execute multi-step work, use `ask`:

```bash
daslab ask "summarize my unread gmail and post a digest to Slack"
daslab ask "search the web for daslab competitors and add a brand benchmark widget"
```

This runs through the OpenAI Responses API (`POST /v1/responses`).
Streaming text + tool spinners; JSON output not guaranteed.

## Programmatic workflow tips

- For LLMs driving the CLI: prefer `daslab tools --json` to discover
  the catalog up front, then `daslab call <tool> --json '{...}'` per
  invocation. Argument names match the tool's `input_schema`.
- For shell scripts: `daslab call ... | jq '.field'` works because
  outputs are JSON-by-default.
- Idempotent installs: `daslab install claude` is safe to re-run
  (replaces the daslab entry without touching other MCP servers).
  `daslab claude-code-install` remains as an old alias.

## Update / uninstall

```bash
daslab update           # check + install latest
daslab update --check   # just check
daslab logout           # clear credentials
```

## Discovery

Run `daslab --help` for the full subcommand list. Each subcommand has
its own `--help` with flag detail.

For the always-current catalog of available tools in your scope, run
`daslab tools` — the list grows as Daslab adds providers and as you
attach new assets to your scenes.
