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: DailyMeals MCP
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.
This server cannot be deployed
Maintenance
Related MCP Connectors
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.
MCP server for Codat — companies, connections, invoices, bills and financial statements.
Read-only MCP access to sessions, funnels, campaigns, errors, live visitors, and anomalies.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceProvides a secured, read-only remote MCP endpoint for a single authorized GoHighLevel sub-account, exposing CRM tools such as contacts, opportunities, conversations, appointments, calendars, and notes via bearer-token authenticated requests.28 npmMIT
- FlicenseNot gradedqualityBmaintenanceEnables an MCP client to read live order data for a DailyMeals account and submit form data with confirmations, protected by bearer-token authentication and idempotent writes.-

eq-mcp-gatewayofficial
FlicenseNot gradedqualityBmaintenanceA remote MCP server that lets MCP clients self-register over OAuth 2.1 and call read-only tools for organization identity, place and region browsing, dashboards, metrics, and ranking, forwarding the caller's own Auth0 token so each service still sees the real user. It also exposes a bearer pass-through surface for first-party apps holding an existing token.-
PurchasePlus MCPofficial
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