Skills meilisearch
git clone https://github.com/TerminalSkills/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/TerminalSkills/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/meilisearch" ~/.claude/skills/terminalskills-skills-meilisearch && rm -rf "$T"
skills/meilisearch/SKILL.md- curl piped into shell
- makes HTTP requests (curl)
- references API keys
Meilisearch
Overview
Meilisearch is a fast, open-source search engine designed for instant, typo-tolerant search experiences. Unlike Elasticsearch, it requires zero configuration to get started — add documents, start searching. This skill covers deployment, document indexing, search queries, faceted filtering, sorting, relevancy tuning, multi-tenancy with tenant tokens, and integration with frontend (InstantSearch) and backend (Node.js, Python) applications.
Instructions
Step 1: Installation and Deployment
# Docker (recommended for production) docker run -d --name meilisearch \ -p 7700:7700 \ -v meili_data:/meili_data \ -e MEILI_MASTER_KEY="your-master-key-min-16-chars" \ getmeili/meilisearch:latest # Binary install (Linux) curl -L https://install.meilisearch.com | sh ./meilisearch --master-key="your-master-key-min-16-chars" # Verify curl http://localhost:7700/health # {"status":"available"}
Step 2: Index Documents
# Add documents via REST API curl -X POST 'http://localhost:7700/indexes/products/documents' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer your-master-key' \ --data-binary '[ {"id": 1, "title": "iPhone 15 Pro", "category": "phones", "brand": "Apple", "price": 999}, {"id": 2, "title": "Galaxy S24 Ultra", "category": "phones", "brand": "Samsung", "price": 1199}, {"id": 3, "title": "MacBook Pro M3", "category": "laptops", "brand": "Apple", "price": 1999} ]'
With the Node.js SDK:
// index_products.js — Index documents from a database into Meilisearch import { MeiliSearch } from 'meilisearch' const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'your-master-key', }) const index = client.index('products') // Add documents (Meilisearch auto-detects the primary key) await index.addDocuments([ { id: 1, title: 'iPhone 15 Pro', category: 'phones', brand: 'Apple', price: 999 }, { id: 2, title: 'Galaxy S24 Ultra', category: 'phones', brand: 'Samsung', price: 1199 }, ]) // Configure searchable attributes (which fields to search) await index.updateSearchableAttributes(['title', 'brand', 'category']) // Configure filterable attributes (for faceted search) await index.updateFilterableAttributes(['category', 'brand', 'price']) // Configure sortable attributes await index.updateSortableAttributes(['price', 'title'])
Step 3: Search Queries
// search.js — Search with filters, facets, and highlighting const results = await index.search('iphone', { limit: 20, offset: 0, filter: 'price < 1500 AND category = "phones"', sort: ['price:asc'], facets: ['category', 'brand'], attributesToHighlight: ['title'], attributesToCrop: ['description'], cropLength: 50, }) // results.hits — matching documents with _formatted (highlighted) versions // results.facetDistribution — { category: { phones: 2 }, brand: { Apple: 1, Samsung: 1 } } // results.estimatedTotalHits — total matches // results.processingTimeMs — typically <50ms
REST API equivalent:
curl -X POST 'http://localhost:7700/indexes/products/search' \ -H 'Content-Type: application/json' \ -H 'Authorization: Bearer your-search-key' \ --data '{"q": "iphon", "filter": "price < 1500", "facets": ["category", "brand"]}' # Note: "iphon" still matches "iPhone" — typo tolerance is on by default
Step 4: Faceted Search and Filtering
// faceted_search.js — Build an e-commerce filter sidebar // First, configure filterable attributes await index.updateFilterableAttributes(['category', 'brand', 'price', 'rating', 'in_stock']) // Search with multiple filters const results = await index.search('laptop', { filter: [ 'category = "laptops"', 'brand IN ["Apple", "Lenovo", "Dell"]', 'price >= 500 AND price <= 2000', 'in_stock = true', ], facets: ['brand', 'category', 'rating'], }) // Geo search (for store locators, nearby results) await index.updateFilterableAttributes(['_geo']) const nearby = await index.search('coffee', { filter: '_geoRadius(48.8566, 2.3522, 5000)', // 5km radius from Paris center sort: ['_geoPoint(48.8566, 2.3522):asc'], })
Step 5: Relevancy Tuning
// relevancy.js — Fine-tune ranking rules and synonyms // Default ranking: words → typo → proximity → attribute → sort → exactness await index.updateRankingRules([ 'words', 'typo', 'proximity', 'attribute', 'sort', 'exactness', 'price:asc', // custom: cheaper products rank higher ]) // Synonyms await index.updateSynonyms({ 'phone': ['smartphone', 'mobile', 'cell phone'], 'laptop': ['notebook', 'ultrabook'], 'tv': ['television', 'monitor', 'display'], }) // Stop words (ignored in search) await index.updateStopWords(['the', 'a', 'an', 'is', 'at', 'of']) // Typo tolerance settings await index.updateTypoTolerance({ enabled: true, minWordSizeForTypos: { oneTypo: 4, twoTypos: 8 }, disableOnAttributes: ['sku', 'isbn'], // exact match for codes })
Step 6: Multi-Tenancy with Tenant Tokens
// tenant_tokens.js — Secure multi-tenant search // Each tenant can only search their own data import { MeiliSearch } from 'meilisearch' import crypto from 'crypto' function generateTenantToken(apiKeyUid, tenantId, searchRules) { /** * Generate a JWT token that restricts search to a specific tenant. * Args: * apiKeyUid: UID of the API key (not the key itself) * tenantId: The tenant/organization ID to restrict access to * searchRules: Index-level filter rules */ const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'your-master-key' }) return client.generateTenantToken(apiKeyUid, searchRules, { expiresAt: new Date(Date.now() + 3600 * 1000), // 1 hour }) } // Usage: frontend gets a token that auto-filters by their org_id const token = generateTenantToken('key-uid', 'org_123', { products: { filter: 'org_id = org_123' }, })
Step 7: Frontend Integration with InstantSearch
// SearchUI.jsx — React component with Meilisearch InstantSearch import { InstantSearch, SearchBox, Hits, RefinementList, Pagination } from 'react-instantsearch' import { instantMeiliSearch } from '@meilisearch/instant-meilisearch' const { searchClient } = instantMeiliSearch('http://localhost:7700', 'your-search-key') export function SearchPage() { return ( <InstantSearch indexName="products" searchClient={searchClient}> <SearchBox placeholder="Search products..." /> <div style={{ display: 'flex', gap: '2rem' }}> <div> <h3>Brand</h3> <RefinementList attribute="brand" /> <h3>Category</h3> <RefinementList attribute="category" /> </div> <div> <Hits hitComponent={ProductHit} /> <Pagination /> </div> </div> </InstantSearch> ) } function ProductHit({ hit }) { return ( <div> <h4>{hit.title}</h4> <p>${hit.price} — {hit.brand}</p> </div> ) }
Examples
Example 1: Add instant search to an e-commerce product catalog
User prompt: "I have a Next.js e-commerce app with 50,000 products in PostgreSQL. Add a search bar with instant results, typo tolerance, and category/brand filters."
The agent will:
- Deploy Meilisearch via Docker with a master key.
- Write a sync script that reads products from PostgreSQL and indexes them in Meilisearch.
- Configure searchable, filterable, and sortable attributes.
- Add synonyms for common product terms.
- Build a React search component using InstantSearch and
.@meilisearch/instant-meilisearch - Set up a cron job or webhook to re-sync products when the database changes.
Example 2: Build a documentation search for a developer portal
User prompt: "Add search to our documentation site. We have 500+ markdown files. Users should be able to search by title and content with highlighted results."
The agent will:
- Write a script to parse all markdown files, extract frontmatter and content, and index them as documents.
- Configure searchable attributes with
ranked abovetitle
.content - Enable highlighting and content cropping for search result snippets.
- Build a search modal component that shows results as the user types.
Guidelines
- Meilisearch is designed for end-user-facing search (product catalogs, documentation, content). For log analytics or time-series data, use Elasticsearch or ClickHouse instead.
- Always set a master key in production — without it, anyone can modify your indexes.
- Use the search API key (not master key) on the frontend. Generate it via the keys API or use tenant tokens for multi-tenant apps.
- Index updates are asynchronous —
returns a task ID. Poll the task status or use webhooks for sync confirmation.addDocuments - Meilisearch stores all data in memory-mapped files. For 1M documents with moderate fields, expect ~1-4 GB RAM. Plan capacity accordingly.
- Re-index from your source of truth (database) on a schedule rather than trying to keep Meilisearch in perfect sync — it's simpler and more reliable.