This walks you from a fresh machine to a public MCP server that Claude (or any MCP client) can call, assuming you've never used Node tooling, Cloudflare, wrangler, or MCP before. Every command is copy-pasteable.
The whole thing takes about 20 minutes, and it costs nothing: Cloudflare Workers' free tier is 100,000 requests/day, forever, and this server has no database and no servers to keep warm.
- Install the tools and the project's dependencies.
- Run the server on your own machine and poke at it.
- Push it to Cloudflare so it has a public
https://…workers.devURL. - Connect it to Claude Desktop and ask it questions.
You don't need to understand the code to do any of this. (If you want to, src/index.ts is ~150 readable lines and the comments explain the MCP bits.)
Wrangler (Cloudflare's deploy tool) runs on Node.js.
-
Go to https://nodejs.org and install the LTS version (v20 or newer).
-
Verify it worked. Open a terminal and run:
node --version
You should see something like
v20.x.xor higher. If the command isn't found, close and reopen your terminal, or restart your computer, and try again.
Where's "a terminal"? On macOS: open the Terminal app (Cmd-Space, type "Terminal"). On Windows: open PowerShell (Start menu, type "PowerShell"). On Linux: your usual terminal.
You'll also want a free Cloudflare account, but you don't have to create it now; the deploy step does it for you in the browser.
Unzip the project somewhere you'll remember (e.g. your Desktop). Then in your terminal, move into the folder:
cd path/to/opendatasheet-mcpA shortcut: type cd (with a trailing space), then drag the folder from your file explorer onto the terminal window; it pastes the path for you. Press Enter.
To confirm you're in the right place:
lsYou should see package.json, wrangler.toml, src, data, and this file.
npm installThis reads package.json and downloads wrangler + TypeScript into a local node_modules folder. It runs once (and again only if you change dependencies). It does not install anything system-wide.
npm run devYou'll see output ending with something like:
[wrangler:info] Ready on http://localhost:8787
That's your server, running on your machine. Leave this terminal open; it stays running until you press Ctrl-C.
Open http://localhost:8787 in a browser. You should get a small JSON page listing the server name and its tools. That confirms the server is alive.
A couple of
Unable to fetch the Request.cf objectwarnings on startup are normal in local mode and harmless.
The MCP Inspector is a free tool from the MCP project that gives you a UI to list a server's tools and call them. It's the easiest way to confirm everything works before you wire it into Claude.
Open a second terminal (leave npm run dev running in the first), and run:
npx @modelcontextprotocol/inspectorSay yes if it asks to install. It prints a URL (something like http://localhost:6274) and opens it in your browser. In the Inspector:
-
Set Transport Type to Streamable HTTP.
-
Set URL to
http://localhost:8787. -
Click Connect.
-
Click List Tools. You should see all 11 tools.
-
Pick a tool, fill in the arguments, and click Run Tool. Try these:
Tool Arguments What you should see list_parts(none) Two parts: ACME-M0 and ACME-IMU6 describe_partmpn=ACME-IMU6Its profiles + a capability index of everything you can query get_conversionmpn=ACME-IMU6,measurand=accel,range=FS_8GSensitivity 4096 LSB/g and the register field ( FS_SEL) that selects itget_measurandsmpn=ACME-IMU6Max sample rate shows 4000 Hz the errata-corrected value, with the original 8000 noted check_constraintsmpn=ACME-M0,config={"vdd_v":3.3,"temp_c":80,"mode":"master","fields":{"BR":"DIV2"}}A violation, because an erratum forbids BR=DIV2 above 70 °C
Prefer the terminal? You can skip the Inspector and hit the server with curl instead:
curl -s http://localhost:8787 -H 'content-type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_parts","arguments":{}}}'
When you're satisfied, you can stop the local server (Ctrl-C in the first terminal). Time to put it online.
npx wrangler loginThis opens your browser. If you don't have a Cloudflare account, you can create one for free right there. Click Allow to let wrangler deploy on your behalf, then return to the terminal.
npm run deployHeads-up:
deployrebuilds the STM32 data first. This project ships a real part,STM32G474RE, whosedata/stm32g474re.jsonis generated from a CMSIS-SVD by a Python pipeline (seetools/README.md). Apredeployhook inpackage.jsonregenerates it on every deploy, so the live data can never drift from the source. That meansnpm run deployneeds Python 3 installed (python3 --version). If you don't have Python and just want to ship the committed JSON as-is, runnpx wrangler deploydirectly (it skips the rebuild).
The first time, wrangler may ask to register a free workers.dev subdomain (pick anything, it's the YOUR-SUBDOMAIN part of your URL). When it finishes you'll see your live URL:
https://opendatasheet-mcp.YOUR-SUBDOMAIN.workers.dev
Copy that URL. That's your public MCP server. Anyone, or any AI agent, can now reach it.
Confirm it's live by opening that URL in a browser; you should see the same info page as before.
Re-deploying later is just
npm run deployagain. Each deploy replaces the old version in a few seconds.
Claude Desktop talks to local (stdio) servers natively and to remote servers through a tiny bridge called mcp-remote. You point it at your URL in a config file.
-
Find (or create) Claude Desktop's config file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
If the file doesn't exist, create it. (On macOS you can run
open -e ~/Library/Application\ Support/Claude/claude_desktop_config.json; if it says the file doesn't exist, create it in that folder.) - macOS:
-
Put this in it, replacing the URL with your deployed URL:
{ "mcpServers": { "opendatasheet": { "command": "npx", "args": ["-y", "mcp-remote", "https://opendatasheet-mcp.YOUR-SUBDOMAIN.workers.dev"] } } }If the file already has an
mcpServersblock, just add the"opendatasheet"entry alongside your existing ones. -
Fully quit and reopen Claude Desktop (not just close the window, quit the app).
-
In a new chat, you should see the OpenDatasheet tools available (look for the tools/connector indicator). Now ask it things; see the prompts below.
Other clients: Cursor, Windsurf, VS Code, and others use the same
mcp-remotepattern in their own MCP config. Some newer Claude builds also let you paste a remote MCP URL directly as a "custom connector" in settings, which skips the config file entirely; if your version has that, just paste theworkers.devURL there and pick the Streamable HTTP transport.
Once connected, try (in plain English, Claude picks the right tool):
- "What parts can you look up?"
- "Describe the ACME-IMU6, what can I query about it?"
- "On the ACME-M0, what does the BR field in SPI1's CR1 register do?"
- "What's the max SPI clock on the ACME-M0 at 1.8 V versus 3.3 V?" → 12 MHz vs 24 MHz, each with its conditions.
- "For the ACME-IMU6 accelerometer at ±8 g, what's the sensitivity, and which register selects that range?" → 4096 LSB/g, set by
FS_SEL = FS_8G. - "What's the real maximum sample rate of the ACME-IMU6 accelerometer?" → 4000 Hz (the datasheet says 8000; the erratum corrects it).
- "Is it safe to run the ACME-M0 SPI at BR=DIV2 in master mode at 80 °C?" → No, flagged by an erratum, with the citation.
That last pair is the whole point of the project: the server hands back the corrected, cited answer instead of whatever a PDF said on page 812.
Edit the name in wrangler.toml (this changes your URL) and SERVER_INFO in src/index.ts, then npm run deploy.
The data is just JSON files in data/. To add a part:
-
Create
data/my-part.jsonfollowing the shape ofacme-m0.json. (The full schema and what each profile means is in the OpenDatasheet doc set,docs/03-SCHEMA.md.) -
Open
src/lib.tsand (a) import it at the top, (b) add it to thePARTSmap:import myPart from "../data/my-part.json"; // ... const PARTS: Record<string, Part> = { "ACME-M0": m0 as Part, "ACME-IMU6": imu6 as Part, "MY-PART": myPart as Part, // <- add this };
-
If it has an errata overlay, add it to
ERRATAthe same way. -
npm run deploy.
That's the entire workflow. No database, no migrations.
- Free tier: 100,000 requests/day, indefinitely, not a trial. A read-only datasheet server is unlikely to approach that.
- No egress fees, ~0 ms cold start. You're not paying for idle warm instances the way you would on most platforms, and a traffic spike doesn't create a bandwidth bill.
- Watch usage: Cloudflare dashboard → Workers & Pages → your worker → Metrics.
- If you ever outgrow free: the Workers Paid plan is about $5/month including millions of requests, then pennies per additional million. Even going viral is coffee money. Check the current numbers at https://developers.cloudflare.com/workers/platform/pricing/.
- On the free plan you don't get a surprise invoice: you get rate-limited, not billed into the ground.
| Symptom | Fix |
|---|---|
command not found: node / npm |
Node isn't installed or the terminal predates the install. Reinstall from nodejs.org, then open a fresh terminal. |
command not found: wrangler |
Run commands as npm run dev / npm run deploy, or prefix with npx (e.g. npx wrangler login). Don't install wrangler globally. |
npm run dev errors about a port in use |
Something's already on 8787. Stop it, or run npx wrangler dev --port 8799 and use that port. |
| Inspector / Claude won't connect | Make sure the transport is Streamable HTTP (not SSE). Confirm the URL opens in a browser and shows the info page. |
| Claude doesn't show the tools | Did you fully quit and reopen the app? Check the JSON config is valid (no trailing commas) and the URL is exactly your deployed one. |
| Deploy says you're not logged in | Run npx wrangler login again and complete the browser step. |
A tool returns an error like Peripheral 'X' not found |
That's expected behavior; the message lists the valid names. Call describe_part first to see what's queryable. |
| Need to see what's happening on the live server | npx wrangler tail streams your deployed worker's live logs. |
opendatasheet-mcp/
├── README.md ← project front page + the mission
├── DEV.md ← you are here (run it, deploy it, connect it)
├── docs/ ← the OpenDatasheet spec (mission, schema, MCP interface, roadmap)
├── package.json ← dependencies + the dev/deploy scripts
├── tsconfig.json ← TypeScript settings
├── wrangler.toml ← Cloudflare Worker config (name, entry point)
├── src/
│ ├── index.ts ← the Worker: CORS, routing, MCP JSON-RPC handling
│ ├── tools.ts ← the 11 tools (this is where the verbs live)
│ └── lib.ts ← data registry, errata resolver, path resolver
├── tools/ ← Python pipeline that builds part JSON from CMSIS-SVD + RM
└── data/
├── stm32g474re.json ← real part (STM32G474RE), built by the pipeline
├── acme-m0.json ← example MCU (register-map profile)
├── acme-m0-errata.json
├── acme-imu6.json ← example IMU (register-map + sensor profiles)
└── acme-imu6-errata.json
The two ACME parts are fictional, synthetic examples chosen to exercise every feature (two composed profiles, a cross-profile link, and both kinds of errata). Swap in real parts when you're ready.
Besides parts, the worker serves reference-manual prose from a D1 database: GET /refman/index.json, /refman/{doc}/toc, /refman/{doc}/section/{id}, /refman/{doc}/search?q=…, plus the refman_search / refman_read / refman_toc MCP tools. Parts stay build-time static JSON; only refman uses D1.
npx wrangler d1 create refman # then put the printed id in wrangler.toml
npx wrangler d1 execute refman --file schema/refman.sql --local
npx wrangler d1 execute refman --file schema/refman.sql --remote-
Licensing gate: add the doc to
tools/refman_allowlist.json(deliberately — hosting ingested vendor text is a licensing decision, see the note in that file's neighbors). The ingest CLI refuses docs that aren't listed. -
Ingest (offline; needs
pypdf+pdftotext):python3 tools/refman_ingest.py datasheets/stm32u575/RM0456.pdf \ --doc RM0456 --rev 6 \ --title "STM32U5 series Arm-based 32-bit MCUs reference manual"Outputs land in
build/refman/(git-ignored): a manifest, a sections JSONL, and{doc}.import.sql. -
Import — always
--localfirst as the dry run, then--remote:npx wrangler d1 execute refman --file build/refman/RM0456.import.sql --local npx wrangler d1 execute refman --file build/refman/RM0456.import.sql --remote
The SQL file for a full RM is tens of MB; the remote import can take minutes. It starts with per-doc
DELETEs, so re-importing the same doc is idempotent. -
Verify (against
npm run devlocally, then your deployed URL):curl -s localhost:8787/refman/index.json curl -s 'localhost:8787/refman/RM0456/toc?depth=1' curl -s 'localhost:8787/refman/RM0456/search?q=RCC_CFGR1' curl -s localhost:8787/refman/RM0456/section/11.4.9
A register-name query should return its register section as the top hit; a section read should return the page-granular text (a section's first/last page may include a neighbor's tail — that's expected fidelity, not a bug).