Generated banner for Azure Cosmos TS
Generated logo for Azure Cosmos TS

Azure Cosmos TS

MicrosoftOfficialCreated by Microsoft

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

Azure Cosmos DB JavaScript/TypeScript SDK (@azure/cosmos) for data plane operations. Use for CRUD operations on documents, queries, bulk operations, and container management. Triggers: "Cosmos DB", "@azure/cosmos", "CosmosClient", "document CRUD", "NoSQL queries", "bulk operations", "partition key", "container.items".
— the description by Microsoft

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The azure-cosmos-ts skill provides the interface for the @azure/cosmos SDK, enabling developers to interact with Azure Cosmos DB NoSQL data plane resources. It covers document CRUD operations, NoSQL query execution, bulk data processing, and container management within a TypeScript or JavaScript environment.

What it does

  • Performs CRUD operations on documents within Cosmos DB containers.
  • Executes NoSQL queries using parameterized query specifications.
  • Manages bulk operations for batch processing of data.
  • Handles container and database resource interactions.
  • Supports authentication via Microsoft Entra ID or account keys.
  • Implements optimistic concurrency control using ETags.

When to use it

  • When building applications that require data storage in Azure Cosmos DB.
  • When performing document-level operations in a Node.js environment.
  • When implementing batch data processing or bulk updates.
  • When needing to execute complex NoSQL queries with pagination.

Advantages

  • Provides native TypeScript support for type-safe database interactions.
  • Includes built-in support for bulk operation execution.
  • Supports secure authentication patterns using Azure Identity.
  • Offers structured error handling for common database response codes.

Usage examples

“How do I create a new document in my Cosmos DB container using the TypeScript SDK?”
The agent provides a code snippet using container.items.create, demonstrating how to define an interface for the document and pass it to the SDK method.
“Show me how to perform a parameterized query to find products by category.”
The agent generates a SqlQuerySpec object with the query string and parameters array, then demonstrates calling container.items.query with that specification.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/microsoft/azure-cosmos-ts/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip azure-cosmos-ts.zip -d .agents/skills

Documentation by Microsoft

@azure/cosmos (TypeScript/JavaScript)

Data plane SDK for Azure Cosmos DB NoSQL API operations — CRUD on documents, queries, bulk operations.

⚠️ Data vs Management Plane

  • This SDK (@azure/cosmos): CRUD operations on documents, queries, stored procedures

  • Management SDK (@azure/arm-cosmosdb): Create accounts, databases, containers via ARM

Installation

npm install @azure/cosmos @azure/identity

Current Version: 4.9.0
Node.js: >= 20.0.0

Environment Variables

COSMOS_ENDPOINT=https://<account>.documents.azure.com:443/
COSMOS_DATABASE=<database-name>
COSMOS_CONTAINER=<container-name>
# For key-based auth only (prefer AAD)
COSMOS_KEY=<account-key>
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production

Authentication

import { CosmosClient } from "@azure/cosmos";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();

const client = new CosmosClient({
  endpoint: process.env.COSMOS_ENDPOINT!,
  aadCredentials: credential,
});
Key-Based Authentication
import { CosmosClient } from "@azure/cosmos";

// Option 1: Endpoint + Key
const client = new CosmosClient({
  endpoint: process.env.COSMOS_ENDPOINT!,
  key: process.env.COSMOS_KEY!,
});

// Option 2: Connection String
const client = new CosmosClient(process.env.COSMOS_CONNECTION_STRING!);

Resource Hierarchy

CosmosClient
└── Database
    └── Container
        ├── Items (documents)
        ├── Scripts (stored procedures, triggers, UDFs)
        └── Conflicts

Core Operations

Database & Container Setup
const { database } = await client.databases.createIfNotExists({
  id: "my-database",
});

const { container } = await database.containers.createIfNotExists({
  id: "my-container",
  partitionKey: { paths: ["/partitionKey"] },
});
Create Document
interface Product {
  id: string;
  partitionKey: string;
  name: string;
  price: number;
}

