Learn-skills.dev ai-provider-mistral-sdk
Official Mistral AI TypeScript SDK patterns — client setup, chat completions, streaming, function calling, structured outputs, embeddings, vision, Codestral FIM, and production best practices
git clone https://github.com/NeverSight/learn-skills.dev
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/agents-inc/skills/ai-provider-mistral-sdk" ~/.claude/skills/neversight-learn-skills-dev-ai-provider-mistral-sdk && rm -rf "$T"
data/skills-md/agents-inc/skills/ai-provider-mistral-sdk/SKILL.mdMistral SDK Patterns
Quick Guide: Use
(ESM-only) to interact with Mistral's API. Use@mistralai/mistralaifor chat,client.chat.complete()for streaming (async iterable viaclient.chat.stream()),for awaitwith a Zod schema for structured outputs, andclient.chat.parse()for Codestral fill-in-middle code completion. The SDK usesclient.fim.complete()(camelCase) notresponseFormat. Streaming events expose content viaresponse_format. Retries default toevent.data.choices[0]?.delta?.content-- you must configure them explicitly for production.strategy: "none"
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use
(camelCase) in SDK calls -- NOT responseFormat
(snake_case). The SDK uses camelCase property names throughout.)response_format
(You MUST configure retries explicitly -- the SDK defaults to
(no retries), unlike OpenAI's SDK which retries automatically)strategy: "none"
(You MUST consume streaming results with
and access content via for await (const event of result)
-- the event shape differs from OpenAI)event.data.choices[0]?.delta?.content
(You MUST never hardcode API keys -- use
with the bracket notation the SDK documents)process.env["MISTRAL_API_KEY"]
(You MUST use
with a Zod schema for structured outputs -- NOT manual client.chat.parse()
on completion content)JSON.parse()
</critical_requirements>
Auto-detection: Mistral, mistral, @mistralai/mistralai, client.chat.complete, client.chat.stream, client.chat.parse, client.fim.complete, client.embeddings.create, mistral-large, mistral-small, codestral, pixtral, ministral, magistral, devstral, MISTRAL_API_KEY, responseFormat, mistral-embed
When to use:
- Building applications that call Mistral models directly (Mistral Large, Small, Codestral, etc.)
- Implementing chat completions with SSE streaming
- Using Codestral for code generation and fill-in-middle (FIM) completion
- Extracting structured data with
and Zod schemasclient.chat.parse() - Implementing function calling / tool use
- Creating embeddings for RAG pipelines or semantic search
- Processing images with vision-capable models (Mistral Small, Medium, Large, Ministral)
- Using Mistral Agents API for pre-configured agent completions
Key patterns covered:
- Client initialization and configuration (retries, timeouts, custom HTTP client)
- Chat completions (
) and streaming (chat.complete
)chat.stream - Structured outputs with
and Zod schemaschat.parse() - Function calling / tool use with tool call loop
- Embeddings (
) withembeddings.createmistral-embed - Vision (image URL / base64 with vision-capable models)
- Codestral FIM (
) for code completionfim.complete - Error handling, retry configuration, and production patterns
When NOT to use:
- Multi-provider applications where you need to switch between Mistral, OpenAI, Anthropic, etc. -- use a unified provider SDK
- React-specific chat UI hooks (
) -- use a framework-integrated AI SDKuseChat - When you need OpenAI-compatible endpoints -- use OpenAI SDK with Mistral's compatible endpoint instead
Examples Index
- Core: Setup & Configuration -- Client init, production config, error handling, retries, custom HTTP client
- Chat & Streaming -- Chat completions, streaming with async iteration, multi-turn
- Structured Output --
with Zod, JSON mode, typed responseschat.parse() - Function Calling -- Tool definitions, tool call loop, streaming tools
- Embeddings & Vision -- Semantic search, image analysis with vision-capable models
- Codestral FIM -- Fill-in-middle code completion, code generation
- Quick API Reference -- Model IDs, method signatures, error types, configuration options
<philosophy>
Philosophy
The
@mistralai/mistralai SDK is auto-generated from Mistral's OpenAPI spec using Speakeasy, giving you a thin, type-safe wrapper over the REST API. It is ESM-only and uses camelCase property names (not snake_case like the REST API).
Core principles:
- ESM-only -- The package is published as ESM only. CommonJS projects must use
. This is a hard constraint, not optional.await import() - camelCase API surface -- SDK properties use camelCase (
,responseFormat
,maxTokens
) even though the REST API uses snake_case. This catches OpenAI SDK migrants who writetoolChoice
.response_format - No automatic retries -- Unlike OpenAI's SDK (2 retries by default), Mistral defaults to
. You must configure retries explicitly for production.strategy: "none" - Streaming via async iterables --
returns anchat.stream()
consumed withEventStream
. Events have afor await...of
wrapper:data
.event.data.choices[0]?.delta?.content - Structured outputs via
-- Pass a Zod schema directly tochat.parse()
and accessresponseFormat
for typed results. No manual JSON schema construction needed.message.parsed - Codestral FIM -- Dedicated
endpoint for fill-in-middle code completion, separate from chat.fim.complete()
When to use the Mistral SDK directly:
- You only use Mistral models and want the simplest, most direct integration
- You need Mistral-specific features (Codestral FIM, Mistral Agents, Voxtral audio)
- You want minimal dependencies and zero abstraction overhead
- You need the latest Mistral API features on day one
When NOT to use:
- You need to switch between providers (OpenAI, Anthropic, Mistral) -- use a unified provider SDK
- You want React-specific chat UI hooks -- use a framework-integrated AI SDK
- You want an OpenAI-compatible wrapper -- Mistral exposes an OpenAI-compatible endpoint, use the OpenAI SDK for that
<patterns>
Core Patterns
Pattern 1: Client Setup
Initialize the Mistral client. It reads
MISTRAL_API_KEY from the environment.
// lib/mistral.ts -- basic setup import { Mistral } from "@mistralai/mistralai"; const client = new Mistral({ apiKey: process.env["MISTRAL_API_KEY"] ?? "", }); export { client };
// lib/mistral.ts -- production configuration import { Mistral } from "@mistralai/mistralai"; const TIMEOUT_MS = 30_000; const client = new Mistral({ apiKey: process.env["MISTRAL_API_KEY"] ?? "", timeoutMs: TIMEOUT_MS, retryConfig: { strategy: "backoff", backoff: { initialInterval: 1_000, maxInterval: 30_000, exponent: 1.5, maxElapsedTime: 120_000, }, retryConnectionErrors: true, }, }); export { client };
Why good: Explicit retry config (SDK defaults to no retries), named constants, env var with bracket notation
See: examples/core.md for custom HTTP client, async API key provider, error handling
Pattern 2: Chat Completions
Basic chat using
chat.complete().
const result = await client.chat.complete({ model: "mistral-large-latest", messages: [ { role: "system", content: "You are a helpful coding assistant." }, { role: "user", content: "Explain TypeScript generics." }, ], }); const content = result?.choices?.[0]?.message?.content; console.log(content);
Why good: Uses
system role for instructions, safe optional chaining on nullable response
// BAD: Using snake_case properties (REST API style, not SDK style) const result = await client.chat.complete({ model: "mistral-large-latest", messages: [{ role: "user", content: "hello" }], response_format: { type: "json_object" }, // WRONG: use responseFormat max_tokens: 100, // WRONG: use maxTokens });
Why bad: SDK uses camelCase properties --
response_format and max_tokens will be silently ignored
See: examples/chat.md for multi-turn, token tracking, temperature control
Pattern 3: Streaming
Use
chat.stream() for streaming. Events are async iterables.
const result = await client.chat.stream({ model: "mistral-large-latest", messages: [ { role: "system", content: "You are a helpful assistant." }, { role: "user", content: "Explain async/await in TypeScript." }, ], }); for await (const event of result) { const content = event.data.choices[0]?.delta?.content; if (content) { process.stdout.write(content as string); } } console.log();
Why good: Proper
for await iteration, accesses event.data (not event directly), handles nullable delta
// BAD: Trying to access content directly on event (OpenAI pattern) for await (const chunk of result) { process.stdout.write(chunk.choices[0]?.delta?.content ?? ""); // WRONG }
Why bad: Mistral streaming events wrap data in
event.data -- direct access on chunk will fail
See: examples/chat.md for complete streaming examples
Pattern 4: Structured Outputs with Zod
Use
chat.parse() with a Zod schema for type-safe structured responses.
import { Mistral } from "@mistralai/mistralai"; import { z } from "zod"; const client = new Mistral({ apiKey: process.env["MISTRAL_API_KEY"] ?? "" }); const BookSchema = z.object({ name: z.string(), authors: z.array(z.string()), }); const MAX_TOKENS = 256; const result = await client.chat.parse({ model: "mistral-large-latest", messages: [ { role: "system", content: "Extract the book information." }, { role: "user", content: "I recently read 'Dune' by Frank Herbert." }, ], responseFormat: BookSchema, maxTokens: MAX_TOKENS, temperature: 0, }); const parsed = result.choices?.[0]?.message?.parsed; // parsed is typed as { name: string; authors: string[] }
Why good: Schema passed directly to
responseFormat, message.parsed is fully typed, named constants
See: examples/structured-output.md for JSON mode, complex schemas
Pattern 5: Function Calling / Tool Use
Define tools and handle the tool call loop.
const tools = [ { type: "function" as const, function: { name: "get_weather", description: "Get current weather for a city", parameters: { type: "object", properties: { location: { type: "string", description: "City name" }, }, required: ["location"], }, }, }, ]; const result = await client.chat.complete({ model: "mistral-large-latest", messages: [{ role: "user", content: "Weather in Paris?" }], tools, toolChoice: "any", }); const toolCall = result?.choices?.[0]?.message?.toolCalls?.[0]; if (toolCall) { const args = JSON.parse(toolCall.function.arguments); console.log(`Call ${toolCall.function.name} with:`, args); }
Why good: Uses
toolChoice (camelCase), toolCalls (camelCase), proper as const for type literal
See: examples/function-calling.md for complete tool loop, parallel calls
Pattern 6: Embeddings
Create embeddings with
mistral-embed. Note: uses inputs (plural), not input.
const EMBEDDING_MODEL = "mistral-embed"; const result = await client.embeddings.create({ model: EMBEDDING_MODEL, inputs: ["First document", "Second document", "Third document"], }); const vectors = result.data?.map((item) => item.embedding) ?? [];
Why good: Uses
inputs (Mistral-specific, plural), named model constant, safe optional chaining
// BAD: Using singular 'input' (OpenAI pattern) const result = await client.embeddings.create({ model: "mistral-embed", input: ["First document"], // WRONG: Mistral uses 'inputs' (plural) });
Why bad: Mistral SDK uses
inputs (plural) -- input (singular) will error or be silently ignored
See: examples/embeddings-vision.md for cosine similarity, semantic search
Pattern 7: Vision
Send images to vision-capable models using multi-part content arrays.
const result = await client.chat.complete({ model: "mistral-small-latest", messages: [ { role: "user", content: [ { type: "text", text: "What is in this image?" }, { type: "image_url", imageUrl: "https://example.com/photo.jpg", }, ], }, ], });
Why good: Uses
imageUrl (camelCase string), not image_url: { url } (OpenAI's nested object pattern)
See: examples/embeddings-vision.md for base64 images, multiple images
Pattern 8: Codestral FIM
Fill-in-middle code completion using the dedicated FIM endpoint.
const result = await client.fim.complete({ model: "codestral-latest", prompt: "function fibonacci(n: number): number {\n if (n <= 1) return n;\n", suffix: "}\n\nconsole.log(fibonacci(10));", temperature: 0, }); const completion = result.choices?.[0]?.message?.content; // completion fills the gap between prompt and suffix
Why good: Dedicated FIM endpoint, separate
prompt + suffix (not messages), deterministic with temperature: 0
See: examples/codestral.md for code generation patterns
Pattern 9: Error Handling
The SDK provides specific error types. Configure retries since the default is no retries.
import { Mistral } from "@mistralai/mistralai"; import { SDKError, SDKValidationError, HTTPValidationError, } from "@mistralai/mistralai/models/errors"; try { const result = await client.chat.complete({ model: "mistral-large-latest", messages: [{ role: "user", content: "Hello" }], }); } catch (error) { if (error instanceof HTTPValidationError) { console.error("Validation error:", error.message); } else if (error instanceof SDKValidationError) { console.error("Input validation error:", error.message); } else if (error instanceof SDKError) { console.error(`API error [${error.statusCode}]: ${error.message}`); } else { throw error; } }
Why good: Specific error types checked in order of specificity, re-throws unexpected errors
See: examples/core.md for full error handling, timeout handling, retry configuration
</patterns><performance>
Performance Optimization
Model Selection
General purpose (most capable) -> mistral-large-latest (Mistral Large 3) Balanced cost/quality -> mistral-medium-latest (Mistral Medium 3.1) Cost-sensitive / fast -> mistral-small-latest (Mistral Small 4) Edge / minimal -> ministral-3b-latest or ministral-8b-latest Complex reasoning -> magistral-medium-latest Code generation -> codestral-latest or devstral-latest Code completion (FIM) -> codestral-latest (dedicated FIM endpoint) Vision / images -> mistral-small-latest or mistral-large-latest Embeddings -> mistral-embed (1024 dimensions) Code embeddings -> codestral-embed-latest
Key Optimization Patterns
- Configure retries -- Default is no retries. Always set
for production.retryConfig - Set timeouts -- Default is no timeout (
). Set-1
to avoid hanging requests.timeoutMs - Use
for deterministic output (enables server-side caching).temperature: 0 - Batch embedding inputs -- Pass multiple strings to
array in one call.inputs - Use FIM for code completion --
is purpose-built and more efficient than chat for code completion tasks.fim.complete()
<decision_framework>
Decision Framework
Which Method to Use
What do you need? +-- Chat completion (text in, text out)? | +-- Need streaming? -> client.chat.stream() | +-- Need structured JSON? -> client.chat.parse() with Zod schema | +-- Basic completion? -> client.chat.complete() +-- Code completion / fill-in-middle? | +-- YES -> client.fim.complete() with Codestral +-- Embeddings for search/RAG? | +-- YES -> client.embeddings.create() with mistral-embed +-- Pre-configured agent? +-- YES -> client.agents.complete() with agent ID
Which Model to Choose
What is your task? +-- Most capable general purpose -> mistral-large-latest +-- Balanced cost/performance -> mistral-medium-latest +-- Fast + cost-efficient -> mistral-small-latest +-- Minimal / edge deployment -> ministral-3b-latest +-- Complex reasoning / math -> magistral-medium-latest +-- Code generation (chat) -> codestral-latest or devstral-latest +-- Code completion (FIM) -> codestral-latest +-- Vision / image analysis -> mistral-small-latest (or any vision-capable model) +-- Embeddings -> mistral-embed +-- Code embeddings -> codestral-embed-latest
Streaming vs Non-Streaming
Is the response user-facing? +-- YES -> Use client.chat.stream() | +-- Iterate with: for await (const event of result) | +-- Access content: event.data.choices[0]?.delta?.content +-- NO -> Use client.chat.complete() +-- Background processing -> chat.complete() +-- Structured output -> chat.parse() with Zod
When to Use This SDK vs a Provider-Agnostic SDK
Do you need multiple LLM providers (Mistral + others)? +-- YES -> Not this skill's scope -- use a unified provider SDK +-- NO -> Do you need Mistral-specific features? +-- YES -> Use Mistral SDK directly | Examples: Codestral FIM, Mistral Agents, | Voxtral audio, OCR, custom endpoints +-- NO -> Mistral SDK is simplest for Mistral-only use
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Using
(snake_case) instead ofresponse_format
(camelCase) -- silently ignored, no error thrownresponseFormat - Using
(singular) for embeddings instead ofinput
(plural) -- Mistral-specific naminginputs - Not configuring retries for production (SDK defaults to
-- zero retries)strategy: "none" - Hardcoding API keys instead of using environment variables
- Accessing
directly on streaming events instead ofchunk.choices[0]?.delta?.contentevent.data.choices[0]?.delta?.content
Medium Priority Issues:
- Not setting
for production (default istimeoutMs
, meaning no timeout -- requests can hang indefinitely)-1 - Using
instead ofmax_tokens
(camelCase SDK convention)maxTokens - Missing
role message for behavior guidancesystem - Using
instead oftool_choicetoolChoice - Using
instead oftool_calls
when reading responsestoolCalls
Common Mistakes:
- Importing from
instead of"mistralai"
-- the correct package name has the org scope"@mistralai/mistralai" - Using CommonJS
-- the package is ESM-only, userequire()
orimportawait import() - Confusing Mistral's
(flat string) with OpenAI'simageUrl: "url"
(nested object)image_url: { url: "..." } - Using
(OpenAI pattern) instead ofclient.chat.completions.create()
(Mistral pattern)client.chat.complete() - Assuming embedding dimensions match OpenAI's --
returns 1024-dimensional vectors, not 1536mistral-embed
Gotchas & Edge Cases:
- The SDK is ESM-only. In CommonJS projects, you must use
.const { Mistral } = await import("@mistralai/mistralai") - Streaming content may be
-- cast or check type when writing to stdout.string | string[]
requires a Zod schema passed tochat.parse()
-- it does not acceptresponseFormat
.{ type: "json_object" }- The
constructor option accepts a string OR an async functionapiKey
for dynamic key rotation.() => Promise<string> - Model aliases like
resolve to the latest version of that model tier. Pin to specific versions (e.g.,mistral-large-latest
) for reproducibility.mistral-large-3-25-12
forces the model to call a tool.toolChoice: "any"
lets the model decide.toolChoice: "auto"
prevents tool calls.toolChoice: "none"
forces sequential tool calling (defaultparallelToolCalls: false
allows parallel).true- FIM endpoint (
) usesfim.complete()
+prompt
parameters, NOT thesuffix
array.messages
injects Mistral's safety system prompt before your messages.safePrompt: true- The SDK provides standalone functions (e.g.,
fromchatComplete()
) for tree-shaking in browser/edge runtimes."@mistralai/mistralai/funcs/chatComplete.js"
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST use
(camelCase) in SDK calls -- NOT responseFormat
(snake_case). The SDK uses camelCase property names throughout.)response_format
(You MUST configure retries explicitly -- the SDK defaults to
(no retries), unlike OpenAI's SDK which retries automatically)strategy: "none"
(You MUST consume streaming results with
and access content via for await (const event of result)
-- the event shape differs from OpenAI)event.data.choices[0]?.delta?.content
(You MUST never hardcode API keys -- use
with the bracket notation the SDK documents)process.env["MISTRAL_API_KEY"]
(You MUST use
with a Zod schema for structured outputs -- NOT manual client.chat.parse()
on completion content)JSON.parse()
Failure to follow these rules will produce broken API calls (snake_case properties silently ignored), unreliable production services (no retries), or incorrectly parsed streaming data.
</critical_reminders>