> ## Documentation Index
> Fetch the complete documentation index at: https://muveya.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MCP tools

> Every tool of the muveya MCP server: what it returns, its arguments, the scope it needs, what it hides, and a question a manager could ask.

The muveya MCP server offers ten tools. All of them are **read-only**: each one is marked with `readOnlyHint: true`, and none can create, change or approve anything. `tools/list` returns them in this order.

| Tool | Title | OAuth scope | Internal permission | With OAuth |
| - | - | - | - | - |
| `clinics.list` | List clinics and warehouses | `inventory:read` | `inventory.read` | Works |
| `catalog.search` | Search the catalog | `catalog:read` | `catalog.read` | Works |
| `inventory.check` | Check stock availability | `inventory:read` | `inventory.read` | Works |
| `orders.get` | Get an order | `orders:read` | `orders.read.all` | Works |
| `approvals.list_pending` | List pending approvals | None | `approvals.decide` | Always refused |
| `management.pending_decisions` | Pending management decisions | None | `approvals.decide` | Always refused |
| `fulfillment.get_pick_list` | Get a pick list | None | `fulfillment.pick` | Always refused |
| `analytics.consumption` | Consumption per supply | `analytics:read` | `reports.read` | Works |
| `management.briefing` | Management briefing | `analytics:read` | `reports.read` | Works |
| `management.compare_clinics` | Compare clinics | `analytics:read` | `reports.read` | Works |

## How to read this page

* **Scope.** Each tool's description, as a client shows it, ends with a sentence such as "Requires the `inventory.read` scope granted to this connection." That name is the **internal permission**. The table above maps it to the OAuth scope you ask for. The check happens when the tool is called, before anything is read; a missing scope answers `common.forbidden`.
* **Arguments.** Every argument is optional unless marked required. Arguments are strict: a name the tool does not declare (for example `tenantId` or `clinicId`) is rejected, and so is a value outside the limits shown. No tool accepts a workspace selector: the workspace always comes from the connection.
* **Results.** A result is one text item holding a JSON document. The field tables below describe that document. A field marked "only with" is **absent** when the connection lacks the scope; it is never `null`.
* **Ids.** Ids are opaque strings. The same id works in the console address bar, in the `/v1` API and in other tools. Names of people are not available through MCP: a field such as `requesterId` is an id.
* **Language.** On the OAuth connection endpoint, titles, descriptions, error texts and labels come back in the requested language. Tool and field names never change.
* **Example questions** are what a manager could type to an assistant connected to muveya. The assistant decides which tool to call.

## `clinics.list`

**List clinics and warehouses.** Lists the locations (clinic sites) and warehouses your membership can access, with each one's id, name and status. Assistants use it to discover the ids other tools need.

| | |
| - | - |
| OAuth scope | `inventory:read` (internal `inventory.read`). `clinics:read` is not enough. |
| Arguments | None |
| Redaction | Nothing to hide: names, kinds and statuses only |

The result has two complete lists, in creation order, including inactive sites:

| Field | Meaning |
| - | - |
| `clinics[].clinicId` | Id of the location. |
| `clinics[].name` | Name of the location, for example "Clínica Norte". |
| `clinics[].status` | `active` or `inactive`. |
| `warehouses[].warehouseId` | Id of the warehouse. |
| `warehouses[].name` | Name of the warehouse. |
| `warehouses[].kind` | `central` or `express`. |
| `warehouses[].clinicId` | The location an express warehouse belongs to. Absent for a central warehouse. |
| `warehouses[].status` | `active` or `inactive`. |

Example questions:

* "Which warehouses does Clínica Norte have?"
* "List our inactive locations."

## `catalog.search`

**Search the catalog.** Searches the catalog by item name or SKU and returns the matching items with their unit, category and status. Cost appears only when the connection may see it.

| | |
| - | - |
| OAuth scope | `catalog:read` (internal `catalog.read`) |
| Cost fields | Only with `catalog.cost:read` (internal `catalog.cost.read`) |