const item: Product = {
  id: "product-1",
  partitionKey: "electronics",
  name: "Laptop",
  price: 999.99,
};

const { resource } = await container.items.create<Product>(item);
Read Document
const { resource } = await container
  .item("product-1", "electronics") // id, partitionKey
  .read<Product>();

if (resource) {
  console.log(resource.name);
}
Update Document (Replace)
const { resource: existing } = await container
  .item("product-1", "electronics")
  .read<Product>();

if (existing) {
  existing.price = 899.99;
  const { resource: updated } = await container
    .item("product-1", "electronics")
    .replace<Product>(existing);
}
Upsert Document
const item: Product = {
  id: "product-1",
  partitionKey: "electronics",
  name: "Laptop Pro",
  price: 1299.99,
};

const { resource } = await container.items.upsert<Product>(item);
Delete Document
await container.item("product-1", "electronics").delete();
Patch Document (Partial Update)
import { PatchOperation } from "@azure/cosmos";

const operations: PatchOperation[] = [
  { op: "replace", path: "/price", value: 799.99 },
  { op: "add", path: "/discount", value: true },
  { op: "remove", path: "/oldField" },
];

const { resource } = await container
  .item("product-1", "electronics")
  .patch<Product>(operations);

Queries

Simple Query
const { resources } = await container.items
  .query<Product>("SELECT * FROM c WHERE c.price < 1000")
  .fetchAll();
import { SqlQuerySpec } from "@azure/cosmos";

const querySpec: SqlQuerySpec = {
  query: "SELECT * FROM c WHERE c.partitionKey = @category AND c.price < @maxPrice",
  parameters: [
    { name: "@category", value: "electronics" },
    { name: "@maxPrice", value: 1000 },
  ],
};

const { resources } = await container.items
  .query<Product>(querySpec)
  .fetchAll();
Query with Pagination
const queryIterator = container.items.query<Product>(querySpec, {
  maxItemCount: 10, // Items per page
});

while (queryIterator.hasMoreResults()) {
  const { resources, continuationToken } = await queryIterator.fetchNext();
  console.log(`Page with ${resources?.length} items`);
  // Use continuationToken for next page if needed
}
Cross-Partition Query
const { resources } = await container.items
  .query<Product>(
    "SELECT * FROM c WHERE c.price > 500",
    { enableCrossPartitionQuery: true }
  )
  .fetchAll();

Bulk Operations

Execute Bulk Operations
import { BulkOperationType, OperationInput } from "@azure/cosmos";

const operations: OperationInput[] = [
  {
    operationType: BulkOperationType.Create,
    resourceBody: { id: "1", partitionKey: "cat-a", name: "Item 1" },
  },
  {
    operationType: BulkOperationType.Upsert,
    resourceBody: { id: "2", partitionKey: "cat-a", name: "Item 2" },
  },
  {
    operationType: BulkOperationType.Read,
    id: "3",
    partitionKey: "cat-b",
  },
  {
    operationType: BulkOperationType.Replace,
    id: "4",
    partitionKey: "cat-b",
    resourceBody: { id: "4", partitionKey: "cat-b", name: "Updated" },
  },
  {
    operationType: BulkOperationType.Delete,
    id: "5",
    partitionKey: "cat-c",
  },
  {
    operationType: BulkOperationType.Patch,
    id: "6",
    partitionKey: "cat-c",
    resourceBody: {
      operations: [{ op: "replace", path: "/name", value: "Patched" }],
    },
  },
];

const response = await container.items.executeBulkOperations(operations);

response.forEach((result, index) => {
  if (result.statusCode >= 200 && result.statusCode < 300) {
    console.log(`Operation ${index} succeeded`);
  } else {
    console.error(`Operation ${index} failed: ${result.statusCode}`);
  }
});

Partition Keys

Simple Partition Key
const { container } = await database.containers.createIfNotExists({
  id: "products",
  partitionKey: { paths: ["/category"] },
});
Hierarchical Partition Key (MultiHash)
import { PartitionKeyDefinitionVersion, PartitionKeyKind } from "@azure/cosmos";

