beeswax-mcp 1.5.0 → 1.7.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 +8 -1
- package/dist/client.js +12 -7
- package/dist/tools.js +203 -33
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -134,6 +134,7 @@ reports the same on demand.
|
|
|
134
134
|
| `list_expenses`, `get_expense`, `list_expense_transactions` | `/expenses` |
|
|
135
135
|
| `create_expense`, `update_expense`, `finalise_expense`, `void_expense`, `delete_expense`, `list_expense_groups`, `add_expense_group`, `update_expense_group`, `delete_expense_group`, `add_expense_line`, `update_expense_line`, `delete_expense_line`, `list_expense_versions`, `get_expense_version`, `restore_expense_version` (writes as for invoices) | `/expenses` … |
|
|
136
136
|
| `query_payments`, `get_payment`, `list_payment_transactions` | `/payments` |
|
|
137
|
+
| `list_invoice_files`, `attach_invoice_file` (write), `delete_invoice_file` (write), and the same for expenses and quotes | `/invoices/:id/files` … — attachments; the file rides inline as base64 (25 MB cap) |
|
|
137
138
|
| `apply_payment` (write), `remove_payment` (write) | `/payments` — one payment per allocated document, one money side per call, all-or-nothing; no update |
|
|
138
139
|
| `query_journal_entries`, `get_journal_entry`, `list_journal_entry_transactions` | `/journal_entries` |
|
|
139
140
|
| `list_time_entries`, `get_time_entry` | `/time_entries` |
|
|
@@ -143,7 +144,7 @@ reports the same on demand.
|
|
|
143
144
|
| `list_transaction_accounts` | `/active_account/transaction_accounts` |
|
|
144
145
|
| `list_account_transactions` | `/active_account/transaction_accounts/:id/transactions?from=&to=` — one account's posted ledger for a period with opening/running/closing balances (the reconciliation query) |
|
|
145
146
|
| `list_taxes` | `/active_account/taxes` |
|
|
146
|
-
| `list_companies` | `/active_account/companies` |
|
|
147
|
+
| `list_companies`, `get_company`, `create_company` (write), `update_company` (write) | `/active_account/companies` — clients and suppliers with their contact people; a company must be a client, a supplier or both |
|
|
147
148
|
| `list_task_files`, `list_task_file_versions` | `/tasks/:id/files` (+ per-file version history) |
|
|
148
149
|
| `list_project_documents`, `get_project_document`, `create_project_document` (write), `update_project_document` (write), `list_project_document_versions`, `get_project_document_version`, `restore_project_document_version` (write) | `/project_documents` (+ per-document version history) |
|
|
149
150
|
| `list_themes`, `get_theme_spec`, `get_theme`, `upsert_theme` (write), `validate_theme` (write), `preview_theme`, `activate_theme` (write), `delete_theme` (write) | `/themes` (custom document themes, THEME_SPEC v1) |
|
|
@@ -153,6 +154,12 @@ reports the same on demand.
|
|
|
153
154
|
|
|
154
155
|
### Notes
|
|
155
156
|
|
|
157
|
+
- The create tools and `apply_payment` accept `idempotency_key`. Reuse it on a
|
|
158
|
+
retry and the API replays the original result instead of posting twice.
|
|
159
|
+
- Every `list_*` tool accepts `updated_since` (ISO 8601) so an agent can ask
|
|
160
|
+
for "what changed since yesterday" instead of re-reading everything; rows
|
|
161
|
+
carry `updated_at`.
|
|
162
|
+
|
|
156
163
|
- **Scopes.** `query_journal_entries` / `get_journal_entry` need an `all`-scoped
|
|
157
164
|
token for a bare listing, a show, or a long-tail type (credits, bank_transfers,
|
|
158
165
|
payrolls); a typed listing (`type: "invoices"`) accepts that type's read scope.
|
package/dist/client.js
CHANGED
|
@@ -124,18 +124,23 @@ export class BeeswaxClient {
|
|
|
124
124
|
}
|
|
125
125
|
// Write request (POST / PATCH / DELETE) with a JSON body. Returns the parsed
|
|
126
126
|
// response body, or throws with the API's error message on a non-2xx.
|
|
127
|
-
async mutate(method, path, body = {}) {
|
|
127
|
+
async mutate(method, path, body = {}, opts = {}) {
|
|
128
128
|
const url = new URL(this.baseUrl + path);
|
|
129
|
+
const headers = {
|
|
130
|
+
Authorization: `Bearer ${this.token}`,
|
|
131
|
+
Accept: "application/json",
|
|
132
|
+
"Content-Type": "application/json",
|
|
133
|
+
"User-Agent": USER_AGENT,
|
|
134
|
+
};
|
|
135
|
+
// Idempotency-Key: the API replays the original response for a retried
|
|
136
|
+
// write instead of posting twice (see the API tokens help).
|
|
137
|
+
if (opts.idempotencyKey)
|
|
138
|
+
headers["Idempotency-Key"] = String(opts.idempotencyKey).slice(0, 255);
|
|
129
139
|
let res;
|
|
130
140
|
try {
|
|
131
141
|
res = await fetch(url, {
|
|
132
142
|
method,
|
|
133
|
-
headers
|
|
134
|
-
Authorization: `Bearer ${this.token}`,
|
|
135
|
-
Accept: "application/json",
|
|
136
|
-
"Content-Type": "application/json",
|
|
137
|
-
"User-Agent": USER_AGENT,
|
|
138
|
-
},
|
|
143
|
+
headers,
|
|
139
144
|
body: JSON.stringify(body),
|
|
140
145
|
});
|
|
141
146
|
}
|
package/dist/tools.js
CHANGED
|
@@ -38,7 +38,26 @@ const DATE_PROPS = {
|
|
|
38
38
|
},
|
|
39
39
|
to: { type: "string", description: "Latest document date (sent_on). Same format as `from`." },
|
|
40
40
|
};
|
|
41
|
+
// Optional on every create/apply tool: reuse the same key when retrying a call
|
|
42
|
+
// that may have gone through (timeout, unclear error) and the API returns the
|
|
43
|
+
// original result instead of creating a second record.
|
|
44
|
+
const IDEMPOTENCY_PROP = {
|
|
45
|
+
idempotency_key: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "Optional. A unique string (e.g. a UUID) for this operation. If you retry the same call after a timeout or an unclear error, pass the SAME key and Beeswax returns the original result instead of creating a duplicate. Use a new key for a genuinely new record.",
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
// Incremental sync: every listing accepts updated_since (ISO 8601) and the
|
|
51
|
+
// API echoes meta.filter.updated_since + meta.server_time.
|
|
52
|
+
const SYNC_PROPS = {
|
|
53
|
+
updated_since: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Incremental sync: only rows updated on or after this ISO 8601 instant (e.g. 2026-09-05T00:00:00Z). " +
|
|
56
|
+
"Line and contact-person edits touch their parent, so a changed line surfaces the document.",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
41
59
|
const ACCOUNTING_LIST_PROPS = {
|
|
60
|
+
...SYNC_PROPS,
|
|
42
61
|
state: { type: "string", description: "Filter by state (draft, finalised, pdf_sent, partial_paid, paid, …)." },
|
|
43
62
|
project_id: { type: "integer", description: "Filter to a single project." },
|
|
44
63
|
company_id: { type: "integer", description: "Filter to a single client/supplier company by id." },
|
|
@@ -98,6 +117,7 @@ function accountingTools(resource, singular, label, listName, opts = {}) {
|
|
|
98
117
|
(payable ? " Pass overdue:true to get just the overdue ones (with due dates) in a single call." : ""),
|
|
99
118
|
inputSchema: { type: "object", properties: { ...listProps, ...POSTED_PROPS } },
|
|
100
119
|
handler: (client, args) => client.fetchAll(`/${resource}`, resource, {
|
|
120
|
+
updated_since: args.updated_since,
|
|
101
121
|
posted: args.posted,
|
|
102
122
|
include_drafts: args.include_drafts,
|
|
103
123
|
state: args.state,
|
|
@@ -182,7 +202,7 @@ function ledgerDocumentTools(resource) {
|
|
|
182
202
|
`Requires ${scope}.`,
|
|
183
203
|
inputSchema: {
|
|
184
204
|
type: "object",
|
|
185
|
-
properties: {
|
|
205
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
186
206
|
project_id: { type: "integer", description: "Project the document belongs to. Required." },
|
|
187
207
|
...partyProp,
|
|
188
208
|
title: { type: "string" },
|
|
@@ -230,7 +250,7 @@ function ledgerDocumentTools(resource) {
|
|
|
230
250
|
default_tax_id: args.default_tax_id,
|
|
231
251
|
groups: (args.groups ?? []).map((g) => ({ ...g, due_on: normalizeDate(g.due_on) })),
|
|
232
252
|
},
|
|
233
|
-
}),
|
|
253
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
234
254
|
},
|
|
235
255
|
{
|
|
236
256
|
name: `update_${singular}`,
|
|
@@ -411,7 +431,7 @@ const PAYMENT_TOOLS = [
|
|
|
411
431
|
"There is no edit: remove a wrong payment (remove_payment) and apply it again. Confirm amounts with the user first. Requires payments:write.",
|
|
412
432
|
inputSchema: {
|
|
413
433
|
type: "object",
|
|
414
|
-
properties: {
|
|
434
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
415
435
|
bank_account_id: { type: "integer", description: "A transaction account flagged as a bank account." },
|
|
416
436
|
paid_on: { type: "string", description: "Payment date, e.g. '2026-09-04'." },
|
|
417
437
|
allocations: {
|
|
@@ -431,7 +451,7 @@ const PAYMENT_TOOLS = [
|
|
|
431
451
|
},
|
|
432
452
|
handler: (client, args) => client.mutate("POST", "/payments", {
|
|
433
453
|
payment: { bank_account_id: args.bank_account_id, paid_on: normalizeDate(args.paid_on), allocations: args.allocations },
|
|
434
|
-
}),
|
|
454
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
435
455
|
},
|
|
436
456
|
{
|
|
437
457
|
name: "remove_payment",
|
|
@@ -441,6 +461,71 @@ const PAYMENT_TOOLS = [
|
|
|
441
461
|
handler: (client, args) => client.mutate("DELETE", `/payments/${args.id}`),
|
|
442
462
|
},
|
|
443
463
|
];
|
|
464
|
+
// Which tool groups a token's scopes leave unusable. `all` unlocks everything;
|
|
465
|
+
// a :write scope implies its :read. Surfaced by check_beeswax_connection so a
|
|
466
|
+
// "403 on list_transaction_accounts" is explained before it happens.
|
|
467
|
+
const SCOPE_TOOL_GROUPS = [
|
|
468
|
+
{ scope: "transaction_accounts:read", tools: "list_transaction_accounts, list_taxes, list_account_transactions", why: "chart of accounts, tax codes and the per-account ledger — needed for any reconciliation and for pricing document lines" },
|
|
469
|
+
{ scope: "transactions:read", tools: "list_*_transactions, list_journal_entry_transactions, list_account_transactions", why: "ledger postings" },
|
|
470
|
+
{ scope: "companies:read", tools: "list_companies, get_company", why: "resolving a client or supplier by name" },
|
|
471
|
+
{ scope: "projects:read", tools: "list_projects, get_project", why: "resolving a project by name" },
|
|
472
|
+
{ scope: "invoices:read", tools: "list_invoices, get_invoice, list_invoice_versions", why: "" },
|
|
473
|
+
{ scope: "expenses:read", tools: "list_expenses, get_expense, list_expense_versions", why: "" },
|
|
474
|
+
{ scope: "payments:read", tools: "query_payments, get_payment", why: "" },
|
|
475
|
+
{ scope: "journal_entries:read", tools: "list_manual_journals, get_manual_journal", why: "" },
|
|
476
|
+
];
|
|
477
|
+
function scopeGaps(scopes) {
|
|
478
|
+
if (scopes.includes("all"))
|
|
479
|
+
return [];
|
|
480
|
+
const has = (scope) => scopes.includes(scope) || scopes.includes(scope.replace(/:read$/, ":write"));
|
|
481
|
+
return SCOPE_TOOL_GROUPS.filter((g) => !has(g.scope)).map((g) => ({
|
|
482
|
+
missing_scope: g.scope,
|
|
483
|
+
blocks: g.tools,
|
|
484
|
+
...(g.why ? { why: g.why } : {}),
|
|
485
|
+
}));
|
|
486
|
+
}
|
|
487
|
+
// ── Attachments on documents ─────────────────────────────────────────────
|
|
488
|
+
// Files ride along inline as base64 — an MCP client has no other way to send
|
|
489
|
+
// bytes — so this is for receipts and signed documents, not bulk transfer.
|
|
490
|
+
function attachmentTools(resource) {
|
|
491
|
+
const singular = resource.slice(0, -1);
|
|
492
|
+
const idKey = `${singular}_id`;
|
|
493
|
+
const scope = `${resource}:write`;
|
|
494
|
+
return [
|
|
495
|
+
{
|
|
496
|
+
name: `list_${singular}_files`,
|
|
497
|
+
description: `List the files attached to a ${singular} (receipts, purchase orders, signed documents), newest first, with download URLs. Requires ${resource}:read.`,
|
|
498
|
+
inputSchema: { type: "object", properties: { [idKey]: { type: "integer" } }, required: [idKey] },
|
|
499
|
+
handler: (client, args) => client.getList(`/${resource}/${args[idKey]}/files`, "files"),
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: `attach_${singular}_file`,
|
|
503
|
+
description: `Attach a file to a ${singular}. Send the file inline as base64 (name, content_type, base64) — keep it to receipts and documents, not large media; the API caps this at 25 MB. ` +
|
|
504
|
+
`The file counts towards the account's storage quota. A voided ${singular} is refused. Requires ${scope}.`,
|
|
505
|
+
inputSchema: {
|
|
506
|
+
type: "object",
|
|
507
|
+
properties: {
|
|
508
|
+
[idKey]: { type: "integer" },
|
|
509
|
+
name: { type: "string", description: "File name with extension, e.g. receipt.pdf." },
|
|
510
|
+
content_type: { type: "string", description: "MIME type; detected from the content if omitted." },
|
|
511
|
+
base64: { type: "string", description: "The file's bytes, base64-encoded." },
|
|
512
|
+
description: { type: "string" },
|
|
513
|
+
},
|
|
514
|
+
required: [idKey, "name", "base64"],
|
|
515
|
+
},
|
|
516
|
+
handler: (client, args) => client.mutate("POST", `/${resource}/${args[idKey]}/files`, {
|
|
517
|
+
description: args.description,
|
|
518
|
+
file: { name: args.name, content_type: args.content_type, base64: args.base64 },
|
|
519
|
+
}),
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
name: `delete_${singular}_file`,
|
|
523
|
+
description: `Delete a file attached to a ${singular}. The uploader, or a manager of the document. Confirm with the user first. Requires ${scope}.`,
|
|
524
|
+
inputSchema: { type: "object", properties: { [idKey]: { type: "integer" }, file_id: { type: "integer" } }, required: [idKey, "file_id"] },
|
|
525
|
+
handler: (client, args) => client.mutate("DELETE", `/${resource}/${args[idKey]}/files/${args.file_id}`),
|
|
526
|
+
},
|
|
527
|
+
];
|
|
528
|
+
}
|
|
444
529
|
export const TOOLS = [
|
|
445
530
|
{
|
|
446
531
|
name: "check_beeswax_connection",
|
|
@@ -462,6 +547,7 @@ export const TOOLS = [
|
|
|
462
547
|
account: meta?.account,
|
|
463
548
|
user: meta?.user,
|
|
464
549
|
token_scopes: meta?.scopes,
|
|
550
|
+
scope_gaps: scopeGaps(meta?.scopes ?? []),
|
|
465
551
|
server_time: meta?.server_time,
|
|
466
552
|
api_base_url: client.origin,
|
|
467
553
|
};
|
|
@@ -474,6 +560,9 @@ export const TOOLS = [
|
|
|
474
560
|
...ledgerDocumentTools("invoices"),
|
|
475
561
|
...ledgerDocumentTools("expenses"),
|
|
476
562
|
...PAYMENT_TOOLS,
|
|
563
|
+
...attachmentTools("invoices"),
|
|
564
|
+
...attachmentTools("expenses"),
|
|
565
|
+
...attachmentTools("quotes"),
|
|
477
566
|
// Generic journal-entries reader for the long tail.
|
|
478
567
|
{
|
|
479
568
|
name: "query_journal_entries",
|
|
@@ -484,13 +573,13 @@ export const TOOLS = [
|
|
|
484
573
|
"Long-tail types (credits, bank_transfers, payrolls) and bare listings require an `all`-scoped token.",
|
|
485
574
|
inputSchema: {
|
|
486
575
|
type: "object",
|
|
487
|
-
properties: {
|
|
576
|
+
properties: { ...SYNC_PROPS,
|
|
488
577
|
type: { type: "string", description: "STI type filter, e.g. invoices, payments, payrolls, credits, bank_transfers." },
|
|
489
578
|
state: { type: "string", description: "Filter by state (switches the posted default off)." },
|
|
490
579
|
...POSTED_PROPS,
|
|
491
580
|
},
|
|
492
581
|
},
|
|
493
|
-
handler: (client, args) => client.fetchAll("/journal_entries", "journal_entries", {
|
|
582
|
+
handler: (client, args) => client.fetchAll("/journal_entries", "journal_entries", { updated_since: args.updated_since,
|
|
494
583
|
type: args.type, state: args.state, posted: args.posted, include_drafts: args.include_drafts,
|
|
495
584
|
}),
|
|
496
585
|
},
|
|
@@ -514,12 +603,12 @@ export const TOOLS = [
|
|
|
514
603
|
description: "List all time entries for the configured account (every page).",
|
|
515
604
|
inputSchema: {
|
|
516
605
|
type: "object",
|
|
517
|
-
properties: {
|
|
606
|
+
properties: { ...SYNC_PROPS,
|
|
518
607
|
project_id: { type: "integer", description: "Filter to a single project." },
|
|
519
608
|
billable: { type: "boolean", description: "Filter by billable flag." },
|
|
520
609
|
},
|
|
521
610
|
},
|
|
522
|
-
handler: (client, args) => client.fetchAll("/time_entries", "time_entries", { project_id: args.project_id, billable: args.billable }),
|
|
611
|
+
handler: (client, args) => client.fetchAll("/time_entries", "time_entries", { updated_since: args.updated_since, project_id: args.project_id, billable: args.billable }),
|
|
523
612
|
},
|
|
524
613
|
{
|
|
525
614
|
name: "get_time_entry",
|
|
@@ -532,9 +621,9 @@ export const TOOLS = [
|
|
|
532
621
|
description: "List calendar events visible to the token's user (private events the user is not invited to are excluded).",
|
|
533
622
|
inputSchema: {
|
|
534
623
|
type: "object",
|
|
535
|
-
properties: { project_id: { type: "integer", description: "Filter to a single project." }, ...DATE_PROPS },
|
|
624
|
+
properties: { ...SYNC_PROPS, project_id: { type: "integer", description: "Filter to a single project." }, ...DATE_PROPS },
|
|
536
625
|
},
|
|
537
|
-
handler: (client, args) => client.fetchAll("/events", "events", {
|
|
626
|
+
handler: (client, args) => client.fetchAll("/events", "events", { updated_since: args.updated_since,
|
|
538
627
|
project_id: args.project_id,
|
|
539
628
|
from: normalizeDate(args.from),
|
|
540
629
|
to: normalizeDate(args.to),
|
|
@@ -551,12 +640,12 @@ export const TOOLS = [
|
|
|
551
640
|
description: "List all milestones (project TaskLists) for the configured account.",
|
|
552
641
|
inputSchema: {
|
|
553
642
|
type: "object",
|
|
554
|
-
properties: {
|
|
643
|
+
properties: { ...SYNC_PROPS,
|
|
555
644
|
project_id: { type: "integer", description: "Filter to a single project." },
|
|
556
645
|
complete: { type: "boolean", description: "Filter by completion." },
|
|
557
646
|
},
|
|
558
647
|
},
|
|
559
|
-
handler: (client, args) => client.fetchAll("/milestones", "milestones", { project_id: args.project_id, complete: args.complete }),
|
|
648
|
+
handler: (client, args) => client.fetchAll("/milestones", "milestones", { updated_since: args.updated_since, project_id: args.project_id, complete: args.complete }),
|
|
560
649
|
},
|
|
561
650
|
{
|
|
562
651
|
name: "get_milestone",
|
|
@@ -567,8 +656,8 @@ export const TOOLS = [
|
|
|
567
656
|
{
|
|
568
657
|
name: "list_projects",
|
|
569
658
|
description: "List the account's active projects. Use this to resolve a project name to the project_id that create_quote and the other document tools need.",
|
|
570
|
-
inputSchema: { type: "object", properties: {} },
|
|
571
|
-
handler: (client) => client.getList("/active_account/projects", "projects"),
|
|
659
|
+
inputSchema: { type: "object", properties: { ...SYNC_PROPS } },
|
|
660
|
+
handler: (client, args) => client.getList("/active_account/projects", "projects", { updated_since: args.updated_since }),
|
|
572
661
|
},
|
|
573
662
|
{
|
|
574
663
|
name: "get_project",
|
|
@@ -599,12 +688,12 @@ export const TOOLS = [
|
|
|
599
688
|
description: "List project documents. Freeform documents are markdown written in Beeswax's document editor — proposals, briefs, meeting notes — styled by the account's invoice theme; other document_types are generated from library templates and are read-only via this API. Each row carries state (draft/published/finalized/archived), current_version_number, and editing_lock (who has it open in the web editor right now). Filter with project_id and/or document_type (e.g. 'freeform').",
|
|
600
689
|
inputSchema: {
|
|
601
690
|
type: "object",
|
|
602
|
-
properties: {
|
|
691
|
+
properties: { ...SYNC_PROPS,
|
|
603
692
|
project_id: { type: "integer", description: "Only documents on this project" },
|
|
604
693
|
document_type: { type: "string", description: "e.g. 'freeform' for markdown documents" },
|
|
605
694
|
},
|
|
606
695
|
},
|
|
607
|
-
handler: (client, args) => client.fetchAll("/project_documents", "project_documents", {
|
|
696
|
+
handler: (client, args) => client.fetchAll("/project_documents", "project_documents", { updated_since: args.updated_since,
|
|
608
697
|
project_id: args.project_id,
|
|
609
698
|
document_type: args.document_type,
|
|
610
699
|
}),
|
|
@@ -620,7 +709,7 @@ export const TOOLS = [
|
|
|
620
709
|
description: "Create a freeform (markdown) document on a project. `body` is markdown (headings, lists, tables, blockquotes all render through the account's invoice theme). The optional `note` becomes the first version's 'what changed' line. Requires a token with the `project_documents:write` scope.",
|
|
621
710
|
inputSchema: {
|
|
622
711
|
type: "object",
|
|
623
|
-
properties: {
|
|
712
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
624
713
|
project_id: { type: "integer" },
|
|
625
714
|
name: { type: "string", description: "Document title (defaults to 'Untitled document')" },
|
|
626
715
|
body: { type: "string", description: "Markdown content" },
|
|
@@ -630,7 +719,7 @@ export const TOOLS = [
|
|
|
630
719
|
},
|
|
631
720
|
handler: (client, args) => client.mutate("POST", "/project_documents", {
|
|
632
721
|
project_document: { project_id: args.project_id, name: args.name, body: args.body, note: args.note },
|
|
633
|
-
}),
|
|
722
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
634
723
|
},
|
|
635
724
|
{
|
|
636
725
|
name: "update_project_document",
|
|
@@ -760,7 +849,7 @@ export const TOOLS = [
|
|
|
760
849
|
description: "List the account's products & services catalogue (stored as 'transaction templates'): everything the business sells or buys as a line item — products and services/activities with their unit, sell/buy prices, linked income/expense accounts and taxes. Use this whenever the user asks about products, services, price lists, rates or catalogue items.",
|
|
761
850
|
inputSchema: {
|
|
762
851
|
type: "object",
|
|
763
|
-
properties: {
|
|
852
|
+
properties: { ...SYNC_PROPS,
|
|
764
853
|
kind: {
|
|
765
854
|
type: "string",
|
|
766
855
|
enum: ["sell", "buy", "buy_and_sell", "product", "service"],
|
|
@@ -780,7 +869,7 @@ export const TOOLS = [
|
|
|
780
869
|
},
|
|
781
870
|
},
|
|
782
871
|
},
|
|
783
|
-
handler: (client, args) => client.fetchAll("/transaction_templates", "transaction_templates", {
|
|
872
|
+
handler: (client, args) => client.fetchAll("/transaction_templates", "transaction_templates", { updated_since: args.updated_since,
|
|
784
873
|
kind: args.kind,
|
|
785
874
|
side: args.side,
|
|
786
875
|
active: args.active,
|
|
@@ -807,7 +896,7 @@ export const TOOLS = [
|
|
|
807
896
|
"Titles must be unique within the account, and prices default to 0 if you omit them, so pass them explicitly. Requires a token with transaction_templates:write.",
|
|
808
897
|
inputSchema: {
|
|
809
898
|
type: "object",
|
|
810
|
-
properties: {
|
|
899
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
811
900
|
title: { type: "string", description: "Catalogue name, e.g. 'Shaker Door — Painted'. Must be unique in the account." },
|
|
812
901
|
kind: {
|
|
813
902
|
type: "string",
|
|
@@ -861,7 +950,7 @@ export const TOOLS = [
|
|
|
861
950
|
physical_resource: args.physical_resource,
|
|
862
951
|
gantt_color: args.gantt_color,
|
|
863
952
|
},
|
|
864
|
-
}),
|
|
953
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
865
954
|
},
|
|
866
955
|
{
|
|
867
956
|
name: "update_product_service",
|
|
@@ -1197,12 +1286,12 @@ export const TOOLS = [
|
|
|
1197
1286
|
"Requires journal_entries:read (write implies read).",
|
|
1198
1287
|
inputSchema: {
|
|
1199
1288
|
type: "object",
|
|
1200
|
-
properties: {
|
|
1289
|
+
properties: { ...SYNC_PROPS,
|
|
1201
1290
|
state: { type: "string", enum: ["draft", "finalised"], description: "Optional state filter (switches the posted default off)." },
|
|
1202
1291
|
...POSTED_PROPS,
|
|
1203
1292
|
},
|
|
1204
1293
|
},
|
|
1205
|
-
handler: (client, args) => client.fetchAll("/raw_journal_entries", "raw_journal_entries", {
|
|
1294
|
+
handler: (client, args) => client.fetchAll("/raw_journal_entries", "raw_journal_entries", { updated_since: args.updated_since,
|
|
1206
1295
|
state: args.state, posted: args.posted, include_drafts: args.include_drafts,
|
|
1207
1296
|
}),
|
|
1208
1297
|
},
|
|
@@ -1224,9 +1313,9 @@ export const TOOLS = [
|
|
|
1224
1313
|
"Requires a token with the journal_entries:write scope.",
|
|
1225
1314
|
inputSchema: {
|
|
1226
1315
|
type: "object",
|
|
1227
|
-
properties: {
|
|
1316
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
1228
1317
|
date: { type: "string", description: "Entry date, e.g. '2024-04-01'." },
|
|
1229
|
-
narration: { type: "string", description: "What the journal is for, e.g. 'BMW write-off'." },
|
|
1318
|
+
narration: { type: "string", maxLength: 255, description: "What the journal is for, e.g. 'BMW write-off'. At most 255 characters — put detail in the line descriptions." },
|
|
1230
1319
|
status: { type: "string", enum: ["finalised", "draft"], description: "Optional. Default 'finalised' posts to the ledger immediately; 'draft' stages it for review." },
|
|
1231
1320
|
lines: {
|
|
1232
1321
|
type: "array",
|
|
@@ -1252,7 +1341,7 @@ export const TOOLS = [
|
|
|
1252
1341
|
status: args.status,
|
|
1253
1342
|
lines: args.lines,
|
|
1254
1343
|
},
|
|
1255
|
-
}),
|
|
1344
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
1256
1345
|
},
|
|
1257
1346
|
{
|
|
1258
1347
|
name: "finalise_manual_journal",
|
|
@@ -1275,7 +1364,7 @@ export const TOOLS = [
|
|
|
1275
1364
|
"Requires a token with transaction_accounts:read.",
|
|
1276
1365
|
inputSchema: {
|
|
1277
1366
|
type: "object",
|
|
1278
|
-
properties: {
|
|
1367
|
+
properties: { ...SYNC_PROPS,
|
|
1279
1368
|
account_type: {
|
|
1280
1369
|
type: "string",
|
|
1281
1370
|
enum: ["income", "expense", "asset", "loan", "equity", "transfer"],
|
|
@@ -1285,7 +1374,7 @@ export const TOOLS = [
|
|
|
1285
1374
|
bank: { type: "boolean", description: "true returns only bank accounts." },
|
|
1286
1375
|
},
|
|
1287
1376
|
},
|
|
1288
|
-
handler: (client, args) => client.getList("/active_account/transaction_accounts", "transaction_accounts", {
|
|
1377
|
+
handler: (client, args) => client.getList("/active_account/transaction_accounts", "transaction_accounts", { updated_since: args.updated_since,
|
|
1289
1378
|
account_type: args.account_type,
|
|
1290
1379
|
active: args.active,
|
|
1291
1380
|
bank: args.bank,
|
|
@@ -1335,12 +1424,93 @@ export const TOOLS = [
|
|
|
1335
1424
|
"Fetches every page. Requires a token with companies:read.",
|
|
1336
1425
|
inputSchema: {
|
|
1337
1426
|
type: "object",
|
|
1338
|
-
properties: {
|
|
1427
|
+
properties: { ...SYNC_PROPS,
|
|
1339
1428
|
role: { type: "string", enum: ["client", "supplier"], description: "Filter to one role. A quote is addressed to a client." },
|
|
1340
1429
|
name: { type: "string", description: "Case-insensitive substring match on the company name." },
|
|
1341
1430
|
},
|
|
1342
1431
|
},
|
|
1343
|
-
handler: (client, args) => client.fetchAll("/active_account/companies", "companies", { role: args.role, name: args.name }),
|
|
1432
|
+
handler: (client, args) => client.fetchAll("/active_account/companies", "companies", { updated_since: args.updated_since, role: args.role, name: args.name }),
|
|
1433
|
+
},
|
|
1434
|
+
{
|
|
1435
|
+
name: "get_company",
|
|
1436
|
+
description: "Get one client or supplier by id, with its contact people. Requires companies:read.",
|
|
1437
|
+
inputSchema: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
|
|
1438
|
+
handler: (client, args) => client.getOne(`/active_account/companies/${args.id}`, "company"),
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
name: "create_company",
|
|
1442
|
+
description: "Create a client or supplier (a company / contact) so an invoice or quote can be addressed to it without a trip to the web app. " +
|
|
1443
|
+
"It must be a client (you sell to them), a supplier (you buy from them), or both. `people` are optional; the first becomes the contact documents are addressed to. " +
|
|
1444
|
+
"Check list_companies first — names are unique per account and a duplicate is refused. Nothing here emails anyone. Requires companies:write.",
|
|
1445
|
+
inputSchema: {
|
|
1446
|
+
type: "object",
|
|
1447
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
1448
|
+
name: { type: "string" },
|
|
1449
|
+
client: { type: "boolean", description: "You sell to them." },
|
|
1450
|
+
supplier: { type: "boolean", description: "You buy from them." },
|
|
1451
|
+
phone: { type: "string" },
|
|
1452
|
+
address: { type: "string" },
|
|
1453
|
+
city: { type: "string" },
|
|
1454
|
+
state: { type: "string" },
|
|
1455
|
+
postcode: { type: "string" },
|
|
1456
|
+
country: { type: "string" },
|
|
1457
|
+
web_address: { type: "string" },
|
|
1458
|
+
invoice_details: { type: "string", description: "Free text printed on documents: ABN / tax number, reference lines." },
|
|
1459
|
+
default_tax_id: { type: "integer", description: "See list_taxes." },
|
|
1460
|
+
people: {
|
|
1461
|
+
type: "array",
|
|
1462
|
+
items: {
|
|
1463
|
+
type: "object",
|
|
1464
|
+
properties: {
|
|
1465
|
+
first_name: { type: "string" }, last_name: { type: "string" }, email: { type: "string" },
|
|
1466
|
+
phone: { type: "string" }, mobile: { type: "string" },
|
|
1467
|
+
},
|
|
1468
|
+
required: ["first_name", "last_name", "email"],
|
|
1469
|
+
},
|
|
1470
|
+
},
|
|
1471
|
+
},
|
|
1472
|
+
required: ["name"],
|
|
1473
|
+
},
|
|
1474
|
+
handler: (client, args) => client.mutate("POST", "/active_account/companies", { company: args }, { idempotencyKey: args.idempotency_key }),
|
|
1475
|
+
},
|
|
1476
|
+
{
|
|
1477
|
+
name: "update_company",
|
|
1478
|
+
description: "Update a client or supplier. Only the fields you pass change. `people` APPENDS contacts (an existing email is left alone); removing a person stays a web-app action. " +
|
|
1479
|
+
"A company must remain a client, a supplier, or both. The business's own company and system companies are refused. Requires companies:write.",
|
|
1480
|
+
inputSchema: {
|
|
1481
|
+
type: "object",
|
|
1482
|
+
properties: {
|
|
1483
|
+
id: { type: "integer" },
|
|
1484
|
+
name: { type: "string" },
|
|
1485
|
+
client: { type: "boolean" },
|
|
1486
|
+
supplier: { type: "boolean" },
|
|
1487
|
+
phone: { type: "string" },
|
|
1488
|
+
address: { type: "string" },
|
|
1489
|
+
city: { type: "string" },
|
|
1490
|
+
state: { type: "string" },
|
|
1491
|
+
postcode: { type: "string" },
|
|
1492
|
+
country: { type: "string" },
|
|
1493
|
+
web_address: { type: "string" },
|
|
1494
|
+
invoice_details: { type: "string" },
|
|
1495
|
+
default_tax_id: { type: ["integer", "null"] },
|
|
1496
|
+
people: {
|
|
1497
|
+
type: "array",
|
|
1498
|
+
items: {
|
|
1499
|
+
type: "object",
|
|
1500
|
+
properties: {
|
|
1501
|
+
first_name: { type: "string" }, last_name: { type: "string" }, email: { type: "string" },
|
|
1502
|
+
phone: { type: "string" }, mobile: { type: "string" },
|
|
1503
|
+
},
|
|
1504
|
+
required: ["first_name", "last_name", "email"],
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
},
|
|
1508
|
+
required: ["id"],
|
|
1509
|
+
},
|
|
1510
|
+
handler: (client, args) => {
|
|
1511
|
+
const { id, ...body } = args;
|
|
1512
|
+
return client.mutate("PATCH", `/active_account/companies/${id}`, { company: body });
|
|
1513
|
+
},
|
|
1344
1514
|
},
|
|
1345
1515
|
// ── Quote authoring ──────────────────────────────────────────────────
|
|
1346
1516
|
// The boundary here is deliberate: create and edit, never send. There is no
|
|
@@ -1358,7 +1528,7 @@ export const TOOLS = [
|
|
|
1358
1528
|
"Requires a token with the quotes:write scope.",
|
|
1359
1529
|
inputSchema: {
|
|
1360
1530
|
type: "object",
|
|
1361
|
-
properties: {
|
|
1531
|
+
properties: { ...IDEMPOTENCY_PROP,
|
|
1362
1532
|
project_id: { type: "integer", description: "Project the quote belongs to. Required." },
|
|
1363
1533
|
company_id: { type: "integer", description: "Client the quote is addressed to. Required — see list_companies." },
|
|
1364
1534
|
title: { type: "string", description: "Quote title, e.g. 'Cambridge Market Admin App Website'." },
|
|
@@ -1424,7 +1594,7 @@ export const TOOLS = [
|
|
|
1424
1594
|
start_on: normalizeDate(group.start_on),
|
|
1425
1595
|
})),
|
|
1426
1596
|
},
|
|
1427
|
-
}),
|
|
1597
|
+
}, { idempotencyKey: args.idempotency_key }),
|
|
1428
1598
|
},
|
|
1429
1599
|
{
|
|
1430
1600
|
name: "update_quote",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeswax-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Official MCP server for Beeswax (beeswaxapp.com) — query invoices, quotes, expenses, payments, journals, time entries, projects, products & services and tax returns from Claude and other MCP clients, and build quotes priced from your catalogue.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://www.beeswaxapp.com/support/mcp-setup",
|