{
  "$schema": "https://modelcontextprotocol.io/schema/mcp-server-manifest.json",
  "name": "opifico-cutting-stock",
  "version": "1.0.0",
  "title": "Opifico",
  "description": "Cutting-stock optimizer: 1D linear cutting and 2D guillotine sheet layouts from a cut list and stock (wood, sheet metal, steel and aluminium bar and tube, T-slot extrusion), plus the user's inventory, supplier catalog and saved projects. Solvers work in millimetres; tools accept mm, cm, in or ft.",
  "website": "https://opifico.com",
  "documentation_url": "https://opifico.com/llms-full.txt",
  "guidelines_url": "https://opifico.com/llms.txt",
  "openapi_url": "https://api.opifico.com/api/agent/spec",
  "api_url": "https://api.opifico.com",
  "pricing_url": "https://api.opifico.com/api/pricing",
  "terms_url": "https://opifico.com/terms",
  "privacy_url": "https://opifico.com/privacy",
  "support_email": "support@opifico.com",
  "transport": {
    "type": "streamable-http",
    "url": "https://api.opifico.com/mcp",
    "methods": [
      "POST"
    ],
    "responses": "json",
    "protocol_versions": [
      "2024-11-05",
      "2025-03-26",
      "2025-06-18"
    ]
  },
  "authentication": {
    "required": true,
    "methods": [
      {
        "type": "oauth2",
        "description": "OAuth 2.1 sign-in with PKCE (S256) and dynamic client registration. The client opens Opifico, the user signs in and approves. Tokens work only on /mcp and can be revoked in Settings, Account, Connected apps.",
        "protected_resource_metadata": "https://api.opifico.com/.well-known/oauth-protected-resource/mcp",
        "authorization_server_metadata": "https://api.opifico.com/.well-known/oauth-authorization-server"
      },
      {
        "type": "api_key",
        "description": "API key created in Settings, Account after signing in at https://opifico.com; shown once, a new key replaces the old one.",
        "header": "Authorization",
        "scheme": "Bearer",
        "format": "sk-wood-live-..."
      }
    ]
  },
  "billing": {
    "currency": "usd",
    "model": "Prepaid balance. opifico_cutting_solve charges the quoted price up front and refunds it in full if the solve fails. opifico_cutting_estimate and all inventory, supplier and project tools are free. Agents always pay; the daily free tier is for the web app.",
    "pricing_url": "https://api.opifico.com/api/pricing"
  },
  "tools": [
    {
      "name": "opifico_cutting_estimate",
      "title": "Estimate a cutting job",
      "description": "Fast, free feasibility check for 1D (lumber/pipes) and 2D (plywood/metal sheets) cutting stock optimization problems. Computes required material vs available stock, minimum boards/sheets needed, and theoretical yield percentage, and reports any part material no stock item has. Accepts structured parts/stock, raw CSV cut list text (`parts_csv`, `stock_csv`) and the workshop inventory. With `project_id` it lists the project's material lines, where each comes from and the exact price `opifico_cutting_solve` will charge.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "description": "Saved project (id or name from opifico_projects_list): cuts it exactly as the web app's Project solver does, with the project's saved cutting setup (kerf, trim, offcut rules, miters, grain, edge banding, saw or nesting) and stock from the workshop inventory, plus any supplier_materials from the supplier list. Do not pass parts or stock with it. A project with both sheet and linear parts needs cut_type '2d' or '1d'."
          },
          "supplier_materials": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "With project_id: material lines to buy from the supplier list (opifico_supplier_get) instead of taking them from inventory, as the Project solver's supplier switch does. Use the line names opifico_cutting_estimate lists (e.g. 'Pine plank 50 × 100 mm'), or 'all'. Those boards come back with source 'supplier' and a buy list."
          },
          "use_inventory": {
            "type": "boolean",
            "description": "If True, the stock is the workshop inventory: the sheet classes for 2D, the linear classes for 1D. Each inventory row's material reads like 'Pine plank 50 × 100 mm' or 'Birch sheet 18 mm' (material, class, section), so parts you pass must use those exact materials. Not needed with project_id."
          },
          "parts_csv": {
            "type": "string",
            "description": "Optional raw CSV or Excel cut list text for parts (Part_ID,Length,Width,Quantity,Material,Description)."
          },
          "stock_csv": {
            "type": "string",
            "description": "Optional raw CSV or Excel stock list text (Stock_ID,Length,Width,Quantity,Material,Cost)."
          },
          "units": {
            "type": "string",
            "enum": [
              "mm",
              "cm",
              "in",
              "ft"
            ],
            "default": "mm",
            "description": "Units of measurement for all dimensions"
          },
          "cut_type": {
            "type": "string",
            "enum": [
              "auto",
              "1d",
              "2d"
            ],
            "default": "auto",
            "description": "Cut type: auto, 1d (linear), or 2d (sheets)"
          },
          "kerf": {
            "type": "number",
            "description": "Saw blade kerf in `units` (router bit with nesting). Default: the project's cutting setup with project_id, else 2.5 mm for 1D and 3.5 mm for 2D, the web app's defaults."
          },
          "stock": {
            "type": "array",
            "description": "List of available stock items (optional if use_inventory, project_id, or stock_csv is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "length": {
                  "type": "number"
                },
                "width": {
                  "type": "number",
                  "description": "Width for 2D sheets; omit for 1D"
                },
                "quantity": {
                  "type": "integer",
                  "default": -1,
                  "description": "Available qty (-1 for unlimited)"
                },
                "cost": {
                  "type": "number",
                  "description": "Price per board/sheet (omit if unknown)"
                },
                "material": {
                  "type": "string",
                  "description": "Parts are cut only from stock of the same material (case-insensitive). Leave material off parts or stock entirely to cut everything from all stock."
                }
              },
              "required": [
                "length"
              ]
            }
          },
          "parts": {
            "type": "array",
            "description": "List of required parts to cut (optional if project_id or parts_csv is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "length": {
                  "type": "number"
                },
                "width": {
                  "type": "number",
                  "description": "Width for 2D sheets; omit for 1D"
                },
                "quantity": {
                  "type": "integer",
                  "default": 1
                },
                "can_rotate": {
                  "type": "boolean",
                  "default": true
                },
                "label": {
                  "type": "string"
                },
                "material": {
                  "type": "string",
                  "description": "Parts are cut only from stock of the same material (case-insensitive). Leave material off parts or stock entirely to cut everything from all stock."
                }
              },
              "required": [
                "length"
              ]
            }
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_cutting_solve",
      "title": "Solve a cutting job",
      "description": "Calculates optimal 1D or 2D cutting plans with kerf compensation, cutting sequence, layout coordinates, and PDF download link. Each material is solved on its own stock. Charges the quoted cost to your Opifico balance before solving; a failed solve is refunded. 1D: $0.10 per material, $0.30 per material with `minimize_cost`. 2D: per material by piece count, $0.20 (up to 50 pieces) to $5.00 (up to 10,000), plus $0.30 when a Pro option is used. Pro (set `pro`, a `priority` other than 'balanced', or `minimize_cost`) runs the full 2D search, which often saves a sheet on mixed-size inventories, and lets you choose the priority. Every agent account has access to Pro. Supports solving saved projects exactly as the web app's Project solver does (their saved cutting setup, inventory stock, supplier-sourced lines), drawing stock from the workshop inventory, raw CSV cutlists (`parts_csv`, `stock_csv`), deducting used inventory stock, and returns a workshop cutting schedule in CSV/Excel format.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "description": "Saved project (id or name from opifico_projects_list): cuts it exactly as the web app's Project solver does, with the project's saved cutting setup (kerf, trim, offcut rules, miters, grain, edge banding, saw or nesting) and stock from the workshop inventory, plus any supplier_materials from the supplier list. Do not pass parts or stock with it. A project with both sheet and linear parts needs cut_type '2d' or '1d'."
          },
          "supplier_materials": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "With project_id: material lines to buy from the supplier list (opifico_supplier_get) instead of taking them from inventory, as the Project solver's supplier switch does. Use the line names opifico_cutting_estimate lists (e.g. 'Pine plank 50 × 100 mm'), or 'all'. Those boards come back with source 'supplier' and a buy list."
          },
          "use_inventory": {
            "type": "boolean",
            "description": "If True, the stock is the workshop inventory: the sheet classes for 2D, the linear classes for 1D. Each inventory row's material reads like 'Pine plank 50 × 100 mm' or 'Birch sheet 18 mm' (material, class, section), so parts you pass must use those exact materials. Not needed with project_id."
          },
          "parts_csv": {
            "type": "string",
            "description": "Optional raw CSV or Excel cut list text for parts (Part_ID,Length,Width,Quantity,Material,Description)."
          },
          "stock_csv": {
            "type": "string",
            "description": "Optional raw CSV or Excel stock list text (Stock_ID,Length,Width,Quantity,Material,Cost)."
          },
          "deduct_inventory": {
            "type": "boolean",
            "default": false,
            "description": "If True with use_inventory or project_id, takes the boards/sheets the plan uses off the inventory rows they came from (supplier boards are not in the inventory)."
          },
          "units": {
            "type": "string",
            "enum": [
              "mm",
              "cm",
              "in",
              "ft"
            ],
            "default": "mm"
          },
          "cut_type": {
            "type": "string",
            "enum": [
              "auto",
              "1d",
              "2d"
            ],
            "default": "auto"
          },
          "kerf": {
            "type": "number",
            "description": "Saw blade kerf in `units` (router bit with nesting). Default: the project's cutting setup with project_id, else 2.5 mm for 1D and 3.5 mm for 2D, the web app's defaults."
          },
          "pro": {
            "type": "boolean",
            "description": "2D only. Pro search (adds a flat Pro surcharge to the piece-count price): the full search, which often saves a sheet on mixed-size inventories. Implied by a priority other than 'balanced' and by minimize_cost. Default off. With project_id, overrides the project's cutting setup when given."
          },
          "priority": {
            "type": "string",
            "enum": [
              "balanced",
              "fastest_work",
              "biggest_offcuts"
            ],
            "description": "2D only. What to optimise after material: 'balanced' (few cuts and big offcuts; the default), 'fastest_work' (fewest cuts and least saw time; Pro) or 'biggest_offcuts' (spare room in the biggest reusable pieces; Pro). With project_id, overrides the project's cutting setup when given."
          },
          "minimize_cost": {
            "type": "boolean",
            "description": "Pro. Cheapest stock mix from the listed costs instead of the least material; needs a cost above zero on every stock item. Default off. With project_id, overrides the project's cutting setup when given."
          },
          "stock": {
            "type": "array",
            "description": "Available stock (optional if use_inventory, project_id, or stock_csv is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "length": {
                  "type": "number"
                },
                "width": {
                  "type": "number"
                },
                "quantity": {
                  "type": "integer",
                  "default": -1
                },
                "cost": {
                  "type": "number",
                  "description": "Price per board/sheet (omit if unknown)"
                },
                "material": {
                  "type": "string",
                  "description": "Parts are cut only from stock of the same material (case-insensitive). Leave material off parts or stock entirely to cut everything from all stock."
                }
              },
              "required": [
                "length"
              ]
            }
          },
          "parts": {
            "type": "array",
            "description": "Parts list to cut (optional if project_id or parts_csv is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "length": {
                  "type": "number"
                },
                "width": {
                  "type": "number"
                },
                "quantity": {
                  "type": "integer",
                  "default": 1
                },
                "can_rotate": {
                  "type": "boolean",
                  "default": true
                },
                "label": {
                  "type": "string"
                },
                "material": {
                  "type": "string",
                  "description": "Parts are cut only from stock of the same material (case-insensitive). Leave material off parts or stock entirely to cut everything from all stock."
                }
              },
              "required": [
                "length"
              ]
            }
          }
        }
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_cutting_wallet_balance",
      "title": "Wallet balance and recent charges",
      "description": "Check current prepaid balance, loaded amounts, and recent transactions for your Opifico wallet.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_inventory_get",
      "title": "Read the workshop inventory",
      "description": "Retrieves the authenticated user's workshop inventory in CSV, Excel-compatible tabular format, or JSON: planks, sheet goods, dowels, sheet metal, flat bar, round bar, square and round tube, T-slot extrusion, and the materials library.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string",
            "enum": [
              "all",
              "rectangular",
              "sheet",
              "dowels",
              "sheetMetal",
              "flatBar",
              "round",
              "squareTube",
              "roundTube",
              "extrusion"
            ],
            "default": "all",
            "description": "Optional category filter. Defaults to 'all'."
          },
          "format": {
            "type": "string",
            "enum": [
              "csv",
              "excel",
              "json"
            ],
            "default": "csv",
            "description": "Output format: 'csv' (default, standard Opifico CSV), 'excel', or 'json'."
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_inventory_update",
      "title": "Edit the workshop inventory",
      "description": "Adds, updates, replaces, or removes stock items in the authenticated user's workshop inventory. Accepts raw CSV/Excel text (`csv_data`) or structured items list.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "add",
              "update",
              "delete",
              "replace"
            ],
            "default": "add",
            "description": "'add': add new items or increment identical stock; 'update': modify by id; 'delete': remove by id; 'replace': replace category or full inventory."
          },
          "category": {
            "type": "string",
            "enum": [
              "rectangular",
              "sheet",
              "dowels",
              "sheetMetal",
              "flatBar",
              "round",
              "squareTube",
              "roundTube",
              "extrusion"
            ],
            "description": "Category to update (e.g. 'rectangular' or 'sheet')."
          },
          "csv_data": {
            "type": "string",
            "description": "Optional raw CSV or Excel-copied text (Type,Material,Length,Width,Thickness,Quantity,Price,Description)."
          },
          "items": {
            "type": "array",
            "description": "List of stock items to add, update, or delete (optional if csv_data is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Item ID (auto-generated if omitted on add)"
                },
                "length": {
                  "type": "number",
                  "description": "Stock length"
                },
                "width": {
                  "type": "number",
                  "description": "Stock width (for 2D sheets)"
                },
                "thickness": {
                  "type": "number",
                  "description": "Thickness (for planks/sheets/tubes)"
                },
                "diameter": {
                  "type": "number",
                  "description": "Diameter (for round rods)"
                },
                "wallThickness": {
                  "type": "number",
                  "description": "Wall thickness (for tubes)"
                },
                "quantity": {
                  "type": "integer",
                  "default": 1,
                  "description": "Available quantity"
                },
                "material": {
                  "type": "string",
                  "default": "Default",
                  "description": "Wood/metal material name"
                },
                "price": {
                  "type": "number",
                  "default": 0.0,
                  "description": "Unit cost/price"
                },
                "description": {
                  "type": "string",
                  "description": "Item description/notes"
                }
              },
              "required": [
                "length"
              ]
            }
          },
          "raw_inventory": {
            "type": "object",
            "description": "Full inventory object when replacing the entire inventory structure."
          }
        }
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_projects_list",
      "title": "List saved projects",
      "description": "Lists the user's projects: the same projects the Opifico web app shows in its Projects library, with their folder and the part count per stock class.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_project_get",
      "title": "Read a saved project",
      "description": "Returns one project's parts, one CSV row per part line (Type = stock class, e.g. rectangular or sheet, plus Material, Length, Width, Thickness, Diameter, WallThickness, Quantity, and AngleLeft/AngleRight for mitred ends in degrees off square), or the raw project JSON with its cutting setup (`solveOptions`). Projects hold parts only; solving one takes stock from the inventory and supplier list.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "description": "Project id from opifico_projects_list"
          },
          "name": {
            "type": "string",
            "description": "Project name (matched if project_id is omitted)"
          },
          "format": {
            "type": "string",
            "enum": [
              "csv",
              "excel",
              "json"
            ],
            "default": "csv",
            "description": "'csv' (default), 'excel' (same CSV) or 'json' (the project as stored)."
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_project_save",
      "title": "Save a project's parts and cutting setup",
      "description": "Creates a project in the user's Projects library, or replaces stock classes on an existing one (classes not sent stay as they are), and sets its cutting setup. Open web app tabs pick the change up when they regain focus.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Project title (required for a new project)"
          },
          "project_id": {
            "type": "string",
            "description": "Existing project to update; omit to create one."
          },
          "parts_csv": {
            "type": "string",
            "description": "CSV/Excel parts list in the opifico_project_get format: Type,Material,Length,Width,Thickness,Diameter,WallThickness,Quantity,Description,AngleLeft,AngleRight. Planks give Width and Thickness of the section, sheets give the part's Length and Width plus the sheet Thickness. AngleLeft/AngleRight: miter of each end in degrees off square (45 for a frame corner), linear parts only."
          },
          "category": {
            "type": "string",
            "enum": [
              "rectangular",
              "sheet",
              "dowels",
              "sheetMetal",
              "flatBar",
              "round",
              "squareTube",
              "roundTube",
              "extrusion"
            ],
            "description": "Stock class for parts_csv rows without a Type column."
          },
          "data": {
            "type": "object",
            "description": "Parts per stock class, as stored by the web app: {\"rectangular\": [{\"length\": 600, \"width\": 100, \"thickness\": 20, \"quantity\": 4, \"material\": \"Pine\"}], \"sheet\": [...]}."
          },
          "solve_options": {
            "type": "object",
            "description": "The project's cutting setup, as the web app's Project solver shows and saves it; options not given keep their value. {\"oneD\": {<linear class>: {cutThickness, trimEdges, trimEdgesValue, minLeftover, minLeftoverValue, scrapFilter, scrapFilterMin, scrapFilterMax, miterWorkflow ('direct_inline'|'square_first'), allowFlip, minimizeCost}}, \"twoD\": {kerfAllowance, bitDiameter, minPartSpacing, algorithm ('wood_guillotine' panel saw | 'nonguillotine' CNC/laser nesting), grainDirection, trimEdges, trimEdgesValue, edgeBanding, edgeBandingValue, cutPhase ('auto'|'rip_first'|'crosscut_first'), maxRecutLevel (2-5), panelTurnaround, goalPref ('balanced'|'fastest_work'|'biggest_offcuts'), proSearchPref, minimizeCost}}. Lengths in mm; booleans switch an option on."
          }
        }
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_project_delete",
      "title": "Delete a saved project",
      "description": "Deletes a project from the user's Projects library by id.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "project_id": {
            "type": "string",
            "description": "Project id to delete"
          }
        },
        "required": [
          "project_id"
        ]
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_supplier_get",
      "title": "Read the supplier catalog",
      "description": "Retrieves the authenticated user's supplier stock catalog in CSV, Excel-compatible tabular format, or JSON. The supplier catalog lists sizes and prices the user's usual supplier/hardware store sells; the web app's 1D, 2D and Project solvers can buy from it as many of each size as needed; quantity is informational. MCP solves take stock from `stock`/`stock_csv` or the inventory.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "category": {
            "type": "string",
            "enum": [
              "all",
              "rectangular",
              "sheet",
              "dowels",
              "sheetMetal",
              "flatBar",
              "round",
              "squareTube",
              "roundTube",
              "extrusion"
            ],
            "default": "all",
            "description": "Optional category filter. Defaults to 'all'."
          },
          "format": {
            "type": "string",
            "enum": [
              "csv",
              "excel",
              "json"
            ],
            "default": "csv",
            "description": "Output format: 'csv' (default, standard Opifico CSV), 'excel', or 'json'."
          }
        }
      },
      "annotations": {
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    },
    {
      "name": "opifico_supplier_update",
      "title": "Edit the supplier catalog",
      "description": "Adds, updates, replaces, or removes stock items in the authenticated user's supplier catalog. The supplier catalog lists sizes and prices the user's usual supplier/hardware store sells; the web app's 1D, 2D and Project solvers can buy from it as many of each size as needed; quantity is informational. Accepts raw CSV/Excel text (`csv_data`) or structured items list.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "action": {
            "type": "string",
            "enum": [
              "add",
              "update",
              "delete",
              "replace"
            ],
            "default": "add",
            "description": "'add': add new items or increment identical stock; 'update': modify by id; 'delete': remove by id; 'replace': replace category or full inventory."
          },
          "category": {
            "type": "string",
            "enum": [
              "rectangular",
              "sheet",
              "dowels",
              "sheetMetal",
              "flatBar",
              "round",
              "squareTube",
              "roundTube",
              "extrusion"
            ],
            "description": "Category to update (e.g. 'rectangular' or 'sheet')."
          },
          "csv_data": {
            "type": "string",
            "description": "Optional raw CSV or Excel-copied text (Type,Material,Length,Width,Thickness,Quantity,Price,Description)."
          },
          "items": {
            "type": "array",
            "description": "List of stock items to add, update, or delete (optional if csv_data is provided)",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "description": "Item ID (auto-generated if omitted on add)"
                },
                "length": {
                  "type": "number",
                  "description": "Stock length"
                },
                "width": {
                  "type": "number",
                  "description": "Stock width (for 2D sheets)"
                },
                "thickness": {
                  "type": "number",
                  "description": "Thickness (for planks/sheets/tubes)"
                },
                "diameter": {
                  "type": "number",
                  "description": "Diameter (for round rods)"
                },
                "wallThickness": {
                  "type": "number",
                  "description": "Wall thickness (for tubes)"
                },
                "quantity": {
                  "type": "integer",
                  "default": 1,
                  "description": "Available quantity"
                },
                "material": {
                  "type": "string",
                  "default": "Default",
                  "description": "Wood/metal material name"
                },
                "price": {
                  "type": "number",
                  "default": 0.0,
                  "description": "Unit cost/price"
                },
                "description": {
                  "type": "string",
                  "description": "Item description/notes"
                }
              },
              "required": [
                "length"
              ]
            }
          },
          "raw_inventory": {
            "type": "object",
            "description": "Full inventory object when replacing the entire inventory structure."
          }
        }
      },
      "annotations": {
        "readOnlyHint": false,
        "destructiveHint": true,
        "idempotentHint": false,
        "openWorldHint": false
      }
    }
  ],
  "resources": {
    "uri_templates": {
      "project": "opifico://projects/{project_id} (application/json, listed by resources/list)",
      "solution_pdf": "opifico://solutions/{solution_id}/pdf (application/pdf, kept 30 days)"
    }
  },
  "instructions_url": "https://api.opifico.com/skills/opifico-cutting/SKILL.md"
}
