Skip to content

Mistral Example

examples/mistral_analysis.py is a self-contained script that uses function-calling (tool use) to drive the full client-led analysis workflow against the REST endpoints — the LLM itself decides which tool to call next (ingest → get_next_resource → submit_analysis → report), rather than the server orchestrating it.

No MCP client is needed — the LLM calls the server tools directly. This is one worked example of the client-led pattern; it happens to use Mistral because that’s what’s bundled, but nothing about the pattern is Mistral-specific — see Adapting for other models below.


sequenceDiagram
    participant Script as User Script
    participant Mistral as Mistral API
    participant API as REST API

    Script->>Mistral: system prompt + plan text
    Mistral->>API: GET /terraform-plan/rules?type=aws
    API-->>Mistral: ruleset (Markdown)
    Mistral->>API: POST /terraform-plan/{ns}/ingest
    API-->>Mistral: resource units[]

    loop until HTTP 208
        Mistral->>API: GET /{ns}/resource/next
        API-->>Mistral: resource unit
        Note over Mistral: analyse body against rules internally
        Mistral->>API: PUT /resource/{hash}/analysis
    end

    Mistral->>API: GET /{ns}/report
    API-->>Mistral: Markdown report
    Mistral-->>Script: final report text
    Note over Script: print report

Mistral receives the plan text in the first message and autonomously decides which tools to call and in what order — the script only executes the HTTP calls and feeds results back as tool responses.


Terminal window
pip install mistralai httpx

The server must be running:

Terminal window
python src/server.py
# or (first time: builds the image)
docker compose up --build

Terminal window
export MISTRAL_API_KEY=your-key-here
# Analyse a plan (defaults: mistral-large-latest, http://localhost:8000)
python examples/mistral_analysis.py path/to/plan.txt
VariableDefaultDescription
MISTRAL_API_KEYMistral API key (required unless CODESTRAL_API_KEY is set)
CODESTRAL_API_KEYCodestral API key — takes priority over MISTRAL_API_KEY
MISTRAL_SERVER_URLCustom API endpoint, e.g. https://codestral.mistral.ai/v1
MISTRAL_MODELmistral-large-latestAny Mistral model with tool use
MCP_BASE_URLhttp://localhost:8000Server base URL
MCP_API_KEY(empty)Server API key if auth.api_key is set
Terminal window
export MISTRAL_API_KEY=sk-...
export MISTRAL_MODEL=mistral-large-latest
export MCP_BASE_URL=https://terraform-analyzer.example.com
export MCP_API_KEY=my-server-key
python examples/mistral_analysis.py infra/prod.tfplan.txt
Terminal window
export CODESTRAL_API_KEY=sk-...
export MISTRAL_SERVER_URL=https://codestral.mistral.ai/v1
export MISTRAL_MODEL=codestral-latest
python examples/mistral_analysis.py infra/prod.tfplan.txt

Namespace : a3f1c2d4e5b6c7d8e9f0...
Plan file : infra/prod.tfplan.txt
Model : mistral-large-latest
API base : http://localhost:8000
→ get_rules(analyzer_name='terraform-plan', rule_type='aws')
→ ingest(analyzer_name='terraform-plan', namespace='a3f1c2...')
→ get_next_resource(namespace='a3f1c2...')
→ submit_analysis(hash='d4e5f6...', summary='S3 bucket exposes public read...')
→ get_next_resource(namespace='a3f1c2...')
→ submit_analysis(hash='b7c8d9...', summary='Security group allows unrestricted...')
→ get_next_resource(namespace='a3f1c2...') # HTTP 208 → loop ends
→ get_report(namespace='a3f1c2...')
# Terraform Security Report
...

Tool handlers (TOOL_HANDLERS) — thin httpx wrappers that map tool names to REST calls. Each returns the HTTP status code plus the response body so Mistral knows whether a call succeeded or failed.

Tool schemas (TOOLS) — JSON Schema definitions passed to mistral.chat.complete(). The schemas mirror the REST API’s request/response contract so Mistral can construct valid payloads without guidance.

System prompt (SYSTEM_PROMPT) — instructs Mistral on the exact workflow order, loop termination condition (status=208), and finding format rules.

Agent loop — calls mistral.chat.complete() in a loop, executes any tool calls that come back, appends the results, and repeats until Mistral returns a plain text message (the report).


Any model that supports function / tool calling works. Replace the SDK and tool call handling:

from mistralai import Mistral
client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])
response = client.chat.complete(model="mistral-large-latest", messages=messages, tools=TOOLS)
from openai import OpenAI
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")
response = client.chat.completions.create(model="llama3", messages=messages, tools=TOOLS)

Tool call extraction is identical — both SDKs expose response.choices[0].message.tool_calls.

The TOOLS list and TOOL_HANDLERS dict require no changes.