beeswax-mcp 1.0.0 → 1.2.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.
Files changed (3) hide show
  1. package/README.md +8 -5
  2. package/dist/tools.js +161 -0
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,8 +7,9 @@ quotes, expenses, payments, journals, time entries, milestones, products & servi
7
7
  and tax returns.
8
8
 
9
9
  It is **read-mostly**: writes are limited to quotes, manual journals, tax-return
10
- fields and the products & services catalogue, and each write is gated behind its
11
- own token scope — everything else is strictly read-only.
10
+ fields, the products & services catalogue, freeform project documents and custom
11
+ document themes, and each write is gated behind its own token scope — everything
12
+ else is strictly read-only.
12
13
 
13
14
  **Nothing here sends anything to your clients.** The quote tools create and edit
14
15
  drafts; emailing a quote, and marking one accepted, stay human actions in the
@@ -59,9 +60,9 @@ account.
59
60
  `journal_entries:read`, `tax_returns:read`, `events:read`, `milestones:read`,
60
61
  `projects:read`, `time_entries:read`, `transaction_templates:read`,
61
62
  `transaction_accounts:read`, …) rather than `all` — `all` grants read **and**
62
- write. Add `quotes:write`, `journal_entries:write`, `tax_returns:write` or
63
- `transaction_templates:write` only if the agent should be able to use the
64
- matching write tools.
63
+ write. Add `quotes:write`, `journal_entries:write`, `tax_returns:write`,
64
+ `transaction_templates:write`, `project_documents:write` or `themes:write` only
65
+ if the agent should be able to use the matching write tools.
65
66
 
66
67
  To build quotes you need `quotes:write` plus the three lookups a quote is
67
68
  assembled from: `companies:read` (the client), `projects:read` (the project) and
@@ -107,6 +108,8 @@ reports the same on demand.
107
108
  | `list_transaction_accounts` | `/active_account/transaction_accounts` |
108
109
  | `list_companies` | `/active_account/companies` |
109
110
  | `list_task_files`, `list_task_file_versions` | `/tasks/:id/files` (+ per-file version history) |
111
+ | `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) |
112
+ | `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) |
110
113
  | `list_products_services`, `get_product_service`, `create_product_service` (write), `update_product_service` (write), `delete_product_service` (write) | `/transaction_templates` |
111
114
  | `list_manual_journals`, `get_manual_journal`, `create_manual_journal` (write), `finalise_manual_journal` (write) | `/raw_journal_entries` |
112
115
  | `list_tax_returns`, `get_tax_return`, `get_tax_return_field_transactions`, `list_tax_return_findings`, `update_tax_return_field` (write), `confirm_tax_return_section` (write) | `/tax_returns` |
package/dist/tools.js CHANGED
@@ -233,6 +233,167 @@ export const TOOLS = [
233
233
  },
234
234
  handler: (client, args) => client.getOne(`/tasks/${args.task_id}/files/${args.file_id}/versions`, ""),
235
235
  },
