Skip to content
dtoolkit

SDK

The @dtoolkit/sdk package provides typed HTTP clients for the four dtoolkit services that expose REST APIs: dbrain (memory), dops (observability), dwork (project management), and dproxy (model invocation). Each client wraps a shared HttpClient base with Bearer token authentication, configurable timeouts, and structured error handling via SdkError.

install
npm install @dtoolkit/sdk
# or
pnpm add @dtoolkit/sdk
import { DBrainClient, DWorkClient, DProxyClient, DOpsClient } from '@dtoolkit/sdk';
const brain = new DBrainClient('http://localhost:7878', 'your-token');
const work = new DWorkClient('http://localhost:7881', 'your-token');
const proxy = new DProxyClient('http://localhost:7880', 'your-token');
const ops = new DOpsClient('http://localhost:7883', 'your-token');

Each client supports two constructor signatures:

// Positional arguments
const client = new DBrainClient('http://localhost:7878', 'your-token');
// Options object (supports timeoutMs)
const client = new DBrainClient({
baseUrl: 'http://localhost:7878',
token: 'your-token',
timeoutMs: 10_000,
});

All clients throw SdkError on non-2xx responses. The error includes the HTTP status code, response body, and request path.

import { SdkError } from '@dtoolkit/sdk';
try {
await brain.getEntity('nonexistent');
} catch (err) {
if (err instanceof SdkError) {
console.error(`HTTP ${err.status} on ${err.path}: ${err.body}`);
}
}

Typed client for the dbrain REST API (default port 7878).

