An MCP server that exposes MiniZinc constraint solving and optimization to LLM clients such as opencode, Claude Desktop, and Cursor. It lets an agent parse, type-check, and solve MiniZinc models directly from a chat session.
Built with the MCP Python SDK v2 and the MiniZinc Python binding.
Only two things need to be installed, once per machine:
- uv —
curl -LsSf https://astral.sh/uv/install.sh | sh - MiniZinc 2.6+ with the
minizincexecutable onPATH(includes a default solver, Gecode)
Everything else is fetched automatically by uv — there is no clone, no venv setup, and no manual pip install on your side.
Install it globally (best if you use it in several projects):
uv tool install --from git+https://github.com/carban/minizinc-mcp minizinc-mcp
Or run it on demand each time, with nothing installed:
uvx --from git+https://github.com/carban/minizinc-mcp minizinc-mcp
The server runs over stdio. Tell your MCP client to launch it:
opencode — project level (add this to opencode.jsonc in your project):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"minizinc": {
"type": "local",
"command": ["uvx", "--from", "git+https://github.com/carban/minizinc-mcp", "minizinc-mcp"]
}
}
}
opencode — global (add the same mcp.minizinc block to ~/.config/opencode/opencode.json):
{
"mcp": {
"minizinc": {
"type": "local",
"command": ["uvx", "--from", "git+https://github.com/carban/minizinc-mcp", "minizinc-mcp"]
}
}
}
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"minizinc": {
"command": "uvx",
"args": ["--from", "git+https://github.com/carban/minizinc-mcp", "minizinc-mcp"]
}
}
}
Restart your client. Six tools should now be available, prefixed with minizinc_:
minizinc_list_solversminizinc_validate_modelminizinc_solve_modelminizinc_solve_model_by_pathminizinc_get_model_infominizinc_get_flatzinc
Quick sanity check — ask your client: "list the available MiniZinc solvers". You should see gecode, chuffed, highs, and anything else installed on the machine.
| Tool | Description |
|---|---|
list_solvers |
Lists every MiniZinc solver installed on the machine. The returned tag names (e.g. gecode ,chuffed ,highs ) can be passed tosolve_model . |
validate_model |
Parses and type-checks MiniZinc model code without solving it . Useful for checking model syntax up front. ReturnsVALID orINVALID with an error message. |
solve_model |
Solves a MiniZinc model given as source code: once, exhaustively ( all_solutions ), or with a solution / time limit. Returns the status, solution(s), objective value (for optimization problems), and solver statistics. |
solve_model_by_path |
Same as solve_model but loads the model and its optional data (.dzn ) file from paths instead of source code. |
get_model_info |
Inspects a model without solving it : returns its solve method (satisfy/minimize/maximize) and the declared input parameters and output variables with their types. Useful for an agent to know exactly whichparams a model expects. |
get_flatzinc |
Compiles a model (and optional data) to FlatZinc text without solving it. Returns the .fzn model, the.ozn output model, and flattening statistics. Useful for debugging and low-level inspection. |
| Argument | Type | Default | Description |
|---|---|---|---|
model_code |
str |
(required) | The MiniZinc source code ( .mzn ) of the model. |
params |
dict | str |
None |
Parameter assignments like a .dzn file: a JSON object mapping names to values (a JSON string encoding such an object is also accepted). |
solver |
str |
"gecode" |
Which solver to use (see list_solvers ). |
all_solutions |
bool |
False |
Compute all solutions of a solve satisfy problem. |
max_solutions |
int | None |
None |
Stop after at most this many solutions. |
timeout_seconds |
int | None |
None |
Solver time limit in seconds. |
The result is a JSON object like:
{
"status": "OPTIMAL_SOLUTION",
"objective": 9,
"solution": { "objective": 9, "x": 9, "y": 1 },
"statistics": { "time": 0.204, "nodes": 3, ... }
}
status is one of SATISFIED, OPTIMAL_SOLUTION, ALL_SOLUTIONS, UNSATISFIABLE, UNKNOWN, or ERROR. validate_model and solve_model never raise in normal operation — errors are returned inside the result dict.
Clone the repo, then:
uv sync # create the environment and install mcp + minizinc
The server speaks the MCP stdio transport, so it is launched as a subprocess by an MCP client. Run it with the SDK inspector:
uv run mcp dev server.py
that opens the MCP Inspector in the browser where every tool can be called interactively. A minimal programmatic smoke test:
uv run python -c "
import asyncio
from mcp import Client
from mcp.client.stdio import StdioServerParameters
async def main():
params = StdioServerParameters(command='uv', args=['run', 'python', 'server.py'], cwd='.')
async with Client(params) as client:
result = await client.call_tool('solve_model', {
'model_code': 'var 1..10: x; var 1..10: y; constraint x + y = 10; solve maximize x;'
})
print(result.content[0].text)
asyncio.run(main())
"
Install the test dependencies, then run the suite:
uv sync --group dev
uv run pytest -q
The tests in tests/ launch the server end-to-end over stdio and call every tool through the MCP protocol, solving the example model in example/. They need a working MiniZinc install (the same prerequisite as for developers).
paramsfollows JSON representation: JSON arrays map to MiniZinc arrays; numbers, strings, and booleans map to their native MiniZinc types. Exotic types like sets and enums are not fully expressible this way.- Do not combine
all_solutionswithmax_solutions; the MiniZinc driver rejects the combination. - MiniZinc requires a solver that supports the model (e.g.
chuffed/gecodefor CP,highs/cbcfor MIP models). Uselist_solversto see what is installed. - Solutions are returned inline in the tool result;
read_only_hintis set on all tools, so they do not modify your files or system.