@frihet/mcp-server 1.16.5 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -31
- package/dist/agent-onboarding.d.ts +98 -0
- package/dist/agent-onboarding.js +275 -0
- package/dist/api-origin.d.ts +9 -0
- package/dist/api-origin.js +41 -0
- package/dist/canary/mcp-harness.d.ts +77 -0
- package/dist/canary/mcp-harness.js +549 -0
- package/dist/capability-truth.d.ts +29 -0
- package/dist/capability-truth.js +254 -0
- package/dist/client-interface.d.ts +22 -14
- package/dist/client.d.ts +114 -18
- package/dist/client.js +520 -58
- package/dist/demo-client.d.ts +16 -13
- package/dist/demo-client.js +209 -29
- package/dist/demo-fixtures.d.ts +3 -3
- package/dist/demo-fixtures.js +7 -7
- package/dist/fiscal-aliases.d.ts +44 -0
- package/dist/fiscal-aliases.js +62 -0
- package/dist/index.js +41 -56
- package/dist/logger.d.ts +2 -2
- package/dist/logger.js +140 -8
- package/dist/observability.d.ts +18 -29
- package/dist/observability.js +192 -105
- package/dist/openai-profile.d.ts +27 -15
- package/dist/openai-profile.js +1191 -145
- package/dist/openai-review-contract.d.ts +49 -0
- package/dist/openai-review-contract.js +919 -0
- package/dist/openai-review-oauth.d.ts +98 -0
- package/dist/openai-review-oauth.js +136 -0
- package/dist/prompts/register-all.d.ts +2 -0
- package/dist/prompts/register-all.js +2 -0
- package/dist/public-capability-contract.d.ts +39 -0
- package/dist/public-capability-contract.js +186 -0
- package/dist/redaction.d.ts +10 -0
- package/dist/redaction.js +74 -0
- package/dist/resources/register-all.js +6 -12
- package/dist/server-composition.d.ts +16 -0
- package/dist/server-composition.js +44 -0
- package/dist/tool-exposure.d.ts +25 -22
- package/dist/tool-exposure.js +199 -77
- package/dist/tools/accountingClose.d.ts +14 -5
- package/dist/tools/accountingClose.js +41 -23
- package/dist/tools/backend-availability.d.ts +3 -3
- package/dist/tools/backend-availability.js +3 -3
- package/dist/tools/clients.js +9 -9
- package/dist/tools/crm.js +22 -21
- package/dist/tools/deposits.js +30 -11
- package/dist/tools/einvoice.d.ts +1 -1
- package/dist/tools/expenses.d.ts +16 -0
- package/dist/tools/expenses.js +47 -27
- package/dist/tools/hr.d.ts +2 -3
- package/dist/tools/hr.js +8 -9
- package/dist/tools/intelligence.js +95 -4
- package/dist/tools/invoices.js +150 -47
- package/dist/tools/payroll.d.ts +5 -8
- package/dist/tools/payroll.js +19 -32
- package/dist/tools/permissions.d.ts +4 -7
- package/dist/tools/permissions.js +11 -14
- package/dist/tools/products.js +6 -6
- package/dist/tools/quotes.js +109 -24
- package/dist/tools/shared.d.ts +1431 -75
- package/dist/tools/shared.js +386 -76
- package/dist/tools/team.js +18 -14
- package/dist/tools/vendors.js +9 -9
- package/dist/tools/webhooks.js +35 -30
- package/dist/types.d.ts +28 -4
- package/docs/agent-onboarding.json +381 -0
- package/package.json +32 -7
package/README.md
CHANGED
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
<a href="https://smithery.ai/servers/frihet/frihet-mcp"><img src="https://smithery.ai/badge/frihet/frihet-mcp" alt="Smithery installs"></a>
|
|
22
22
|
<a href="https://registry.modelcontextprotocol.io/?q=io.frihet"><img src="https://img.shields.io/badge/MCP_Registry-io.frihet%2Ferp-4A90D9?style=flat&logo=anthropic&logoColor=white" alt="MCP Registry"></a>
|
|
23
23
|
<a href="https://github.com/Frihet-io/frihet-mcp/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-18181b?style=flat&labelColor=09090b" alt="license"></a>
|
|
24
|
-
<img src="https://img.shields.io/badge/
|
|
25
|
-
<img src="https://img.shields.io/badge/node-%3E%
|
|
24
|
+
<img src="https://img.shields.io/badge/catalogue-157_operations-18181b?style=flat&labelColor=09090b" alt="157 canonical catalogue operations">
|
|
25
|
+
<img src="https://img.shields.io/badge/node-%3E%3D20-18181b?style=flat&labelColor=09090b" alt="node >=20">
|
|
26
26
|
<a href="https://www.typescriptlang.org/"><img src="https://img.shields.io/badge/TypeScript-18181b?style=flat&labelColor=09090b" alt="TypeScript"></a>
|
|
27
27
|
</p>
|
|
28
28
|
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
| **ChatGPT Apps** | Coming soon | [chatgpt.com](https://chatgpt.com) |
|
|
48
48
|
| **Anthropic Claude Directory** | Coming soon | [claude.ai/settings/connectors](https://claude.ai/settings/connectors) |
|
|
49
49
|
|
|
50
|
-
> **
|
|
50
|
+
> **Surface truth:** the catalogue contains 157 canonical operations. The local full profile serves those plus 5 fiscal alias names (162 names). The hosted grouped profile also adds 3 local discovery names (165 names). Catalogue membership is not a promise that a backing API is enabled for every workspace.
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -60,7 +60,7 @@ You: "Create an invoice for TechStart SL, 40 hours of consulting at 75 EUR/h
|
|
|
60
60
|
Claude: Done. Invoice INV-2026-089 created. Total: 3,000.00 EUR + 21% IVA = 3,630.00 EUR.
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
157
|
|
63
|
+
157 canonical operations. Five fiscal aliases. Ten prompts. The local package serves 11 resources; the hosted Worker deliberately serves the 7 static resources, while API-backed workspace resources remain local-profile only.
|
|
64
64
|
|
|
65
65
|
<!-- v1.12.0-beta.1 — D4-B megasprint: HR (9), payroll (2), onboarding (2), permissions (2), period close (3), webhook test (1) = +19 = 157 tools total -->
|
|
66
66
|
|
|
@@ -80,6 +80,26 @@ When you're ready for your real data, drop the flag and add your key ([app.frihe
|
|
|
80
80
|
|
|
81
81
|
---
|
|
82
82
|
|
|
83
|
+
## For AI agents
|
|
84
|
+
|
|
85
|
+
If you are an agent reading this repository rather than a person reading a page, everything you need is machine-readable and generated from the running server — you do not have to parse this README.
|
|
86
|
+
|
|
87
|
+
| What | Where |
|
|
88
|
+
|------|-------|
|
|
89
|
+
| Onboarding contract: quickstart per client, auth, safe workflow, human-authority tool lists, error recovery | [`docs/agent-onboarding.json`](./docs/agent-onboarding.json) — also shipped inside the npm package |
|
|
90
|
+
| Per-tool capability truth: `callability`, `writesFrihet`, `externalInteraction`, `externalSideEffects` | `_meta["io.frihet/capability"]` on every entry of `tools/list` |
|
|
91
|
+
| How to behave once connected | the `instructions` string returned by `initialize` — your client hands it to you automatically |
|
|
92
|
+
|
|
93
|
+
Three rules the contract encodes, in short:
|
|
94
|
+
|
|
95
|
+
1. **Orient before acting.** `get_business_context` and the `frihet://tax/rates` resource decide the correct fiscal treatment. Do not recall a Spanish tax rate from memory.
|
|
96
|
+
2. **Draft, show, stop.** `create_invoice`, `create_quote` and `create_credit_note` all default to `status=draft` — no fiscal number, no hash, nothing sent to a tax authority. Present the draft and hand back.
|
|
97
|
+
3. **Human authority is not yours to assume.** Any tool with a non-empty `externalSideEffects` reaches a client's inbox, a webhook, money, or AEAT / VeriFactu / TicketBAI / FACe. Several also take `confirm=true`; that flag records a human decision — never set it to satisfy your own plan.
|
|
98
|
+
|
|
99
|
+
`docs/agent-onboarding.json` is regenerated from the live surface by `npm run generate:agent-onboarding` and gated in CI by `npm run gate:agent-onboarding`, so its tool lists and counts cannot drift from the server.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
83
103
|
## Install
|
|
84
104
|
|
|
85
105
|
### One-line (Claude Code, Cursor, Copilot, Codex, Windsurf, Gemini CLI, and more)
|
|
@@ -106,7 +126,36 @@ Once available in the community marketplace:
|
|
|
106
126
|
|
|
107
127
|
Skill invocation: `/frihet-erp:frihet-mcp`. The bundled `.mcp.json` launches `@frihet/mcp-server` via `npx` — set `FRIHET_API_KEY` in your environment (get one at [app.frihet.io](https://app.frihet.io) → Settings → API keys).
|
|
108
128
|
|
|
109
|
-
### Claude Code
|
|
129
|
+
### Claude Code — one command
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
claude mcp add frihet -s user -e FRIHET_API_KEY=fri_your_key_here -- npx -y @frihet/mcp-server
|
|
133
|
+
claude mcp list # verify: frihet ✓ Connected
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The CLI owns the config file, so there is nothing to hand-edit and no path to get wrong. (User scope writes `~/.claude.json`, not `~/.claude/mcp.json`.)
|
|
137
|
+
|
|
138
|
+
### Codex CLI — one command
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
codex mcp add frihet --env FRIHET_API_KEY=fri_your_key_here -- npx -y @frihet/mcp-server
|
|
142
|
+
codex mcp list # verify
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Codex config is **TOML**, not JSON. `codex mcp add` writes:
|
|
146
|
+
|
|
147
|
+
```toml
|
|
148
|
+
[mcp_servers.frihet]
|
|
149
|
+
command = "npx"
|
|
150
|
+
args = ["-y", "@frihet/mcp-server"]
|
|
151
|
+
|
|
152
|
+
[mcp_servers.frihet.env]
|
|
153
|
+
FRIHET_API_KEY = "fri_your_key_here"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
> Pasting a JSON `mcpServers` block into `~/.codex/config.toml` is a TOML parse error that takes down your **whole** Codex config, not just this server. Use the command above.
|
|
157
|
+
|
|
158
|
+
### Claude Desktop, Cursor, Windsurf, Cline — JSON config
|
|
110
159
|
|
|
111
160
|
```json
|
|
112
161
|
{
|
|
@@ -124,14 +173,12 @@ Skill invocation: `/frihet-erp:frihet-mcp`. The bundled `.mcp.json` launches `@f
|
|
|
124
173
|
|
|
125
174
|
| Tool | Config file |
|
|
126
175
|
|------|------------|
|
|
127
|
-
| Claude Code | `~/.claude/mcp.json` |
|
|
128
176
|
| Claude Desktop | `~/Library/Application Support/Claude/claude_desktop_config.json` |
|
|
129
177
|
| Cursor | `.cursor/mcp.json` or `~/.cursor/mcp.json` |
|
|
130
178
|
| Windsurf | `~/.windsurf/mcp.json` |
|
|
131
179
|
| Cline | VS Code settings or `.cline/mcp.json` |
|
|
132
|
-
| Codex CLI | `~/.codex/config.toml` (MCP section) |
|
|
133
180
|
|
|
134
|
-
The JSON
|
|
181
|
+
The JSON above is identical for these four clients; only the file path changes. Claude Code and Codex are **not** in this table — they manage their own config through the CLI commands shown above.
|
|
135
182
|
|
|
136
183
|
### Remote (no install)
|
|
137
184
|
|
|
@@ -222,7 +269,7 @@ Talk to your ERP. These are real prompts, not marketing copy.
|
|
|
222
269
|
|
|
223
270
|
## What to expect
|
|
224
271
|
|
|
225
|
-
This MCP is a **structured data interface** -- you describe what you want in natural language, and the AI creates, queries, or modifies business records in Frihet. Most of the 157
|
|
272
|
+
This MCP is a **structured data interface** -- you describe what you want in natural language, and the AI creates, queries, or modifies business records in Frihet. Most of the 157 canonical operations are CRUD operations over the REST API; the rest are read-only summaries and fiscal/e-invoice actions. Alias and discovery names are counted separately.
|
|
226
273
|
|
|
227
274
|
**Works great:**
|
|
228
275
|
|
|
@@ -249,7 +296,7 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
249
296
|
|
|
250
297
|
---
|
|
251
298
|
|
|
252
|
-
##
|
|
299
|
+
## Catalogue operations (157)
|
|
253
300
|
|
|
254
301
|
### Invoices (12)
|
|
255
302
|
|
|
@@ -259,12 +306,12 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
259
306
|
| `get_invoice` | Get full invoice details by ID |
|
|
260
307
|
| `create_invoice` | Create a new invoice with line items |
|
|
261
308
|
| `update_invoice` | Update any invoice field |
|
|
262
|
-
| `delete_invoice` |
|
|
309
|
+
| `delete_invoice` | Delete a draft invoice; a sent/paid one is cancelled, not destroyed (`confirm=true` required) |
|
|
263
310
|
| `search_invoices` | Find invoices by client name, date, or status |
|
|
264
|
-
| `send_invoice` | Email invoice to client (PDF attachment) |
|
|
311
|
+
| `send_invoice` | Email invoice to client (PDF attachment) — reaches a third party, `confirm=true` required |
|
|
265
312
|
| `mark_invoice_paid` | Mark an invoice as paid with optional payment date |
|
|
266
|
-
| `get_invoice_pdf` | Get
|
|
267
|
-
| `get_invoice_einvoice` | Get
|
|
313
|
+
| `get_invoice_pdf` | Get bounded invoice PDF bytes as base64 |
|
|
314
|
+
| `get_invoice_einvoice` | Get bounded XML or Factur-X PDF bytes for an invoice |
|
|
268
315
|
| `create_credit_note` | Create a credit note linked to an existing invoice |
|
|
269
316
|
| `apply_late_fee` | Apply a late payment fee to an overdue invoice |
|
|
270
317
|
|
|
@@ -329,7 +376,7 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
329
376
|
| `get_quote` | Get quote details |
|
|
330
377
|
| `create_quote` | Draft a new quote |
|
|
331
378
|
| `update_quote` | Modify a quote |
|
|
332
|
-
| `delete_quote` | Delete a
|
|
379
|
+
| `delete_quote` | Delete only a clean draft with no delivery, response, attachment, or conversion evidence; refuse protected drafts; cancel non-drafts (`confirm=true` required) |
|
|
333
380
|
| `send_quote` | Email quote to client for acceptance |
|
|
334
381
|
|
|
335
382
|
### Webhooks (6)
|
|
@@ -395,9 +442,9 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
395
442
|
|
|
396
443
|
| Tool | What it does |
|
|
397
444
|
|------|-------------|
|
|
398
|
-
| `list_team_members` | List
|
|
399
|
-
| `invite_team_member` | Invite a new member by email with role (admin/
|
|
400
|
-
| `update_team_member_role` | Change an existing member's role |
|
|
445
|
+
| `list_team_members` | List active members + pending invites (owner excluded) |
|
|
446
|
+
| `invite_team_member` | Invite a new member by email with role (admin/editor/accountant/viewer) |
|
|
447
|
+
| `update_team_member_role` | Change an existing member's role (admin/editor/accountant/viewer) |
|
|
401
448
|
| `remove_team_member` | Remove a member from the workspace (confirm=true required) |
|
|
402
449
|
|
|
403
450
|
### Gestoria — Accountants (5)
|
|
@@ -542,15 +589,15 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
542
589
|
| `leave_list` | List leave requests with filters (user, status, date range) |
|
|
543
590
|
| `attendance_clock_in` | Record clock-in for an employee |
|
|
544
591
|
| `attendance_clock_out` | Record clock-out for an employee |
|
|
545
|
-
| `overtime_report` |
|
|
592
|
+
| `overtime_report` | Read daily/weekly overtime, aggregate minutes/hours, and compliance alerts computed over the selected YYYY or YYYY-MM records |
|
|
546
593
|
| `anomaly_list` | List attendance anomalies (missing punches, excessive overtime) |
|
|
547
594
|
|
|
548
595
|
### Payroll (2)
|
|
549
596
|
|
|
550
597
|
| Tool | What it does |
|
|
551
598
|
|------|-------------|
|
|
552
|
-
| `payroll_export` |
|
|
553
|
-
| `payroll_checklist` |
|
|
599
|
+
| `payroll_export` | Read normalized payroll-ready employee data; the format value is an echoed destination label, not a generated file |
|
|
600
|
+
| `payroll_checklist` | List payable employees with payroll-profile readiness, missing fields, and monthly review state |
|
|
554
601
|
|
|
555
602
|
### Onboarding (2)
|
|
556
603
|
|
|
@@ -563,32 +610,45 @@ If you need to digitize paper invoices or receipts, extract the data first (e.g.
|
|
|
563
610
|
|
|
564
611
|
| Tool | What it does |
|
|
565
612
|
|------|-------------|
|
|
566
|
-
| `permissions_matrix` | Get the
|
|
567
|
-
| `permissions_me` |
|
|
613
|
+
| `permissions_matrix` | Get the documented RBAC-model snapshot (not a runtime authorization guarantee) |
|
|
614
|
+
| `permissions_me` | Compare RBAC-model fields with actual API-key scopes and known scope denials (non-exhaustive) |
|
|
568
615
|
|
|
569
616
|
### Period Close (3)
|
|
570
617
|
|
|
571
618
|
| Tool | What it does |
|
|
572
619
|
|------|-------------|
|
|
573
|
-
| `period_close_status` | Get the
|
|
620
|
+
| `period_close_status` | Get the current or selected YYYY fiscal-year range, open/closed state, and nullable closing details |
|
|
574
621
|
| `period_close` | Close an accounting period (gestor/admin only — TRUST AREA) |
|
|
575
622
|
| `period_reopen` | Reopen a closed period with a mandatory reason (TRUST AREA) |
|
|
576
623
|
|
|
577
|
-
All
|
|
624
|
+
All canonical operations (and their aliases) return **structured output** via `outputSchema` -- typed JSON, not raw text. List response shapes follow their API family; not every list endpoint is paginated.
|
|
625
|
+
|
|
626
|
+
### Capability and side-effect truth
|
|
627
|
+
|
|
628
|
+
On the full MCP surfaces, every `tools/list` entry includes `_meta["io.frihet/capability"]`:
|
|
629
|
+
|
|
630
|
+
- `registered` means the name and handler exist in this server build;
|
|
631
|
+
- `callability` is `api_dependent` (the handler calls the API; deployment, workspace enablement, and authorization still decide), `runtime_checked` (the handler explicitly distinguishes an absent backend from empty data), `deferred`, `unavailable`, or `local`—never an unconditional “available” claim;
|
|
632
|
+
- `writesFrihet`, `externalInteraction`, and `externalSideEffects` distinguish state changes and calls to external entities/providers;
|
|
633
|
+
- MCP action annotations remain the standard source for read-only, destructive, idempotent, and open-world hints.
|
|
634
|
+
|
|
635
|
+
The ChatGPT/OpenAI host is a separately reviewed surface: exactly 33 business operations with complete descriptions, 0 discovery meta-tools, 0 prompts, and 0 resources. Its 17 reads and 16 writes are deliberately narrow, and all writes require literal `confirm=true`. Ten writes may deliver full business events to active endpoints previously configured by the workspace owner; webhook administration itself remains excluded. Direct email delivery, the legacy monthly summary, raw invoice PDFs, invoice lifecycle transitions, updating an existing quote, regulated filing, deleting client parent records, deleting expenses with their linked files, deleting products, and deleting vendors are also excluded. This surface must not be inferred from the full catalogue.
|
|
578
636
|
|
|
579
637
|
---
|
|
580
638
|
|
|
581
|
-
## Resources
|
|
639
|
+
## Resources
|
|
582
640
|
|
|
583
641
|
Context the AI can read to make smarter decisions.
|
|
584
642
|
|
|
643
|
+
The local package serves 11 resources: 7 static references plus 4 API-backed workspace resources. The hosted Worker serves the 7 static resources. The OpenAI-reviewed host serves 0 resources.
|
|
644
|
+
|
|
585
645
|
**Static** (reference data, no API calls):
|
|
586
646
|
|
|
587
647
|
| Resource | URI | What it provides |
|
|
588
648
|
|----------|-----|-----------------|
|
|
589
649
|
| API Schema | `frihet://api/schema` | OpenAPI summary: endpoints, auth, rate limits, pagination, error codes |
|
|
590
650
|
| Tax Rates | `frihet://tax/rates` | Tax rates by Spanish fiscal zone: IVA, IGIC, IPSI, EU reverse charge, IRPF |
|
|
591
|
-
| Tax Calendar | `frihet://tax/calendar` | Quarterly filing deadlines
|
|
651
|
+
| Tax Calendar | `frihet://tax/calendar` | Quarterly and annual filing deadlines for the listed Spanish tax models |
|
|
592
652
|
| Expense Categories | `frihet://config/expense-categories` | 8 categories with deductibility rules, IVA treatment, amortization |
|
|
593
653
|
| Invoice Statuses | `frihet://config/invoice-statuses` | Status flow (draft > sent > paid/overdue > cancelled), transition rules, webhook events |
|
|
594
654
|
| Currencies | `frihet://config/currencies` | 40 supported currencies with ISO codes, symbols, decimal places, locale formatting |
|
|
@@ -670,10 +730,10 @@ Frihet's differentiator is **depth** — full ES/EU fiscal coverage plus native
|
|
|
670
730
|
|
|
671
731
|
| Mode | Behavior |
|
|
672
732
|
|------|----------|
|
|
673
|
-
| `full` (default) |
|
|
733
|
+
| `full` (default) | Canonical tools and fiscal aliases are exposed with full descriptions and schemas. Public descriptors add conservative callability and side-effect truth; operation names, schemas and handlers are unchanged. |
|
|
674
734
|
| `grouped` | **Progressive disclosure.** Each tool's description collapses to a one-line `[group] summary — full schema via describe_tool('name')`, and three lightweight discovery tools are added. The agent loads depth only for the tools it actually needs. |
|
|
675
735
|
|
|
676
|
-
In `grouped` mode
|
|
736
|
+
In `grouped` mode operation names, input schemas and handlers are unchanged. Descriptors also expose the same conservative capability and action truth as the full profile. Discovery flows through three meta-tools:
|
|
677
737
|
|
|
678
738
|
- **`list_tool_groups()`** — the domain map (invoicing, expenses, fiscal/compliance, banking, CRM, HR/payroll, stay/PMS, POS, intelligence, products, platform) with a one-line blurb and tool count for each.
|
|
679
739
|
- **`search_tools(query)`** — free-text search across tool name, title, summary and group; returns matching tools with their group, summary, read-only flag and input fields. Optional `group` filter and `limit`.
|
|
@@ -695,7 +755,7 @@ In `grouped` mode the tools are unchanged — same names, same input schemas, sa
|
|
|
695
755
|
}
|
|
696
756
|
```
|
|
697
757
|
|
|
698
|
-
>
|
|
758
|
+
> Grouped exposure changes description density, not operation behavior. The versioned OpenAI-reviewed profile is composed separately and remains independently gated.
|
|
699
759
|
|
|
700
760
|
---
|
|
701
761
|
|
|
@@ -797,7 +857,7 @@ npm run build # must pass before submitting
|
|
|
797
857
|
|
|
798
858
|
| Package | What it is |
|
|
799
859
|
|---------|-----------|
|
|
800
|
-
| [`@frihet/mcp-server`](https://www.npmjs.com/package/@frihet/mcp-server) | This MCP server (157
|
|
860
|
+
| [`@frihet/mcp-server`](https://www.npmjs.com/package/@frihet/mcp-server) | This MCP server (157 canonical operations + 5 alias names; 11 local resources; 10 prompts) |
|
|
801
861
|
| [`@frihet/sdk`](https://github.com/Frihet-io/frihet-sdk) | TypeScript SDK (`frihet.invoices.create()`) |
|
|
802
862
|
| [`frihet`](https://www.npmjs.com/package/frihet) | CLI (`frihet invoices list --status overdue`) |
|
|
803
863
|
| [`n8n-nodes-frihet`](https://www.npmjs.com/package/n8n-nodes-frihet) | n8n community node for workflow automation |
|
|
@@ -816,6 +876,7 @@ npm run build # must pass before submitting
|
|
|
816
876
|
- [MCP Registry](https://registry.modelcontextprotocol.io/?q=io.frihet) -- Anthropic official registry
|
|
817
877
|
- [Remote endpoint](https://mcp.frihet.io) -- Hosted MCP server (Cloudflare Workers)
|
|
818
878
|
- [OpenAPI spec](https://api.frihet.io/openapi.json) -- Machine-readable API definition
|
|
879
|
+
- [Security policy](./SECURITY.md) -- Private vulnerability reporting guidance
|
|
819
880
|
|
|
820
881
|
---
|
|
821
882
|
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-native onboarding contract.
|
|
3
|
+
*
|
|
4
|
+
* Two artefacts, one source:
|
|
5
|
+
*
|
|
6
|
+
* 1. `AGENT_SERVER_INSTRUCTIONS` — the MCP `instructions` string returned by
|
|
7
|
+
* `initialize`. Every MCP client hands this to the model before the first
|
|
8
|
+
* tool call, so it is the only onboarding channel that costs the human
|
|
9
|
+
* zero configuration. It deliberately contains NO counts: a number in a
|
|
10
|
+
* per-session prompt is a number that drifts. It teaches the *mechanism*
|
|
11
|
+
* for finding the current numbers instead.
|
|
12
|
+
*
|
|
13
|
+
* 2. `captureAgentOnboardingDescriptor()` — the machine-readable descriptor
|
|
14
|
+
* written to `docs/agent-onboarding.json` by
|
|
15
|
+
* `scripts/generate-agent-onboarding.mjs`. It is captured through the real
|
|
16
|
+
* MCP SDK against the real registration path, so the tool lists in it
|
|
17
|
+
* cannot drift from the server: `npm run gate:agent-onboarding` re-captures
|
|
18
|
+
* and byte-compares.
|
|
19
|
+
*
|
|
20
|
+
* Nothing here registers a tool, a resource or a prompt. The surface counts the
|
|
21
|
+
* conformance baseline and the public capability contract already pin are
|
|
22
|
+
* unchanged by this module, on purpose.
|
|
23
|
+
*/
|
|
24
|
+
export declare const AGENT_ONBOARDING_CONTRACT_VERSION = 1;
|
|
25
|
+
/** Where an API key actually comes from — a deep link, not a navigation
|
|
26
|
+
* instruction, so the human has one click instead of a hunt.
|
|
27
|
+
*
|
|
28
|
+
* Verified against the ERP: `settingsSectionToPath` renders `/settings/<id>`
|
|
29
|
+
* and `api` is in `ALL_SETTINGS_SECTIONS`, so this URL resolves to the
|
|
30
|
+
* ApiKeysSettings panel. The old index.ts wording ("Settings > Developers >
|
|
31
|
+
* API Keys") pointed at a section that does not exist. */
|
|
32
|
+
export declare const API_KEY_LOCATION = "https://app.frihet.io/settings/api";
|
|
33
|
+
export declare const AGENT_SERVER_INSTRUCTIONS = "Frihet ERP \u2014 real business and fiscal records. Read freely, propose before you mutate.\n\nAUTH\n- Local (stdio): FRIHET_API_KEY=fri_... \u2014 create one at https://app.frihet.io/settings/api.\n- No key yet: FRIHET_DEMO=1 serves example fixtures. No network call, nothing persisted, demo_-prefixed IDs, fiscal actions simulated.\n- Hosted: https://mcp.frihet.io/mcp \u2014 OAuth 2.1 + PKCE, or Authorization: Bearer fri_...\n\nORIENT BEFORE ACTING (read-only)\n- get_business_context \u2014 fiscal zone, currency, IRPF/IVA defaults, plan limits. Call this first; the correct tax treatment depends on it.\n- Resources frihet://tax/rates and frihet://tax/calendar carry the Spanish rate table (IVA / IGIC / IPSI) and the filing deadlines. Read them instead of recalling a rate.\n\nCAPABILITY DISCOVERY\n- Every tool in tools/list carries _meta[\"io.frihet/capability\"] = {callability, writesFrihet, externalInteraction, externalSideEffects}. Read it before you call.\n- callability is a conservative fact, not a promise: api_dependent | runtime_checked | deferred | unavailable | local. Registration does NOT mean the backing API is enabled for this workspace; deferred and unavailable tools will refuse.\n- Large surface: set FRIHET_TOOL_MODE=grouped for progressive disclosure (list_tool_groups, search_tools, describe_tool) instead of loading every schema.\n\nTHE SAFE WORKFLOW \u2014 draft, show, stop\n- create_invoice defaults to status=draft: no fiscal number, no hash, nothing submitted to a tax authority. create_quote and create_credit_note are drafts too.\n- Build the draft, read it back, present it to the human, and stop there. Issuing is a separate, human-authorised step.\n\nHUMAN AUTHORITY \u2014 do not call unprompted\n- Any tool whose capability shows a non-empty externalSideEffects reaches outside Frihet \u2014 email to a client, webhook delivery, money movement, or a submission to AEAT / VeriFactu / TicketBAI / FACe. You cannot undo those.\n- Several also take confirm=true. That flag records the human's decision. Never set it to satisfy your own plan; ask, then pass what you were told.\n- delete_invoice does not always delete: an issued invoice is CANCELLED (status=cancelled) because VeriFactu forbids breaking the hash chain. Same for quotes.\n\nWHEN A CALL FAILS\n- Errors carry sanitized remediation text \u2014 read it, do not guess. 401 = key; 403 = scope or plan; 429 = wait Retry-After seconds.\n- After 409 IDEMPOTENCY_REQUEST_IN_PROGRESS, do NOT retry with a fresh Idempotency-Key. Read the resource back and decide from its actual state; a new key would create a second fiscal document.\n\nMachine-readable version of this contract: https://github.com/Frihet-io/frihet-mcp/blob/main/docs/agent-onboarding.json";
|
|
34
|
+
export interface AgentOnboardingDescriptor {
|
|
35
|
+
onboardingContractVersion: number;
|
|
36
|
+
server: {
|
|
37
|
+
mcpName: string;
|
|
38
|
+
npmPackage: string;
|
|
39
|
+
remoteEndpoint: string;
|
|
40
|
+
transport: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
auth: {
|
|
43
|
+
envVar: string;
|
|
44
|
+
keyFormat: string;
|
|
45
|
+
obtainAt: string;
|
|
46
|
+
remoteMethods: readonly string[];
|
|
47
|
+
noAuthTrial: {
|
|
48
|
+
env: Record<string, string>;
|
|
49
|
+
persists: false;
|
|
50
|
+
network: false;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
/** One verified command per client. Every `command` here was executed against
|
|
54
|
+
* the real binary in an isolated HOME before being written down. */
|
|
55
|
+
quickstart: Record<string, {
|
|
56
|
+
steps: readonly string[];
|
|
57
|
+
configFormat: string;
|
|
58
|
+
verify: string;
|
|
59
|
+
}>;
|
|
60
|
+
capabilityDiscovery: {
|
|
61
|
+
metaKey: string;
|
|
62
|
+
callabilityValues: readonly string[];
|
|
63
|
+
groupedModeEnv: Record<string, string>;
|
|
64
|
+
counts: {
|
|
65
|
+
toolNames: number;
|
|
66
|
+
readOnly: number;
|
|
67
|
+
writesFrihet: number;
|
|
68
|
+
externalInteraction: number;
|
|
69
|
+
destructive: number;
|
|
70
|
+
resources: number;
|
|
71
|
+
prompts: number;
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
safeWorkflow: {
|
|
75
|
+
description: string;
|
|
76
|
+
steps: readonly {
|
|
77
|
+
call: string;
|
|
78
|
+
kind: string;
|
|
79
|
+
note: string;
|
|
80
|
+
}[];
|
|
81
|
+
};
|
|
82
|
+
humanAuthority: {
|
|
83
|
+
rule: string;
|
|
84
|
+
confirmRequired: readonly string[];
|
|
85
|
+
externalSideEffects: readonly {
|
|
86
|
+
tool: string;
|
|
87
|
+
effects: readonly string[];
|
|
88
|
+
}[];
|
|
89
|
+
};
|
|
90
|
+
errors: readonly {
|
|
91
|
+
condition: string;
|
|
92
|
+
meaning: string;
|
|
93
|
+
agentAction: string;
|
|
94
|
+
}[];
|
|
95
|
+
}
|
|
96
|
+
export declare function captureAgentOnboardingDescriptor(): Promise<AgentOnboardingDescriptor>;
|
|
97
|
+
export declare function serializeAgentOnboardingDescriptor(descriptor: AgentOnboardingDescriptor): string;
|
|
98
|
+
//# sourceMappingURL=agent-onboarding.d.ts.map
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-native onboarding contract.
|
|
3
|
+
*
|
|
4
|
+
* Two artefacts, one source:
|
|
5
|
+
*
|
|
6
|
+
* 1. `AGENT_SERVER_INSTRUCTIONS` — the MCP `instructions` string returned by
|
|
7
|
+
* `initialize`. Every MCP client hands this to the model before the first
|
|
8
|
+
* tool call, so it is the only onboarding channel that costs the human
|
|
9
|
+
* zero configuration. It deliberately contains NO counts: a number in a
|
|
10
|
+
* per-session prompt is a number that drifts. It teaches the *mechanism*
|
|
11
|
+
* for finding the current numbers instead.
|
|
12
|
+
*
|
|
13
|
+
* 2. `captureAgentOnboardingDescriptor()` — the machine-readable descriptor
|
|
14
|
+
* written to `docs/agent-onboarding.json` by
|
|
15
|
+
* `scripts/generate-agent-onboarding.mjs`. It is captured through the real
|
|
16
|
+
* MCP SDK against the real registration path, so the tool lists in it
|
|
17
|
+
* cannot drift from the server: `npm run gate:agent-onboarding` re-captures
|
|
18
|
+
* and byte-compares.
|
|
19
|
+
*
|
|
20
|
+
* Nothing here registers a tool, a resource or a prompt. The surface counts the
|
|
21
|
+
* conformance baseline and the public capability contract already pin are
|
|
22
|
+
* unchanged by this module, on purpose.
|
|
23
|
+
*/
|
|
24
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
25
|
+
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
26
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
27
|
+
import { CAPABILITY_META_KEY } from "./capability-truth.js";
|
|
28
|
+
import { localMcpSurfaceComposition, registerMcpSurface, } from "./server-composition.js";
|
|
29
|
+
export const AGENT_ONBOARDING_CONTRACT_VERSION = 1;
|
|
30
|
+
/** Where an API key actually comes from — a deep link, not a navigation
|
|
31
|
+
* instruction, so the human has one click instead of a hunt.
|
|
32
|
+
*
|
|
33
|
+
* Verified against the ERP: `settingsSectionToPath` renders `/settings/<id>`
|
|
34
|
+
* and `api` is in `ALL_SETTINGS_SECTIONS`, so this URL resolves to the
|
|
35
|
+
* ApiKeysSettings panel. The old index.ts wording ("Settings > Developers >
|
|
36
|
+
* API Keys") pointed at a section that does not exist. */
|
|
37
|
+
export const API_KEY_LOCATION = "https://app.frihet.io/settings/api";
|
|
38
|
+
/* ------------------------------------------------------------------ */
|
|
39
|
+
/* 1. initialize.instructions */
|
|
40
|
+
/* ------------------------------------------------------------------ */
|
|
41
|
+
export const AGENT_SERVER_INSTRUCTIONS = `Frihet ERP — real business and fiscal records. Read freely, propose before you mutate.
|
|
42
|
+
|
|
43
|
+
AUTH
|
|
44
|
+
- Local (stdio): FRIHET_API_KEY=fri_... — create one at ${API_KEY_LOCATION}.
|
|
45
|
+
- No key yet: FRIHET_DEMO=1 serves example fixtures. No network call, nothing persisted, demo_-prefixed IDs, fiscal actions simulated.
|
|
46
|
+
- Hosted: https://mcp.frihet.io/mcp — OAuth 2.1 + PKCE, or Authorization: Bearer fri_...
|
|
47
|
+
|
|
48
|
+
ORIENT BEFORE ACTING (read-only)
|
|
49
|
+
- get_business_context — fiscal zone, currency, IRPF/IVA defaults, plan limits. Call this first; the correct tax treatment depends on it.
|
|
50
|
+
- Resources frihet://tax/rates and frihet://tax/calendar carry the Spanish rate table (IVA / IGIC / IPSI) and the filing deadlines. Read them instead of recalling a rate.
|
|
51
|
+
|
|
52
|
+
CAPABILITY DISCOVERY
|
|
53
|
+
- Every tool in tools/list carries _meta["${CAPABILITY_META_KEY}"] = {callability, writesFrihet, externalInteraction, externalSideEffects}. Read it before you call.
|
|
54
|
+
- callability is a conservative fact, not a promise: api_dependent | runtime_checked | deferred | unavailable | local. Registration does NOT mean the backing API is enabled for this workspace; deferred and unavailable tools will refuse.
|
|
55
|
+
- Large surface: set FRIHET_TOOL_MODE=grouped for progressive disclosure (list_tool_groups, search_tools, describe_tool) instead of loading every schema.
|
|
56
|
+
|
|
57
|
+
THE SAFE WORKFLOW — draft, show, stop
|
|
58
|
+
- create_invoice defaults to status=draft: no fiscal number, no hash, nothing submitted to a tax authority. create_quote and create_credit_note are drafts too.
|
|
59
|
+
- Build the draft, read it back, present it to the human, and stop there. Issuing is a separate, human-authorised step.
|
|
60
|
+
|
|
61
|
+
HUMAN AUTHORITY — do not call unprompted
|
|
62
|
+
- Any tool whose capability shows a non-empty externalSideEffects reaches outside Frihet — email to a client, webhook delivery, money movement, or a submission to AEAT / VeriFactu / TicketBAI / FACe. You cannot undo those.
|
|
63
|
+
- Several also take confirm=true. That flag records the human's decision. Never set it to satisfy your own plan; ask, then pass what you were told.
|
|
64
|
+
- delete_invoice does not always delete: an issued invoice is CANCELLED (status=cancelled) because VeriFactu forbids breaking the hash chain. Same for quotes.
|
|
65
|
+
|
|
66
|
+
WHEN A CALL FAILS
|
|
67
|
+
- Errors carry sanitized remediation text — read it, do not guess. 401 = key; 403 = scope or plan; 429 = wait Retry-After seconds.
|
|
68
|
+
- After 409 IDEMPOTENCY_REQUEST_IN_PROGRESS, do NOT retry with a fresh Idempotency-Key. Read the resource back and decide from its actual state; a new key would create a second fiscal document.
|
|
69
|
+
|
|
70
|
+
Machine-readable version of this contract: https://github.com/Frihet-io/frihet-mcp/blob/main/docs/agent-onboarding.json`;
|
|
71
|
+
function makeRegistrationClient() {
|
|
72
|
+
return new Proxy({}, { get: () => async () => ({ data: [], total: 0, limit: 0, offset: 0 }) });
|
|
73
|
+
}
|
|
74
|
+
/** True when the tool's JSON Schema declares a top-level `confirm` property.
|
|
75
|
+
* Derived, never hand-listed — a new confirm guard shows up in the descriptor
|
|
76
|
+
* on the next generate, and the gate fails until it is committed. */
|
|
77
|
+
function declaresConfirm(inputSchema) {
|
|
78
|
+
if (typeof inputSchema !== "object" || inputSchema === null)
|
|
79
|
+
return false;
|
|
80
|
+
const properties = inputSchema.properties;
|
|
81
|
+
if (typeof properties !== "object" || properties === null)
|
|
82
|
+
return false;
|
|
83
|
+
return Object.hasOwn(properties, "confirm");
|
|
84
|
+
}
|
|
85
|
+
async function captureLocalFullSurface() {
|
|
86
|
+
const server = new McpServer({
|
|
87
|
+
name: "agent-onboarding-capture",
|
|
88
|
+
version: "1.0.0",
|
|
89
|
+
});
|
|
90
|
+
registerMcpSurface(server, makeRegistrationClient(), localMcpSurfaceComposition(false, false));
|
|
91
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
|
|
92
|
+
const client = new Client({ name: "agent-onboarding-capture-client", version: "1.0.0" }, { capabilities: {} });
|
|
93
|
+
try {
|
|
94
|
+
await Promise.all([
|
|
95
|
+
server.connect(serverTransport),
|
|
96
|
+
client.connect(clientTransport),
|
|
97
|
+
]);
|
|
98
|
+
const listed = await client.listTools();
|
|
99
|
+
const tools = listed.tools
|
|
100
|
+
.map((tool) => {
|
|
101
|
+
const meta = tool._meta;
|
|
102
|
+
const capability = meta?.[CAPABILITY_META_KEY];
|
|
103
|
+
return {
|
|
104
|
+
name: tool.name,
|
|
105
|
+
annotations: (tool.annotations ?? {}),
|
|
106
|
+
...(capability ? { capability } : {}),
|
|
107
|
+
hasConfirm: declaresConfirm(tool.inputSchema),
|
|
108
|
+
};
|
|
109
|
+
})
|
|
110
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
111
|
+
const [resources, prompts] = await Promise.all([
|
|
112
|
+
client.listResources(),
|
|
113
|
+
client.listPrompts(),
|
|
114
|
+
]);
|
|
115
|
+
return {
|
|
116
|
+
tools,
|
|
117
|
+
resources: resources.resources.length,
|
|
118
|
+
prompts: prompts.prompts.length,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
await Promise.allSettled([client.close(), server.close()]);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export async function captureAgentOnboardingDescriptor() {
|
|
126
|
+
const { tools, resources, prompts } = await captureLocalFullSurface();
|
|
127
|
+
const confirmRequired = tools
|
|
128
|
+
.filter((tool) => tool.hasConfirm)
|
|
129
|
+
.map((tool) => tool.name);
|
|
130
|
+
const externalSideEffects = tools
|
|
131
|
+
.filter((tool) => (tool.capability?.externalSideEffects.length ?? 0) > 0)
|
|
132
|
+
.map((tool) => ({
|
|
133
|
+
tool: tool.name,
|
|
134
|
+
effects: [...(tool.capability?.externalSideEffects ?? [])],
|
|
135
|
+
}));
|
|
136
|
+
return {
|
|
137
|
+
onboardingContractVersion: AGENT_ONBOARDING_CONTRACT_VERSION,
|
|
138
|
+
server: {
|
|
139
|
+
mcpName: "io.frihet/erp",
|
|
140
|
+
npmPackage: "@frihet/mcp-server",
|
|
141
|
+
remoteEndpoint: "https://mcp.frihet.io/mcp",
|
|
142
|
+
transport: ["stdio", "streamable-http"],
|
|
143
|
+
},
|
|
144
|
+
auth: {
|
|
145
|
+
envVar: "FRIHET_API_KEY",
|
|
146
|
+
keyFormat: "fri_<key>",
|
|
147
|
+
obtainAt: API_KEY_LOCATION,
|
|
148
|
+
remoteMethods: ["oauth2.1-pkce", "bearer"],
|
|
149
|
+
noAuthTrial: {
|
|
150
|
+
env: { FRIHET_DEMO: "1" },
|
|
151
|
+
persists: false,
|
|
152
|
+
network: false,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
quickstart: {
|
|
156
|
+
"claude-code": {
|
|
157
|
+
steps: [
|
|
158
|
+
"claude mcp add frihet -s user -e FRIHET_API_KEY=fri_... -- npx -y @frihet/mcp-server",
|
|
159
|
+
],
|
|
160
|
+
configFormat: "managed by the CLI (user scope writes ~/.claude.json)",
|
|
161
|
+
verify: "claude mcp list",
|
|
162
|
+
},
|
|
163
|
+
codex: {
|
|
164
|
+
steps: [
|
|
165
|
+
"codex mcp add frihet --env FRIHET_API_KEY=fri_... -- npx -y @frihet/mcp-server",
|
|
166
|
+
],
|
|
167
|
+
configFormat: "toml (~/.codex/config.toml — JSON in this file is a parse error)",
|
|
168
|
+
verify: "codex mcp list",
|
|
169
|
+
},
|
|
170
|
+
"generic-mcp-client-stdio": {
|
|
171
|
+
steps: [
|
|
172
|
+
'mcpServers.frihet = { "command": "npx", "args": ["-y", "@frihet/mcp-server"], "env": { "FRIHET_API_KEY": "fri_..." } }',
|
|
173
|
+
],
|
|
174
|
+
configFormat: "json",
|
|
175
|
+
verify: "initialize → tools/list returns a non-empty catalogue",
|
|
176
|
+
},
|
|
177
|
+
"generic-mcp-client-http": {
|
|
178
|
+
steps: [
|
|
179
|
+
'mcpServers.frihet = { "type": "streamable-http", "url": "https://mcp.frihet.io/mcp", "headers": { "Authorization": "Bearer fri_..." } }',
|
|
180
|
+
],
|
|
181
|
+
configFormat: "json",
|
|
182
|
+
verify: "initialize → tools/list returns a non-empty catalogue",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
capabilityDiscovery: {
|
|
186
|
+
metaKey: CAPABILITY_META_KEY,
|
|
187
|
+
callabilityValues: [
|
|
188
|
+
"api_dependent",
|
|
189
|
+
"runtime_checked",
|
|
190
|
+
"deferred",
|
|
191
|
+
"unavailable",
|
|
192
|
+
"local",
|
|
193
|
+
],
|
|
194
|
+
groupedModeEnv: { FRIHET_TOOL_MODE: "grouped" },
|
|
195
|
+
counts: {
|
|
196
|
+
toolNames: tools.length,
|
|
197
|
+
readOnly: tools.filter((tool) => tool.annotations.readOnlyHint === true)
|
|
198
|
+
.length,
|
|
199
|
+
writesFrihet: tools.filter((tool) => tool.capability?.writesFrihet === true).length,
|
|
200
|
+
externalInteraction: externalSideEffects.length,
|
|
201
|
+
destructive: tools.filter((tool) => tool.annotations.destructiveHint === true).length,
|
|
202
|
+
resources,
|
|
203
|
+
prompts,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
safeWorkflow: {
|
|
207
|
+
description: "Discover → orient → retrieve → propose → hand back. No step mutates data or reaches a third party.",
|
|
208
|
+
steps: [
|
|
209
|
+
{
|
|
210
|
+
call: "get_business_context",
|
|
211
|
+
kind: "read",
|
|
212
|
+
note: "Fiscal zone, currency and defaults. Everything downstream depends on it.",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
call: "resources/read frihet://tax/rates",
|
|
216
|
+
kind: "read",
|
|
217
|
+
note: "Authoritative IVA / IGIC / IPSI table. Do not recall a rate from memory.",
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
call: "list_clients",
|
|
221
|
+
kind: "read",
|
|
222
|
+
note: "Resolve the client by name before referencing an id.",
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
call: "STOP",
|
|
226
|
+
kind: "handoff",
|
|
227
|
+
note: "Present the proposed draft inputs and all disclosed effects. Creating, issuing, sending, or submitting is a separate human decision.",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
humanAuthority: {
|
|
232
|
+
rule: "Do not call a tool with non-empty externalSideEffects, and do not set confirm=true, without an explicit human instruction for that specific action.",
|
|
233
|
+
confirmRequired,
|
|
234
|
+
externalSideEffects,
|
|
235
|
+
},
|
|
236
|
+
errors: [
|
|
237
|
+
{
|
|
238
|
+
condition: "startup exits 1 with code FRIHET_API_KEY_MISSING",
|
|
239
|
+
meaning: "No credential in the environment.",
|
|
240
|
+
agentAction: "Ask the human for a key from " +
|
|
241
|
+
API_KEY_LOCATION +
|
|
242
|
+
", or re-launch with FRIHET_DEMO=1 to explore without one.",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
condition: "HTTP 401",
|
|
246
|
+
meaning: "Key missing, malformed or revoked.",
|
|
247
|
+
agentAction: "Stop and ask for a new key. Do not retry.",
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
condition: "HTTP 403",
|
|
251
|
+
meaning: "The key is valid but lacks the scope, or the plan excludes it.",
|
|
252
|
+
agentAction: "Report which operation was refused. Do not try a different tool to route around it.",
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
condition: "HTTP 429",
|
|
256
|
+
meaning: "Rate limited.",
|
|
257
|
+
agentAction: "Wait the Retry-After seconds, then retry once.",
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
condition: "HTTP 409 IDEMPOTENCY_REQUEST_IN_PROGRESS",
|
|
261
|
+
meaning: "The first attempt's outcome could not be recorded; the server refuses to execute twice.",
|
|
262
|
+
agentAction: "Read the resource back and reconcile from its actual state. Never retry with a fresh Idempotency-Key.",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
condition: "capability.callability is deferred or unavailable",
|
|
266
|
+
meaning: "The tool is registered but the backing API is not serving it.",
|
|
267
|
+
agentAction: "Do not call it. Tell the human the capability is not live for this workspace.",
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
export function serializeAgentOnboardingDescriptor(descriptor) {
|
|
273
|
+
return JSON.stringify(descriptor, null, 2) + "\n";
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=agent-onboarding.js.map
|