<ParamField body="term" type="string">
  Text to find in the item name or the SKU. Case-insensitive, matched anywhere in the value. 1 to 120 characters. Without it, the tool returns items of the chosen status.
</ParamField>

<ParamField body="status" type="string" default="active">
  Lifecycle status to search: `active`, `inactive` or `draft`.
</ParamField>

The result is `{ items, truncated }`. It holds at most 50 items, oldest first. When `truncated` is `true`, more items matched: ask again with a more specific `term`.

| Field | Meaning |
| - | - |
| `itemId` | Id of the item. |
| `sku` | SKU, for example `GLV-NIT-M`. |
| `name` | Item name. |
| `description` | Description, when the item has one. |
| `categoryId` | Id of the item's category (the category name is not included). |
| `unitOfMeasure` | Unit the item is counted in, for example `box` or `unit`. See the values in [`muveya://catalog/policies`](/docs/en/mcp/resources). |
| `packaging` | Packaging text, when set. |
| `criticality` | `low`, `medium` or `high`. |
| `tracksLot`, `tracksSerial`, `tracksExpiry` | Whether boxes of this item record a lot, a serial number, an expiry date. |
| `highValue` | Whether the item is marked high value. |
| `status` | `draft`, `active` or `inactive`. Only `active` items can be ordered. |
| `costStatus` | Only with `catalog.cost:read`. `unset`, `verified`, `review_required` or `invalid`. |
| `cost`, `currency` | Only with `catalog.cost:read`. The recorded cost in minor units of `currency` (for example cents). |
| `costMeasurement` | Only with `catalog.cost:read`. The unit and measurement version the cost was recorded for. |

<Warning>
  Only a `verified` cost applies to the item's current unit. A cost in `review_required` or `invalid` is not a price you can use. See [Costs](/docs/en/catalog/costs).
</Warning>

Example questions:

* "Search the catalog for nitrile gloves."
* "Which catalog items are still in draft?"
* "What does a box of GLV-NIT-M cost?" (answers only if the connection has `catalog.cost:read`)

## `inventory.check`

**Check stock availability.** Returns how much of one catalog item is on hand, reserved and available in each warehouse, optionally for one warehouse only.

| | |
| - | - |
| OAuth scope | `inventory:read` (internal `inventory.read`) |
| Redaction | Quantities only; nothing to hide |

<ParamField body="catalogItemId" type="string" required>
  Id of the catalog item, 1 to 64 characters. An assistant usually finds it first with `catalog.search`.
</ParamField>

<ParamField body="warehouseId" type="string">
  Id of one warehouse, 1 to 64 characters, to narrow the answer to it.
</ParamField>

| Field | Meaning |
| - | - |
| `catalogItemId` | The item you asked about. |
| `warehouses[].warehouseId` | A warehouse that holds boxes of the item. Sorted by id. |
| `warehouses[].onHand` | Quantity physically in that warehouse, in the item's unit. |
| `warehouses[].reserved` | Quantity already reserved for orders. |
| `warehouses[].available` | `onHand` minus `reserved`. |
| `truncated` | `true` when the item has more than 1,000 boxes to add up: the figures are then partial. Narrow to one warehouse. |

```json Example result theme={null}
{
  "catalogItemId": "6650aa00000000000000a001",
  "warehouses": [
    { "warehouseId": "6650bb00000000000000b001", "onHand": 40, "reserved": 12, "available": 28 },
    { "warehouseId": "6650bb00000000000000b002", "onHand": 6, "reserved": 0, "available": 6 }
  ],
  "truncated": false
}
```

The figures add up the balance of every box of the item in each warehouse. They do not say which boxes are expired or in quarantine; use [Boxes](/docs/en/inventory/boxes) in the console for box detail. A warehouse without boxes of the item is not listed, and an item with no stock (or an id that does not exist) returns an empty `warehouses` list, not an error.