const { container } = await database.containers.createIfNotExists({
  id: "orders",
  partitionKey: {
    paths: ["/tenantId", "/userId", "/sessionId"],
    version: PartitionKeyDefinitionVersion.V2,
    kind: PartitionKeyKind.MultiHash,
  },
});

// Operations require array of partition key values
const { resource } = await container.items.create({
  id: "order-1",
  tenantId: "tenant-a",
  userId: "user-123",
  sessionId: "session-xyz",
  total: 99.99,
});

// Read with hierarchical partition key
const { resource: order } = await container
  .item("order-1", ["tenant-a", "user-123", "session-xyz"])
  .read();

Error Handling

import { ErrorResponse } from "@azure/cosmos";

try {
  const { resource } = await container.item("missing", "pk").read();
} catch (error) {
  if (error instanceof ErrorResponse) {
    switch (error.code) {
      case 404:
        console.log("Document not found");
        break;
      case 409:
        console.log("Conflict - document already exists");
        break;
      case 412:
        console.log("Precondition failed (ETag mismatch)");
        break;
      case 429:
        console.log("Rate limited - retry after:", error.retryAfterInMs);
        break;
      default:
        console.error(`Cosmos error ${error.code}: ${error.message}`);
    }
  }
  throw error;
}

Optimistic Concurrency (ETags)

// Read with ETag
const { resource, etag } = await container
  .item("product-1", "electronics")
  .read<Product>();

if (resource && etag) {
  resource.price = 899.99;
  
  try {
    // Replace only if ETag matches
    await container.item("product-1", "electronics").replace(resource, {
      accessCondition: { type: "IfMatch", condition: etag },
    });
  } catch (error) {
    if (error instanceof ErrorResponse && error.code === 412) {
      console.log("Document was modified by another process");
    }
  }
}

TypeScript Types Reference

import {
  // Client & Resources
  CosmosClient,
  Database,
  Container,
  Item,
  Items,
  
  // Operations
  OperationInput,
  BulkOperationType,
  PatchOperation,
  
  // Queries
  SqlQuerySpec,
  SqlParameter,
  FeedOptions,
  
  // Partition Keys
  PartitionKeyDefinition,
  PartitionKeyDefinitionVersion,
  PartitionKeyKind,
  
  // Responses
  ItemResponse,
  FeedResponse,
  ResourceResponse,
  
  // Errors
  ErrorResponse,
} from "@azure/cosmos";

Best Practices

  1. Use Microsoft Entra Token Credential — Use DefaultAzureCredential for local development; use ManagedIdentityCredential or WorkloadIdentityCredential for production

  2. Always use parameterized queries — Prevents injection, improves plan caching

  3. Specify partition key — Avoid cross-partition queries when possible

  4. Use bulk operations — For multiple writes, use executeBulkOperations

  5. Handle 429 errors — Implement retry logic with exponential backoff

  6. Use ETags for concurrency — Prevent lost updates in concurrent scenarios

  7. Close client on shutdown — Call client.dispose() in cleanup

Common Patterns

Service Layer Pattern
export class ProductService {
  private container: Container;

  constructor(client: CosmosClient) {
    this.container = client
      .database(process.env.COSMOS_DATABASE!)
      .container(process.env.COSMOS_CONTAINER!);
  }

  async getById(id: string, category: string): Promise<Product | null> {
    try {
      const { resource } = await this.container
        .item(id, category)
        .read<Product>();
      return resource ?? null;
    } catch (error) {
      if (error instanceof ErrorResponse && error.code === 404) {
        return null;
      }
      throw error;
    }
  }

  async create(product: Omit<Product, "id">): Promise<Product> {
    const item = { ...product, id: crypto.randomUUID() };
    const { resource } = await this.container.items.create<Product>(item);
    return resource!;
  }

