beeswax-mcp 1.5.0 → 1.6.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 +2 -1
- package/dist/tools.js +150 -0
- 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) |
|
package/dist/tools.js
CHANGED
|
@@ -441,6 +441,71 @@ const PAYMENT_TOOLS = [
|
|
|
441
441
|
handler: (client, args) => client.mutate("DELETE", `/payments/${args.id}`),
|
|
442
442
|
},
|
|
443
443
|
];
|
|
444
|
+
// Which tool groups a token's scopes leave unusable. `all` unlocks everything;
|
|
445
|
+
// a :write scope implies its :read. Surfaced by check_beeswax_connection so a
|
|
446
|
+
// "403 on list_transaction_accounts" is explained before it happens.
|
|
447
|
+
const SCOPE_TOOL_GROUPS = [
|
|
448
|
+
{ 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" },
|
|
449
|
+
{ scope: "transactions:read", tools: "list_*_transactions, list_journal_entry_transactions, list_account_transactions", why: "ledger postings" },
|
|
450
|
+
{ scope: "companies:read", tools: "list_companies, get_company", why: "resolving a client or supplier by name" },
|
|
451
|
+
{ scope: "projects:read", tools: "list_projects, get_project", why: "resolving a project by name" },
|
|
452
|
+
{ scope: "invoices:read", tools: "list_invoices, get_invoice, list_invoice_versions", why: "" },
|
|
453
|
+
{ scope: "expenses:read", tools: "list_expenses, get_expense, list_expense_versions", why: "" },
|
|
454
|
+
{ scope: "payments:read", tools: "query_payments, get_payment", why: "" },
|
|
455
|
+
{ scope: "journal_entries:read", tools: "list_manual_journals, get_manual_journal", why: "" },
|
|
456
|
+
];
|
|
457
|
+
function scopeGaps(scopes) {
|
|
458
|
+
if (scopes.includes("all"))
|
|
459
|
+
return [];
|
|
460
|
+
const has = (scope) => scopes.includes(scope) || scopes.includes(scope.replace(/:read$/, ":write"));
|
|
461
|
+
return SCOPE_TOOL_GROUPS.filter((g) => !has(g.scope)).map((g) => ({
|
|
462
|
+
missing_scope: g.scope,
|
|
463
|
+
blocks: g.tools,
|
|
464
|
+
...(g.why ? { why: g.why } : {}),
|
|
465
|
+
}));
|
|
466
|
+
}
|
|
467
|
+
// ── Attachments on documents ─────────────────────────────────────────────
|
|
468
|
+
// Files ride along inline as base64 — an MCP client has no other way to send
|
|
469
|
+
// bytes — so this is for receipts and signed documents, not bulk transfer.
|
|
470
|
+
function attachmentTools(resource) {
|
|
471
|
+
const singular = resource.slice(0, -1);
|
|
472
|
+
const idKey = `${singular}_id`;
|
|
473
|
+
const scope = `${resource}:write`;
|
|
474
|
+
return [
|
|
475
|
+
{
|
|
476
|
+
name: `list_${singular}_files`,
|
|
477
|
+
description: `List the files attached to a ${singular} (receipts, purchase orders, signed documents), newest first, with download URLs. Requires ${resource}:read.`,
|
|
478
|
+
inputSchema: { type: "object", properties: { [idKey]: { type: "integer" } }, required: [idKey] },
|
|
479
|
+
handler: (client, args) => client.getList(`/${resource}/${args[idKey]}/files`, "files"),
|
|
480
|
+
},
|
|
481
|
+
{
|
|
482
|
+
name: `attach_${singular}_file`,
|
|
483
|
+
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. ` +
|
|
484
|
+
`The file counts towards the account's storage quota. A voided ${singular} is refused. Requires ${scope}.`,
|
|
485
|
+
inputSchema: {
|
|
486
|
+
type: "object",
|
|
487
|
+
properties: {
|
|
488
|
+
[idKey]: { type: "integer" },
|
|
489
|
+
name: { type: "string", description: "File name with extension, e.g. receipt.pdf." },
|
|
490
|
+
content_type: { type: "string", description: "MIME type; detected from the content if omitted." },
|
|
491
|
+
base64: { type: "string", description: "The file's bytes, base64-encoded." },
|
|
492
|
+
description: { type: "string" },
|
|
493
|
+
},
|
|
494
|
+
required: [idKey, "name", "base64"],
|
|
495
|
+
},
|
|
496
|
+
handler: (client, args) => client.mutate("POST", `/${resource}/${args[idKey]}/files`, {
|
|
497
|
+
description: args.description,
|
|
498
|
+
file: { name: args.name, content_type: args.content_type, base64: args.base64 },
|
|
499
|
+
}),
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
name: `delete_${singular}_file`,
|
|
503
|
+
description: `Delete a file attached to a ${singular}. The uploader, or a manager of the document. Confirm with the user first. Requires ${scope}.`,
|
|
504
|
+
inputSchema: { type: "object", properties: { [idKey]: { type: "integer" }, file_id: { type: "integer" } }, required: [idKey, "file_id"] },
|
|
505
|
+
handler: (client, args) => client.mutate("DELETE", `/${resource}/${args[idKey]}/files/${args.file_id}`),
|
|
506
|
+
},
|
|
507
|
+
];
|
|
508
|
+
}
|
|
444
509
|
export const TOOLS = [
|
|
445
510
|
{
|
|
446
511
|
name: "check_beeswax_connection",
|
|
@@ -462,6 +527,7 @@ export const TOOLS = [
|
|
|
462
527
|
account: meta?.account,
|
|
463
528
|
user: meta?.user,
|
|
464
529
|
token_scopes: meta?.scopes,
|
|
530
|
+
scope_gaps: scopeGaps(meta?.scopes ?? []),
|
|
465
531
|
server_time: meta?.server_time,
|
|
466
532
|
api_base_url: client.origin,
|
|
467
533
|
};
|
|
@@ -474,6 +540,9 @@ export const TOOLS = [
|
|
|
474
540
|
...ledgerDocumentTools("invoices"),
|
|
475
541
|
...ledgerDocumentTools("expenses"),
|
|
476
542
|
...PAYMENT_TOOLS,
|
|
543
|
+
...attachmentTools("invoices"),
|
|
544
|
+
...attachmentTools("expenses"),
|
|
545
|
+
...attachmentTools("quotes"),
|
|
477
546
|
// Generic journal-entries reader for the long tail.
|
|
478
547
|
{
|
|
479
548
|
name: "query_journal_entries",
|
|
@@ -1342,6 +1411,87 @@ export const TOOLS = [
|
|
|
1342
1411
|
},
|
|
1343
1412
|
handler: (client, args) => client.fetchAll("/active_account/companies", "companies", { role: args.role, name: args.name }),
|
|
1344
1413
|
},
|
|
1414
|
+
{
|
|
1415
|
+
name: "get_company",
|
|
1416
|
+
description: "Get one client or supplier by id, with its contact people. Requires companies:read.",
|
|
1417
|
+
inputSchema: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
|
|
1418
|
+
handler: (client, args) => client.getOne(`/active_account/companies/${args.id}`, "company"),
|
|
1419
|
+
},
|
|
1420
|
+
{
|
|
1421
|
+
name: "create_company",
|
|
1422
|
+
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. " +
|
|
1423
|
+
"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. " +
|
|
1424
|
+
"Check list_companies first — names are unique per account and a duplicate is refused. Nothing here emails anyone. Requires companies:write.",
|
|
1425
|
+
inputSchema: {
|
|
1426
|
+
type: "object",
|
|
1427
|
+
properties: {
|
|
1428
|
+
name: { type: "string" },
|
|
1429
|
+
client: { type: "boolean", description: "You sell to them." },
|
|
1430
|
+
supplier: { type: "boolean", description: "You buy from them." },
|
|
1431
|
+
phone: { type: "string" },
|
|
1432
|
+
address: { type: "string" },
|
|
1433
|
+
city: { type: "string" },
|
|
1434
|
+
state: { type: "string" },
|
|
1435
|
+
postcode: { type: "string" },
|
|
1436
|
+
country: { type: "string" },
|
|
1437
|
+
web_address: { type: "string" },
|
|
1438
|
+
invoice_details: { type: "string", description: "Free text printed on documents: ABN / tax number, reference lines." },
|
|
1439
|
+
default_tax_id: { type: "integer", description: "See list_taxes." },
|
|
1440
|
+
people: {
|
|
1441
|
+
type: "array",
|
|
1442
|
+
items: {
|
|
1443
|
+
type: "object",
|
|
1444
|
+
properties: {
|
|
1445
|
+
first_name: { type: "string" }, last_name: { type: "string" }, email: { type: "string" },
|
|
1446
|
+
phone: { type: "string" }, mobile: { type: "string" },
|
|
1447
|
+
},
|
|
1448
|
+
required: ["first_name", "last_name", "email"],
|
|
1449
|
+
},
|
|
1450
|
+
},
|
|
1451
|
+
},
|
|
1452
|
+
required: ["name"],
|
|
1453
|
+
},
|
|
1454
|
+
handler: (client, args) => client.mutate("POST", "/active_account/companies", { company: args }),
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
name: "update_company",
|
|
1458
|
+
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. " +
|
|
1459
|
+
"A company must remain a client, a supplier, or both. The business's own company and system companies are refused. Requires companies:write.",
|
|
1460
|
+
inputSchema: {
|
|
1461
|
+
type: "object",
|
|
1462
|
+
properties: {
|
|
1463
|
+
id: { type: "integer" },
|
|
1464
|
+
name: { type: "string" },
|
|
1465
|
+
client: { type: "boolean" },
|
|
1466
|
+
supplier: { type: "boolean" },
|
|
1467
|
+
phone: { type: "string" },
|
|
1468
|
+
address: { type: "string" },
|
|
1469
|
+
city: { type: "string" },
|
|
1470
|
+
state: { type: "string" },
|
|
1471
|
+
postcode: { type: "string" },
|
|
1472
|
+
country: { type: "string" },
|
|
1473
|
+
web_address: { type: "string" },
|
|
1474
|
+
invoice_details: { type: "string" },
|
|
1475
|
+
default_tax_id: { type: ["integer", "null"] },
|
|
1476
|
+
people: {
|
|
1477
|
+
type: "array",
|
|
1478
|
+
items: {
|
|
1479
|
+
type: "object",
|
|
1480
|
+
properties: {
|
|
1481
|
+
first_name: { type: "string" }, last_name: { type: "string" }, email: { type: "string" },
|
|
1482
|
+
phone: { type: "string" }, mobile: { type: "string" },
|
|
1483
|
+
},
|
|
1484
|
+
required: ["first_name", "last_name", "email"],
|
|
1485
|
+
},
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
required: ["id"],
|
|
1489
|
+
},
|
|
1490
|
+
handler: (client, args) => {
|
|
1491
|
+
const { id, ...body } = args;
|
|
1492
|
+
return client.mutate("PATCH", `/active_account/companies/${id}`, { company: body });
|
|
1493
|
+
},
|
|
1494
|
+
},
|
|
1345
1495
|
// ── Quote authoring ──────────────────────────────────────────────────
|
|
1346
1496
|
// The boundary here is deliberate: create and edit, never send. There is no
|
|
1347
1497
|
// tool that emails a quote to a client or marks one accepted — those stay
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "beeswax-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|