TypeScript SDK — Code Examples
Recipes and patterns for common Cosmos SDK use cases
Updated 2026-11-03
Verified 2026-11-03
Practical recipes for building with @cosmos/sdk. Each example is self-contained and copy-pasteable.
Table of Contents
- Initialize from Environment Variables
- Login and Create Client
- Multi-Turn Conversation
- Streaming with Callbacks
- Register a Local Tool
- Connect to Cosmos MCP Server
- Upload and Manage Files
- Search RAGflow Collections
- Parse a Stored Document
- Bulk Registry Lookups
- Show the User's Cost Limits
- Async Agent Requests with Webhooks
- Observability with Custom Adapter
- Context Activation Tools
- Authorization Decision
- Data Source Registry
- Error Handling
Initialize from Environment Variables
import { CosmosClient, buildServiceEndpointsFromEnv } from '@cosmos/sdk';
const cosmos = new CosmosClient({
orgId: process.env.COSMOS_ORG_ID!,
projectId: process.env.COSMOS_PROJECT_ID,
// COSMOS_USER_SERVICE_URL, COSMOS_CONFIGURATOR_SERVICE_URL, COSMOS_ADK_SERVICE_URL, ...
services: buildServiceEndpointsFromEnv(),
// Optional fallback for services without a COSMOS_*_SERVICE_URL, only for path-routing gateways
gatewayUrl: process.env.COSMOS_GATEWAY_URL,
authManager: {
tokens: {
accessToken: process.env.COSMOS_ACCESS_TOKEN!,
refreshToken: process.env.COSMOS_REFRESH_TOKEN!,
expiresAt: new Date(Date.now() + 3600_000),
},
},
});
Login and Create Client
import { CosmosClient } from '@cosmos/sdk';
async function createAuthenticatedClient(email: string, password: string) {
const loginRes = await fetch('http://localhost:8101/api/v1/users/login/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
const { access_token, refresh_token, expires_in } = await loginRes.json();
return new CosmosClient({
orgId: '87654321-4321-8765-4321-876543218765',
services: {
user: 'http://localhost:8101',
configurator: 'http://localhost:8100',
adk: 'http://localhost:8103',
storage: 'http://localhost:8107',
},
authManager: {
tokens: {
accessToken: access_token,
refreshToken: refresh_token,
expiresAt: new Date(Date.now() + expires_in * 1000),
},
onAuthExpired: () => console.error('Session expired'),
},
});
}
const cosmos = await createAuthenticatedClient('admin@cosmos.dev', 'admin123');
Multi-Turn Conversation
ADKClient.converse() requests carry no session ID, so use the ADK v2 WebSocket for multi-turn conversations and pass the session ID from the previous turn:
// First message
const r1 = await cosmos.sendMessageViaADK(null, 'What products did we sell in Q1?', {
agentId: 'agent-uuid',
agentVersion: '1.0',
onChunk: (text) => process.stdout.write(text),
});
// Follow-up on the same session (ADK keeps the conversation history)
const r2 = await cosmos.sendMessageViaADK(r1.sessionId, 'Which had the highest revenue?', {
agentId: 'agent-uuid',
agentVersion: '1.0',
onChunk: (text) => process.stdout.write(text),
});
console.log('\nSession:', r2.sessionId);
Streaming with Callbacks
const chunks: string[] = [];
const result = await cosmos.sendMessageViaADK(null, 'Write a haiku about coding', {
agentId: 'agent-uuid',
agentVersion: '1.0',
sessionState: { projectId: 'project-uuid' },
temperature: 0.9,
onChunk: (text) => {
chunks.push(text);
process.stdout.write(text);
},
});
const fullResponse = chunks.join('');
console.log('\n---');
console.log('Session:', result.sessionId);
console.log('Usage:', result.usage);
cosmos.closeADKSession();
Register a Local Tool
Register tools that ADK v2 can invoke via WebSocket callbacks:
cosmos.registerTool(
'lookup_customer',
['crm.customer.lookup'],
{
name: 'lookup_customer',
description: 'Look up a customer by email address',
inputSchema: {
type: 'object',
properties: {
email: { type: 'string', description: 'Customer email address' },
},
required: ['email'],
},
},
async (input) => {
const email = input.email as string;
// Your business logic here
const customer = await db.customers.findByEmail(email);
if (!customer) {
return {
ok: false,
error: { code: 'TOOL_NOT_FOUND', message: `No customer with email ${email}` },
};
}
return { ok: true, data: customer };
},
);
// On { ok: false }, the SDK sends error.message as the tool_result error,
// so the model sees "No customer with email ..." rather than a generic failure.
// A thrown error is reported the same way with code TOOL_EXECUTION_FAILED.
// Tool hooks for logging or transformation
cosmos.toolManager.addPreHook('*', async (ctx, input) => {
console.log(`[pre] ${ctx.toolName}`, input);
});
cosmos.toolManager.addPostHook(/^lookup_/, async (ctx, input, result) => {
console.log(`[post] ${ctx.toolName} ok=${result.ok}`);
});
Connect to Cosmos MCP Server
const token = await cosmos.auth.getAccessToken();
await cosmos.connectMCP({
id: 'cosmos',
transport: 'network',
url: 'http://localhost:8102/mcp/',
headers: {
Authorization: `Bearer ${token}`,
'X-Org-Id': process.env.COSMOS_ORG_ID!,
},
});
// List all tools (local + MCP)
const allTools = cosmos.toolManager.listAll();
console.log(`${allTools.length} tools available`);
for (const tool of allTools) {
console.log(` [${tool.source}] ${tool.name}`);
}
// Execute a tool via MCP
const result = await cosmos.toolManager.executeTool(
'test_organization-api-weather_tool-1_0',
{ city: 'London' },
);
console.log(result);
Upload and Manage Files
// List files
const items = await cosmos.storageClient.listItems({ search: 'report' });
for (const item of items) {
console.log(`${item.name} (${item.size} bytes)`);
}
// Ensure folder exists
const folder = await cosmos.storageClient.ensureFolder('/reports/2026/q1');
// Upload a file
const fileBlob = new Blob(['Hello, world!'], { type: 'text/plain' });
const uploaded = await cosmos.storageClient.uploadFile(fileBlob, 'hello.txt', {
parentId: folder.id,
});
console.log('Uploaded:', uploaded.id);
// Get download URL
// The optional second argument rewrites Docker-internal hostnames in the URL to a public base URL
const url = await cosmos.storageClient.getDownloadUrl(
uploaded.id,
'https://<public-storage-base-url>',
);
console.log('Download:', url);
// Delete
await cosmos.storageClient.deleteItem(uploaded.id);
Search RAGflow Collections
if (cosmos.ragflowClient) {
// One collection, one query: returns matching text chunks ([] on failure)
const chunks = await cosmos.ragflowClient.search(
'collection-uuid-1',
'revenue forecast methodology',
{ topK: 5, searchType: 'hybrid' },
);
chunks.forEach((text) => console.log(text.slice(0, 100)));
// Several collections and queries: results grouped by collection
const grouped = await cosmos.ragflowClient.searchCollections(
['collection-uuid-1', 'collection-uuid-2'],
['revenue forecast', 'pricing assumptions'],
);
for (const { collectionId, results } of grouped) {
console.log(collectionId, results.length);
}
}
Parse a Stored Document
parseDocument extracts text from a file that is already in Cosmos Storage. Identify the file with exactly one of fileId or localPath (optionally with a version); passing both or neither throws.
if (cosmos.ragflowClient) {
const parsed = await cosmos.ragflowClient.parseDocument(
{ fileId: 'storage-file-uuid' },
{
enableChunking: true,
maxTokens: 800,
overlapTokens: 100,
metadata: true, // required to receive chunks, elements, and document metadata
},
);
console.log(parsed.mime_type, parsed.file_version);
console.log(parsed.content.raw_text.slice(0, 200));
console.log(parsed.content.chunks?.length ?? 0, 'chunks');
}
Other options are useAzureDi and includeElements. file_name in the response is null when the file is identified by fileId. The call maps to RAGflow POST /api/v1/parse.
Bulk Registry Lookups
Resolve many registry references in one request instead of one call per ID:
// Agent IDs that still exist
const liveAgents = await cosmos.bulkCheckAgents(savedAgentIds);
// Display names for mixed entity references (max 200 per call; missing IDs are omitted)
const names = await cosmos.bulkFetchEntityNames([
{ type: 'agent', id: 'agent-uuid' },
{ type: 'collection', id: 'collection-uuid' },
{ type: 'graph', id: 'graph-uuid' },
]);
const nameById = new Map(names.map((n) => [`${n.type}:${n.id}`, n.name]));
// Stale references of one type
const existing = await cosmos.bulkCheckEntities('skill', skillIds);
const stale = skillIds.filter((id) => !existing.has(id));
Show the User's Cost Limits
getMyCostLimits() returns the signed-in user's effective limits and needs no admin permission:
const limits = await cosmos.costLimits.getMyCostLimits();
for (const scope of ['session', 'weekly', 'monthly'] as const) {
const { enabled, limit_usd, is_custom } = limits[scope];
if (enabled) {
console.log(`${scope}: $${limit_usd ?? 'unlimited'}${is_custom ? ' (custom)' : ''}`);
}
}
// Pair with current usage from Observer
const usage = await cosmos.costLimits.getCurrentPeriodUsage();
Async Agent Requests with Webhooks
// Submit a long-running request
const { request_id } = await cosmos.adkClient.submitAsync({
agent_id: 'report-agent-uuid',
agent_version: '1.0',
user_id: 'user-123',
query: 'Generate a 50-page market analysis for APAC region',
callback_url: 'https://my-app.com/api/webhooks/cosmos',
callback_method: 'POST',
callback_headers: { 'X-Webhook-Secret': 'my-secret' },
});
console.log('Request queued:', request_id);
// Option A: Poll until done
const result = await cosmos.adkClient.pollAsyncUntilDone(request_id);
console.log('Status:', result.status);
console.log('Result:', result.response?.content?.slice(0, 200));
// Option B: List all async requests
const all = await cosmos.adkClient.listAsyncRequests({
user_id: 'user-123',
status: 'completed',
});
console.log(`${all.total} completed requests`);
Observability with Custom Adapter
import { CosmosClient, type ObservabilityAdapter, type ObservabilityEvent } from '@cosmos/sdk';
const consoleAdapter: ObservabilityAdapter = {
adapterId: 'console',
async send(events: ObservabilityEvent[]) {
for (const e of events) {
console.log(`[${e.eventType}] ${e.timestamp}`, JSON.stringify(e.metadata ?? {}));
}
},
};
const cosmos = new CosmosClient({
orgId: 'your-org-uuid',
services: { /* per-service URLs, see Initialize from Environment Variables */ },
authManager: { tokens: { /* ... */ } },
observability: {
adapter: consoleAdapter,
batchSize: 10,
flushIntervalMs: 5000,
enabled: true,
},
});
// Events are automatically emitted during agent execution
// Manual flush
await cosmos.observabilityManager.flush();
// Check stats
const stats = cosmos.observabilityManager.getStats();
console.log(`Sent: ${stats.eventsSent}, Dropped: ${stats.eventsDropped}`);
Context Activation Tools
Create local tools that let an agent load and unload artefact templates during a conversation. Skills and knowledge bases are managed by ADK itself, so this helper only handles artefacts:
import {
buildResourceInventory,
buildInventoryPrompt,
createContextActivationTools,
} from '@cosmos/sdk';
import type { ContextActivationCallbacks } from '@cosmos/sdk';
// KB collections from the registry; append app-specific artefacts yourself
const inventory = await buildResourceInventory(cosmos, { contextIds: ['collection-uuid'] });
inventory.artefacts.push(/* ArtefactSummary entries from your app */);
const callbacks: ContextActivationCallbacks = {
onArtefactLoaded: (id, name, path, reason) => console.log(`Loaded ${name} from ${path}: ${reason}`),
onArtefactUnloaded: (id, name, reason) => console.log(`Unloaded ${name}: ${reason}`),
};
// Returns load_artefact / unload_artefact definitions and handlers
const { definitions, handlers } = createContextActivationTools({
conversationId: 'conversation-uuid',
inventory,
callbacks,
});
// Generate a prompt section describing available resources
const promptSection = buildInventoryPrompt(inventory);
console.log(definitions.length, handlers.length, promptSection);
Authorization Decision
const allowed = await cosmos.authzClient.can({
actor: { type: 'user', id: 'user-uuid' },
action: 'bridge.connect',
resource: { type: 'organization', id: process.env.COSMOS_ORG_ID! },
context: { channel: 'jumpstart' },
});
if (!allowed) {
throw new Error('Bridge access is not allowed for this user');
}
Data Source Registry
const sources = await cosmos.searchDataSources({ query: 'warehouse', engine: 'sqlite' });
const source = await cosmos.createDataSource({
name: 'Assessment SQLite',
engine: 'sqlite',
connection_profile: {
path: '/dms/projects/acme/assessment.db',
},
});
await cosmos.syncDataSourceTool(source.id);
const test = await cosmos.testDataSource(source.id, 'SELECT 1');
console.log(sources.total, test);
Error Handling
import { CosmosApiError, CosmosAuthError } from '@cosmos/sdk';
try {
const agent = await cosmos.getAgent('nonexistent-uuid');
} catch (err) {
if (err instanceof CosmosApiError) {
console.error(`API error ${err.statusCode}: ${err.body}`);
if (err.statusCode === 404) {
console.log('Agent not found');
} else if (err.statusCode === 403) {
console.log('Insufficient permissions');
}
} else if (err instanceof CosmosAuthError) {
console.error('Authentication failed — call setTokens() or login again');
} else {
throw err;
}
}
// Tool execution errors
const result = await cosmos.toolManager.executeTool('missing_tool', {});
if (!result.ok) {
console.error(`Tool error [${result.error?.code}]: ${result.error?.message}`);
}
// Batch tool execution (errors don't stop other tools)
const results = await cosmos.toolManager.executeBatch([
{ name: 'tool_a', input: {} },
{ name: 'tool_b', input: {} },
]);
for (const r of results) {
console.log(r.ok ? '✅' : `❌ ${r.error?.message}`);
}
See Also
- Quick Start — install and first call
- CosmosClient Reference — full client API
- ADKClient Reference — agent execution details
- Auto-generated API reference — TypeDoc output