new DBrainClient(baseUrl: string, token: string)
new DBrainClient(options: DBrainClientOptions)
OptionTypeRequiredDescription
baseUrlstringYesdbrain server URL (e.g., http://localhost:7878)
tokenstringYesBearer token for authentication
timeoutMsnumberNoRequest timeout in milliseconds
MethodSignatureDescription
health()() => Promise<HealthResponse>Server health, entity/fact/conversation counts, brain type
connect()() => Promise<ConnectResponse>MCP connection config, permissions, and CLAUDE.md content
MethodSignatureDescription
listEntities(filters?)(filters?: { category?: string; type?: string }) => Promise<EntityRow[]>List all entities, optionally filtered by category or type
getEntity(id)(id: string) => Promise<EntityWithFacts>Get a single entity with all its facts
createEntity(entity)(entity: { id, name, type, category, metadata? }) => Promise<{...}>Create a new entity
archiveEntity(id)(id: string) => Promise<{ id, status }>Archive (soft-delete) an entity
MethodSignatureDescription
listFacts(entityId, filters?)(entityId: string, filters?: { tier?: string }) => Promise<FactRow[]>List facts for an entity, optionally filtered by tier
addFact(entityId, fact)(entityId: string, fact: { id, fact, category?, timestamp?, source?, relatedEntities? }) => Promise<{...}>Add a fact to an entity
bumpFact(id)(id: string) => Promise<{ id, bumped }>Touch a fact to keep it in the hot tier
MethodSignatureDescription
search(query, options?)(query: string, options?: { limit?, entityId?, tier? }) => Promise<SearchResult[]>Full-text search across facts
searchFederated(query, options?)(query: string, options?: { limit?, entityId?, tier? }) => Promise<FederatedSearchResponse>Search across local and connected brains
memorySummary()() => Promise<MemorySummaryRow[]>Per-entity breakdown of hot/warm/cold fact counts
MethodSignatureDescription
listConversations(filters?)(filters?: { source?, limit? }) => Promise<ConversationSummary[]>List conversations
getConversation(id)(id: string) => Promise<ConversationWithMessages>Get conversation with all messages
startConversation(source, id?)(source: string, id?: string) => Promise<{...}>Start a new conversation
sendMessages(conversationId, messages)(id: string, messages: Array<{ role, content }>) => Promise<Array<{...}>>Add messages to a conversation
listMessages(conversationId, filters?)(id: string, filters?: { since?, processed? }) => Promise<Message[]>List messages with optional filters
pendingMessages()() => Promise<PendingMessages>Get unprocessed message counts
MethodSignatureDescription
listDocuments()() => Promise<DocumentListItem[]>List all workspace documents
getDocument(key)(key: string) => Promise<Document>Get document by key
putDocument(key, doc)(key: string, doc: { title, content }) => Promise<{...}>Create or update a document
deleteDocument(key)(key: string) => Promise<{ key, deleted }>Delete a document
MethodSignatureDescription
createApiKey(params)(params: { userId, userName, permissions? }) => Promise<ApiKeyCreateResponse>Create an API key (shared brains only)
listApiKeys()() => Promise<ApiKeyListItem[]>List all API keys
revokeApiKey(id)(id: string) => Promise<{ id, revoked }>Revoke an API key
listConnections()() => Promise<ConnectionStatus[]>List connected brains with online status
shareFact(factId, targetBrain?)(factId: string, targetBrain?: string) => Promise<ShareResult>Share a fact to a connected brain
import { DBrainClient } from '@dtoolkit/sdk';
const brain = new DBrainClient('http://localhost:7878', 'dtk_abc123');
// Create an entity
await brain.createEntity({
id: 'project-acme',
name: 'Project ACME',
type: 'project',
category: 'work',
});
// Add a fact
await brain.addFact('project-acme', {
id: 'fact-001',
fact: 'Uses React 19 with Vite 7 and pnpm workspaces',
category: 'tech',
});
// Search across all facts
const results = await brain.search('React workspace');
for (const r of results) {
console.log(`[${r.entity.name}] ${r.fact.fact} (score: ${r.score})`);
}
// Federated search (includes connected brains)
const federated = await brain.searchFederated('deployment strategy');
console.log(`Local: ${federated.federation.local}, Remote: ${federated.federation.remote}`);

Typed client for the dwork REST API (default port 7881).

new DWorkClient(baseUrl: string, token: string)
new DWorkClient(options: DWorkClientOptions)
OptionTypeRequiredDescription
baseUrlstringYesdwork server URL (e.g., http://localhost:7881)
tokenstringYesBearer token for authentication
timeoutMsnumberNoRequest timeout in milliseconds
MethodSignatureDescription
health()() => Promise<DWorkHealthResponse>Server health with project/task/doc counts
overview()() => Promise<DWorkOverview>Global overview: all projects with task breakdowns by status and priority
whatToDoNext(project?)(project?: string) => Promise<DWorkTask[]>AI-ranked next tasks
MethodSignatureDescription
listProjects(status?)(status?: string) => Promise<DWorkProjectSummary[]>List projects with task count summaries
getProject(slug)(slug: string) => Promise<DWorkProject>Get full project details including README and tech stack
createProject(project)(project: { slug, name, description?, source_path? }) => Promise<DWorkProject>Scaffold a new project
updateProject(slug, changes)(slug: string, changes: { name?, description?, status?, source_path? }) => Promise<DWorkProject>Update project metadata
deleteProject(slug)(slug: string) => Promise<{ deleted, slug }>Delete a project
MethodSignatureDescription
listTasks(project, filters?)(project: string, filters?: { status?, priority? }) => Promise<DWorkTask[]>List tasks for a project
addTask(project, task)(project: string, task: { title, type?, priority?, status?, estimate?, deadline?, detail?, detail_doc? }) => Promise<DWorkTask>Add a task to a project
updateTask(id, changes)(id: string, changes: { title?, status?, priority?, type?, estimate?, deadline?, detail?, project? }) => Promise<{...}>Update a task
deleteTask(id)(id: string) => Promise<{ deleted, id }>Delete a task
MethodSignatureDescription
listDocs(project, type?)(project: string, type?: string) => Promise<DWorkDoc[]>List docs for a project
getDoc(id)(id: string) => Promise<DWorkDoc>Get a doc with content
addDoc(project, doc)(project: string, doc: { title, body, type }) => Promise<DWorkDoc>Add a doc to a project
updateDoc(id, changes)(id: string, changes: { title?, body?, type? }) => Promise<{...}>Update a doc
deleteDoc(id)(id: string) => Promise<{ deleted, id }>Delete a doc
MethodSignatureDescription
search(query, options?)(query: string, options?: { project?, limit? }) => Promise<DWorkSearchResult[]>Full-text search across tasks and docs
sync(project)(project: string) => Promise<DWorkSyncResult>Sync markdown files to the SQLite index
MethodSignatureDescription
listGraphs()() => Promise<DWorkGraph[]>List all uploaded code graphs
getGraph(name)(name: string) => Promise<DWorkGraph>Get graph metadata
uploadGraph(name, dbBuffer)(name: string, dbBuffer: Uint8Array) => Promise<DWorkGraphUploadResult>Upload a codegraph-sdk database
deleteGraph(name)(name: string) => Promise<void>Delete a graph
linkGraphProjects(name, slugs)(name: string, slugs: string[]) => Promise<{ linked }>Link a graph to projects
graphStats(name)(name: string) => Promise<DWorkGraphStats>Node/edge/file counts by kind and language
searchGraphNodes(name, query, opts?)(name: string, query: string, opts?: { kind?, limit? }) => Promise<DWorkGraphSearchResult[]>Search for nodes in a graph
getGraphNode(name, nodeId)(name: string, nodeId: string) => Promise<DWorkGraphNode>Get full node details
getGraphCallers(name, nodeId, depth?)(name: string, nodeId: string, depth?: number) => Promise<Array<{...}>>Trace callers of a node
getGraphImpact(name, nodeId, depth?)(name: string, nodeId: string, depth?: number) => Promise<DWorkGraphSubgraph>Impact analysis: what does this node affect?
traceGraphPath(name, from, to)(name: string, from: string, to: string) => Promise<{ path }>Find the shortest path between two nodes
graphDeadCode(name, kinds?)(name: string, kinds?: string) => Promise<DWorkGraphNode[]>Find unreferenced nodes (dead code)
graphCircularDependencies(name)(name: string) => Promise<string[][]>Detect circular dependency chains
MethodSignatureDescription
createApiKey(params)(params: { userId, userName, permissions? }) => Promise<DWorkApiKeyCreateResponse>Create an API key
listApiKeys()() => Promise<DWorkApiKeyListItem[]>List API keys
revokeApiKey(id)(id: string) => Promise<{ id, revoked }>Revoke an API key
import { DWorkClient } from '@dtoolkit/sdk';
const work = new DWorkClient('http://localhost:7881', 'dtk_abc123');
// List active projects
const projects = await work.listProjects('active');
for (const p of projects) {
console.log(`${p.name}: ${p.taskCounts.todo ?? 0} todo, ${p.taskCounts.done ?? 0} done`);
}
// Add a task
await work.addTask('my-project', {
title: 'Implement user auth',
type: 'feature',
priority: 'high',
estimate: '3h',
});
// Get the global overview
const overview = await work.overview();
console.log(`${overview.totalProjects} projects, ${overview.totalTasks} tasks`);

Typed client for the dproxy REST API (default port 7880). All endpoints are under the /v1 prefix.

new DProxyClient(baseUrl: string, token?: string)
new DProxyClient(options: DProxyClientOptions)
OptionTypeRequiredDescription
baseUrlstringYesdproxy server URL (e.g., http://localhost:7880)
tokenstringNoBearer token for authentication
MethodSignatureDescription
health()() => Promise<DProxyHealthResponse>Server health, active provider, version
MethodSignatureDescription
ask(prompt, options?)(prompt: string, options?: AskOptions) => Promise<AskResponse>Execute a prompt and get the full response
askStream(prompt, options?)(prompt: string, options?: AskOptions) => AsyncGenerator<AdapterStreamEvent>Stream a prompt response via SSE

AskOptions:

FieldTypeDescription
provider'claude' | 'codex' | 'gemini' | 'opencode'Provider to use (default: claude)
modelstringModel override
maxTurnsnumberMaximum agentic turns
maxBudgetUsdnumberCost cap in USD
systemPromptstringCustom system prompt
memoryboolean | string[]Include memory context (all or specific keys)
filesInputFile[]Files to include in context
sessionIdstringSession ID for multi-turn conversations
continueSessionbooleanContinue an existing session
saveHistorybooleanPersist to history
MethodSignatureDescription
listHistory(limit?)(limit?: number) => Promise<HistoryEntry[]>List prompt history
getHistory(id)(id: string) => Promise<HistoryEntry>Get a single history entry
searchHistory(query)(query: string) => Promise<HistoryEntry[]>Search history by query
clearHistory(before?)(before?: string) => Promise<{ removed }>Clear history entries
MethodSignatureDescription
listMemoryKeys()() => Promise<string[]>List all memory keys
getMemory(key)(key: string) => Promise<string>Get memory content by key
setMemory(key, content)(key: string, content: string) => Promise<void>Create or update a memory entry
deleteMemory(key)(key: string) => Promise<void>Delete a memory entry
searchMemory(query)(query: string) => Promise<MemorySearchResult[]>Search across memory entries
MethodSignatureDescription
listTemplates()() => Promise<TemplateDefinition[]>List all prompt templates
getTemplate(name)(name: string) => Promise<TemplateDefinition>Get a template by name
saveTemplate(name, template)(name: string, template: TemplateDefinition) => Promise<void>Create or update a template
runTemplate(name, options?)(name: string, options?: TemplateRunOptions) => Promise<AskResponse>Execute a template with variable substitution
deleteTemplate(name)(name: string) => Promise<void>Delete a template
MethodSignatureDescription
getConfig()() => Promise<unknown>Get the full dproxy config
getConfigValue(key)(key: string) => Promise<unknown>Get a single config value
setConfigValue(key, value)(key: string, value: string) => Promise<void>Set a config value
import { DProxyClient } from '@dtoolkit/sdk';
const proxy = new DProxyClient('http://localhost:7880', 'dtk_abc123');
// Simple prompt
const response = await proxy.ask('Explain the builder pattern in 3 sentences', {
provider: 'claude',
});
console.log(response.text);
console.log(`Cost: $${response.costUsd}, Duration: ${response.durationMs}ms`);
// Streaming
for await (const event of proxy.askStream('Write a haiku about TypeScript')) {
if (event.type === 'text') {
process.stdout.write(event.text);
}
}

Typed client for the dops REST API (default port 7883).

new DOpsClient(baseUrl: string, token: string)
new DOpsClient(options: DOpsClientOptions)
OptionTypeRequiredDescription
baseUrlstringYesdops server URL (e.g., http://localhost:7883)
tokenstringYesBearer token for authentication
timeoutMsnumberNoRequest timeout in milliseconds
MethodSignatureDescription
health()() => Promise<DOpsHealthResponse>Server health, session/event/tool counts, pricing
MethodSignatureDescription
createSession(session)(session: DOpsCreateSession) => Promise<{ id }>Create a new observability session
endSession(id, data?)(id: string, data?: DOpsEndSession) => Promise<{ updated, id }>Mark a session as completed/failed/abandoned
listSessions(filters?)(filters?: DOpsSessionFilters) => Promise<DOpsSessionRow[]>List sessions (filter by source, model, status, date range, limit/offset)
getSession(id)(id: string) => Promise<DOpsSessionDetail>Full session detail with token usage, tool calls, and errors
MethodSignatureDescription
ingestEvent(event)(event: DOpsEvent) => Promise<{ id }>Ingest a single event
ingestBatch(events)(events: DOpsEvent[]) => Promise<{ ingested }>Batch-ingest events (up to 1000)
recordToolCall(toolCall)(toolCall: DOpsToolCall) => Promise<{ id }>Record a tool invocation with success/failure and duration
recordTokenUsage(usage)(usage: DOpsTokenUsage) => Promise<{ id }>Record token usage per model (input, output, cache read/write)
recordError(error)(error: DOpsError) => Promise<{ id }>Record an error linked to a session
MethodSignatureDescription
timeseries(filters?)(filters?: DOpsTimeSeriesFilters) => Promise<DOpsTimeSeriesBucket[]>Token usage over time (1h or 15m intervals)
toolStats(filters?)(filters?: DOpsStatsFilters) => Promise<DOpsToolStat[]>Per-tool stats: call count, success rate, avg duration
modelStats(filters?)(filters?: DOpsDateFilters) => Promise<DOpsModelStat[]>Stats by model: sessions, input/output tokens, cache
sourceStats(filters?)(filters?: DOpsDateFilters) => Promise<DOpsSourceStat[]>Stats by source: sessions and token counts
import { DOpsClient } from '@dtoolkit/sdk';
const ops = new DOpsClient('http://localhost:7883', 'dtk_abc123');
// Create a session
const { id } = await ops.createSession({
source: 'claude-code',
model: 'claude-sonnet-4-6',
metadata: { project: 'my-app' },
});
// Record token usage
await ops.recordTokenUsage({
session_id: id,
model: 'claude-sonnet-4-6',
input_tokens: 1500,
output_tokens: 320,
cache_read: 800,
});
// Record a tool call
await ops.recordToolCall({
session_id: id,
tool_name: 'Read',
success: true,
duration_ms: 45,
});
// End the session
await ops.endSession(id, { status: 'completed' });
// Query analytics
const tools = await ops.toolStats({ from: '2025-01-01' });
for (const t of tools) {
console.log(`${t.tool_name}: ${t.total} calls, ${t.success} ok`);
}

The examples/ directory in the dtoolkit repo contains ready-to-run TypeScript scripts for every SDK client:

ExampleClientWhat it covers
dbrain.tsDBrainClientHealth, entity CRUD, facts, search, memory summary, conversations, federation
dproxy.tsDProxyClientBatch ask, streaming, system prompts, file attachments, history, memory
dwork.tsDWorkClientProjects, tasks, docs, search, overview
dops.tsDOpsClientSessions, token usage, tool calls, errors, analytics queries
demo.tsAll clientsCombined smoke test

Run all examples with zero config:

terminal
cd examples
npm install
npm start

The setup script creates temporary servers, runs the demo, and cleans up automatically.

The SDK re-exports all response types for use in your own code:

import type {
// dbrain
EntityRow, EntityWithFacts, FactRow, SearchResult,
FederatedSearchResponse, HealthResponse, ConversationSummary,
// dops
DOpsHealthResponse, DOpsSessionRow, DOpsSessionDetail,
DOpsToolStat, DOpsModelStat, DOpsSourceStat, DOpsTimeSeriesBucket,
// dwork
DWorkProject, DWorkTask, DWorkDoc, DWorkOverview, DWorkSearchResult,
DWorkGraph, DWorkGraphNode, DWorkGraphStats,
// dproxy
AskOptions, AskResponse, DProxyHealthResponse, ProviderName,
// core (re-exported)
AdapterStreamEvent, AdapterUsage,
} from '@dtoolkit/sdk';
ServiceREST APIDashboard
dbrain78787879
dproxy7880
dwork78817882
dops78837884