Dropstone CLI

Server

Interact with dropstone server over HTTP.

The dropstone serve command runs a headless HTTP server that exposes an OpenAPI endpoint a Dropstone client can use.


Usage

dropstone serve [--port <number>] [--hostname <string>] [--cors <origin>]

Options

FlagDescriptionDefault
--portPort to listen on4096
--hostnameHostname to listen on127.0.0.1
--mdnsEnable mDNS discoveryfalse
--mdns-domainCustom domain name for mDNS servicedropstone.local
--corsAdditional browser origins to allow[]

--cors can be passed multiple times:

dropstone serve --cors http://localhost:5173 --cors https://app.example.com

Authentication

Set DROPSTONE_SERVER_PASSWORD to protect the server with HTTP basic auth. The username defaults to dropstone, or set DROPSTONE_SERVER_USERNAME to override it. This applies to both dropstone serve and dropstone web.

DROPSTONE_SERVER_PASSWORD=your-password dropstone serve

Agent credentials

DROPSTONE_SERVER_PASSWORD protects the server itself. It does not tell the agent how to reach Dropstone. On an unattended machine there is no signed-in account to inherit, so pass an API key through the provider config:

{
  "provider": {
    "dropstone": {
      "options": {
        "apiKey": "dsk_live_...",
        "baseURL": "https://api.dropstone.io/api/v1"
      }
    }
  }
}

baseURL matters. API keys are accepted at /api/v1, not at /v1, and a key sent to /v1 is rejected with 403 Invalid token format. Please log in again. Set it here rather than through DROPSTONE_BASE_URL, which is also used to build the account, usage, and memory endpoints and will break them if you point it at a versioned path.

Generate a key at dropstone.io/dashboard/settings.

Note

The default build agent asks before every tool call. Nothing here can answer that prompt, so the request hangs rather than failing. Either send "agent": "accept all" or set an explicit permission allowlist. See Permissions.


Sending a prompt

POST /session/:id/message requires both agent and model. Omitting model resolves no default: the turn returns 200 with an empty body and nothing runs.

curl -X POST "http://127.0.0.1:4096/session/$SID/message?directory=$PWD" \
  -H "Content-Type: application/json" \
  -d '{
    "agent": "build",
    "model": { "providerID": "dropstone", "modelID": "dropstone-pro" },
    "parts": [{ "type": "text", "text": "Add error handling to src/index.ts" }]
  }'

A turn that fails still returns 200. The reason is at data.info.error, not at the transport level, so check that field rather than relying on the status code.


How it works

dropstone serve exposes Dropstone's capabilities over an OpenAPI 3.1 HTTP endpoint. The same endpoint is used to generate the SDK.

Use the server when you want to drive Dropstone programmatically: from a script, a CI pipeline, or an integration of your own. The interactive session and the server are independent. Running dropstone serve starts a fresh standalone server regardless of whether you have an interactive session open.

You can override the bind address with the --hostname and --port flags.


Spec

The server publishes an OpenAPI 3.1 spec that can be viewed at:

http://<hostname>:<port>/doc

For example, http://localhost:4096/doc. Use the spec to generate clients or inspect request and response types. Or view it in a Swagger explorer.


APIs

The dropstone server exposes the following APIs.


Global

MethodPathDescriptionResponse
GET/global/healthGet server health and version{ healthy: true, version: string }
GET/global/eventGet global events (SSE stream)Event stream

Project

MethodPathDescriptionResponse
GET/projectList all projectsProject[]
GET/project/currentGet the current projectProject

Path & VCS

MethodPathDescriptionResponse
GET/pathGet the current pathPath
GET/vcsGet VCS info for the current projectVcsInfo

Config

MethodPathDescriptionResponse
GET/configGet config infoConfig
PATCH/configUpdate configConfig

Sessions

MethodPathDescriptionNotes
GET/sessionList all sessionsReturns Session[]
POST/sessionCreate a new sessionbody: { parentID?, title? }, returns Session
GET/session/statusGet session status for all sessionsReturns { [sessionID: string]: SessionStatus }
GET/session/:idGet session detailsReturns Session
DELETE/session/:idDelete a session and all its dataReturns boolean
PATCH/session/:idUpdate session propertiesbody: { title? }, returns Session
GET/session/:id/childrenGet a session's child sessionsReturns Session[]
GET/session/:id/todoGet the todo list for a sessionReturns Todo[]
POST/session/:id/initAnalyze app and create AGENTS.mdbody: { messageID, providerID, modelID }, returns boolean
POST/session/:id/forkFork an existing session at a messagebody: { messageID? }, returns Session
POST/session/:id/abortAbort a running sessionReturns boolean
GET/session/:id/diffGet the diff for this sessionquery: messageID?, returns FileDiff[]
POST/session/:id/summarizeSummarize the sessionbody: { providerID, modelID }, returns boolean
POST/session/:id/revertRevert a messagebody: { messageID, partID? }, returns boolean
POST/session/:id/unrevertRestore all reverted messagesReturns boolean
POST/session/:id/permissions/:permissionIDRespond to a permission requestbody: { response, remember? }, returns boolean

Messages

MethodPathDescriptionNotes
GET/session/:id/messageList messages in a sessionquery: limit?, returns { info: Message, parts: Part[]}[]
POST/session/:id/messageSend a message and wait for responsebody: { messageID?, model?, agent?, noReply?, system?, tools?, parts }, returns { info: Message, parts: Part[]}
GET/session/:id/message/:messageIDGet message detailsReturns { info: Message, parts: Part[]}
POST/session/:id/prompt_asyncSend a message asynchronously (no wait)body: same as /session/:id/message, returns 204 No Content
POST/session/:id/commandExecute a slash commandbody: { messageID?, agent?, model?, command, arguments }, returns { info: Message, parts: Part[]}
POST/session/:id/shellRun a shell commandbody: { agent, model?, command }, returns { info: Message, parts: Part[]}

Commands

MethodPathDescriptionResponse
GET/commandList all commandsCommand[]

Files

MethodPathDescriptionResponse
GET/find?pattern=<pat>Search for text in filesArray of match objects with path, lines, line_number, absolute_offset, submatches
GET/find/file?query=<q>Find files and directories by namestring[] (paths)
GET/find/symbol?query=<q>Find workspace symbolsSymbol[]
GET/file?path=<path>List files and directoriesFileNode[]
GET/file/content?path=<p>Read a fileFileContent
GET/file/statusGet status for tracked filesFile[]

/find/file query parameters

  • query (required): search string (fuzzy match)
  • type (optional): limit results to "file" or "directory"
  • directory (optional): override the project root for the search
  • limit (optional): max results (1–200)
  • dirs (optional): legacy flag ("false" returns only files)

LSP, Formatters & MCP

MethodPathDescriptionResponse
GET/lspGet LSP server statusLSPStatus[]
GET/formatterGet formatter statusFormatterStatus[]
GET/mcpGet MCP server status{ [name: string]: MCPStatus }
POST/mcpAdd MCP server dynamicallybody: { name, config }, returns MCP status object

Agents

MethodPathDescriptionResponse
GET/agentList all available agentsAgent[]

Logging

MethodPathDescriptionResponse
POST/logWrite log entry. Body: { service, level, message, extra? }boolean

Auth

MethodPathDescriptionResponse
PUT/auth/:idSet authentication credentials for the given target.boolean

Events

MethodPathDescriptionResponse
GET/eventServer-sent events stream. First event is server.connected, then bus eventsServer-sent events stream

Docs

MethodPathDescriptionResponse
GET/docOpenAPI 3.1 specificationHTML page with OpenAPI spec
Ctrl+I