Example questions:

* "How many GLV-NIT-M boxes are available in each warehouse?"
* "Is there any stock of that item in Bodega Central?"

## `orders.get`

**Get an order.** Returns one order by its id, with its lines, status and approval plan.

| | |
| - | - |
| OAuth scope | `orders:read` (internal `orders.read.all`) |
| Line cost | Only with `catalog.cost:read` |
| Order value and `patientRef` | Never with OAuth |

<ParamField body="orderId" type="string" required>
  Id of the order, 1 to 64 characters. It is the opaque id, not the order number. In the console you can copy it from the address of the order page, `console.muveya.com/orders/` followed by the id.
</ParamField>

| Field | Meaning |
| - | - |
| `orderId`, `number` | Id and human order number. |
| `type` | `general` or `clinical`. |
| `status` | Current status, for example `pending_approval` or `dispatched`. See [`muveya://orders/status-model`](/docs/en/mcp/resources). |
| `requesterId` | Id of the team member who created the order. |
| `clinicId` | Location the order belongs to. |
| `destinationWarehouseId` | Warehouse that receives the goods. |
| `preferredSourceWarehouseId` | Preferred warehouse to supply from, when set. |
| `justification` | Reason given by the requester, when set. |
| `lines[]` | `lineId`, `catalogItemId`, `sku`, `name`, `requestedQty`, `unitOfMeasure`, `category` (the category id when the line was added) and `highValue`. |
| `lines[].unitCost`, `lines[].currency` | Only with `catalog.cost:read`: the cost frozen on the line. |
| `approvalPlan` | Present once the order has been evaluated for approval: `policyVersion`, `requiresApproval` and `steps` (`stage`, `requiredScope`, `minApprovers`). Its `evaluatedValue` and `currency` are never shown to an OAuth connection. |
| `version` | Version number of the order record. |
| `value`, `currency` | Order total. Never shown to an OAuth connection. |
| `patientRef` | External patient reference of a clinical order. Never shown to an OAuth connection. |

If no order with that id exists in your account, or the id is malformed or belongs to another account, the result is `isError` with `code` `orders.not_found`. MCP has no tool to list or search orders; to list them, use `GET /v1/orders` or the [Orders](/docs/en/orders/create-and-track) screen.

Example questions:

* "What is the status of order 6650cc00000000000000c001, and which approval steps does it need?"
* "Which items and quantities are on that order?"

## `approvals.list_pending`

**List pending approvals.** Returns the orders waiting for the connected person's own approval decision.

| | |
| - | - |
| Internal permission | `approvals.decide` |
| With OAuth | Always refused with `common.forbidden` |
| Arguments | None |

This tool reads a person's approval inbox. The connection acts as the signed-in clinic member, but the current read-only OAuth scopes do not include `approvals.decide`, so the call is refused before anything is read.

To review and decide approvals today, use **Order approvals** in the console: see [Approvals](/docs/en/orders/approvals). For a count of pending approvals, use `management.briefing`, whose figures include the approval backlog by age.

Example question: "What is waiting for my approval?" (with OAuth the assistant will report that the tool is not allowed)

## `management.pending_decisions`

**Pending management decisions.** The same approval inbox as `approvals.list_pending`, presented for managers: each order with its submission time (to judge its age against a service level), its steps and the value hidden unless the reader may see it.

| | |
| - | - |
| Internal permission | `approvals.decide` |
| With OAuth | Always refused with `common.forbidden` |
| Arguments | None |

It returns exactly what `approvals.list_pending` returns and is refused for the same reason. Use **Order approvals** in the console, or `management.briefing` for the backlog figures.

Example question: "Which decisions have been waiting on me for more than two days?"

## `fulfillment.get_pick_list`

**Get a pick list.** Returns the boxes to pick for one order, nearest expiry first (FEFO), only boxes still active.