  async findByCategory(category: string): Promise<Product[]> {
    const querySpec: SqlQuerySpec = {
      query: "SELECT * FROM c WHERE c.partitionKey = @category",
      parameters: [{ name: "@category", value: category }],
    };
    const { resources } = await this.container.items
      .query<Product>(querySpec)
      .fetchAll();
    return resources;
  }
}
SDKPurposeInstall
@azure/cosmosData plane (this SDK)npm install @azure/cosmos
@azure/arm-cosmosdbManagement plane (ARM)npm install @azure/arm-cosmosdb
@azure/identityAuthenticationnpm install @azure/identity

Files

references/bulk-operations.md

Bulk Operations Reference

High-throughput bulk operations for Azure Cosmos DB using the @azure/cosmos TypeScript SDK.

Overview

Bulk operations enable efficient batch processing of Create, Upsert, Read, Replace, Delete, and Patch operations with automatic batching and retry handling.

BulkOperationType Enum

import { BulkOperationType } from "@azure/cosmos";

enum BulkOperationType {
  Create = "Create",
  Upsert = "Upsert",
  Read = "Read",
  Replace = "Replace",
  Delete = "Delete",
  Patch = "Patch"
}

OperationInput Interface

import { OperationInput, PatchOperation } from "@azure/cosmos";

// Base operation types
interface CreateOperationInput {
  operationType: BulkOperationType.Create;
  resourceBody: Record<string, unknown>;
  partitionKey?: PartitionKey;
}

interface UpsertOperationInput {
  operationType: BulkOperationType.Upsert;
  resourceBody: Record<string, unknown>;
  partitionKey?: PartitionKey;
}

interface ReadOperationInput {
  operationType: BulkOperationType.Read;
  id: string;
  partitionKey: PartitionKey;
}

interface ReplaceOperationInput {
  operationType: BulkOperationType.Replace;
  id: string;
  resourceBody: Record<string, unknown>;
  partitionKey?: PartitionKey;
}

interface DeleteOperationInput {
  operationType: BulkOperationType.Delete;
  id: string;
  partitionKey: PartitionKey;
}

interface PatchOperationInput {
  operationType: BulkOperationType.Patch;
  id: string;
  partitionKey: PartitionKey;
  resourceBody: {
    operations: PatchOperation[];
    condition?: string;
  };
}

type OperationInput = 
  | CreateOperationInput
  | UpsertOperationInput
  | ReadOperationInput
  | ReplaceOperationInput
  | DeleteOperationInput
  | PatchOperationInput;

Basic Bulk Operations

import { 
  CosmosClient, 
  BulkOperationType, 
  OperationInput,
  BulkOperationResponse 
} from "@azure/cosmos";

const client = new CosmosClient({ endpoint, key });
const container = client.database("mydb").container("mycontainer");

// Mixed bulk operations
const operations: OperationInput[] = [
  // Create
  {
    operationType: BulkOperationType.Create,
    resourceBody: { 
      id: "item-1", 
      partitionKey: "category-a", 
      name: "Item 1",
      price: 10.99
    }
  },
  // Upsert
  {
    operationType: BulkOperationType.Upsert,
    resourceBody: { 
      id: "item-2", 
      partitionKey: "category-a", 
      name: "Item 2",
      price: 20.99
    }
  },
  // Read
  {
    operationType: BulkOperationType.Read,
    id: "item-3",
    partitionKey: "category-b"
  },
  // Replace
  {
    operationType: BulkOperationType.Replace,
    id: "item-4",
    partitionKey: "category-b",
    resourceBody: { 
      id: "item-4", 
      partitionKey: "category-b", 
      name: "Updated Item 4",
      price: 15.99
    }
  },
  // Delete
  {
    operationType: BulkOperationType.Delete,
    id: "item-5",
    partitionKey: "category-c"
  }
];

const response: BulkOperationResponse = await container.items.bulk(operations);

