Breww MCP server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Breww MCP serverWhat's in the fermenters right now?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Breww MCP server
An MCP server that lets Claude, ChatGPT and other MCP clients work with a Breww brewery account: products and stock, production batches, trade customers, orders (which in Breww are also the invoices) and deliveries, and (when enabled) creating a sales order. It is built from Breww's public API documentation: the OpenAPI 3.0.3 document at https://breww.com/api/schema/ ("Breww public API", version "v0.1.0 beta") and the guide at https://breww.com/docs/breww-public-api/.
Once it's connected, someone at the brewery can ask things like:
"Which pubs are overdue to reorder, and who looks after them?"
"What does the Red Lion owe us, and which invoices are past due?"
"What's in the fermenters right now, and what's planned for next week?"
"How much Maris Otter do we have, and where is it?"
"What's going out on Friday's deliveries?"
With writes enabled: "Draft an order for the Red Lion: three firkins of Session Pale and two cases of Hazy IPA."
Tools
Tool | What it does | API calls |
| Sellable products (cask, keg, smallpack, multi-pack, stock item, service) with code, type, price and the drinks they contain. Filters: name, code, type, tag (by name, assumed; see Status); obsolete products left out unless asked for. |
|
| One product: barcode, volumes, duty, the drinks and stock items it is made of, tags, custom fields. |
|
| Current stock of stock items (ingredients, packaging, chemicals, guest beer, merchandise), summed per item and location, optionally with the individual lots (with a location filter, the lot counts say which cover that location and which every location); and the stock of finished products as Breww reports it. Filters: stock item, location, batch code, expiry date, product name. |
|
| Drink batches (brews) with drink, status, dates, volumes and current vessel. Filters: status, drink, batch reference, start and planned dates. |
|
| One batch: brew type, ABV, starting and final gravity, volumes, vessel and fill, recipe version. |
|
| Customers, leads and suppliers (one list in Breww) with last order, predicted next order, cadence, average order value and Breww's churn and overdue probabilities. Filters: name, entity type, last order date, next expected order, churn and overdue probability, sales person, tag (by name, assumed). |
|
| One customer in full, with its most recent orders. |
|
| Orders/invoices with status, payment status, dates, value, total and amount due. Filters: customer, order status, payment status, source, issue and due dates, amount due, number, PO number. |
|
| One order/invoice with its lines, adjustment lines (deposits, delivery), totals and delivery. |
|
| Deliveries, collections, courier shipments and uplifts ("fulfillments") with date, customer, order, dispatch and completion, drop window and lines. Filters: date range, type, completed, failed, run, courier, order. |
|
| Creates a sales order, as a draft by default, with Breww's automatic customer emails suppressed by default. Fetches the customer and the products first and refuses locally if the record is not a customer, order processing is blocked (or draft-only and a confirmed order was asked for), or a product is unknown, obsolete or "packaged only". Only registered when writes are enabled. |
|
Every tool refuses arguments it does not know (a misspelt filter name is an error, not an unfiltered list) and text filters that are empty after trimming.
list_products, list_batches, list_customers, list_orders and list_deliveries also take ordering (the API's documented sort parameter: a field name, - for descending, several separated by commas), max_results and page.
There is no separate list_invoices: Breww's GET /orders/ is documented as "API endpoint to manage your orders/invoices" and returns PaginatedInvoiceList, so list_orders with order_status invoiced is that list.
Not covered on purpose: the other 121 of the spec's 132 operations, including accountancy sync, customer payments and payments, credit notes, purchase orders and supplier invoices, users, CRM activities and deals, fermentation readings, vessels, containers and ingredient batches, and every PUT, PATCH and DELETE.
Related MCP server: Pitchup.com MCP server
Setup
Requires Node 18 or later.
npm install
npm run buildYou need an API key. In Breww, an Admin goes to Settings > Breww Apps & API, creates a Private app, then creates an API key on the app's page. Keys start with BRW. and are sent as Authorization: Bearer <key>. Each key has one access level, chosen when it is created and not changeable afterwards: Read only (GET, HEAD and OPTIONS only) or Full access. A Read only key makes writes impossible whatever this server allows; use one unless you want create_order. Breww reviews every app, private ones included; the docs say apps "can be used prior to the review", and that a rejected app's requests are refused.
Claude Desktop: add this to claude_desktop_config.json:
{
"mcpServers": {
"breww": {
"command": "node",
"args": ["/absolute/path/to/breww-mcp/dist/index.js"],
"env": { "BREWW_API_KEY": "BRW.your-key" }
}
}
}Claude Code:
claude mcp add breww -e BREWW_API_KEY=BRW.your-key -- node /absolute/path/to/breww-mcp/dist/index.jsVariable | Required | Meaning |
| yes | Your API key, sent as a Bearer token. A value pasted with its |
| no |
|
| no | How many requests this server allows itself per minute, 1 to 600. Defaults to 60, Breww's documented limit; lower it if other integrations use the same account. |
| no | Time budget per tool call in seconds, above 0 and at most 55. Defaults to 45 (see Safety defaults). Lowered by the tests. |
| no | Defaults to |
Safety defaults
Read-only unless
BREWW_ALLOW_WRITES=true. Read tools carry the MCPreadOnlyHintannotation;create_orderis marked as a non-idempotent, non-destructive write. With a Read only key Breww refuses thePOST(the docs: "A 403 (or similar) on POST, PUT, PATCH or DELETE from a read-only key is expected behaviour") and the error says a Full access key is needed.create_ordercreates a draft unless asked forconfirmed; it never invoices. It sendsallow_automatic_emailing_to_customer: falseunlessemail_customeris true (the API's own default is true, so Breww's configured customer emails would otherwise go out).Personal data is only returned when a tool is called with
include_contact_details=true: customers' primary email, phone number, billing and delivery addresses, billing name override, tax number and custom fields; the names, job titles, emails and phones of a customer's contacts (by default only their number is returned); an order's contact email, billing and delivery addresses and its invoice PDF and customer-facing links (the documents carry the addresses; they are never downloaded); a delivery's full address, phone number, coordinates and delivery-note and invoice PDF links (by default only the town); and staff names (sales person, batch creator, order creator, delivery completed by), which are returned as user IDs by default.In free text (customer names, comments and alert notes, order notes and private notes, order line notes, batch references, delivery instructions, stock lot notes and the names and values of product custom fields) email addresses are replaced with
[email redacted], phone-number-like sequences with[phone redacted]and UK postcodes with[postcode redacted]by default; the raw text comes withinclude_contact_details. Nothing else is taken out of free text: a person's name or a street address typed into a comment or delivery instruction ("Call Sam", "12 Mill Lane") is returned as written. The phone match is a heuristic (the same as the Signable server's): international numbers written with+or00(including+44 (0)7700 …), UK numbers with a bracketed area code such as(0117) 496 0000, and UK-style0…numbers of 9 to 11 digits with spaces, dots or hyphens between groups. The postcode match is a heuristic too, on the shape of a UK postcode in either case (BS3 4QT,bs34qt); a short code of the same shape, such asFV1 2HL, is redacted as well. Product custom fields are always redacted (the product tools have no switch). Breww's error messages are redacted the same way before they are passed on, and the API key is replaced with[redacted]in any error text that echoes it.Never returned, even on request: payment records (
payments_refunds), payment integration data (payment_integration_details) and whether a customer pays by direct debit. No bank or card endpoint is called.IDs must be positive whole numbers (every ID used here is an integer key in the spec) and are checked before any call. Date filters must be real calendar dates (2026-02-30 is refused). Filters typed
date-timeaccept a date alone, which is sent as the start of that day in UTC (2026-09-01becomes2026-09-01T00:00:00Z).orderingmust be field names separated by commas.Every tool call has one time budget, 45 seconds by default (
BREWW_TOOL_BUDGET_S), shared by all its requests and waits, because the MCP SDK's default request timeout is 60 seconds and one call can make several requests (create_ordermakes three, a list up to ten pages). A retry wait or a wait for the rate window that would end past the budget is not started, a request still unanswered at the budget is abandoned, and the call ends with an error that says so, before the MCP client gives up.POST /orders/is only sent if at least 15 seconds of the budget are left (a third of the budget, if that is shorter), so an order is not sent that the server might have to abandon; if it is abandoned in flight anyway, the error says the order may exist and to checklist_ordersfirst. A list that runs out of time after its first page returns the pages it has, with a note and thenext_pageto continue from. A call cancelled by the MCP client ends its waits at once, aborts a request in flight and sends nothing more, so aPOST /orders/that has not been sent by then is not sent (one already sent may still be processed by Breww).Rate limits, as Breww documents them: 60 requests per minute and 5,000 per day; above them the API answers
429with the messageRequest was throttled.and possibly the wait "in both the message and in aRetry-AfterHTTP header". This server keeps itself under the minute limit with a sliding one-minute window (BREWW_REQUESTS_PER_MINUTE, default 60); if the window is full and the next slot is more than 15 seconds away, the call fails at once with the wait in the message rather than hanging. The daily limit is not tracked. A 429 is retried at most twice for any method, includingPOST /orders/, on the assumption that a throttled request was not processed (see Status), waiting forRetry-After(whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent or unreadable). Each wait is capped at 10 seconds; if Breww asks for a longer wait the call gives up at once and says how long to wait.502, 503 and 504 are retried the same way for
GETonly; after three failures the error says the service may be unavailable, without the gateway's HTML. APOST /orders/is never retried after a gateway error, because the order may already exist; the error says to checklist_ordersfirst.A 200 whose body is not a JSON object (a proxy or a login page, or JSON
null, a string or a list) is an error that namesBREWW_BASE_URLand describes the body by type and size, or by JSON kind, without quoting it. A rejected key produces a message that says where keys are created.
Tests
npm testThe test suite:
Validates every fixture record against the component schemas in Breww's published OpenAPI spec (
Product,Location,StockReceived,DrinkBatch,Customerfor 230 records,InvoicewithSaleBasiclines,Fulfillment) with Ajv's draft-04 dialect (OpenAPI 3.0 uses draft-04's booleanexclusiveMinimum/exclusiveMaximum) andajv-formats, plus a key-by-key walk that fails on any field the spec does not declare. The spec is downloaded frombreww.com/api/schema/tospec.yamlon the first run. The spec needs translating before Ajv can read it; the translations, intest/schema.mjs, are: (1)nullable: truebecomes "or null"; (2)type: "decimal"(not a JSON Schema type; 27 properties such asStockReceived.current_quantity) becomes "a decimal written as a string, or a number"; (3)type: "array", format: "binary"with an objectexample(34 properties such asProduct.liquid_volume_gross, example{"litre": 100, "us_gallon": 26.4…}) is validated against the shape of its example, since the spec's type contradicts its own example; (4)format: "decimal"on a string becomes a numeric pattern; (5) for request bodies,readOnlyproperties are dropped frompropertiesandrequired, as OpenAPI 3.0 prescribes; (6) thetimeformat accepts a time without a zone (09:00:00). Negative controls check that the translated schemas still reject a string ID, a wrong example shape, a null in a non-nullable field and a non-numeric decimal, and that the key walk reports undeclared keys at any depth.Starts a local mock of the API under
/apithat serves those fixtures with the documented paging (page,page_sizedefaulting to 50 and capped at 200,count/next/previous/results,nextnull on the last page), Bearer-key auth (401 for a missing or wrong key; a Read only key mayGETbut gets 403 onPOST; the spec's second scheme,tokenAuthwith aTokenprefix, is not modelled, because whether aBRW.key works under it is undocumented and the server always sendsBearer), 404 for unknown IDs, therunandcourierdelivery filters matching nothing (theFulfillmentschema has neither field, so no fixture delivery can carry one), the conditionally includedquantity_in_stock_in_formatonly wheninclude_fieldsasks for it, and a one-off 429 withRetry-After: 1onGET /drink-batches/. The mock's list, detail and created-order (201) responses are validated against the response schemas the spec names for each operation. Breww publishes no error schema, so the 429 body is checked against a schema written from the documented message (Request was throttled.; thedetailkey holding it is an assumption) and the 400 body against a schema written from the release note of 2026-08-15, whose example is read from the spec and must pass verbatim; the mock's 400 for an unknown product is exactly that example. The 401, 403 and 404 bodies ({"detail": …}) are placeholders with no documented shape and are not validated.Starts the built server and drives it over stdio with the official MCP client: 35 checks, plus 7 on the built modules directly (listed after this step). They cover tools/list and annotations; every read tool; paging across pages 1 and 2 of 200 to the documented end (
nextnull), whole-page continuation withnext_page, a page past the end reported as such, andpage_sizefollowingmax_results; every filter each tool offers passed through exactly as the spec names it (status__in,beer,batch_ref__contains,datetime_started__gte/__lt,planned_start_date__gte/__lte,name__contains,code,type,tags,obsolete,is_empty=False,stock_item,batch_code__contains,expiry_date__lte,include_fields,entity_type__bitand,last_order_date__lt/__gte,next_expected_order__lte,prob_churned__gte,prob_overdue__gte,sales_person,customer,order_status__in,payment_status__in,source__in,issue_date__gte/__lte,due_date__lt,amount_due__gt,number,po_number__contains,date_scheduled__gte/__lte,type__in,completed,failed,run,courier,invoice,ordering), with__invalues comma-separated as the spec'sexplode: falsesays and date-only values completed to date-times; stock summed per item and location with the location filter applied locally; redaction of emails, phone numbers, addresses, contacts, tax number, custom fields (names and values) and staff names by default and their return on request, with the national,+44 (0), bracketed and00-prefixed phone forms and postcodes redacted in free text, and the street of a delivery instruction shown to be returned as written; payment records never returned, even on request; the write gate with the variable unset and set tofalse; thePOST /orders/body validated againstInvoiceCreateand each line againstSaleInlineCreate(readOnly fields left out), drafts and suppressed customer emails by default; the local refusals (blocked, draft-only and non-customer records, obsolete, packaged-only and unknown products, a discount without its type) with noPOSTmade; a 400 in the documented index-keyed shape flattened into the message, and an error text that echoes the key, an email and a phone number passed on with all three redacted; the 403 from a Read only key onPOSTand the different 403 message on aGET; a 400 body with twelve messages cut to ten; bad IDs, impossible dates, malformedordering, text filters that are only spaces or empty, and unknown arguments rejected before any call; the 404 message, and a 404 on page 1 of a list reported as not found rather than as a page past the end; an empty page ending a list walk even whennextis set; the 429 retry waiting forRetry-Afterin the seconds, fractional-seconds and HTTP-date forms, and 2 s then 4 s when the header is missing or unreadable, giving up after three attempts on a persistent 429 and at once on aRetry-Afterabove the cap, a 429 onPOST /orders/retried once; a 502 retried forGETand never forPOST /orders/; a 504 then two 503s on aGETreported with advice; a 200 whose body is HTML, JSONnull, a string or a list reported without quoting it; acreate_ordercancelled by the MCP client during a retry wait, with no further request and noPOST; with a server whose budget is lowered to 3 seconds,create_orderwith 429s armed on all three of its requests ending in an error inside the budget with noPOST, thePOSTnot started when less than the write reserve is left, aPOSTstill unanswered at the deadline abandoned with the "may already have been created" warning, and a list stopped for time after page 1 returning that page withnext_page; the 401 message without the key; a key pasted withBearersent once; and that every request usedAuthorization: Bearer <key>, a documented method and path, and only query parameters the spec documents for that operation (include_fieldsexcepted, which the guide documents in prose for/products/).
The 7 direct checks: the client-side rate window on the built client with a 1-second window (three requests go at once, the fourth waits for the window), with a one-minute window (the second request past a limit of one is refused at once, without a request), and with six concurrent requests against a limit of two per second (no one-second span carries more than two); the retry delay (a Retry-After of exactly 10 seconds is honoured, 10.5 gives up, 2 s then 4 s without a readable header, an HTTP-date) and that the default time budget is at most 55 seconds; a cancellation ending a 5-second retry wait at once, with no further request, and aborting a request in flight; redactDeep cutting lists nested deeper than 20 levels; the delivery type labels compared with the ones the spec documents; and start-up refused (a separate process) without a key, with an out-of-range BREWW_REQUESTS_PER_MINUTE or BREWW_TOOL_BUDGET_S, or with credentials in BREWW_BASE_URL. The MCP clients in step 3 that make most of the calls run with BREWW_REQUESTS_PER_MINUTE=600, because the suite makes more than 60 requests through one server process; the clients for the write gate, the Read only key and the wrong key run with the default of 60.
Status
This is a working prototype. It has not yet been run against the live API, because it was built without a Breww account. Everything below is taken from the published spec and guide and should be confirmed on a real account (Breww offers a free trial; whether a trial account can create a Private app and key is unconfirmed):
Authentication end to end with a real
BRW.key, and the bodies of 401, 403 and 404 responses, which are not documented (the mock uses{"detail": …}; the server passes on adetailstring, or else the string values of a JSON error body flattened as for the documented 400 shape, at most ten, redacted; a non-JSON error body is never quoted). That a Read only key'sPOSTis answered with 403 (the guide says "403 (or similar)").The 429: the key that holds
Request was throttled., the form ofRetry-After, and whether a throttledPOST /orders/is never processed (the server assumes so and retries it at most twice). Whether the 60-per-minute limit is per key or per account: this server counts only its own requests, per process.Paging: what the API does with
page_sizeabove 200 (the mock caps it at 200) and with a page past the end (the mock answers 404 "Invalid page.", a guess; the server stops atnextnull on its own, and a 404 on a later page asked for by the caller is reported as a page that does not exist). Which fieldsorderingaccepts on each endpoint; the documented examples arenumber,issue_dateandvalueon orders.quantity_in_stock_in_format: the guide names it as a conditionally included field on/products/, but the spec does not define it, so its shape is unknown. The mock returns a plain number; the server passes whatever comes back through untouched.Value formats the spec leaves unclear: whether
type: "decimal"values (stock quantities and prices) come as strings or numbers (the fixtures use strings such as"125.500", which the server converts to numbers); whether thearray/binaryfields (volumes, weights, gravities, ABV, current vessel, delivery address) really are objects shaped like their examples (the server readslitre,kg,value_decimal,reading_unit,name,cityand so on from them); whethertimevalues carry a zone.Fields the spec marks as required and not nullable that a live account may leave empty:
DrinkBatch.third_party_brewery(so every fixture batch has a partner brewery),DrinkBatch.current_vessel_info, andFulfillment.completed_by/completed_dateon deliveries not yet completed (the server shows them only whencompletedis true). AlsoCustomer.entity_type, whose enum lists only single flags (1, 2, 4, 8, 16, 32) although its description says flags combine (a customer and supplier is 3); the fixtures follow the enum, and the server decodes the value as a bit field.Filter semantics: whether
tags(on products and customers) takes a tag's name, as the server assumes and the mock implements, or a slug or an ID, and whether it matches exactly (the spec gives the parameter no description); whatrunandcourieron deliveries match (theFulfillmentschema returns neither field, so the fixtures cannot show it); whethername__containsand the other__containsfilters are case-sensitive; whetherentity_type__bitand=1matches every record with that flag set (assumed); whether booleans are read astrue/false(sent forobsolete,completed,failed) andFalse(sent foris_empty, as that endpoint's description writes it); whether a date-time filter reads theZof2026-09-01T00:00:00Zas UTC.GET /stock-received/covers stock items (ingredients, packaging, chemicals, guest beer, merchandise). It has no location parameter, so the location filter is applied after fetching; on a large accountmax_resultsmay need raising.create_order: thatPOST /orders/accepts the body as sent (customer,order_status1 or 2,allow_automatic_emailing_to_customer, optional dates, PO number and notes, and lines withproduct,quantityand optionallyproduct_original_unit_value,discount,discount_type,product_name); what number Breww gives a draft; which fields the 201 response carries (the mock followsInvoiceCreate); whetherGET /products/?id__in=…returns obsolete products (the local check assumes it does, and otherwise reports an obsolete product as not found); and thatblock_order_processing2 and 3 mean what their labels say.The order detail path: the spec types
/orders/{id}/'s parameter as a string matching^[^/]+$with no description; the server sends the integerInvoice.id.Soft-deleted records are never asked for: the guide describes
include_soft_deleted=true, but none of the operations used here lists it as a parameter.The spec is a beta ("There may be breaking changes made to the schema/structure at any time"), with a breaking change to validation errors as recent as 2026-08-15.
Going to production
This version runs locally over stdio with the brewery's own API key. For breweries to connect from claude.ai or ChatGPT without handling keys, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by Breww. Breww already runs an OAuth2 Authorization Code server with PKCE, read and write scopes, one-hour access tokens and rotating 30-day refresh tokens for public apps, so the remote server can use each brewery's own grant. Then a listing in the Claude and ChatGPT connector directories.
Licence
MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.
Available Tools
10 toolsget_batchGet production batchBRead-onlyIdempotent
One drink batch in full: drink, status, brew type, dates, planned and actual volumes, ABV, starting and final gravity, current vessel and fill, recipe version. Uses GET /drink-batches/{id}/.
| Name | Required | Description | Default |
|---|---|---|---|
| batch_id | Yes | Batch ID (a positive whole number) | |
| include_contact_details | No | Include the name of the staff member who created the batch. Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, covering the safety profile. The description adds the returned field set and the underlying endpoint, which is useful context, but says nothing about permissions, error behavior for unknown IDs, or payload size.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single, front-loaded sentence that puts the resource and scope first and the endpoint last. The field enumeration is dense but earns its place given the absence of an output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description usefully compensates by listing what a batch contains, and the read-only annotations cover mutation risk. Only the missing guidance on the optional contact-details flag and failure modes keeps it short of complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%: both batch_id and include_contact_details are documented in the schema, including the default-off behavior of the contact flag. The description adds no parameter detail, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb-and-resource combination ('One drink batch in full') and enumerates the returned fields, so an agent knows it retrieves a single batch record rather than a collection. It contrasts implicitly with list_batches but never names the sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no statement of when to use this tool versus list_batches or a hypothetical search tool, nor any prerequisites or conditions. Usage is only inferable from the word 'One' and the single-ID endpoint.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_customerGet customerARead-onlyIdempotent
One customer in full: type, sales person, price book, credit limit, payment terms, ordering cadence and predictions, trade store access, comments, number of contacts, and their most recent orders. Contacts (names, emails, phones), addresses, tax number and custom fields only with include_contact_details. Uses GET /customers-suppliers/{id}/ and GET /orders/?customer={id}&ordering=-issue_date.
| Name | Required | Description | Default |
|---|---|---|---|
| customer_id | Yes | Customer ID (a positive whole number) | |
| recent_orders | No | How many of the newest orders to include (0 for none) | |
| include_contact_details | No | Include contacts' names, job titles, emails and phones, the customer's primary email, phone, billing and delivery addresses, tax number, custom fields and staff names. Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With readOnlyHint/idempotentHint/openWorldHint already declared, the bar is lower, yet the description still adds real context: it reveals the tool fans out to two endpoints (GET /customers-suppliers/{id}/ and GET /orders/?customer={id}&ordering=-issue_date) and that sensitive fields are gated behind include_contact_details by default.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the high-value payload description, then conditional caveats, then endpoint detail. Dense but no filler; the endpoint citation is the only slightly marginal element.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, so the description carries the return-value burden and does it well by enumerating returned fields and conditional inclusions. Combined with full schema coverage, an agent has everything needed to call it correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds value beyond the schema by spelling out the field groups that appear only when include_contact_details is set (contacts, addresses, tax number, custom fields) and by framing recent_orders as the newest-first ordering implied by ordering=-issue_date.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('One customer in full') and immediately enumerates the exact fields returned, which lets an agent distinguish it from list_customers without opening anything. The scope is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the singular 'one customer' plus a required customer_id, and the description notes that contact/address data only arrives with include_contact_details. However it never explicitly states when to pick this over list_customers or that it is a read-only single-record fetch.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_orderGet orderARead-onlyIdempotent
One order/invoice in full: lines (product, quantity, dispatched, returned, unit price, discount, value, tax, total), adjustment lines (delivery, deposits), totals, statuses, delivery, notes. Billing and delivery addresses, the contact email and the invoice PDF and customer links only with include_contact_details; payment records are never returned. Uses GET /orders/{id}/.
| Name | Required | Description | Default |
|---|---|---|---|
| order_id | Yes | Order ID (a positive whole number) | |
| include_contact_details | No | Include billing and delivery addresses, the contact email, staff names, the invoice PDF and customer-facing links, and unredacted notes. Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, so the safety bar is covered. The description adds real behavioral value beyond that: it discloses the contact-detail gating rule and explicitly states that payment records are never returned, which is non-obvious scope information an agent would otherwise discover only by failing.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The returned-fields enumeration is front-loaded and dense but every clause maps to actual response content, so little is wasted. It is a long single breath, but the content earns its space given there is no output schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description carries the burden of describing the return shape, and it does so thoroughly (lines, adjustments, totals, addresses gating, exclusions). It is nearly complete; only error/not-found behavior is left unstated.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so both parameters are already fully documented in the schema, establishing the baseline of 3. The description reinforces the include_contact_details gating but adds no syntax or format detail the schema lacks.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('One order/invoice in full') and enumerates exactly what is returned, which cleanly separates it from the sibling list_orders. An agent can tell this is the single-record detail fetch without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies lookup-by-ID ('Uses GET /orders/{id}/') but never states when to prefer it over list_orders or what to do if the order is missing. Usage is inferable from the required order_id param, so it lands at minimum-viable rather than explicit guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_productGet productARead-onlyIdempotent
One product in full: code, type, price, barcode, volumes, duty, the drinks and stock items it is made of, tags and custom fields. Uses GET /products/{id}/.
| Name | Required | Description | Default |
|---|---|---|---|
| product_id | Yes | Product ID (a positive whole number) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint and destructiveHint=false, so the safety profile is covered. The description adds useful context by enumerating the returned data (code, type, price, barcode, volumes, duty, composition, tags, custom fields) and naming the underlying endpoint, which is valuable given no output schema exists.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with the record scope front-loaded and the field inventory packed into one list. Nothing is padded and the endpoint reference is a compact bonus.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a one-parameter read tool with no output schema, the description compensates well by listing the returned fields. Missing only minor operational details such as error behavior for an unknown ID, which is not critical here.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the single parameter is fully described there, so the description need not repeat it. 'GET /products/{id}/' faintly reinforces that product_id is a path identifier, but adds no format or constraint beyond the schema; baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (implicitly retrieves) and resource ('One product in full') and enumerates the fields returned, so the agent knows exactly what it gets. The single-record scope is clear against the sibling list_products, though that sibling is not named explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: 'One product' signals this is the single-record fetch that pairs with list_products, and the required product_id makes the precondition evident. However, there is no explicit when-to-use or when-not-to-use guidance, nor any mention of the alternative for listing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_batchesList production batchesBRead-onlyIdempotent
Drink batches (brews) with drink, status (planned, in progress, complete), planned and actual dates, volumes and current vessel. Uses GET /drink-batches/.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | API page to start from (use next_page from a previous call, with the same max_results) | |
| status | No | Only these statuses (API parameter `status__in`) | |
| drink_id | No | Only batches of this drink (API parameter `beer`) | |
| ordering | No | Sort order (API parameter `ordering`): a field name, - in front for descending, several separated by commas. The API's default is ascending id. | |
| planned_to | No | Planned start date on or before (API parameter `planned_start_date__lte`) | |
| max_results | No | Most records to return in this call; whole API pages only (up to 200 per page), so fewer may come back with a next_page to continue from | |
| planned_from | No | Planned start date on or after (API parameter `planned_start_date__gte`) | |
| started_from | No | Started at or after this date/time (API parameter `datetime_started__gte`; a date alone means 00:00 UTC) | |
| started_before | No | Started before this date/time (API parameter `datetime_started__lt`; a date alone means 00:00 UTC) | |
| batch_ref_contains | No | Part of the batch reference (API parameter `batch_ref__contains`) | |
| include_contact_details | No | Include the name of the staff member who created each batch. Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, so the safety profile is covered. The description adds the GET endpoint and enumerates the fields returned, which helps the agent judge fitness, but it says nothing about auth, rate limits, or ordering/pagination behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two short sentences, front-loaded with the resource and its contents. The trailing 'Uses GET /drink-batches/' is mildly redundant for an agent but not wasteful enough to drop below 4.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With a fully documented schema, no output schema needed, and safety annotations present, the definition is adequate. What is missing is routing context: with get_batch and list_products as siblings, the agent gets no signal for when this list tool is the right choice.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% across all 11 parameters, including API mapping details (status__in, beer, ordering, next_page), so the schema carries the full semantic load. The description contributes no additional parameter meaning, which is the baseline 3 when schema does the heavy lifting.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Drink batches (brews)') and enumerates the returned attributes (drink, status, dates, volumes, vessel), so an agent knows what it retrieves. It does not explicitly differentiate itself from the sibling get_batch (single batch) or list_products, so it falls short of a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No when-to-use or when-not-to-use guidance is given. It never mentions that pagination should continue via next_page (that lives only in the schema) or when to prefer get_batch for a single record. Usage must be inferred from the name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_customersList customersARead-onlyIdempotent
Trade customers, leads and suppliers (Breww keeps them in one list) with last order date, predicted next order, ordering cadence, average order value and Breww's nightly churn and overdue probabilities. Good for 'who is overdue to reorder?' (next_expected_order_before today) and 'who have we lost?' (min_prob_churned). Contact details only with include_contact_details. Uses GET /customers-suppliers/.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Only customers with this tag: the tag's name (assumed; API parameter `tags`, which the spec does not describe) | |
| page | No | API page to start from (use next_page from a previous call, with the same max_results) | |
| ordering | No | Sort order (API parameter `ordering`): a field name, - in front for descending, several separated by commas. The API's default is ascending id. | |
| entity_type | No | Only records with this entity type flag set (API parameter `entity_type__bitand`) | |
| max_results | No | Most records to return in this call; whole API pages only (up to 200 per page), so fewer may come back with a next_page to continue from | |
| name_contains | No | Part of the customer name (API parameter `name__contains`) | |
| sales_person_id | No | Only customers of this sales person (API parameter `sales_person`) | |
| min_prob_churned | No | Churn probability at least this, 0 to 1 (API parameter `prob_churned__gte`) | |
| min_prob_overdue | No | Overdue probability at least this, 0 to 1 (API parameter `prob_overdue__gte`) | |
| last_ordered_since | No | Last order on or after this date/time (API parameter `last_order_date__gte`) | |
| last_ordered_before | No | Last order before this date/time (API parameter `last_order_date__lt`; a date alone means 00:00 UTC) | |
| include_contact_details | No | Include each customer's primary email, phone number and delivery address, the sales person's name, and stop redacting emails and phone numbers typed into names. Off by default. | |
| next_expected_order_before | No | Predicted next order on or before this date (API parameter `next_expected_order__lte`) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive), so the added value is the disclosure that contact details are opt-in via include_contact_details and that emails/phones embedded in names are otherwise redacted. That redaction behavior is non-obvious and materially affects results. Pagination nuance is left to the max_results schema field rather than the description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what the list contains before the use-case mappings and the contact-detail caveat. Dense but each sentence carries distinct information; slightly long, though nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 13 parameters, no output schema and no required fields, the description compensates by naming the returned analytic fields (last order date, predicted next order, cadence, average order value, churn/overdue probabilities) and the key filter intents. It stops short of documenting pagination continuation or the tag/ordering syntax, which remain schema-only.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description earns above baseline by tying named parameters to intent (next_expected_order_before for overdue reorders, min_prob_churned for churn) and by explaining that include_contact_details governs both contact fields and redaction of names — meaning the schema alone does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('list') and resource (trade customers, leads and suppliers, noting Breww unifies them) and immediately enumerates the derived fields returned. An agent can distinguish it from the singular sibling get_customer and from list_orders/list_products without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly frames two concrete use cases and binds each to the parameter that serves it ('who is overdue to reorder?' -> next_expected_order_before today; 'who have we lost?' -> min_prob_churned). It does not state when not to use this tool or name an alternative sibling, so it falls short of 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_deliveriesList deliveriesARead-onlyIdempotent
Deliveries, collections, courier shipments and uplifts (Breww calls them fulfillments) with scheduled date, customer, order, dispatch and completion status, drop window and what is on board. The town is returned by default; the full address, phone number, coordinates and delivery-note links only with include_contact_details. Uses GET /fulfillments/.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | API page to start from (use next_page from a previous call, with the same max_results) | |
| types | No | Only these types (API parameter `type__in`) | |
| failed | No | Failed or not (API parameter `failed`) | |
| run_id | No | Only this delivery run (API parameter `run`) | |
| date_to | No | Scheduled on or before (API parameter `date_scheduled__lte`) | |
| order_id | No | Only deliveries for this order (API parameter `invoice`) | |
| ordering | No | Sort order (API parameter `ordering`): a field name, - in front for descending, several separated by commas. The API's default is ascending id. | |
| completed | No | Completed or not (API parameter `completed`) | |
| date_from | No | Scheduled on or after (API parameter `date_scheduled__gte`) | |
| courier_id | No | Only this courier (API parameter `courier`) | |
| max_results | No | Most records to return in this call; whole API pages only (up to 200 per page), so fewer may come back with a next_page to continue from | |
| include_contact_details | No | Include delivery addresses, phone numbers, coordinates, delivery-note and invoice PDF links, staff names, and unredacted instructions. Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare the safe read-only/idempotent profile, and the description adds genuinely new context: town is returned by default while full address, phone, coordinates and delivery-note links require include_contact_details, plus the underlying GET /fulfillments/ call. It doesn't discuss pagination or rate behavior, but that gap is small.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences with no filler; the resource enumeration comes first and the output-visibility rule follows. Every clause carries information about what is listed or returned.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 12-parameter read tool with full schema coverage, annotations, and no output schema, the description covers the resource scope and the sensitive-field gating rule. It omits return shape/pagination description, but the schema already handles the parameters, so the definition is sufficient.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all 12 parameters including their API mappings and defaults are already documented in the schema. The description only restates the include_contact_details toggle and default output shape, adding little parameter-level meaning beyond the schema baseline.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource set (deliveries, collections, courier shipments, uplifts) with the domain synonym (fulfillments) and the endpoint used. No sibling tool covers fulfillments, so the resource is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Use is implied by the listed filters (status, date, courier, order), but there is no explicit when-to-use or when-not guidance, and no alternative tool is named or excluded. Adequate but minimal.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_ordersList ordersARead-onlyIdempotent
Sales orders, which in Breww are also the invoices: number, customer, issue and due dates, order and payment status, value, total and amount due. Filter by customer, status, payment status, dates, amount due, number, PO number or source. Good for 'what is unpaid and overdue?' (payment_status unpaid/part_paid, due_before today). Uses GET /orders/.
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | API page to start from (use next_page from a previous call, with the same max_results) | |
| number | No | Exact order/invoice number (API parameter `number`) | |
| source | No | Only orders from these sources (API parameter `source__in`) | |
| ordering | No | Sort order (API parameter `ordering`): a field name, - in front for descending, several separated by commas. The API's default is ascending id. | |
| issued_to | No | Issue date on or before (API parameter `issue_date__lte`) | |
| due_before | No | Due date before this date (API parameter `due_date__lt`) | |
| customer_id | No | Only this customer's orders (API parameter `customer`) | |
| issued_from | No | Issue date on or after (API parameter `issue_date__gte`) | |
| max_results | No | Most records to return in this call; whole API pages only (up to 200 per page), so fewer may come back with a next_page to continue from | |
| order_status | No | Only these order statuses (API parameter `order_status__in`) | |
| payment_status | No | Only these payment statuses (API parameter `payment_status__in`) | |
| amount_due_over | No | Amount due greater than this (API parameter `amount_due__gt`) | |
| po_number_contains | No | Part of the customer's PO number (API parameter `po_number__contains`) | |
| include_contact_details | No | Include customer names as stored (without redacting emails and phone numbers typed into them). Off by default. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and openWorld, so the safety profile is covered; the description adds the underlying GET /orders/ endpoint, the redaction behavior of include_contact_details (off by default), and pagination semantics. That is meaningful extra context beyond the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with what the resource is, then the filter surface, then a high-value example and the endpoint. The field enumeration is dense but each element is informative, and nothing is redundant.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 14 parameters and no output schema, the description helpfully enumerates the returned fields and clarifies filter behavior, which compensates for the missing output schema. Minor gaps remain, such as not restating pagination limits, but the schema covers those.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds value by grouping the filters conceptually and showing how payment_status and due_before compose for the overdue-unpaid case. It does not add syntax or formats beyond what the schema already documents.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States the resource (sales orders, which in Breww are also the invoices), the fields returned, and the filter dimensions, so an agent can immediately distinguish it from list_customers, list_products, or the singular get_order. The verb is implicit but unambiguous from the name and content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides a concrete worked example of the intended query pattern ('what is unpaid and overdue?' via payment_status unpaid/part_paid with due_before today), which tells the agent when this tool fits. It does not explicitly name get_order as the alternative for a single order, so it stops short of full routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_productsList productsARead-onlyIdempotent
The brewery's sellable products (casks, kegs, smallpack, multi-packs, stock items, services) with code, type, price and the drink(s) each contains. Obsolete products are left out unless asked for. Uses GET /products/.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Only products with this tag: the tag's name (assumed; API parameter `tags`, which the spec does not describe) | |
| code | No | Exact product code (API parameter `code`) | |
| page | No | API page to start from (use next_page from a previous call, with the same max_results) | |
| type | No | Product type (API parameter `type`) | |
| ordering | No | Sort order (API parameter `ordering`): a field name, - in front for descending, several separated by commas. The API's default is ascending id. | |
| max_results | No | Most records to return in this call; whole API pages only (up to 200 per page), so fewer may come back with a next_page to continue from | |
| name_contains | No | Part of the product name (API parameter `name__contains`) | |
| include_obsolete | No | Include obsolete products (otherwise `obsolete=false` is sent) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, open world), so the bar is lower. The description still adds real behavioral value by disclosing the default obsolete-filtering behavior and the fields each record carries, which the annotations do not convey.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences that front-load what the resource is before the filtering default. The trailing 'Uses GET /products/' is largely redundant with the tool name and adds little, but the rest earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an eight-parameter read tool with no output schema, the description compensates by naming the returned fields and the default filtering, and the schema fully documents pagination and filters. It is nearly complete, missing only explicit guidance on paging behavior or error cases.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all eight parameters are already documented in the schema, including defaults for page, max_results, and include_obsolete. The description adds no parameter syntax or semantics beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (list) and resource (products) and enumerates the concrete item kinds returned (casks, kegs, smallpack, multi-packs, stock items, services) plus the fields (code, type, price, drinks). This clearly separates it from get_product, though it never names that sibling explicitly.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The note that obsolete products are excluded 'unless asked for' implies how to shape a call, but there is no explicit when-to-use/when-not statement and no routing to alternatives like get_product for a single item. Usage must be inferred from the plural name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
stock_levelsStock levelsARead-onlyIdempotent
Current stock. Two sources: (1) stock items (ingredients, packaging, chemicals, guest beer, merchandise) currently in stock, per stock item and location, from GET /stock-received/ with is_empty=False (quantities in kg, litres or units depending on the item's tracking type); (2) finished products, from GET /products/ with include_fields=quantity_in_stock_in_format, a conditionally included field that Breww's API guide names but its spec does not define, so the value is passed through exactly as returned. The API has no location filter on stock received, so location_id is applied here after fetching.
| Name | Required | Description | Default |
|---|---|---|---|
| what | No | Which stock to report | all |
| expiring_by | No | Only lots with an expiry date on or before this date (API parameter `expiry_date__lte`) | |
| location_id | No | Only this location (filtered here; the API has no location parameter on stock received) | |
| max_results | No | Most lots and most products to read | |
| include_lots | No | Also list the individual lots (batch code, expiry, quantity) | |
| stock_item_id | No | Only this stock item (API parameter `stock_item`) | |
| batch_code_contains | No | Part of the stock lot's batch code (API parameter `batch_code__contains`) | |
| product_name_contains | No | Part of the product name, for product stock (API parameter `name__contains`) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare this as a safe, idempotent read, and the description adds substantial context beyond them: the exact upstream endpoints, the fact that location filtering is applied client-side because the API has no such filter, unit semantics (kg/litres/units by tracking type), and a caveat that quantity_in_stock_in_format is an undefined pass-through field.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded with the core answer ('Current stock') followed by the two sources, and every clause carries operational information. It is dense and slightly long, but no sentence is filler; the pass-through warning in particular earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 8 optional parameters and no output schema, the description supplies the missing context: where data comes from, how units are expressed, the undefined-field caveat, and why location filtering happens locally. An agent can call this correctly without further inference.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds meaningful interpretation: it explains that the two sources drive `what`, that quantities vary by tracking type, and it corroborates the non-obvious client-side nature of `location_id`. It does not add syntax detail beyond the schema for filters like expiring_by.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific resource ('current stock') and immediately disambiguates scope by naming two distinct data sources (stock items via /stock-received/, finished products via /products/). This clearly separates it from siblings like list_batches or list_products, which cover only one of those domains.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains what each source covers and ties them to item categories (ingredients, packaging, chemicals, guest beer, merchandise), giving the agent enough to pick the right scope via `what`. It stops short of naming explicit when-not-to-use conditions or sibling alternatives such as list_batches for lot-level queries.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
10 tool updates
v0.1.0- First observed
get_batch - First observed
get_customer - First observed
get_order - First observed
get_product - First observed
list_batches - First observed
list_customers - First observed
list_deliveries - First observed
list_orders - First observed
list_products - First observed
stock_levels
TDQS
Scored across 10 tools
Each tool targets a distinct resource or action: list/get pairs for products, batches, customers, and orders, plus dedicated stock_levels and list_deliveries. There is no meaningful overlap that would cause an agent to misselect, and descriptions clarify boundaries.
Nine of ten tools follow a consistent list_/get_ verb_noun snake_case pattern. stock_levels breaks the pattern with a noun-only name, and there is no get_delivery counterpart, but overall naming remains predictable.
Ten tools is well-scoped for a brewery data access server, giving read coverage of products, stock, batches, customers, orders, and deliveries without bloat. Each tool earns its place.
The set covers read access to core brewery entities but lacks any create, update, or delete operations, which are notable gaps for a management domain. There is also no get_delivery tool and payment records are explicitly unavailable, leaving some lifecycle dead ends.
Maintenance
Related MCP Connectors
Managed MCP gateway with OAuth, per-agent permissions, shared accounts and activity logs.
Public, read-only MCP server for FarmNeural company facts, packages, and capabilities.
REST-to-MCP for UK hospitality. Safety proxy: circuit-breakers, rate limits, whitelists. Apache 2.0.
BakeryOS bakery-management MCP — recipes, costs, production; propose changes via OAuth.
Related MCP Servers
AlicenseNot gradedqualityAmaintenanceEnables MCP-capable clients to sign in with OAuth and perform purchaser tasks such as browsing catalogues, checking inventory, managing stocktakes and recipes, drafting requisitions, updating buy lists, exporting reports, and processing invoices. It also supports supplier access for listing and inspecting connections, catalogues, products, purchase orders, invoices, and entitled reports.MIT- AlicenseAqualityCmaintenanceEnables MCP clients such as Claude and ChatGPT to read a campsite's supplier account data from the Pitchup.com API — campsites, pitch types, pitches, charge types, prices and stay rules, allocation, extras and arrivals and bookings. When writes are explicitly enabled, it also sets allocation, prices and charge types, with writes off and the sandbox environment used by default.14MIT
- AlicenseAqualityCmaintenanceLets MCP clients such as Claude and ChatGPT read a WorkMobile account's jobs, job types, forms, submitted form records, approvals and mobile users, and, when writes are enabled, create and allocate jobs and approve or reject records.10MIT
- AlicenseAqualityCmaintenanceGives Agent Studio agents read-only access to a merchant's stock, sales orders and delivery records, so they can check sellable availability across SKUs and warehouses before pitching, and pull carrier, AWB and delivery facts as evidence for disputes or "where is my order" questions. It handles OAuth, rate limiting, PII masking and pagination, and runs offline in demo mode.9MIT