biztera-mcp
Officialby mozmeao
README.md
# biztera-mcp
Read-only MCP server for the Biztera API — projects, purchase orders, and
contract attachments, exposed as MCP tools. Nothing here can modify Biztera.
Runs on macOS, Linux, and Windows. Needs **Node 20+** and a Biztera account.
> **Status: proof of concept, under evaluation.** This is an internal test build
> for validating whether an MCP integration with Biztera is useful. It is not a
> supported product, may change or be withdrawn without notice, and has had no
> formal security or compliance review. It is read-only and cannot modify Biztera,
> but **verify anything important against Biztera directly before acting on it** —
> AI-generated summaries of contracts and figures can be wrong.
API notes, quirks, and design rationale live in [`docs/`](docs/README.md).
Licensed under [MPL-2.0](LICENSE).
## Two ways to run it
| | For | Needs |
|---|---|---|
| **Claude Desktop bundle** (`.zip` / `.mcpb`) | Non-technical users; any OS | Claude Desktop + a Biztera token. No Node. |
| **From source** | Developers; Claude Code or Desktop | Node 20+ |
**Bundle:** get the `biztera-mcp-<version>.zip` from whoever built it (or build
one — see [Building the bundle](#building-the-bundle)), then follow
[INSTALL.md](INSTALL.md). Extract, install from the folder, paste token, done.
**From source:** continue below.
## Setup
**1. Install**
```bash
npm install
```
**2. Create a personal token.** In a browser: **biztera.com/developer → My Auths →
Generate new personal token**. Tick exactly these scopes:
- `read_profile`
- `read_org`
- `read_ar` — approval requests; unused by current tools, included so the token won't need recreating when they're added
- `read_project` — easy to miss; unlocks projects
- `read_po` — easy to miss; unlocks spend data
Tick **no `write_*` scopes**. The token then cannot modify anything, independent
of the code. Biztera documents `read_project` and `read_po` but no endpoints for
them, so it is not obvious they matter — without them you get `403`.
**3. Save the token.** Copy `.env.example` to `.env` (`cp` on macOS/Linux, `copy`
on Windows) and paste the token in with an editor:
```
BIZTERA_ACCESS_TOKEN=paste-it-here
```
It does not expire — treat it like a password. Nothing else goes in `.env`.
**4. Build and check**
```bash
npm run build
npm run probe
```
`probe` prints your identity, your orgs with their visibility flags, an attachment
download check, and a rate-limit sample. If it runs clean, the server works.
## Connect to a client
The server speaks MCP over **stdio**. Both configs below need the **absolute path
to `node`** — GUI apps don't inherit your shell `PATH`, so a version-managed
`node` won't be found otherwise. Get it with `which node` (macOS/Linux) or
`Get-Command node` (PowerShell).
Both configs are the same shape; substitute your two paths:
```json
{
"mcpServers": {
"biztera": {
"command": "/absolute/path/to/node",
"args": ["/absolute/path/to/biztera-mcp/dist/index.js"]
}
}
}
```
On Windows use forward slashes or doubled backslashes in the JSON, e.g.
`"C:/Program Files/nodejs/node.exe"`.
**Claude Code** — save that as `.mcp.json` in the project root, restart Claude
Code, and approve the `biztera` server when prompted. On macOS/Linux this
one-liner writes it with your paths filled in:
```bash
printf '{\n "mcpServers": {\n "biztera": {\n "command": "%s",\n "args": ["%s/dist/index.js"]\n }\n }\n}\n' "$(which node)" "$PWD" > .mcp.json
```
**Claude Desktop / Cowork** — add the same `mcpServers` block to the app config,
or use **Settings → Developer → Local MCP servers**. Then quit and reopen the app.
| OS | Config file |
|---|---|
| macOS | `~/Library/Application Support/Claude/claude_desktop_config.json` |
| Windows | `%APPDATA%\Claude\claude_desktop_config.json` |
| Linux | `~/.config/Claude/claude_desktop_config.json` |
No token goes into any client config. The server reads `.env` from its own
directory regardless of where the client launches it.
**Try it:** *"Which projects are waiting on legal review?"* · *"Total remaining
purchase-order amount grouped by vendor"* · *"Find a statement of work and
summarize the payment terms."*
## Tools
| Tool | Endpoint | Purpose |
|---|---|---|
| `get_me` | `GET /me` | Auth check; org memberships and roles |
| `list_orgs` | `GET /orgs` | Orgs, your role, request visibility, finance settings |
| `list_projects` | `GET /projects` | Primary surface: vendor, budget allocation, dates, legal status. Fetches the full set |
| `get_project` | `GET /projects/:id` | Adds `attachments`, `activities`, full `general`/`vendor`/`legal` |
| `list_purchase_orders` | `GET /pos` | Spend figures plus org-configured dimensions. Fetches the full set |
| `get_purchase_order` | `GET /pos/:id` | One PO with all dimensions and approval info |
| `list_attachments` | via `get_project` | Flat list of contracts/SoWs across projects |
| `download_attachment` | fetch + extract | Returns extracted **text** (DOCX, PDF), not bytes |
List tools paginate internally and return a `complete` flag — **only trust a count
or total when `complete` is true**. Attachment URLs point to vendor contracts;
handle them like the documents themselves.
## Troubleshooting
| Symptom | Fix |
|---|---|
| `spawn node ENOENT` | Use the absolute `node` path in the client config. |
| `BIZTERA_ACCESS_TOKEN is not set` | `.env` is missing or the line is absent. It must sit next to `package.json`. |
| `403` on projects or POs | Token lacks `read_project`/`read_po`. Scopes can't be edited — make a new token. |
| Tools don't appear | Restart the client; servers load at startup. In Claude Code, approve the server. |
| Code changes have no effect | `npm run build`, then restart the client. It runs `dist/`, not `src/`. |
| Empty results, no error | Probably visibility, not a bug — see [`docs/access-model.md`](docs/access-model.md). |
## Building the bundle
```bash
npm run pack
```
Produces `release/biztera-mcp-<version>.mcpb` plus an identical
`biztera-mcp-<version>.zip` (same bytes). Claude Desktop can install either: the
`.mcpb` by double-click, or the extracted zip via **Settings → Extensions →
Advanced settings → Install unpacked extension**. [INSTALL.md](INSTALL.md)
documents the folder route because it works on every setup we tested; ship the
zip.
The pack script works from a throwaway staging copy with production-only
dependencies, so the dev tree is untouched and no
devDependencies ship. `.env` can't be included: the packer excludes `.env*` by
default and the script refuses to continue if it finds one in staging.
Claude Desktop bundles its own Node runtime (macOS and Windows), which is why
end users need nothing installed. The token is declared in `manifest.json` as a
`sensitive` `user_config` field — Claude Desktop prompts for it on install, keeps
it in the OS credential store, and injects it as `BIZTERA_ACCESS_TOKEN`.
Two things to know before editing `.mcpbignore`: it uses gitignore semantics, so
**always anchor directory patterns with a leading `/`** — an unanchored
`release/` also strips `node_modules/bluebird/js/release/` and breaks DOCX
extraction at runtime. And after any change, unpack the result
(`npx @anthropic-ai/mcpb unpack`) and exercise `download_attachment` on a real
PDF and DOCX; `pdf-parse` loads lazily, so a server that boots is not proof
that extraction works.
**Do not sign with `@anthropic-ai/mcpb` 2.1.2.** Its `sign --self-signed` writes a
malformed zip comment length; Claude Desktop rejects the result with *"invalid
comment length"* and `mcpb verify` fails on its own output. Ship the unsigned
bundle. If signing becomes a requirement, test the signer version on a real
install first.
Share the `.mcpb` directly or via a release; it is gitignored and should not be
committed.
## Development
`npm run dev` watches and rebuilds. Everything else — API behaviour, pagination
workarounds, design decisions, open questions — is in [`docs/`](docs/README.md).
One rule when adding a tool: **stdout is the protocol channel**; all diagnostics
go to stderr.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues