Balanced class groups and teams with a real MILP solver: mixed fields, together/apart rules.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Describe what the user wants in plain words and get back whether this server can do it, which limits it would hit, and the closest thing it CAN do. Instant, no solve. Use it when unsure the tool fits (seating charts, tournament brackets, round-robin schedules, random draws, repeated rotations, very large lists).
The user's request in their own words, e.g. 'split 30 students into 6 groups, mixed by reading level, keep two friends apart'.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http balanced-group-maker https://group-maker.kindoflost.com/mcp
Describe what the user wants in plain words and get back whether this server can do it, which limits it would hit, and the closest thing it CAN do. Instant, no solve. Use it when unsure the tool fits (seating charts, tournament brackets, round-robin schedules, random draws, repeated rotations, very large lists).
| Argument | Type | Description |
|---|---|---|
| description* | string | The user's request in their own words, e.g. 'split 30 students into 6 groups, mixed by reading level, keep two friends apart'. |
{
"type": "object",
"required": [
"description"
],
"properties": {
"description": {
"type": "string",
"maxLength": 2000,
"description": "The user's request in their own words, e.g. 'split 30 students into 6 groups, mixed by reading level, keep two friends apart'."
}
},
"additionalProperties": false
}Return the full input JSON of one example, in exactly the shape make_groups accepts: 'classroom' = 30 students, 6 groups, together and apart codes, categorical and numeric fields, blanks; 'teams' = 20 colleagues in 4 teams balanced on a numeric skill and a role. Edit it (people, fields, priorities, number of groups) to match the user's situation.
| Argument | Type | Description |
|---|---|---|
| kind* | string |
{
"type": "object",
"required": [
"kind"
],
"properties": {
"kind": {
"enum": [
"classroom",
"teams"
],
"type": "string"
}
},
"additionalProperties": false
}Fetch the result of a make_groups job that returned status 'running'. Waits up to wait_seconds; call again while status is 'running'. Jobs expire 15 minutes after they finish.
| Argument | Type | Description |
|---|---|---|
| job_id* | string | |
| wait_seconds | integer |
{
"type": "object",
"required": [
"job_id"
],
"properties": {
"job_id": {
"type": "string"
},
"wait_seconds": {
"type": "integer",
"maximum": 45,
"minimum": 0
}
},
"additionalProperties": false
}Split people into balanced groups with a MILP solver and return each group's members, a per-group summary of every field, the overall mix, a balance score per field (100 = perfectly balanced), notes and warnings. Give `groups` (a number of groups; sizes then differ by at most 1) OR `min_size` and/or `max_size` (the server tries every group count that fits and picks the best); with both, `groups` decides and the sizes only validate it. `priorities` maps a field to 1 (most important), 2, 3...; a field left out counts least; equal numbers tie. Solves take up to about 20 s. If the result has status 'running', call get_group_result with its job_id. If it has status 'busy', wait the retry_after_seconds it gives and call again -- one call at a time, never in parallel. If it has status 'error' or 'infeasible', the message says in plain words what to change. Defaults applied (e.g. a field with no priority) are listed in `assumptions`; tell the user about them. Call get_group_example for a complete, realistic input.
| Argument | Type | Description |
|---|---|---|
| goal | string | One sentence, in the user's own words, of what they are trying to do (e.g. 'six mixed-ability reading groups for my class'). Optional; helps improve the tool. Do not put names in it. |
| groups | integer | Number of groups (max 60). |
| people | array | The people to group (2-300). |
| max_size | integer | Largest allowed group size. |
| min_size | integer | Smallest allowed group size. |
We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 11, 2026.
| priorities | object | Field name -> priority number (1 = most important). Each step is worth 4x the next; a field with no priority counts least. |
| wait_seconds | integer | How long to wait for the solve inside this call (default 25). |
{
"type": "object",
"properties": {
"goal": {
"type": "string",
"maxLength": 300,
"description": "One sentence, in the user's own words, of what they are trying to do (e.g. 'six mixed-ability reading groups for my class'). Optional; helps improve the tool. Do not put names in it."
},
"groups": {
"type": "integer",
"maximum": 60,
"minimum": 1,
"description": "Number of groups (max 60)."
},
"people": {
"type": "array",
"items": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Unique name of the person."
},
"apart": {
"type": "array",
"items": {
"type": [
"string",
"integer"
]
},
"description": "Codes. People who share a code are put in DIFFERENT groups (hard rule). e.g. [\"A\"] for two people who must be separated. A code can't contain a comma."
},
"fields": {
"type": "object",
"description": "What to balance for this person, e.g. {\"gender\": \"female\", \"skill\": 7}. Text values are categories (spread proportionally); a field whose values are all numbers is numeric (equal group averages). Leave a key out when unknown.",
"additionalProperties": {
"type": [
"string",
"number",
"boolean",
"null"
]
}
},
"together": {
"type": "array",
"items": {
"type": [
"string",
"integer"
]
},
"description": "Codes. People who share a code are kept in the SAME group (hard rule). e.g. [\"1\"] for siblings or a pair that must stay together. A person may hold several."
}
}
},
"maxItems": 300,
"minItems": 2,
"description": "The people to group (2-300)."
},
"max_size": {
"type": "integer",
"minimum": 1,
"description": "Largest allowed group size."
},
"min_size": {
"type": "integer",
"minimum": 1,
"description": "Smallest allowed group size."
},
"priorities": {
"type": "object",
"description": "Field name -> priority number (1 = most important). Each step is worth 4x the next; a field with no priority counts least.",
"additionalProperties": {
"type": "integer",
"minimum": 1
}
},
"wait_seconds": {
"type": "integer",
"maximum": 45,
"minimum": 0,
"description": "How long to wait for the solve inside this call (default 25)."
}
}
}