// Process results
response.forEach((result, index) => {
  if (result.statusCode >= 200 && result.statusCode < 300) {
    console.log(`Operation ${index} succeeded: ${result.statusCode}`);
    if (result.resourceBody) {
      console.log(`  Resource: ${JSON.stringify(result.resourceBody)}`);
    }
  } else {
    console.error(`Operation ${index} failed: ${result.statusCode}`);
  }
});

Bulk Create Pattern

interface Product {
  id: string;
  partitionKey: string;
  name: string;
  price: number;
}

async function bulkCreate(
  container: Container,
  items: Product[]
): Promise<{ succeeded: number; failed: number }> {
  const operations: OperationInput[] = items.map(item => ({
    operationType: BulkOperationType.Create,
    resourceBody: item
  }));

  const response = await container.items.bulk(operations);

  let succeeded = 0;
  let failed = 0;

  response.forEach((result, index) => {
    if (result.statusCode === 201) {
      succeeded++;
    } else {
      failed++;
      console.error(`Failed to create item ${items[index].id}: ${result.statusCode}`);
    }
  });

  return { succeeded, failed };
}

// Usage
const products: Product[] = [
  { id: "p1", partitionKey: "electronics", name: "Laptop", price: 999 },
  { id: "p2", partitionKey: "electronics", name: "Phone", price: 599 },
  { id: "p3", partitionKey: "clothing", name: "Shirt", price: 29 },
];

const result = await bulkCreate(container, products);
console.log(`Created: ${result.succeeded}, Failed: ${result.failed}`);

Bulk Upsert Pattern

async function bulkUpsert<T extends { id: string }>(
  container: Container,
  items: T[]
): Promise<BulkOperationResponse> {
  const operations: OperationInput[] = items.map(item => ({
    operationType: BulkOperationType.Upsert,
    resourceBody: item as Record<string, unknown>
  }));

  return container.items.bulk(operations);
}

// Usage - creates if not exists, updates if exists
const updates = [
  { id: "p1", partitionKey: "electronics", name: "Laptop Pro", price: 1299 },
  { id: "p4", partitionKey: "electronics", name: "Tablet", price: 399 },
];

await bulkUpsert(container, updates);

Bulk Delete Pattern

interface DeleteItem {
  id: string;
  partitionKey: string;
}

async function bulkDelete(
  container: Container,
  items: DeleteItem[]
): Promise<{ deleted: number; notFound: number; errors: number }> {
  const operations: OperationInput[] = items.map(item => ({
    operationType: BulkOperationType.Delete,
    id: item.id,
    partitionKey: item.partitionKey
  }));

  const response = await container.items.bulk(operations);

  let deleted = 0;
  let notFound = 0;
  let errors = 0;

  response.forEach((result) => {
    if (result.statusCode === 204) {
      deleted++;
    } else if (result.statusCode === 404) {
      notFound++;
    } else {
      errors++;
    }
  });

  return { deleted, notFound, errors };
}

// Usage
const toDelete: DeleteItem[] = [
  { id: "p1", partitionKey: "electronics" },
  { id: "p2", partitionKey: "electronics" },
];

const deleteResult = await bulkDelete(container, toDelete);
console.log(`Deleted: ${deleteResult.deleted}, Not found: ${deleteResult.notFound}`);

Bulk Patch Pattern

import { PatchOperation } from "@azure/cosmos";

interface PatchItem {
  id: string;
  partitionKey: string;
  operations: PatchOperation[];
}

async function bulkPatch(
  container: Container,
  items: PatchItem[]
): Promise<BulkOperationResponse> {
  const operations: OperationInput[] = items.map(item => ({
    operationType: BulkOperationType.Patch,
    id: item.id,
    partitionKey: item.partitionKey,
    resourceBody: {
      operations: item.operations
    }
  }));

  return container.items.bulk(operations);
}

// Usage - partial updates
const patches: PatchItem[] = [
  {
    id: "p1",
    partitionKey: "electronics",
    operations: [
      { op: "replace", path: "/price", value: 899 },
      { op: "add", path: "/onSale", value: true }
    ]
  },
  {
    id: "p2",
    partitionKey: "electronics",
    operations: [
      { op: "incr", path: "/viewCount", value: 1 }
    ]
  }
];

