DEMO IN PROGRESS Demonstration instance — coming soon. Do not drop a confidential BOM.
Four doors, one Intake LOCAL · SINGLE_USER

Four doors, one Intake

The app, the command line, the HTTP API and the Claude connector are four ways into the same product — not four products. Same readers, same resolution, same contract, same installation: a decision made in the app is already in force when Claude analyses the next file.

What does not change from one door to the next

The file is read by the same engine, lines are resolved by the same rules, and the result leaves in the same adesio.bom.canonical/v1 contract. No door ever claims to have found a part: found means the file states it. One asymmetry is deliberate — the CLI applies no operator decisions, because a figure it prints is about what the tool reads, not about what somebody confirmed.

The app

For dropping a file and looking at it.

Drag a BOM onto the home page. The report opens, you correct the column roles where the engine got them wrong, you save the reading as a named format — and the next file of that shape is recognised without a question. Lines waiting on a decision collect in the review queue, one card per part rather than per line.

It is the only door where the confirm buttons exist: a downloaded report shows them inert, because there is no longer a server to talk to.

The command line

For processing a file, or a whole folder, without opening a browser.

It produces exactly the same deliverables as the app: the self-contained report, the canonical JSON, the CSVs and the search worklist. It applies no operator decisions and reads no learned formats — what it measures is what the tool understands on its own.

npm install

# one file — report, canonical JSON, CSV, and the search worklist
npm run analyze -- my-bom.xlsx \
  -o report.html --canonical canonical.json --csv bom.csv --queries queries.csv

# a sheet by name, an encoding the file lied about, a customer profile
npm run analyze -- my-bom.xlsx --sheet "BOM" --profile analyzer/profiles/cadence-atdm.json

# a whole folder, twice: pass 1 learns a format per shape, pass 2 re-reads with it
npm run corpus-run -- --dir ./boms

The HTTP API

For calling Intake from your own system.

The table below is rendered from the routing table itself, the one openapi.json comes from. An undeclared route is therefore invisible here and unreachable there, and a declared one necessarily appears — the document cannot misdescribe what the server does.

MethodPath What it doesReturns
POST/api/analyze Analyse one BOM file (multipart `file`) and write its artefacts. Answers the report summary, or the reason the file was refused.json
POST/api/reports/{id}/reanalyze Re-read the SAME stored bytes with corrections applied — an encoding, a sheet, a header row, column roles. Corrections accumulate across calls.json
POST/api/reports/{id}/decisions Accept, reject or revoke a candidate part for the line’s PART. Keyed on the customer’s part code, so it answers every line that part appears on.json
POST/api/reports/{id}/quote Price the report’s already-resolved lines (no re-upload, no re-read of the file) against Octopart’s public prices and return a summary. Rate-limited. 404 when the report is not this caller’s, the add-on is not configured, or `QUOTE_WEB_ENABLED` is off.json
POST/api/reports/{id}/quote-email Ask for the report’s chiffrage by mail (D2: only a confirmation LINK is sent now, never the attachment). Spends nothing from the discovery cap yet — `confirmQuoteEmail` does, once the address is verified. Rate-limited.json
GET/api/decisions Every decision recorded for this client, accepts and rejects alike.json
GET/api/reports Reports held on this installation, newest first.json
GET/api/profiles Customer profiles available to attach to an analysis.json
GET/api/formats Learned format profiles: what this installation recognises by structure.json
POST/api/formats Save what an operator settled on a report as a named, reusable format.json
POST/api/reports/{id}/confirm-format An Adesio OPERATOR confirms the detected format this report was read with, for every later file of the same shape — allowed on a public instance, operator tokens only.json
DELETE/api/formats/{slug} Forget a learned format.json
GET/api/reports/{id} One report’s summary: coverage, KPIs, the format that was applied.json
GET/api/reports/{id}.{artifact} One artefact of a report: `canonical.json` is the handoff document (adesio.bom.canonical/v1), `html` the self-contained report, `csv` the flat BOM, `queries.csv` the search worklist, `lookups.csv` the distinct PARTS a supplier lookup would be asked about (the figure a per-part bill is counted on), `json` the internal dump.mixed
GET/api/schema The JSON Schema of the canonical document — served so a consumer can validate against the exact version this installation emits.json
GET/api/openapi.json This description, as an OpenAPI 3.1 document.json
POST/api/quote Analyse one BOM attachment in memory and answer an instant quote preview (best price / lead time / stock, an XLSX and an email). Bearer `QUOTE_API_TOKEN`. With a `reply_to` the Apps Script has verified (its `REPLY_TO_SENDER` switch), answers `status: reply` — what to send that sender, and an alert for the owner when the reply is pending — instead of the owner's quote. 404 when this installation has no quote add-on configured.json
GET/api/quote/deliveries Confirmed quotes waiting to be sent, each claimed for a 10-minute lease. Bearer `QUOTE_API_TOKEN`.json
POST/api/quote/deliveries/{deliveryId}/ack The script sent it: forget the delivery. Bearer `QUOTE_API_TOKEN`.json
GET/health Liveness probe: 200 whenever the process is up, whatever else is configured. Use this one on Cloud Run — it reserves paths ending in `z`, so `/healthz` 404s there.json
POST/api/invitations Ask to be invited when Intake goes live. Stores an address and the moment it was given, nothing else. Rate-limited: it is public and appends to a file.json
POST/api/session Exchange a client token for a browser session cookie. The token itself is what authenticates; this only moves it into an HttpOnly cookie so a browser can carry it. Rate-limited: it is public and does cryptographic work.json
DELETE/api/session Clear the browser session. The token stays valid — revoke it with the CLI to withdraw it for good.json
GET/api/me The identity this request carries: client id, token label, and how it was presented. The cheapest way for an integrator to check a token works.json

The contract schema is served at /api/schema, the OpenAPI description at /api/openapi.json. HEAD answers like GET; a verb that does not exist on a path that does returns 405 with an Allow header, not 404.

The Claude connector

So Claude reads your BOMs, with the installation's memory.

An MCP server over stdio. Claude opens the same installation the web app uses: same learned formats, same manufacturer aliases, same catalog, same decisions. A part confirmed in the app is already settled the next time round. No tool ever returns a whole BOM — every listing is paginated, and the full line is a second call.

The tools it exposes

analyze_bom
Reads a BOM from this machine and returns its coverage, counters and report id.
list_reports
Reports this installation holds, with their file name and coverage.
get_coverage
One report's counters, named so they cannot be flattened into “resolved / unresolved”.
list_lines
A page of lines, narrowed by resolution or by why it needs review.
get_line
The full canonical record of one line: identity and its basis, candidates, value, package.
search_queries
Lines the file describes but never names, as deduplicated search queries.
lookup_worklist
The distinct parts a supplier lookup would be asked about — the figure a per-part bill is counted on.
review_queue
Every part waiting on a decision, across all reports, grouped by part.
record_decision
WRITES. Records that a part IS the one on a line, is NOT, or undoes a decision.

Declare it in your Claude client's MCP configuration:

{
  "mcpServers": {
    "adesio-intake": {
      "command": "npm",
      "args": ["run", "--silent", "mcp", "-w", "analyzer"],
      "cwd": "/path/to/bom-analyzer"
    }
  }
}

This page describes what works today. What does not exist yet is marked as such on the system map.