| | |
| - | - |
| Internal permission | `fulfillment.pick` |
| With OAuth | Always refused with `common.forbidden` |

<ParamField body="orderId" type="string" required>
  Id of the order to pick, 1 to 64 characters.
</ParamField>

Picking is work done by a person at a warehouse, so this tool checks the picker's own permission. The current OAuth scopes do not grant `fulfillment.pick` (`fulfillment:read` does not). For a person, each line would carry `lineId`, `catalogItemId`, `sku`, `boxId`, `boxCode`, `warehouseId`, `quantity`, `picked`, `requiresSeparation` and, when tracked, `lotNumber` and `expiryDate`; never cost, value or patient reference.

To pick today, use **Deliveries** in the console: see [Picking](/docs/en/deliveries/picking).

Example question: "Which boxes do I pick for this order?"

## `analytics.consumption`

**Consumption per supply.** Returns how much of each supply was consumed over time, each supply in its own unit, as an evidence report. It never adds up different supplies or units.

| | |
| - | - |
| OAuth scope | `analytics:read` (internal `reports.read`) |
| Redaction | Quantities only; no cost or value |
| Same figure as | The [Consumption report](/docs/en/reports/consumption) in the console and `GET /v1/analytics/consumption-trend` |

<ParamField body="from" type="string">
  Start of the window, ISO-8601 (for example `2026-08-01T00:00:00Z`), inclusive. 1 to 40 characters. Defaults to 30 days before `to`.
</ParamField>

<ParamField body="to" type="string">
  End of the window, ISO-8601, exclusive. 1 to 40 characters. Defaults to now.
</ParamField>

<ParamField body="granularity" type="string" default="day">
  Bucket size: `day` or `week`. Buckets follow UTC.
</ParamField>

<ParamField body="catalogItemId" type="string">
  Id of one catalog item, 1 to 64 characters, to narrow the report to it.
</ParamField>

`from` must be earlier than `to`, and the window can be at most 366 days. A date that cannot be read, a backwards window or a longer one returns `isError` with `code` `common.invalid_request`.