236
+ // ── Project documents (freeform markdown documents) ─────────────────────
237
+ {
238
+ name: "list_project_documents",
239
+ 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').",
240
+ inputSchema: {
241
+ type: "object",
242
+ properties: {
243
+ project_id: { type: "integer", description: "Only documents on this project" },
244
+ document_type: { type: "string", description: "e.g. 'freeform' for markdown documents" },
245
+ },
246
+ },
247
+ handler: (client, args) => client.fetchAll("/project_documents", "project_documents", {
248
+ project_id: args.project_id,
249
+ document_type: args.document_type,
250
+ }),
251
+ },
252
+ {
253
+ name: "get_project_document",
254
+ description: "A single project document including its full markdown `body` and editing_lock status. Check editing_lock before proposing edits — if someone has it open in the web editor, writes will be refused with a conflict until they finish.",
255
+ inputSchema: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
256
+ handler: (client, args) => client.getOne(`/project_documents/${args.id}`, "project_document"),
257
+ },
258
+ {
259
+ name: "create_project_document",
260
+ 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.",
261
+ inputSchema: {
262
+ type: "object",
263
+ properties: {
264
+ project_id: { type: "integer" },
265
+ name: { type: "string", description: "Document title (defaults to 'Untitled document')" },
266
+ body: { type: "string", description: "Markdown content" },
267
+ note: { type: "string", description: "Optional 'what changed' note for the first version" },
268
+ },
269
+ required: ["project_id"],
270
+ },
271
+ handler: (client, args) => client.mutate("POST", "/project_documents", {
272
+ project_document: { project_id: args.project_id, name: args.name, body: args.body, note: args.note },
273
+ }),
274
+ },
275
+ {
276
+ name: "update_project_document",
277
+ description: "Update a freeform document's markdown `body` and/or `name`. Only the fields you pass are changed; `body` REPLACES the whole content, so read the document first and send the complete edited markdown. Each successful update lands as exactly ONE new version in the history — pass `note` to label it. Refused while someone is editing in the web (409), and once the document is finalised or archived. Requires `project_documents:write`.",
278
+ inputSchema: {
279
+ type: "object",
280
+ properties: {
281
+ id: { type: "integer" },
282
+ name: { type: "string" },
283
+ body: { type: "string", description: "The complete replacement markdown" },
284
+ note: { type: "string", description: "Optional 'what changed' note for this version" },
285
+ },
286
+ required: ["id"],
287
+ },
288
+ handler: (client, args) => client.mutate("PATCH", `/project_documents/${args.id}`, {
289
+ project_document: { name: args.name, body: args.body, note: args.note },
290
+ }),
291
+ },
292
+ {
293
+ name: "list_project_document_versions",
294
+ description: "Version history of a project document, newest first. History is append-only — one version per web editing session (with the author's optional note), one per API update, plus 'Autosaved checkpoint' entries during long sessions and 'Restored from vN' entries for restores. Bodies are omitted here; read one with get_project_document_version.",
295
+ inputSchema: { type: "object", properties: { id: { type: "integer" } }, required: ["id"] },
296
+ handler: (client, args) => client.getOne(`/project_documents/${args.id}/versions`, ""),
297
+ },
298
+ {
299
+ name: "get_project_document_version",
300
+ description: "One version of a project document, addressed by its version_number (the 'v3' in the UI), including the full markdown body as it was at that point.",
301
+ inputSchema: {
302
+ type: "object",
303
+ properties: { id: { type: "integer" }, version_number: { type: "integer" } },
304
+ required: ["id", "version_number"],
305
+ },
306
+ handler: (client, args) => client.getOne(`/project_documents/${args.id}/versions/${args.version_number}`, "version"),
307
+ },
308
+ {
309
+ name: "restore_project_document_version",
310
+ description: "Restore a project document to an earlier version. Append-only: the restore itself becomes the next version ('Restored from vN'), so nothing is lost. Returns the document plus restored_version (null when the content already matched). Refused while someone is editing in the web. Requires `project_documents:write`.",
311
+ inputSchema: {
312
+ type: "object",
313
+ properties: { id: { type: "integer" }, version_number: { type: "integer" } },
314
+ required: ["id", "version_number"],
315
+ },
316
+ handler: (client, args) => client.mutate("POST", `/project_documents/${args.id}/versions/${args.version_number}/restore`, {}),
317
+ },
318
+ // ── Document themes (built-in + custom, THEME_SPEC v1 authoring) ────────
319
+ {
320
+ name: "list_themes",
321
+ description: "All document themes available to the account: the user's custom themes (with validation state) plus the built-in registry, and active_theme_key showing which one is live. Themes style every invoice, quote and freeform document the account sends.",
322
+ inputSchema: { type: "object", properties: {} },
323
+ handler: (client) => client.getOne("/themes", ""),
324
+ },
325
+ {
326
+ name: "get_theme_spec",
327
+ description: "THEME_SPEC v1 — the complete contract for authoring a custom theme (page structure, required CSS variables, every data-bx marker, the mirrored font list, forbidden constructs). Read this BEFORE writing theme HTML; validation enforces it exactly.",
328
+ inputSchema: { type: "object", properties: {} },
329
+ handler: (client) => client.getOne("/themes/spec", ""),
330
+ },
331
+ {
332
+ name: "get_theme",
333
+ description: "A single custom theme including its stored invoice_html / document_html layouts and current validation errors.",
334
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] },
335
+ handler: (client, args) => client.getOne(`/themes/${args.key}`, "theme"),
336
+ },
337
+ {
338
+ name: "upsert_theme",
339
+ description: "Create or update a custom theme. Pass `key` to update an existing theme (only the fields you pass change); omit it to create one (the key derives from the name). Layout HTML must follow THEME_SPEC v1 (get_theme_spec); validation runs on every save and failures come back as the theme's validation_errors list — fix and resend. Requires themes:write and an admin-level user.",
340
+ inputSchema: {
341
+ type: "object",
342
+ properties: {
343
+ key: { type: "string", description: "Existing theme key to update; omit to create" },
344
+ name: { type: "string" },
345
+ description: { type: "string" },
346
+ invoice_html: { type: "string", description: "Complete THEME_SPEC v1 invoice layout (required on create)" },
347
+ document_html: { type: "string", description: "Optional freeform-document layout (data-bx=\"document-body\")" },
348
+ accent_color: { type: "string", description: "#rrggbb default accent (the account's own accent overrides it)" },
349
+ font: { type: "string", description: "Primary font family, from the mirrored set in the spec" },
350
+ },
351
+ },
352
+ handler: (client, args) => {
353
+ const theme = {
354
+ name: args.name,
355
+ description: args.description,
356
+ invoice_html: args.invoice_html,
357
+ document_html: args.document_html,
358
+ accent_color: args.accent_color,
359
+ font: args.font,
360
+ };
361
+ return args.key
362
+ ? client.mutate("PATCH", `/themes/${args.key}`, { theme })
363
+ : client.mutate("POST", "/themes", { theme });
364
+ },
365
+ },
366
+ {
367
+ name: "validate_theme",
368
+ description: "Re-run THEME_SPEC validation on a stored theme. Returns { valid, validation_errors, state } — a theme becomes selectable once valid.",
369
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] },
370
+ handler: (client, args) => client.mutate("POST", `/themes/${args.key}/validate`, {}),
371
+ },
372
+ {
373
+ name: "preview_theme",
374
+ description: "Render a sample through the theme and return the full HTML: a synthetic invoice by default, or the sample markdown document with document: \"markdown\". Works on drafts that pass validation — this is the iteration loop before activating.",
375
+ inputSchema: {
376
+ type: "object",
377
+ properties: {
378
+ key: { type: "string" },
379
+ document: { type: "string", enum: ["invoice", "markdown"], description: "Which layout to preview (default invoice)" },
380
+ },
381
+ required: ["key"],
382
+ },
383
+ handler: (client, args) => client.getOne(`/themes/${args.key}/preview${args.document ? `?document=${args.document}` : ""}`, ""),
384
+ },
385
+ {
386
+ name: "activate_theme",
387
+ description: "Switch the account's active theme — every invoice, quote and freeform document restyles immediately. Accepts a custom theme's resolver key (\"custom/<slug>\", must be validated) or any built-in key from list_themes (e.g. \"swiss\"). Requires themes:write and an admin-level user.",
388
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] },
389
+ handler: (client, args) => client.mutate("POST", "/themes/activate", { key: args.key }),
390
+ },
391
+ {
392
+ name: "delete_theme",
393
+ description: "Disable a custom theme (soft delete — it leaves the picker; an account still pointing at it falls back to the default theme). Requires themes:write and an admin-level user.",
394
+ inputSchema: { type: "object", properties: { key: { type: "string" } }, required: ["key"] },
395
+ handler: (client, args) => client.mutate("DELETE", `/themes/${args.key}`),
396
+ },
236
397
  // ── Products & services catalogue (transaction templates) ──────────────
237
398
  {
238
399
  name: "list_products_services",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beeswax-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.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",