Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build search applications with vector, hybrid, and semantic search capabilities.
.claude/skills/azure-search-documents-ts/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-13 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-03 | ✗→✓ | ▲ Improved | — | — |
| case-16 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✗ | = Same ✗ | — | — |
Build search applications with vector, hybrid, and semantic search capabilities.
bashnpm install @azure/search-documents @azure/identity
bashAZURE_SEARCH_ENDPOINT=https://<service-name>.search.windows.net AZURE_SEARCH_INDEX_NAME=my-index AZURE_SEARCH_ADMIN_KEY=<admin-key> # Optional if using Entra ID
typescriptimport { SearchClient, SearchIndexClient } from "@azure/search-documents"; import { DefaultAzureCredential } from "@azure/identity"; const endpoint = process.env.AZURE_SEARCH_ENDPOINT!; const indexName = process.env.AZURE_SEARCH_INDEX_NAME!; const credential = new DefaultAzureCredential(); // For searching const searchClient = new SearchClient(endpoint, indexName, credential); // For index management const indexClient = new SearchIndexClient(endpoint, credential);
typescriptimport { SearchIndex, SearchField, VectorSearch } from "@azure/search-documents"; const index: SearchIndex = { name: "products", fields: [ { name: "id", type: "Edm.String", key: true }, { name: "title", type: "Edm.String", searchable: true }, { name: "description", type: "Edm.String", searchable: true }, { name: "category", type: "Edm.String", filterable: true, facetable: true }, { name: "embedding", type: "Collection(Edm.Single)", searchable: true, vectorSearchDimensions: 1536, vectorSearchProfileName: "vector-profile", }, ], vectorSearch: { algorithms: [ { name: "hnsw-algorithm", kind: "hnsw" }, ], profiles: [ { name: "vector-profile", algorithmConfigurationName: "hnsw-algorithm" }, ], }, }; await indexClient.createOrUpdateIndex(index);
typescriptconst documents = [ { id: "1", title: "Widget", description: "A useful widget", category: "Tools", embedding: [...] }, { id: "2", title: "Gadget", description: "A cool gadget", category: "Electronics", embedding: [...] }, ]; const result = await searchClient.uploadDocuments(documents); console.log(`Indexed ${result.results.length} documents`);
typescriptconst results = await searchClient.search("widget", { select: ["id", "title", "description"], filter: "category eq 'Tools'", orderBy: ["title asc"], top: 10, }); for await (const result of results.results) { console.log(`${result.document.title}: ${result.score}`); }
typescriptconst queryVector = await getEmbedding("useful tool"); // Your embedding function const results = await searchClient.search("*", { vectorSearchOptions: { queries: [ { kind: "vector", vector: queryVector, fields: ["embedding"], kNearestNeighborsCount: 10, }, ], }, select: ["id", "title", "description"], }); for await (const result of results.results) { console.log(`${result.document.title}: ${result.score}`); }
typescriptconst queryVector = await getEmbedding("useful tool"); const results = await searchClient.search("tool", { vectorSearchOptions: { queries: [ { kind: "vector", vector: queryVector, fields: ["embedding"], kNearestNeighborsCount: 50, }, ], }, select: ["id", "title", "description"], top: 10, });
typescript// Index must have semantic configuration const index: SearchIndex = { name: "products", fields: [...], semanticSearch: { configurations: [ { name: "semantic-config", prioritizedFields: { titleField: { name: "title" }, contentFields: [{ name: "description" }], }, }, ], }, }; // Search with semantic ranking const results = await searchClient.search("best tool for the job", { queryType: "semantic", semanticSearchOptions: { configurationName: "semantic-config", captions: { captionType: "extractive" }, answers: { answerType: "extractive", count: 3 }, }, select: ["id", "title", "description"], }); for await (const result of results.results) { console.log(`${result.document.title}`); console.log(` Caption: ${result.captions?.[0]?.text}`); console.log(` Reranker Score: ${result.rerankerScore}`); }
typescript// Filter syntax const results = await searchClient.search("*", { filter: "category eq 'Electronics' and price lt 100", facets: ["category,count:10", "brand"], }); // Access facets for (const [facetName, facetResults] of Object.entries(results.facets || {})) { console.log(`${facetName}:`); for (const facet of facetResults) { console.log(` ${facet.value}: ${facet.count}`); } }
typescript// Create suggester in index const index: SearchIndex = { name: "products", fields: [...], suggesters: [ { name: "sg", sourceFields: ["title", "description"] }, ], }; // Autocomplete const autocomplete = await searchClient.autocomplete("wid", "sg", { mode: "twoTerms", top: 5, }); // Suggestions const suggestions = await searchClient.suggest("wid", "sg", { select: ["title"], top: 5, });
typescript// Batch upload, merge, delete const batch = [ { upload: { id: "1", title: "New Item" } }, { merge: { id: "2", title: "Updated Title" } }, { delete: { id: "3" } }, ]; const result = await searchClient.indexDocuments({ actions: batch });
typescriptimport { SearchClient, SearchIndexClient, SearchIndexerClient, SearchIndex, SearchField, SearchOptions, VectorSearch, SemanticSearch, SearchIterator, } from "@azure/search-documents";
uploadDocuments with arrays, not single docsmergeOrUploadDocuments for updatesincludeTotalCount: true sparingly in productionThis skill is applicable to execute the workflow or actions described in the overview.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +18 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.