Tools
MCP — agents and IDEs
Connect Claude Code, Cursor or any MCP client to ARCNM over HTTP with a bearer token: quote parts, inspect calculations and read your data through typed tools.
ARCNM ships a Model Context Protocol server, auto-generated from the REST API. MCP-speaking agents and IDEs can quote parts, inspect calculations, and read your data through typed tools — without you writing glue code.
Endpoint: https://api.arcnm.io/mcp-server/mcp (HTTP transport,
bearer-JWT protected).
Why MCP, not REST
If you're writing application code, use REST. Pick MCP when an agent is the client (Claude, Cursor, custom runtimes): it discovers tools automatically and routes arguments through structured schemas.
Authentication
The MCP server is a bearer-token protected resource. It publishes
RFC 9728 protected-resource metadata
at /.well-known/oauth-protected-resource/mcp-server/mcp — RFC 9728
appends the resource path to the well-known prefix, so the bare
/.well-known/oauth-protected-resource is a 404 by design. The verifier
accepts either
credential in the Authorization: Bearer … header:
- an ARCNM API key (
ak_live_…/ak_test_…) — recommended for agents: long-lived, revocable, scoped (tried first); or - a tenant-scoped session access token (JWT, audience-pinned to
/api/v1; refresh tokens and other-audience tokens are rejected).
Mint an API key in the dashboard (Settings → API keys) and pass it as the bearer token — that's all most agents need.
Interactive clients (Claude, …) can instead sign in with OAuth when the deployment enables it: ARCNM runs a full OAuth 2.1 authorization-code flow with dynamic client registration and a consent screen where the user picks the org and the scopes to grant (read-only by default). The client receives a short-lived, org-scoped, scope-limited token — no key to copy. Either way the credential resolves to the same tenancy + scope checks.
Connect
Claude Code (CLI)
claude mcp add -t http arcnm https://api.arcnm.io/mcp-server/mcp \
--header "Authorization: Bearer $ARCNM_MCP_TOKEN"
Cursor
Add to ~/.cursor/mcp.json (or project-scoped .cursor/mcp.json):
{
"mcpServers": {
"arcnm": {
"transport": {
"type": "http",
"url": "https://api.arcnm.io/mcp-server/mcp",
"headers": { "Authorization": "Bearer ${ARCNM_MCP_TOKEN}" }
}
}
}
}
Restart Cursor; tools appear under the MCP picker.
Other clients
Any MCP-compliant HTTP client works. Point the transport at
https://api.arcnm.io/mcp-server/mcp, send the bearer header, and call
the standard tools/list method to discover the surface.
What's exposed
The surface is the public developer API and nothing else, exposed as
Tools — everything in one tools/list (a strict, deny-by-default
allow-list; an MCP tool is model-invoked, so the surface is the security
boundary):
- Reads (parts, calculations, uploads, environments, materials, tools,
subcontractors, calibration) are read-only Tools (
readOnlyHint=true). Material search (materials_suggest,materials_lookup) counts as a read even though it is a POST: the query rides in the body, nothing is written. - Your own identity (
GET /users/me,/users/me/context) is a read-only tool for a signed-in app session only. Those two routes refuse every API key and every OAuth grant over REST, so a key or a grant never sees them intools/listeither. A key or a grant acts for the one organization it was issued in. - Writes (create a part, create/run/quote a calculation, upload inputs) are Tools carrying an idempotent / destructive hint.
There are no Resources or Resource Templates — an agent-first data API is
model-invoked, which is Tool semantics, so reads and writes live in one
tools/list (matching GitHub's and Stripe's MCP servers) rather than being
split across tools/list and resources/list. Use readOnlyHint to tell a
data-fetch from a mutation.
Tool names are generated from the REST operation ids (roughly
<tag>_<operation>, e.g. calculations_get_calculation,
calculations_create_calculation). The authoritative list is whatever
tools/list returns for your token — generate it from the live server
rather than hardcoding names.
What's never exposed. Everything outside the public developer API: wallet and billing operations, plan changes, member invites/roster, API-key minting, the credential vault, SSO/SCIM, raw payment-provider webhooks, feature flags, auth/session primitives, the audit log and other admin-only endpoints, the AI surfaces, the platform's background-job queue, and all user identity mutations (including cross-user reads). An agent with a tenant token cannot move money, escalate access, mint credentials, read the admin plane, or rewrite the tenant — those stay human-dashboard-only by design.
Three pricing-configuration surfaces are app-only on top of that, and it is worth being precise about which: the environment
/environments/{id}/effective-ratesread and edit, cutting data (/environments/{id}/cutting-dataread + edit,/tools/{id}/parameterswrite,/tools/{id}/effective-cutting-dataread), and the environment/environments/{id}/assumptionsread, which resolves the whole parameter plane those reads draw from. All resolve the platform's own curated defaults, which are ours and not part of what you integrate against. Your rate rows are yours and stay callable — an agent can read, write and delete them (/environments/{id}/rates), set or clear a material price, and read, set or clear a scrap price (/environments/{id}/rates/scrap) — as do your tools' own cutting-data rows, which rideGET /tools/{id}.The master-data import commits (
POST /import/machines,POST /import/tools) are app-only too: one call bulk-rewrites the fleet or crib an environment prices with, which is exactly the kind of pricing-configuration mutation an autonomous agent must not drive. The dry-run halves stay callable —POST /import/machines/previewandPOST /import/tools/previewread your file and report per-row what a commit would do, without writing anything.The calibration-import price commit (
POST /calibration/imports/{id}/price) is app-only for the same reason: it spends the calculation quota the preflight disclosed, in one call, for potentially hundreds of parts. The rest of the import funnel stays callable —POST /calibration/imports(preflight + cost disclosure, spends nothing),GET /calibration/imports/{id}(status), andPOST /calibration/imports/{id}/calibrate(the metered fit, same stance as auto-calibrate).Job nests (
/nest-runs) and remnant stock (/remnants— list, state, scrap) are on the REST API, and their generated tools are not on the agent surface. An agent nests a backlog through the curatedarcnm_nest_run_*tools below instead: the start plans first and takes an explicitapply=true, because a job nest takes the remnants it cuts from out of your stock. Stating and scrapping remnants stays with the people and the stock system that keep your racks; an agent reads the remnants a job nest could cut from, used and created through those tools. The cutting files — a job nest's sheets, or a calculation's own sheet, as DXF, SVG or the JSON nest plan — come througharcnm_nest_run_exportandarcnm_sheet_nest_export: the plan inline, DXF and SVG as download links, since a tool result is no place for a drawing.Nesting potential (
GET /nesting/potential) and the run meter (GET /nesting/runs) are on the REST API. Their generated tools are not on the agent surface: the rest of that family can return a layout or a selection's price.arcnm_nesting_readreturns what nesting the organization's sheet quotes of the last 90 days together on shared sheets would save, and how many runs are used and left. The figure is a lower bound against each part on its own sheet — floored without the Nesting add-on — and never one quote's price or a layout. It is not a price difference: every sheet quote is nested true-shape on every plan.
Scopes mirror the REST scope catalog: a
token needs parts:read to read and parts:write to create or run a
calculation. Request the minimum the agent needs.
tools/list is filtered to your token's scopes. The server returns only
the tools your credential can actually call — a parts:read key sees the
read-only tools, not the write tools it could only 403 on. (A first-party
session token carries no scopes, so its list follows your role in the
organization instead, matching the REST gate: a viewer sees the read tools
only. Such a session also sees the identity reads, which only it can call.)
So the surface an agent discovers is already least-privilege; widen it by
minting a broader-scoped key, not by working around a missing tool.
The list is intentionally not paginated — the whole (already scope-scoped)
surface is returned in one tools/list response. The MCP cursor is optional
and we don't emit one; paging wouldn't help anyway, since a client that holds
the surface ends up holding all of it either way.
What it costs you is worth knowing, because most hosts put every tool
definition into the model's context. Two things keep it down, and one of them
is yours: the generated tools describe their arguments but not their response
shape — read response schemas from
the API reference, which publishes them once
instead of repeating them per tool — and a narrower credential is a smaller
surface, since the list is scope-filtered. A parts:read key sees about half
the tools of a full-scope one. Scope the credential to what the agent actually
does. For the cost loop, prefer the curated arcnm_* tools: those do declare
typed output and are built to be token-light.
Agent-first workflow tools
On top of the generated CRUD tools the server ships a few curated
arcnm_* tools built for an agent's cost-optimisation loop. Each returns a
concise, structured result (with an outputSchema) instead of the full
response blob, so they're cheap on context:
| Tool | What it returns |
|---|---|
arcnm_explain_cost |
Headline unit / total / setup cost and setup / cycle / unit time, the prediction band, the cost breakdown, the machine used, the material-resolution assumption, and the top cost drivers. detail="detailed" widens the driver list. |
arcnm_cost_drivers |
The top cost drivers, ranked — each with its cost, its share of the unit cost, and why it's costly. |
arcnm_cost_breakdown |
One calculation as a single rough-to-detailed hierarchy — headline → cost category → operation → feature, time and cost side by side — with a persona knob (procurement / workprep / operations / design / full) that returns just the slice that persona needs. Every persona also gets material_stock: the stock the part is bought as (format and who chose it, size and what the part measured, parts per stock, a compound's pieces), where its metal goes (purchased_mass_kg = part_mass_kg + scrap_mass_kg + remnant_mass_kg), and the material line as the stock bought less the scrap and remnant credits — the same stock analytics.material_resolution.stock names and the same amounts the cost sheet shows. |
arcnm_process_plan |
The ordered process plan (routing) all the way to a finished part: the machining steps, each with its process, work center and cycle time, then the post-machining chain (welding, heat treatment, surface finishing, coating) in the order it is performed — plus the plan-level setup / cycle / total time and machine(s). The work plan a work-preparation user acts on. Post-machining steps carry no cycle time: they are priced per lot, in the subcontract line. |
arcnm_compare_calculations |
The offer-price delta between two calculations (the price each one headlines, cost-sheet surcharges included), with the unit-cost delta before surcharges beside it, flagging when they aren't comparable (different lot size / environment). |
arcnm_rank_calculations |
The N-way generalization of compare: ranks 2–20 calculations by offer price, lowest first, each with its premium over the cheapest and time beside cost, flags when they aren't like-with-like, and lists blocked / unpriced calcs last. Pick between several quotes at once. |
arcnm_compare_environments |
The parts × environments matrix of a multi-environment run — per part the offer price in every environment (with the unit cost before surcharges beside it) and the environment with the lowest offer price (ties included); per environment the win count, basket total and median delta vs the baseline, all on the offer price. Pass a batch_id (from a multi-environment quote/batch) or an ad-hoc calculation_ids list; all figures are server-computed, identical to the REST comparison endpoints. Up to 200 ids, and the pivoted grid may not exceed 500 cells (parts × environments). |
arcnm_optimize_lot_size |
The total-cost optimal lot size (production plus inventory holding, capped at one year of demand), the net EUR/yr saving of moving there, the Andler/EOQ cross-check, the cost-vs-quantity curve (each point with its offer_price as well; exact at the quoted lot, exact_at_quantity, and indicative at every other one, by as much as accuracy states — for a firm price at another lot use arcnm_preview_cost), at most two lot-size/setup directions — and every scenario's answer on the same curve (scenarios): the buyer holding stock with its cost per order or delivery, the supplier producing ahead at its finite rate, delivered to order, the joint frame-contract call-off with the supplier's lot behind it, and the same frame contract under consignment (supplier-owned stock at the buyer's site until use). |
arcnm_optimization_directions |
Every optimization direction for a calculation — lot sizing, setup reduction, tolerance-cost review, material utilization, external-process benchmarking, plus the live supplier-quote gap — each tagged with its lever, confidence, and method. |
arcnm_cost_factors |
Every number a costing environment lets you change — each factor's effective value, where it came from (your own statement, inherited, learned or the platform default), the band a what-if may explore, the cost-sheet lines it moves and the endpoint that saves it. Read it before proposing anything. |
arcnm_preview_cost |
Re-prices a calculation with factors moved (and/or at another lot_size) and returns the whole cost sheet plus the change per category — a genuine re-price through the same engine, unbilled: it creates no calculation, changes neither the calculation nor the environment, and consumes none of your included calculations. Refuses by name (details.reason) rather than answering approximately; see the cost-sheet codes in Errors. Counts as a heavy request against the rate limit. |
arcnm_apply_adjustment |
A write. Makes a what-if stick the way the app's "apply to this environment" dialog does: by default it only PLANS (every REST write it would make, today's value, the new one, and what it refuses by name — a machine's own rate, cutting data, the lot size); apply=true performs them under parts:write; recalculate=true also starts a (billable) calculation on the changed environment — asked what this one was asked (its material supply, the grade the caller chose, currency, dataset, language and stock format), recorded as derived from it, created with an Idempotency-Key so a retry replays rather than re-bills — and reports in recalculation_status what running it answered; a refusal after the environment was changed comes back under refused, beside the writes. Not idempotent itself: a base-seconds knob is taught as one calibration observation per apply. |
arcnm_nest_run_create |
A write. Nests a backlog of sheet-metal parts together — several orders, materials and thicknesses at once — from calculations (each with an optional quantity, order, due date and priority), a batch, or a folder, all priced in one costing environment. By default it only PLANS: the lines and copies of each material and thickness, the remnant pieces of it in stock, and what a batch or folder leaves out and why — nothing is started. apply=true starts the job nest under parts:write, with an Idempotency-Key keyed on the request, so the same call within 24 hours answers the job nest it already started. A job nest takes every remnant it cuts from out of stock and puts its reusable rests in, so it is annotated destructiveHint=true. It never changes a calculation's own price. The plan and the start each count as a heavy request against the rate limit. |
arcnm_nest_run_start |
A write. Starts a job nest that was kept as a draft — one arcnm_nest_run_create stored, not queued, while this month's runs were used; a draft never starts by itself. By default (apply=false) it only reads the job nest and says whether it is a draft and what starting it would nest. apply=true (needs parts:write) queues it once: it spends one of this month's runs and is nested at today's stock and prices — poll arcnm_nest_run_status. While the month's runs are still used the start is refused and the draft stays; only a draft starts, and only once. |
arcnm_nest_run_cancel |
A write. Stops a job nest that has not ended — queued, running, or kept as a draft. By default (apply=false) it only reads the job nest and says whether it can be cancelled. apply=true (needs parts:write) cancels it: the materials and thicknesses already nested keep their result, the others are cancelled, and every remnant it holds goes back to stock. Cancelled before any material and thickness is nested, it gives its run back to this month's allowance; once one is nested, it counts as a run. A draft is discarded. A job nest that has ended is not cancelled again. |
arcnm_nest_run_status |
How far a job nest has got: its status, how many of its materials and thicknesses are done, and each one's status, sheets and utilisation so far. |
arcnm_nest_run_result |
What a job nest came to: the sheets of each material and thickness with their utilisation, each order's and each line's share of the sheets (bought, less what the skeleton sells for as scrap and the reusable rests are worth back in stock, in total and per copy — a line's per copy is what a calculation priced on it pays, less where the part's own sheet holds it more densely), what a batch or folder left out, and the remnants it used and created. Each line carries its line_id: create a calculation of the part with nest_item_id set to it to price the part's material on its line. detail="concise" lists the first 50 lines, detail="full" every line. |
arcnm_nest_savings |
The organization's total saved by nesting — the released job nests' savings summed: the sheet their lines buy cut from rectangular blanks, with each part nested alone and nested together, what nesting saved against rectangular blanks and its split into each part's true shape and the orders nested together, money per currency and kg — and, apart from it, the potential of the job nests that ended as simulations. A job nest counts once the shop releases it in the app. |
arcnm_nesting_read |
What nesting the organization's sheet quotes of the last 90 days together on shared sheets would save, and how many nesting runs are used and how many remain this month (none without the add-on: no run starts then). The figure is a lower bound against each part on its own sheet as its quote priced it, floored without the Nesting add-on, and never one quote's price or a layout. |
arcnm_nest_run_export |
The cutting files of a job nest's sheets — every solved material and thickness, each distinct layout once with how many identical sheets it stands for. format="json" (the default) answers the nest plan inline (every part's contour and holes in millimetres, every sheet's container, material, thickness, allowances and placements; past 256 KB without the contours) and its download link; format="dxf" or "svg" answers each sheet's download link — the same GET /nest-runs/{id}/export the API serves, to fetch with the API key — and nothing inline. sheet="all" covers every sheet (the DXF/SVG link is a zip with plan.json and manifest.csv), sheet="<group>:<index>" one sheet. exploded=true asks for DXF polylines in place of blocks. See Cut a job nest. |
arcnm_sheet_nest_export |
The cutting file of the sheet a calculation's own price laid its part out on — the part alone, as many copies as its price put on one sheet — with the same format and exploded as arcnm_nest_run_export. A calculation that stored no sheet layout, or that is not cut from sheet, has nothing to export. |
arcnm_check_quota |
This organization's remaining calculation quota for the current window — included / used / remaining, the overage rate, how overage settles (wallet = prepaid, with wallet_funded_calculations telling you how many more fit), how much of any monthly spend cap is left, and whether billing is suspended — so you can check before committing a run instead of parking a calc as blocked. |
All but arcnm_apply_adjustment, arcnm_nest_run_create, arcnm_nest_run_start and arcnm_nest_run_cancel are read-only, and every one inherits the same scopes, tenancy, and redaction as
the REST API — ideal building blocks for a "cost it, find the drivers, change
the design, re-cost, compare" loop driven from a CAD/agent toolchain.
Every curated tool that quotes a calculation's price or a saving derived
from it — explain, drivers, breakdown, lot size, optimization directions, and
each side of a comparison or ranking — carries
environment_changed_since_run and inputs_changed_since_run, the same two
facts the calculation carries over REST. Null means the price reflects its
costing environment, its part's files and the corrections saved to the part
as they stand. When either is set,
the price predates an edit, the tool's guidance says so, and a new
calculation of the part revision prices with the current setup (running the
same calculation again changes nothing).
Reads are tools too
Everything you need to read — a full calculation (poll
calculations_get_calculation for its status), an environment and its machine
fleet / rates, a part's revisions, the material catalogue — is a read-only
Tool in the same tools/list as the writes (look for readOnlyHint=true), so
there is no second surface to discover. There is no separate job-status tool — a
calculation carries its own status, so polling calculations_get_calculation
is the deterministic way to wait for a result.
Errors and pages
A failed tool call returns isError: true with the API's own error
envelope — the {"error": {"code", "message", "details", "doc_url"}, "request_id"} body the REST API answers — as the result's JSON text. Parse
the text; an error result carries no structuredContent, because
structuredContent must match the tool's outputSchema and an error
envelope does not. That holds for the generated tools and the curated
arcnm_* tools alike. Switch on error.code, and on details.reason where
Errors names one; never on the message. Quote request_id
when you report a problem.
Every list tool that takes a cursor states the next page in its result.
Most collections answer an object with next_cursor and has_more over REST
already. The ones that answer a bare list over REST and page in the
X-Next-Cursor / X-Has-More headers (uploads, environments, tools, stock
formats, the machine library) answer {"data": [...], "next_cursor": ..., "has_more": ...} over MCP, because a tool result carries no headers. Pass
next_cursor back as cursor until has_more is false; never stop on a
short page.
Getting a CAD file in over MCP
An MCP client sends JSON arguments, and a file cannot ride in one as a
multipart part. So the multipart upload routes — POST /calculations/upload-and-quote, POST /calculations/{id}/inputs and the
revision's datasets/upload — are not MCP tools. Use their JSON paths:
- Upload and quote in one call:
calculations_upload_and_quote_json(POST /calculations/upload-and-quote-json) — the same one-shot upload + quote, but the CAD (and optional drawing / RFQ) ride as base64 (ordata:URI) strings in a JSON body. Best for small single-part STEP files — base64 inflates ~33% and rides inside the tool call. - Any file, any size:
uploads_presign→ send the file to theurlit answers the way itsmethodsays →uploads_confirm.methodisPUT(the file's raw bytes as the body, with theheadersit names) orPOST(amultipart/form-dataform with every entry offields, then the file as the last field, namedfile); branch on it, because a file sent the other way never arrives.uploads_confirmanswersstatuspending— the file is checked after it answers — or an error withdetails.reasonupload_not_receivedwhen no file has arrived yet. To add a drawing, mesh or RFQ text to a part revision (whatPOST /calculations/{id}/inputsdoes over REST), attach the upload withdatasets_attach_datasetand itsrole: until the check is done it answers an error withdetails.reasonupload_pendinganddetails.retry_after_s— wait that long and call it again (callinguploads_confirmagain answers the currentstatustoo). The next calculation you run on that revision reads the file.
Finding a material
materials_lookup resolves an exact reference; on a near-miss it now returns the
closest grades in the error detail. For a fuzzy "did you mean" list that never
dead-ends — a partial code, a trade name, or a generic term like Stahl —
use materials_suggest, then pass a candidate's urn back as material_ref.
What you get back
The MCP surface is the public face of the platform, so responses carry the cost answer and the explanation a buyer needs to act — never the engine internals. Selection scoring, per-operation cycle-time math, raw extraction output, calibration parameters, and the platform machine-cost model are stripped from every response an agent receives (the first-party app keeps the full detail). Design around the result, not the derivation.
Read-only mode
A deployment can run the MCP server in read-only mode (MCP_READ_ONLY):
the write tools disappear from tools/list entirely, leaving only the
read tools and the read-only arcnm_* tools. Pair it with a read-only API key
for defence in depth when the agent only needs to read and analyse.
Tool annotations
Each tool carries MCP annotations the agent uses to decide how to call it:
- Every tool's annotations carry its
title,readOnlyHintanddestructiveHint, so a client never has to infer a default. openWorldHintisfalse— every tool acts on your own tenant data (a closed domain), not an open external world like web search.readOnlyHintistrueon every read — the curatedarcnm_*analysis tools, everyGET, and the searches that arePOSTs only because the query rides in the body — andfalseon every write. A read'sdestructiveHintisfalse.idempotentHintisfalseforPOST/PATCH,trueforPUTandDELETE.destructiveHintis set on every write and describes what the operation does, not what the verb says. It istrueby default. That includes the writes that remove your configuration even though they arePOSTs: the four/environments/{id}/…/restorecalls (…/rates/restorecloses every rate row the environment states) and the three "hide this platform item" calls (a tool-catalogue article, a subcontractor preset, a standard stock format). It isfalseon the threeDELETEs that put something back: ending a hide un-hides the article, the preset or the format. Of the two curated writes,arcnm_nest_run_createistrue, because a job nest takes the remnants it cuts from your stock and nothing puts one back, andarcnm_apply_adjustmentisfalse. A client that confirms before destructive calls therefore confirms before every other write.
A well-behaved agent confirms with the user before calling a
destructiveHint tool. The hint is a courtesy to that agent and never a
control: scopes and your own tenancy are enforced at the API, whatever a
client chooses to do with the annotation.
Prompts
The tools are CRUD-shaped because they're generated from the REST API. On top
of them the server ships a small set of prompts — the task-oriented "verbs
that matter" — that walk an agent through a whole workflow using whatever tools
your token exposes. Call prompts/list to discover them:
| Prompt | What it does | Arguments |
|---|---|---|
cost_a_part |
Upload a CAD file, create + run a calculation, poll to completion, report unit / total / setup cost. | part_number, lot_size, environment |
compare_calculations |
Read two calculations and explain the cost delta and its drivers. | calculation_id_a, calculation_id_b |
compare_environments |
Price part(s) across several environments in one batch, poll the grid, and recommend the best environment per part and overall. | part_numbers_or_ids, environment_names_or_ids |
explain_cost |
Explain one calculation's cost in plain language — decomposition and drivers. | calculation_id |
find_cost_drivers |
Rank the top cost drivers for a calculation to target a redesign or re-quote. | calculation_id, top_n |
Prompts are guidance, not privilege: they describe the workflow by capability
and tell the agent to resolve exact tool names from tools/list, so they keep
working as the generated surface evolves. A prompt that ends in a write your
token can't perform simply has that final call rejected at the REST boundary.
Worked example — agent quotes a part
user: Quote this STEP file at lot size 50, on our production env.
[attaches bracket.step]
agent: <calls the upload-and-quote-json tool>
cad_file_b64=<base64 of the STEP>, part_number="BRACKET-001",
lot_size=50, costing_environment_id=<prod env id>
agent: ← { id: "9d…", status: "queued" }
agent: <polls the get-calculation tool for 9d… until status=succeeded>
agent: Unit cost at lot size 50 is €12.84 (total €642.00, setup €52.50).
The full breakdown is on the calculation's analytics blob.
The agent didn't write a line of REST glue.
Worked example — agent compares environments
user: Which of our environments makes these 3 brackets cheapest?
agent: <calls arcnm_check_quota> # a grid costs parts × envs
agent: <lists environments, resolves the 3 part revisions>
agent: <calls the batch-calculation tool>
part_revision_ids=[…,…,…],
costing_environment_ids=[<prod>, <staging>, <sub_a>]
agent: ← { batch_id: "b4…", requested: 9, enqueued: 9 }
agent: <polls arcnm_compare_environments(batch_id="b4…") until complete>
agent: Subcontractor A is cheapest for 2 of 3 parts; basket −9.8% vs
production. BRACKET-002 stays cheaper in-house (+4.1% at Sub A).
One batch call instead of nine single quotes — and the matrix (wins,
deltas, basket totals) is computed server-side, so the agent reports
the same numbers the UI shows. The compare_environments prompt walks
this flow end to end.
Worked example — agent nests this week's orders
user: What does order 4711 cost if we nest it with the rest of this week's
sheet-metal work?
agent: <calls arcnm_nest_run_create(environment_id=<prod>, batch_id="c7…")>
agent: ← plan: 38 lines, 1 420 copies in 4 materials and thicknesses;
7 remnant pieces in stock it would cut from; 2 left out
(not_sheet). Nothing was started.
agent: This takes up to 7 remnants out of stock. Shall I start it?
user: Yes.
agent: <calls arcnm_nest_run_create(…, apply=true)> # → run "e2…", queued
agent: <polls arcnm_nest_run_status("e2…") until solved>
agent: <calls arcnm_nest_run_result("e2…")>
agent: Order 4711's material comes to €1 184.20 on the shared sheets.
23 sheets in all; 3 remnants used, 5 new ones back in stock.
The agent confirmed before the start because the plan said what it takes
from stock, and the annotation told it the call is destructive. To quote a
part at its share of the shared sheets, it creates a calculation with
nest_item_id set to the part's line_id.
Rate limits & billing
MCP traffic is handled by the MCP server's own verification and is not
subject to the REST per-request quota bucket. You're billed for the
underlying billable operation (a successful calculation), not per tool
call — read-only tools are free. A multi-environment
batch is one tool call but bills one calculation per
(revision × environment) cell; arcnm_check_quota first tells you
whether the grid fits.
Transport authentication
The MCP transport reads the Authorization: Bearer header only (an API
key or an OAuth grant); there is no X-API-Key header path on the MCP
server — that header is REST-only. Sandbox keys (ak_test_…) work
against the sandbox environment.
See also
- Authentication — bearer auth, scopes.
- API reference — the REST surface MCP wraps.
- Recipes → Quote a part — end-to-end flow.
- MCP spec — the protocol itself.