Sign in

TypeScript SDK — Code Examples

Recipes and patterns for common Cosmos SDK use cases

sdktypescriptexamplesrecipes

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

typescript
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

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

typescript
// 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

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

typescript
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

typescript
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

typescript
// 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

typescript
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.

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

typescript
// 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:

typescript
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

typescript
// 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

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

typescript
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

typescript
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

typescript
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

typescript
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