AGENTS.md · git:20260817.4af833d · 2026-08-17 · sha256 beb12f4ed5ff1d4e

AGENTS.md git:20260817.4af833dA

Immutable. This exact content is served forever at /api/v1/blob/beb12f4ed5ff1d4e.

# Frihet MCP Server

Open-source MCP server for Frihet ERP. TypeScript, Node >= 20, MIT license. Distributed via npm + Cloudflare Worker (mcp.frihet.io) + Smithery + Anthropic registry.

## Build & Test

```bash
npm install                      # Install deps
npm run build                    # tsc → dist/
npm test                         # Run native node test suite
npm start                        # Run server via stdio (local debug)
```

**Pre-commit**: `npm run build` must succeed. `npm test` must pass.

## Code Style

- TypeScript strict mode (`strict: true`)
- ES2022 target, NodeNext module resolution
- Tools use the established canonical snake_case names, such as `create_invoice`
- Return human-readable MCP `content` plus outputSchema-valid `structuredContent`
- Zod schemas on `inputSchema` (strict)
- Preserve actionable, sanitized API errors through the shared error helpers
- One tool per logical operation — no batching unless explicit
- Async/await throughout, no callbacks
- Logger from `src/logger.ts` — never `console.log` in tool code
- Observability: every tool wrapped via `src/observability.ts` (Langfuse)

## Conventions

- Commit messages: `feat(tools): add <family> CRUD` / `fix(invoices): handle null totals`
- Branch names: `feat/<descriptive-slug>`
- Atomic commits — one tool family per commit minimum
- Update README.md tool count badge when adding tools
- Tool descriptions are concise and bilingual (English / Spanish)
- Test fixtures in `src/__tests__/fixtures/`

## Testing standards

- Every mutating tool must have at least 1 idempotency test
- Every tool must have at least 1 schema rejection test (bad input)
- Every tool must have at least 1 happy path test
- Coverage target: > 80% lines, 100% on auth/scope branches

## Gotchas

- **NEVER mutate `process.env`** — read once into config object
- **API client must respect `Idempotency-Key`** — propagate from tool input when present
- **Don't log full request bodies** — PII (NIF/IBAN/email). Use `mask()` from logger
- **Cloudflare Worker is a separate deployment surface** — pinging the Worker is not a substitute for testing the npm package locally
- **MCP SDK breaking changes** — pin `@modelcontextprotocol/sdk` minor; major bumps require manual review
- **Postinstall script** — `node scripts/postinstall.js || true` runs harmlessly. Do not make required

## Security

- API key (`FRIHET_API_KEY`) read from env at server start, never logged
- Bearer token sent only to `https://api.frihet.io/v1`
- No third-party HTTP outside Frihet API + Langfuse
- Worker (mcp.frihet.io) handles OAuth + key exchange separately
- Errors use bounded, sanitized remediation and never echo credentials or raw provider bodies

## Worker-side static surface (Cloudflare Worker)

The Cloudflare Worker serving `mcp.frihet.io` MUST also serve:
- `/llms.txt` (200, plain text, brief overview)
- `/llms-full.txt` (200, plain text, full reference)
- `/robots.txt` (200, allow AI crawlers explicit)
- `/sitemap.xml` (200, single URL or full)
- `/openapi.json` (200, mirror of api.frihet.io v1)
- `/mcp.json` (200, MCP server manifest)
- `/.well-known/mcp` (200, well-known endpoint discovery)
- `/.well-known/ai-plugin.json` (200, ChatGPT plugin spec)