| Field | Meaning |
| - | - |
| `metric` | `CONSUMPTION_TREND`. |
| `definition` | What the figures mean, in English. |
| `granularity`, `period.from`, `period.to` | The buckets and the window actually used. |
| `timezone`, `asOf`, `freshness` | `UTC`, when it was computed, and `live`. |
| `evidenceId`, `filters` | A stable description of the request, to cite or reproduce the figure. |
| `movementCount` | Consumption movements read in the window. |
| `series[]` | One entry per supply and unit: `seriesKey`, `catalogItemId`, `itemName`, `sku`, `unitOfMeasure`, `totalConsumed`, `usedQuantity`, `issuedQuantity`, `movementCount` and `buckets`. |
| `series[].buckets[]` | `bucketStart`, `consumedQuantity`, `usedQuantity`, `issuedQuantity`, `movementCount`. |
| `unverifiedItems[]` | Supplies whose movements have no verified unit: counted, never quantified. |
| `coverage` | `measuredMovementCount`, `unverifiedMovementCount`, `warehouseScope` (limited to the member's warehouse access) and, when the read was cut short, `coveredFrom`. |
| `truncated` | `true` when the read hit its limit; the figures then cover only from `coverage.coveredFrom`. |
| `narration` | A set of statements a summary may make, included only when every number in it matches the table. |

`usedQuantity` is stock recorded where it was used; `issuedQuantity` is stock delivered to rooms that do not count their stock, an estimated consumption. The two add up to the consumed quantity. [Management analytics](/docs/en/reports/management-analytics) explains each figure.

Example questions:

* "How many units of each supply did we use per week in August?"
* "Show me the daily consumption of GLV-NIT-M over the last 30 days."

## `management.briefing`

**Management briefing.** Returns the reproducible management briefing (a digest where every figure links to its source metric and evidence id) together with the ranked list of operational exceptions.

| | |
| - | - |
| OAuth scope | `analytics:read` (internal `reports.read`) |
| Redaction | Counts, quantities and durations only; no cost or value |
| Same figures as | `GET /v1/analytics/briefing` and `GET /v1/analytics/exceptions` |

<ParamField body="period" type="string" default="daily">
  `daily` or `weekly`. It decides whether the consumption figures inside the briefing are grouped by day or by week; the other figures do not change with it.
</ParamField>

The result is `{ briefing, exceptions }`.

| Field | Meaning |
| - | - |
| `briefing.metric` | `MANAGEMENT_BRIEFING`. |
| `briefing.period`, `briefing.definition` | The period asked for and what the briefing contains. |
| `briefing.timezone`, `briefing.asOf`, `briefing.freshness` | `UTC`, when it was computed, and `live`. |
| `briefing.evidenceId` | `MANAGEMENT_BRIEFING:period=daily` or `MANAGEMENT_BRIEFING:period=weekly`. |
| `briefing.truncated` | `true` if any figure is a lower bound because a read hit its limit. |
| `briefing.figures[]` | `metric`, `dimension` (for a multi-row metric such as an age band), `label` (in English on this endpoint), `value` (`null` when there is no sample, never a made-up zero), `unit` and `evidenceId`. |
| `exceptions.total` | Number of exceptions found. |
| `exceptions.exceptions[]` | `metric` (`EXPIRING_STOCK`, `DELIVERY_EXCEPTIONS`, `LOW_STOCK`, `RECEIPT_DISCREPANCIES` or `PENDING_ORDERS`), `priority` (`0` is the most urgent), `drillDownType` (`order`, `movement` or `item`), `drillDownId`, `reference` (non-sensitive ids), `evidenceId` and, when known, `detectedAt`. |
| `exceptions.asOf`, `exceptions.evidenceId`, `exceptions.truncated` | As for the briefing. |

The `drillDownId` of an exception is the id of an order, a stock movement or a catalog item. An `order` id opens with `orders.get`, an `item` id with `inventory.check`. [Management analytics](/docs/en/reports/management-analytics) defines every figure.

Example questions:

* "Give me today's briefing and the three most urgent exceptions."
* "What changed this week? Use the weekly briefing."

## `management.compare_clinics`

**Compare clinics.** Ranks your locations by order volume, with the metric definition shown. Every value is a count.

| | |
| - | - |
| OAuth scope | `analytics:read` (internal `reports.read`) |
| Arguments | None |
| Redaction | Counts only; order value is never included |
| Same figure as | `GET /v1/analytics/clinics/comparison` |

| Field | Meaning |
| - | - |
| `metric`, `definition`, `unit` | `ORDERS_BY_CLINIC`, its definition in English, and `orders`. |
| `timezone`, `asOf`, `freshness`, `evidenceId`, `filters` | As in the other reports. |
| `truncated` | `true` when the order count hit its limit: the counts are then a lower bound. |
| `clinics[]` | `clinicId`, `clinicName`, `clinicStatus`, `totalOrders` and `pendingApprovalOrders`. |

Every location is listed, including those with no orders. The list is sorted by `totalOrders`, highest first; ties are ordered by `clinicId`.

Example questions:

* "Which location placed the most orders?"
* "Compare our locations by pending approvals."

## Related pages

<CardGroup cols={2}>
  <Card title="MCP server" icon="plug" href="/docs/en/mcp/introduction">
    Scopes, redaction, audit and errors.
  </Card>

  <Card title="Connect a client" icon="link" href="/docs/en/mcp/connect">
    Set up Claude Code, Claude Desktop, Cursor or your own client.
  </Card>

  <Card title="Resources" icon="book" href="/docs/en/mcp/resources">
    Reference documents that help an assistant read these results.
  </Card>

  <Card title="Management analytics" icon="chart-line" href="/docs/en/reports/management-analytics">
    The definition of every management figure.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.