{"name":"moltline-optimize","description":"Routing, packing, cutting and rostering over MCP (OR-Tools). Free: validate_problem, route_plan, distance_matrix_haversine, pack_bins, cutting_stock_1d, knapsack_select, explain_solution. Premium (license): route_plan_fleet, pack_bins_large, cutting_stock_2d, roster_shifts.","url":"https://mcp.moltlinestudio.com/optimize","transport":"streamable-http","homepage":"https://moltlinestudio.com/servers.html","serverInfo":{"name":"moltline-optimize","version":"1.1.0"},"toolCounts":{"total":11,"free":7,"premium":4},"protocolVersion":"2025-11-25","protocolVersions":["2024-11-05","2025-03-26","2025-06-18","2025-11-25"],"pricing":{"freeTier":{"tools":7,"price":"0","currency":"USD","description":"Free forever. No account, no registration and no credentials are required to call them."},"details":"https://moltlinestudio.com/.well-known/pricing","terms":"https://moltlinestudio.com/terms.html","agentPayment":{"protocol":"x402","resource":"https://moltlinestudio.com/api","network":"eip155:8453","asset":"USDC","amount":"19000000","discovery":"https://moltlinestudio.com/.well-known/x402","description":"GET or POST the resource to receive an HTTP 402 carrying a machine-readable payment demand; retry with the transaction hash in the X-PAYMENT header and the licence key is returned in the response."},"premium":{"tools":4,"plan":"All-Access","price":"19.00","currency":"USD","period":"month","description":"One licence key unlocks the premium tools on every Moltline server. Billed monthly, cancellable at any time.","purchaseUrl":"https://moltlinestudio.com/#allaccess"}},"authentication":{"required":false},"tools":[{"name":"route_plan","description":"Order up to 12 stops into the shortest single-vehicle route on your distance matrix. FREE.\n\nTypical input {\"stops\": [{\"id\": \"depot\"}, {\"id\": \"A\"}, {\"id\": \"B\"}],\n\"matrix\": [[0, 5, 9], [5, 0, 4], [9, 4, 0]]} returns {\"routes\":\n[{\"vehicle\": 0, \"stops\": [...], \"distance\": 18.0}], \"total_distance\":\n18.0, \"solver_status\": \"FEASIBLE\", \"note\": \"...\"}. The matrix is in\nyour units (km, minutes, cost) and must be square with the depot at\nindex 0 unless depot says otherwise; optional demand per stop with\nvehicle_capacity turns it into a capacity check. Use for one driver's\nday or a courier's loop. Not for several vehicles or time windows: use\nroute_plan_fleet. Not a map service: bring your own distances or call\ndistance_matrix_haversine for straight-line values. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"stops must be a list of stop objects, depot first\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"stops":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of stop objects, depot first: id, optional demand."},"matrix":{"items":{"items":{"type":"number"},"type":"array"},"type":"array","description":"square distance (or time) matrix, row i column j = cost from stop i to stop j."},"depot":{"default":0,"type":"integer","description":"index of the depot in stops (default 0)."},"vehicle_capacity":{"default":0.0,"type":"number","description":"optional capacity in the units of demand (0 = unlimited)."},"time_limit_s":{"default":3.0,"maximum":15.0,"minimum":0,"type":"number","description":"solver time budget in seconds (default 3, max 15)."}},"required":["stops","matrix"],"type":"object"}},{"name":"route_plan_fleet","description":"Capacitated, time-windowed routing for a fleet over up to 200 stops. PREMIUM (license).\n\nTypical input {\"stops\": [{\"id\": \"depot\", \"window\": [480, 1080]},\n{\"id\": \"A\", \"demand\": 3, \"window\": [540, 720], \"service_min\": 10}, ...],\n\"matrix\": [[...]], \"vehicles\": [{\"id\": \"van1\", \"capacity\": 10},\n{\"id\": \"van2\", \"capacity\": 8, \"max_distance\": 120}]} returns {\"routes\":\n[{\"vehicle\": \"van1\", \"stops\": [{\"id\": \"A\", \"arrive_min\": 545, ...}],\n\"distance\": 42.5, \"load\": 9}], \"unserved\": [], \"solver_status\":\n\"FEASIBLE\"}. Windows and service times are minutes from the start of\nthe day; travel time comes from time_matrix (minutes) or, if absent, the\ndistance matrix read as minutes. Set drop_penalty to allow stops to be\nleft unserved at that cost instead of returning INFEASIBLE. Use for\ndaily dispatch. Not a map service; bring your own matrices. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"stops must be a list of stop objects, depot first\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"premium","inputSchema":{"additionalProperties":false,"properties":{"stops":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"depot first; each: id, demand, window [earliest_min, latest_min], service_min."},"matrix":{"items":{"items":{"type":"number"},"type":"array"},"type":"array","description":"square distance matrix in your units."},"vehicles":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, capacity, max_distance}; capacity in the units of demand."},"depot":{"default":0,"type":"integer","description":"index of the depot in stops (default 0)."},"time_matrix":{"default":[],"items":{"items":{"type":"number"},"type":"array"},"type":"array","description":"optional square travel-time matrix in minutes (defaults to matrix)."},"drop_penalty":{"default":0.0,"type":"number","description":"cost of leaving a stop unserved (0 = every stop must be served)."},"time_limit_s":{"default":10.0,"maximum":60.0,"minimum":0,"type":"number","description":"solver time budget in seconds (default 10, max 60)."}},"required":["stops","matrix","vehicles"],"type":"object"}},{"name":"distance_matrix_haversine","description":"Straight-line (great-circle) distance matrix from coordinates. FREE.\n\nTypical input {\"points\": [{\"id\": \"depot\", \"lat\": 51.5, \"lon\": -0.12},\n{\"id\": \"A\", \"lat\": 51.52, \"lon\": -0.1}]} returns {\"matrix\": [[0,\n2.6], [2.6, 0]], \"unit\": \"km\", \"kind\": \"straight-line (haversine), not\nroad distance\"}. Use when you have no road matrix and a straight-line\napproximation is acceptable, or to sanity-check one. Not road routing:\nreal driving distances are longer and the difference is not uniform. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"points must be a list of at least two <value> objects\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"points":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, lat, lon} (up to 200)."},"unit":{"default":"km","type":"string","description":"km (default) or mi."}},"required":["points"],"type":"object"}},{"name":"pack_bins","description":"Place up to 20 boxes into containers or pallets with rotation, weight and support rules. FREE.\n\nTypical input {\"items\": [{\"id\": \"A\", \"l\": 60, \"w\": 40, \"h\": 30,\n\"weight\": 12, \"qty\": 4}], \"containers\": [{\"id\": \"pallet\", \"l\": 120,\n\"w\": 80, \"h\": 150, \"max_weight\": 500, \"qty\": 2}]} returns\n{\"containers_used\": 1, \"containers\": [{\"placements\": [{\"id\": \"A\",\n\"x\": 0, \"y\": 0, \"z\": 0, \"l\": 60, \"w\": 40, \"h\": 30}, ...],\n\"volume_fill_pct\": 20.0}], \"unplaced\": []}. rotation per item: any,\nupright (rotate around the vertical axis only) or fixed; fragile items\ncarry nothing; rules.min_support (default 0.6) is the share of a box's\nbase that must rest on the floor or on boxes below. Use to decide pallet\nor carton count before booking freight. Not proven optimal: it is a\nfirst-fit-decreasing heuristic, reported as such. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"}. Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"items":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, l, w, h, weight, qty, rotation, fragile}, all in one length unit."},"containers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, l, w, h, max_weight, qty}; used in the order given."},"rules":{"additionalProperties":true,"default":{},"type":"object","description":"optional {min_support: 0-1, default_rotation: any|upright|fixed}."}},"required":["items","containers"],"type":"object"}},{"name":"pack_bins_large","description":"Same packer as pack_bins for up to 300 item units and 200 containers. PREMIUM (license).\n\nTypical input {\"items\": [{\"id\": \"SKU1\", \"l\": 40, \"w\": 30, \"h\": 20,\n\"weight\": 5, \"qty\": 120}, ...], \"containers\": [{\"id\": \"euro-pallet\",\n\"l\": 120, \"w\": 80, \"h\": 180, \"max_weight\": 800, \"qty\": 10}]} returns\nthe same shape as pack_bins: containers with placements, fill\npercentages, weights and any unplaced units. Use for order\nconsolidation and load planning. Not proven optimal (first-fit\ndecreasing on extreme points, reported as such). Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"}. Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"premium","inputSchema":{"additionalProperties":false,"properties":{"items":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, l, w, h, weight, qty, rotation, fragile}."},"containers":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"list of {id, l, w, h, max_weight, qty}."},"rules":{"additionalProperties":true,"default":{},"type":"object","description":"optional {min_support: 0-1, default_rotation: any|upright|fixed}."}},"required":["items","containers"],"type":"object"}},{"name":"cutting_stock_1d","description":"Least-waste cut plan for bars, pipes or boards from stock lengths, with saw kerf. FREE.\n\nTypical input {\"stock\": [{\"length\": 6000, \"cost\": 30}], \"parts\":\n[{\"length\": 2200, \"qty\": 3}, {\"length\": 1500, \"qty\": 4}], \"kerf\": 3}\nreturns {\"bars\": [{\"stock_length\": 6000, \"cuts\": [2200, 2200, 1500],\n\"waste\": 94}], \"bars_used\": 3, \"waste_pct\": 4.2, \"solver_status\":\n\"OPTIMAL\"}. Minimises total stock cost (or count when no cost); CP-SAT\nproves optimality when it finishes inside the time limit and otherwise\nreturns the best plan found as FEASIBLE. Use for a cut list of up to 200\npieces. Not for sheets: use cutting_stock_2d. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"stock and parts must be non-empty lists (<value> and <value>)\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"stock":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"stock lengths available: {length, cost, qty} (qty = how many of that length may be used; default unlimited)."},"parts":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"required pieces: {length, qty}."},"kerf":{"default":0.0,"type":"number","description":"material lost per cut (same unit as lengths)."},"time_limit_s":{"default":3.0,"maximum":15.0,"minimum":0,"type":"number","description":"solver time budget in seconds (default 3, max 15)."}},"required":["stock","parts"],"type":"object"}},{"name":"cutting_stock_2d","description":"Guillotine cut layouts for rectangular parts from sheets, with kerf and grain. PREMIUM (license).\n\nTypical input {\"sheets\": [{\"id\": \"ply\", \"l\": 2440, \"w\": 1220, \"qty\":\n5}], \"parts\": [{\"id\": \"side\", \"l\": 800, \"w\": 400, \"qty\": 6}], \"kerf\":\n3} returns {\"sheets_used\": 1, \"layouts\": [{\"sheet\": \"ply\", \"placements\":\n[{\"id\": \"side\", \"x\": 0, \"y\": 0, \"l\": 800, \"w\": 400, \"rotated\": false}],\n\"fill_pct\": 64.5, \"offcuts\": [...]}], \"unplaced\": []}. Every cut is a\nguillotine cut (edge to edge): the sheet is ripped into strips and each\nstrip cross-cut, which is what a panel saw does; grain true forbids\nrotating parts unless a part sets rotate true. Use for cabinet, sign\nand sheet-metal cut lists. Not proven optimal: a best-fit shelf\nheuristic, reported as such. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"sheets and parts must be non-empty lists (<value> and <value>)\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"premium","inputSchema":{"additionalProperties":false,"properties":{"sheets":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"{id, l, w, qty}; used in the order given."},"parts":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"{id, l, w, qty, rotate}; rotate overrides the grain rule per part."},"kerf":{"default":0.0,"type":"number","description":"saw blade width lost per cut."},"grain":{"default":false,"type":"boolean","description":"true when parts must keep their orientation (l along the sheet's l)."}},"required":["sheets","parts"],"type":"object"}},{"name":"roster_shifts","description":"Assign staff to shifts under availability, skills, hour caps and rest gaps. PREMIUM (license).\n\nTypical input {\"staff\": [{\"id\": \"ana\", \"skills\": [\"till\"],\n\"max_hours\": 40, \"unavailable\": [\"sat-am\"]}, ...], \"shifts\": [{\"id\":\n\"sat-am\", \"start\": \"2026-09-12T08:00\", \"end\": \"2026-09-12T14:00\",\n\"required\": 2, \"skill\": \"till\"}, ...], \"rules\": {\"min_rest_hours\": 11,\n\"max_consecutive_days\": 6}} returns {\"assignments\": [{\"shift\":\n\"sat-am\", \"staff\": [\"ana\", \"ben\"]}], \"unfilled\": [{\"shift\": \"sun-pm\",\n\"short\": 1}], \"hours\": {\"ana\": 30.0}, \"solver_status\": \"OPTIMAL\"}. The\nobjective fills as many required slots as possible, then spreads hours\nevenly, then honours preferences (staff.prefer / staff.avoid shift ids).\nUse for weekly rotas of up to 60 staff and 150 shifts. Not a\ndetermination of labour-law compliance: the rules are the ones you pass. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"staff and shifts must be non-empty lists\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"premium","inputSchema":{"additionalProperties":false,"properties":{"staff":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"{id, skills[], max_hours, min_hours, unavailable[], prefer[], avoid[], max_shifts}."},"shifts":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"{id, start, end (ISO 8601 local), required, skill, weight}."},"rules":{"additionalProperties":true,"default":{},"type":"object","description":"{min_rest_hours (default 0), max_consecutive_days (default 7), max_shifts_per_day (default 1)}."},"time_limit_s":{"default":10.0,"maximum":60.0,"minimum":0,"type":"number","description":"solver time budget in seconds (default 10, max 60)."}},"required":["staff","shifts"],"type":"object"}},{"name":"knapsack_select","description":"Choose the items that maximise value under one or more capacity limits. FREE.\n\nTypical input {\"items\": [{\"id\": \"a\", \"value\": 60, \"weight\": 10, \"cost\":\n120}, {\"id\": \"b\", \"value\": 100, \"weight\": 20, \"cost\": 300}], \"limits\":\n{\"weight\": 25, \"cost\": 400}} returns {\"selected\": [\"a\"], \"value\": 60,\n\"used\": {\"weight\": 10, \"cost\": 120}, \"slack\": {\"weight\": 15, \"cost\":\n280}, \"solver_status\": \"OPTIMAL\"}. Any numeric item field named in\nlimits is a constrained resource; qty lets an item be taken several\ntimes. Use for budgets, cargo, campaign or feature selection. Not for\ndependencies between items. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"items must be a non-empty list of <value>\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"items":{"items":{"additionalProperties":true,"type":"object"},"type":"array","description":"{id, value, qty, <resource fields>}: value to maximise plus one number per limited resource."},"limits":{"additionalProperties":true,"type":"object","description":"{resource_name: capacity} for each constrained field."},"time_limit_s":{"default":3.0,"maximum":15.0,"minimum":0,"type":"number","description":"solver time budget in seconds (default 3, max 15)."}},"required":["items","limits"],"type":"object"}},{"name":"validate_problem","description":"Check a problem's shape and obvious feasibility before spending solver time. FREE.\n\nTypical input {\"type\": \"route\", \"problem\": {\"stops\": [...], \"matrix\":\n[[...]], \"vehicles\": [...]}} returns {\"ok\": false, \"issues\": [\"total\ndemand 34 exceeds total capacity 30\"], \"size\": {\"stops\": 14,\n\"vehicles\": 2}, \"tier_hint\": \"route_plan_fleet (licence) - more than 12\nstops\"}. Types: route, pack, cut1d, cut2d, roster, knapsack; the problem\nobject uses the same fields as the matching tool. Use first when an\nagent has assembled the problem from other data. Not a solve: it never\ncalls the solver. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"type must be one of <value>\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"type":{"type":"string","description":"route, pack, cut1d, cut2d, roster or knapsack."},"problem":{"additionalProperties":true,"type":"object","description":"the same object you would pass to the tool (stops/matrix/vehicles, items/containers, ...)."}},"required":["type","problem"],"type":"object"}},{"name":"explain_solution","description":"Plain-language summary of a solution from this server and the constraints that bind. FREE.\n\nTypical input {\"solution\": <result of route_plan_fleet>} returns\n{\"summary\": \"2 vehicles serve 14 stops over 96.4 km; 1 stop unserved\",\n\"binding_constraints\": [\"van2 is at 100% of capacity\", \"stop C arrives\nat the end of its window\"], \"status\": \"FEASIBLE\"}. It recognises\nresults from route_plan, route_plan_fleet, pack_bins, cutting_stock_1d,\ncutting_stock_2d, roster_shifts and knapsack_select by their fields.\nUse to turn solver output into a message for a dispatcher or a shop\nfloor. Not a re-solve. Errors: on invalid, missing, or malformed input this tool never raises a protocol error — it returns {\"error\": \"<what is wrong and how to fix it>\"} (for example {\"error\": \"solution must be the result object returned by a solve tool on this server\"}). Every call is read-only and idempotent, so after correcting the input it is always safe to retry.","tier":"free","inputSchema":{"additionalProperties":false,"properties":{"solution":{"additionalProperties":true,"type":"object","description":"the result object returned by one of this server's solve tools."}},"required":["solution"],"type":"object"}}],"resources":[],"prompts":[]}