await bulkPatch(container, patches);

Chunked Bulk Operations

For very large datasets, process in chunks to manage memory and handle rate limiting:

async function bulkOperationsChunked<T extends Record<string, unknown>>(
  container: Container,
  items: T[],
  operationType: BulkOperationType.Create | BulkOperationType.Upsert,
  chunkSize: number = 100
): Promise<{ succeeded: number; failed: number }> {
  let succeeded = 0;
  let failed = 0;

  // Process in chunks
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    
    const operations: OperationInput[] = chunk.map(item => ({
      operationType,
      resourceBody: item
    }));

    const response = await container.items.bulk(operations);

    response.forEach(result => {
      if (result.statusCode >= 200 && result.statusCode < 300) {
        succeeded++;
      } else {
        failed++;
      }
    });

    console.log(`Processed ${Math.min(i + chunkSize, items.length)}/${items.length}`);
  }

  return { succeeded, failed };
}

// Usage
const largeDataset = generateItems(10000);
const result = await bulkOperationsChunked(container, largeDataset, BulkOperationType.Create, 100);

Bulk Operation Response

interface BulkOperationResponse extends Array<OperationResponse> {}

interface OperationResponse {
  /** HTTP status code */
  statusCode: number;
  
  /** Request charge (RUs) for this operation */
  requestCharge: number;
  
  /** ETag of the resource (for successful operations) */
  eTag?: string;
  
  /** Resource body (for read/create/upsert/replace) */
  resourceBody?: Record<string, unknown>;
}

// Common status codes
// 200 - Read successful
// 201 - Create successful
// 204 - Delete successful
// 400 - Bad request
// 404 - Not found
// 409 - Conflict (duplicate id on create)
// 412 - Precondition failed
// 429 - Rate limited

Error Handling

async function bulkWithRetry(
  container: Container,
  operations: OperationInput[],
  maxRetries: number = 3
): Promise<BulkOperationResponse> {
  let response = await container.items.bulk(operations);
  let retryCount = 0;

  while (retryCount < maxRetries) {
    const failedOps: OperationInput[] = [];
    const failedIndices: number[] = [];

    response.forEach((result, index) => {
      if (result.statusCode === 429) {
        // Rate limited - retry these
        failedOps.push(operations[index]);
        failedIndices.push(index);
      }
    });

    if (failedOps.length === 0) {
      break; // All succeeded
    }

    console.log(`Retrying ${failedOps.length} rate-limited operations...`);
    
    // Exponential backoff
    await new Promise(resolve => 
      setTimeout(resolve, Math.pow(2, retryCount) * 1000)
    );

    const retryResponse = await container.items.bulk(failedOps);
    
    // Update original response with retry results
    retryResponse.forEach((result, i) => {
      response[failedIndices[i]] = result;
    });

    retryCount++;
  }

  return response;
}

Best Practices

  1. Batch by partition key — Operations in the same partition are more efficient

  2. Use appropriate chunk sizes — 100-500 items per batch is typical

  3. Handle 429 errors — Implement retry with exponential backoff

  4. Monitor RU consumption — Check requestCharge in responses

  5. Use Upsert for idempotency — Safer than Create for retry scenarios

  6. Validate before bulk — Check data before submitting large batches

  7. Process results — Always check individual operation status codes

Performance Considerations

FactorRecommendation
Chunk size100-500 items per batch
Partition distributionSpread across partitions for parallelism
Operation typeUpsert is safer than Create for retries
Error handlingImplement retry logic for 429s
MemoryStream large datasets, don't load all into memory

See Also

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs Node.js

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Created by
Microsoft
Publisher
Microsoft (Official)
Source
microsoft/skills · .github/plugins/azure-sdk-typescript/skills/azure-cosmos-ts · commit 0696600
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download azure-cosmos-ts.zip