MCP Server
Inventoros ships with a built-in Model Context Protocol (MCP) server so AI clients like Claude Desktop, Claude Code, Cursor, and custom agents can read inventory state and act on it on a user's behalf. The server is built on the official laravel/mcp package.
Learn more about MCP: https://modelcontextprotocol.io/
Endpoint at a glance
- URL:
POST {your-host}/mcp - Transport: streamable HTTP (per the 2025-11-25 MCP spec)
- Auth: Sanctum bearer token (same as REST)
- Rate limit: 60 requests per minute per user (shared with REST)
GET and DELETE against /mcp return 405 Method Not Allowed by design; only POST is supported.
Why MCP?
MCP is the open standard that lets AI assistants discover and invoke tools on your behalf, like a USB port for AI applications. The Inventoros MCP server exposes the most useful inventory operations as tools that Claude (or any compatible client) can call without copy-pasting from screen to screen:
- "What's running low in the warehouse?" calls
list_low_stock. - "Adjust SKU WIDGET-001 by -3, dropped pallet." calls
adjust_stockafter asking you to confirm. - "Draft a PO with our usual quantities." calls
list_low_stock, groups by supplier, and asks beforecreate_purchase_order.
Security model
Every MCP request authenticates with the same Sanctum bearer token used by the REST API. The token's user provides:
- Organization scope. Every tool and resource is filtered by
organization_id. Cross-organization access surfaces as a not-found error, never403, so the existence of other organizations' data is never leaked. - Permission set. Every tool calls the same
hasAnyPermission()check the REST controllers use.
Destructive tools carry the standard MCP IsDestructive annotation so well-behaved clients prompt for confirmation before invoking. send_purchase_order is also IsIdempotent. Requests without a valid token return HTTP 401. Lacking the right permission returns an MCP error response with isError: true.
Quick start: Claude Desktop / Claude Code
- Get a token from your Inventoros install:
curl -X POST "${APP_URL}/api/v1/login" \
-H 'Content-Type: application/json' \
-d '{"email":"you@example.com","password":"...","device_name":"Claude Desktop"}'
Copy the token value from the response.
- Add Inventoros to your client's MCP config. For Claude Desktop on macOS, edit
~/Library/Application Support/Claude/claude_desktop_config.json(Claude Code uses~/.claude/mcp.json):
{
"mcpServers": {
"inventoros": {
"type": "http",
"url": "https://inventoros.example.com/mcp",
"headers": {
"Authorization": "Bearer 1|paste-your-token-here"
}
}
}
}
- Restart the client. The Inventoros tools appear in the tool picker.
- Try
who_am_ifirst to confirm the token round-trips.
Quick start: Cursor
Add an entry to ~/.cursor/mcp.json (or the workspace-level .cursor/mcp.json):
{
"mcpServers": {
"inventoros": {
"url": "https://inventoros.example.com/mcp",
"headers": {
"Authorization": "Bearer 1|paste-your-token-here"
}
}
}
}
Tool catalog
22 tools across 7 surfaces. Tools marked destructive mutate state and should be confirmed before invocation.
Identity:
who_am_i(no permission required). Identify the authenticated user, organization, role, and the permissions this token holds. Run first to confirm wiring.
Catalog (read):
list_products(view_productsormanage_products). Paginated product list with search, category, warehouse, and low-stock filters.search_products(view_productsormanage_products). Lightweight substring search returning up to 25 matches.get_product(view_productsormanage_products). Single product with category, location, suppliers, options, and active variants.lookup_barcode(view_productsormanage_products). Exact match on barcode/SKU across products and variants.list_categories(view_categoriesorview_products). Category list.list_locations(view_locationsorview_products). Storage locations, optionally filtered by warehouse.list_warehouses(view_warehousesorview_products). Warehouse list.
Stock:
list_low_stock(view_productsormanage_products). Products at or belowmin_stock, sorted by shortage.adjust_stock(manage_stock). Apply a signed delta with a reason. Destructive; confirm first.
Sales:
list_orders(view_ordersormanage_orders). Paginated orders with status, source, warehouse, and date filters.get_order(view_ordersormanage_orders). Single order with line items.create_order(manage_orders). Create an order; decrements stock; fails if any line is short. Destructive.
Purchasing:
list_suppliers(view_suppliersormanage_suppliers). Supplier list.list_purchase_orders(view_purchase_ordersormanage_purchase_orders). Paginated POs with status and supplier filters.get_purchase_order(view_purchase_ordersormanage_purchase_orders). Single PO with supplier and line items.create_purchase_order(manage_purchase_orders). Create a draft PO. Does not affect stock until received. Destructive.send_purchase_order(edit_purchase_ordersormanage_purchase_orders). Transition draft to sent. Destructive and idempotent.receive_purchase_order(receive_purchase_ordersormanage_purchase_orders). Receive items; writes stock; transitions to partial or received. Destructive.
Manufacturing:
list_work_orders(manage_stock). Paginated work orders with a status filter.start_work_order(manage_stock). Validate component stock and transition to in_progress. Destructive.
Catalog (write):
create_product(manage_products). Create a product. Destructive.
Resources
Browsable read-only data the agent can fetch by URI:
inventoros://low-stock. Snapshot of every product at or below itsmin_stock.inventoros://orders/recent. The 25 most recent sales orders with status and totals.
Prompts
Canned templates the agent can invoke:
reorder_helper(optionalwarehouse_idargument). Walks the user through low-stock review, supplier grouping, draft PO creation, and (after explicit confirmation) marking POs sent.
Error model
Errors are returned as MCP isError: true responses with a human-readable string:
- Unauthenticated: HTTP 401, before any tool runs.
- Forbidden: "Token lacks any of the required permissions: ...".
- Not found: "Product not found in this organization." Cross-org access surfaces here, not as 403.
- Validation: comma-joined Laravel validation messages.
- Domain: for example "Cannot remove 100 units; only 5 on hand."
Extending
Tools live in app/Mcp/Tools/ in the Inventoros codebase. To add one:
- Generate the class:
php artisan make:mcp-tool MyNewTool. - Use the
App\Mcp\Concerns\AuthenticatesMcpRequesttrait foruser(),organizationId(), andauthorize([...]). - Define
description,schema(JsonSchema $schema), andhandle(Request $request). - Annotate destructive tools with
#[IsDestructive]. - Register the class in
app/Mcp/Servers/InventorosServer.php's$toolsarray. - Add a test in
tests/Feature/Mcp/InventorosMcpServerTest.php.
Versioning
The MCP server follows the same release cycle as Inventoros core. Tools may be added in minor releases; signature-breaking changes wait for a major version bump.
Need a hand?
Open an issue or start a discussion on GitHub and the community will help you out.