honeybook-mcp 0.1.7 → 0.1.9

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.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "HoneyBook client-portal tools for Claude Code",
10
- "version": "0.1.7"
10
+ "version": "0.1.9"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "HoneyBook",
16
16
  "source": "./",
17
17
  "description": "HoneyBook client-portal MCP for Claude — view wedding-vendor contracts and invoices via MCP",
18
- "version": "0.1.7",
18
+ "version": "0.1.9",
19
19
  "author": {
20
20
  "name": "Chris Chall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "honeybook",
3
3
  "displayName": "HoneyBook",
4
- "version": "0.1.7",
4
+ "version": "0.1.9",
5
5
  "description": "HoneyBook client-portal MCP for Claude — view wedding-vendor contracts and invoices via MCP",
6
6
  "author": {
7
7
  "name": "Chris Chall",
package/dist/bundle.js CHANGED
@@ -21406,6 +21406,149 @@ function pruneWorkspaceFile(file) {
21406
21406
  }
21407
21407
  return pruned;
21408
21408
  }
21409
+ var WORKSPACE_FILE_SECTIONS = [
21410
+ "summary",
21411
+ "pricing",
21412
+ "agreement",
21413
+ "payments",
21414
+ "all",
21415
+ "raw"
21416
+ ];
21417
+ function getObj(o, key) {
21418
+ const v = o?.[key];
21419
+ return v && typeof v === "object" ? v : void 0;
21420
+ }
21421
+ function getArr(o, key) {
21422
+ const v = o?.[key];
21423
+ return Array.isArray(v) ? v : [];
21424
+ }
21425
+ function getStr(o, key) {
21426
+ const v = o?.[key];
21427
+ return typeof v === "string" ? v : void 0;
21428
+ }
21429
+ function getNum(o, key) {
21430
+ const v = o?.[key];
21431
+ return typeof v === "number" ? v : void 0;
21432
+ }
21433
+ function buildSummary(file) {
21434
+ const vp = getObj(file, "vendor_proposal") ?? {};
21435
+ const payments = getArr(getObj(file, "payments_container"), "payments");
21436
+ const paymentsTotal = payments.reduce((s, p) => s + (getNum(p, "amount") ?? 0), 0);
21437
+ const paymentsPaid = payments.filter((p) => p.is_paid === true).reduce((s, p) => s + (getNum(p, "amount") ?? 0), 0);
21438
+ const owner = getObj(file, "owner");
21439
+ const company = getObj(file, "company");
21440
+ const event = getObj(file, "event");
21441
+ const agreement = getObj(file, "agreement");
21442
+ const packages = getArr(vp, "vendor_packages").map((p) => ({
21443
+ title: getStr(p, "title"),
21444
+ description: (getStr(p, "description") ?? "").slice(0, 400),
21445
+ price: getNum(p, "total_price") ?? getNum(p, "sub_total"),
21446
+ quantity: getNum(p, "quantity")
21447
+ }));
21448
+ const services = getArr(vp, "service_items").map((s) => ({
21449
+ title: getStr(s, "title"),
21450
+ description: (getStr(s, "description") ?? "").slice(0, 400),
21451
+ price: getNum(s, "total_price") ?? getNum(s, "sub_total"),
21452
+ quantity: getNum(s, "quantity")
21453
+ }));
21454
+ return {
21455
+ _id: file._id,
21456
+ file_title: file.file_title,
21457
+ file_type: file.file_type,
21458
+ status: file.status,
21459
+ status_name: file.status_name,
21460
+ status_type: file.status_type,
21461
+ sent_on: file.sent_on,
21462
+ created_at: file.created_at,
21463
+ auto_expiration: file.auto_expiration,
21464
+ is_file_accepted: file.is_file_accepted,
21465
+ is_booked_version: file.is_booked_version,
21466
+ is_canceled: file.is_canceled,
21467
+ has_pending_payment: file.has_pending_payment,
21468
+ currency: file.currency,
21469
+ workspace: {
21470
+ name: getStr(file, "workspace_name"),
21471
+ status_type: getStr(file, "workspace_status_type"),
21472
+ status_name: getStr(file, "workspace_status_name"),
21473
+ active_state: getStr(file, "workspace_active_state")
21474
+ },
21475
+ vendor: {
21476
+ company_name: getStr(company, "company_name"),
21477
+ owner_name: [getStr(owner, "first_name"), getStr(owner, "last_name")].filter(Boolean).join(" "),
21478
+ owner_email: getStr(owner, "email"),
21479
+ owner_phone: getStr(owner, "phone_number")
21480
+ },
21481
+ event: event ? {
21482
+ _id: event._id,
21483
+ date: getStr(event, "event_date"),
21484
+ type: getStr(event, "type"),
21485
+ couple_names: getStr(event, "couple_names")
21486
+ } : null,
21487
+ pricing: {
21488
+ sub_total: getNum(vp, "sub_total"),
21489
+ total_price: getNum(vp, "total_price"),
21490
+ discount: getNum(vp, "discount") ? { value: getNum(vp, "discount"), type: getStr(vp, "discount_type") } : null,
21491
+ tax: getNum(vp, "tax") ? { value: getNum(vp, "tax"), type: getStr(vp, "tax_type") } : null,
21492
+ svc: getNum(vp, "svc") ? { value: getNum(vp, "svc"), type: getStr(vp, "svc_type") } : null,
21493
+ total_tax: getNum(vp, "total_tax"),
21494
+ total_svc: getNum(vp, "total_svc"),
21495
+ packages,
21496
+ services
21497
+ },
21498
+ payments: {
21499
+ total: paymentsTotal,
21500
+ paid: paymentsPaid,
21501
+ remaining: paymentsTotal - paymentsPaid,
21502
+ count: payments.length,
21503
+ schedule: payments.map((p) => ({
21504
+ due_date: getStr(p, "due_date"),
21505
+ amount: getNum(p, "amount"),
21506
+ count_description: getStr(p, "count_description"),
21507
+ invoice: getStr(p, "invoice"),
21508
+ is_paid: p.is_paid === true,
21509
+ is_pending: p.is_pending === true,
21510
+ is_milestone: p.is_milestone === true
21511
+ }))
21512
+ },
21513
+ agreement: agreement ? {
21514
+ present: true,
21515
+ signatures: getArr(agreement, "contract_signatures").map((s) => ({
21516
+ signed_by: getStr(s, "signer_email") ?? getStr(s, "signer_name"),
21517
+ signed_at: getStr(s, "signed_at") ?? getStr(s, "signature_date"),
21518
+ is_vendor: s.is_vendor === true
21519
+ })),
21520
+ html_length: (getStr(agreement, "html_body") ?? "").length
21521
+ } : { present: false },
21522
+ sections_available: WORKSPACE_FILE_SECTIONS,
21523
+ hint: 'Call get_workspace_file again with section="pricing" (full line items + tax/svc detail), "agreement" (contract HTML + signatures), "payments" (full payment-schedule detail), "all" (pruned full response), or "raw" (full unpruned response \u2014 may exceed MCP size limit).'
21524
+ };
21525
+ }
21526
+ function extractPricing(file) {
21527
+ return {
21528
+ _id: file._id,
21529
+ file_title: file.file_title,
21530
+ currency: file.currency,
21531
+ vendor_proposal: file.vendor_proposal
21532
+ };
21533
+ }
21534
+ function extractAgreement(file) {
21535
+ return {
21536
+ _id: file._id,
21537
+ file_title: file.file_title,
21538
+ agreement: file.agreement
21539
+ };
21540
+ }
21541
+ function extractPayments(file) {
21542
+ return {
21543
+ _id: file._id,
21544
+ file_title: file.file_title,
21545
+ currency: file.currency,
21546
+ payment_type: file.payment_type,
21547
+ has_milestone_payment: file.has_milestone_payment,
21548
+ has_pending_payment: file.has_pending_payment,
21549
+ payments_container: file.payments_container
21550
+ };
21551
+ }
21409
21552
  async function listWorkspaceFiles(args) {
21410
21553
  const client = await getActiveClient(args.origin);
21411
21554
  const res = await client.request(
@@ -21417,12 +21560,31 @@ async function listWorkspaceFiles(args) {
21417
21560
  return { content: [{ type: "text", text: prefix + JSON.stringify(filtered, null, 2) }] };
21418
21561
  }
21419
21562
  async function getWorkspaceFile(args) {
21563
+ const section = args.section ?? "summary";
21420
21564
  const client = await getActiveClient(args.origin);
21421
- const res = await client.request(
21422
- "GET",
21423
- `/api/v2/workspace_files/${args.file_id}`
21424
- );
21425
- const body = args.include_raw ? res : pruneWorkspaceFile(res);
21565
+ const raw = await client.request("GET", `/api/v2/workspace_files/${args.file_id}`);
21566
+ let body;
21567
+ switch (section) {
21568
+ case "raw":
21569
+ body = raw;
21570
+ break;
21571
+ case "all":
21572
+ body = pruneWorkspaceFile(raw);
21573
+ break;
21574
+ case "pricing":
21575
+ body = extractPricing(raw);
21576
+ break;
21577
+ case "agreement":
21578
+ body = extractAgreement(raw);
21579
+ break;
21580
+ case "payments":
21581
+ body = extractPayments(raw);
21582
+ break;
21583
+ case "summary":
21584
+ default:
21585
+ body = buildSummary(raw);
21586
+ break;
21587
+ }
21426
21588
  return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
21427
21589
  }
21428
21590
  function registerWorkspaceFileTools(server2) {
@@ -21443,14 +21605,14 @@ function registerWorkspaceFileTools(server2) {
21443
21605
  server2.registerTool(
21444
21606
  "get_workspace_file",
21445
21607
  {
21446
- description: "Get full detail for one workspace file by its _id. The vendor-side admin blob inside `company` (email templates, brochure templates, etc., which can be >1 MB) is stripped by default; pass include_raw:true to keep it.",
21608
+ description: 'Get detail for one workspace file. Returns a compact summary by default (metadata, vendor, event, pricing totals, payment schedule, agreement presence). Use `section` to drill into a specific part of the file: "pricing" for full line items + tax/svc detail, "agreement" for contract HTML + signatures, "payments" for full payment-schedule detail, "all" for the pruned full response, or "raw" for the entirely-unpruned response (may exceed MCP size limits on proposal-class files).',
21447
21609
  inputSchema: {
21448
21610
  file_id: external_exports.string().describe("The file _id from list_workspace_files."),
21449
21611
  origin: external_exports.string().optional().describe(
21450
21612
  "Portal origin (e.g. https://<vendor>.hbportal.co). Optional when only one session is active."
21451
21613
  ),
21452
- include_raw: external_exports.boolean().optional().describe(
21453
- "If true, return the full unpruned response. Default false \u2014 strips heavy vendor-side fields under `company`."
21614
+ section: external_exports.enum(WORKSPACE_FILE_SECTIONS).optional().describe(
21615
+ 'Which view to return. Default "summary" (~5-15 kB). Others return focused sections of the raw response.'
21454
21616
  )
21455
21617
  },
21456
21618
  annotations: { readOnlyHint: true }
@@ -21632,7 +21794,7 @@ try {
21632
21794
  }
21633
21795
  var server = new McpServer({
21634
21796
  name: "honeybook-mcp",
21635
- version: "0.1.7"
21797
+ version: "0.1.9"
21636
21798
  });
21637
21799
  registerSessionTools(server);
21638
21800
  registerWorkspaceFileTools(server);
package/dist/index.js CHANGED
@@ -18,7 +18,7 @@ import { registerContractTools } from './tools/contracts.js';
18
18
  import { registerInvoiceTools } from './tools/invoices.js';
19
19
  const server = new McpServer({
20
20
  name: 'honeybook-mcp',
21
- version: '0.1.7',
21
+ version: '0.1.9',
22
22
  });
23
23
  registerSessionTools(server);
24
24
  registerWorkspaceFileTools(server);
@@ -4,7 +4,7 @@ import { FILE_TYPES } from '../types.js';
4
4
  /**
5
5
  * Vendor-side sub-fields on `company` that a CLIENT never needs but that
6
6
  * balloon the response (observed: `vendor_emails` alone was ~1.3 MB on a
7
- * single real proposal). Stripped by default; pass include_raw=true to keep.
7
+ * single real proposal). Stripped by default; section='raw' keeps them.
8
8
  */
9
9
  const HEAVY_COMPANY_FIELDS = [
10
10
  'vendor_emails',
@@ -34,6 +34,164 @@ export function pruneWorkspaceFile(file) {
34
34
  }
35
35
  return pruned;
36
36
  }
37
+ export const WORKSPACE_FILE_SECTIONS = [
38
+ 'summary',
39
+ 'pricing',
40
+ 'agreement',
41
+ 'payments',
42
+ 'all',
43
+ 'raw',
44
+ ];
45
+ // Short helpers
46
+ function getObj(o, key) {
47
+ const v = o?.[key];
48
+ return v && typeof v === 'object' ? v : undefined;
49
+ }
50
+ function getArr(o, key) {
51
+ const v = o?.[key];
52
+ return Array.isArray(v) ? v : [];
53
+ }
54
+ function getStr(o, key) {
55
+ const v = o?.[key];
56
+ return typeof v === 'string' ? v : undefined;
57
+ }
58
+ function getNum(o, key) {
59
+ const v = o?.[key];
60
+ return typeof v === 'number' ? v : undefined;
61
+ }
62
+ /**
63
+ * Compact summary: ~5-15 kB even for proposal-class files. Contains everything
64
+ * a caller typically needs up front (identity, status, vendor, event, totals,
65
+ * payment schedule, package titles + pricing). For the contract text or the
66
+ * full vendor_proposal, call again with section='agreement' or 'pricing'.
67
+ */
68
+ export function buildSummary(file) {
69
+ const vp = getObj(file, 'vendor_proposal') ?? {};
70
+ const payments = getArr(getObj(file, 'payments_container'), 'payments');
71
+ const paymentsTotal = payments.reduce((s, p) => s + (getNum(p, 'amount') ?? 0), 0);
72
+ const paymentsPaid = payments
73
+ .filter((p) => p.is_paid === true)
74
+ .reduce((s, p) => s + (getNum(p, 'amount') ?? 0), 0);
75
+ const owner = getObj(file, 'owner');
76
+ const company = getObj(file, 'company');
77
+ const event = getObj(file, 'event');
78
+ const agreement = getObj(file, 'agreement');
79
+ const packages = getArr(vp, 'vendor_packages').map((p) => ({
80
+ title: getStr(p, 'title'),
81
+ description: (getStr(p, 'description') ?? '').slice(0, 400),
82
+ price: getNum(p, 'total_price') ?? getNum(p, 'sub_total'),
83
+ quantity: getNum(p, 'quantity'),
84
+ }));
85
+ const services = getArr(vp, 'service_items').map((s) => ({
86
+ title: getStr(s, 'title'),
87
+ description: (getStr(s, 'description') ?? '').slice(0, 400),
88
+ price: getNum(s, 'total_price') ?? getNum(s, 'sub_total'),
89
+ quantity: getNum(s, 'quantity'),
90
+ }));
91
+ return {
92
+ _id: file._id,
93
+ file_title: file.file_title,
94
+ file_type: file.file_type,
95
+ status: file.status,
96
+ status_name: file.status_name,
97
+ status_type: file.status_type,
98
+ sent_on: file.sent_on,
99
+ created_at: file.created_at,
100
+ auto_expiration: file.auto_expiration,
101
+ is_file_accepted: file.is_file_accepted,
102
+ is_booked_version: file.is_booked_version,
103
+ is_canceled: file.is_canceled,
104
+ has_pending_payment: file.has_pending_payment,
105
+ currency: file.currency,
106
+ workspace: {
107
+ name: getStr(file, 'workspace_name'),
108
+ status_type: getStr(file, 'workspace_status_type'),
109
+ status_name: getStr(file, 'workspace_status_name'),
110
+ active_state: getStr(file, 'workspace_active_state'),
111
+ },
112
+ vendor: {
113
+ company_name: getStr(company, 'company_name'),
114
+ owner_name: [getStr(owner, 'first_name'), getStr(owner, 'last_name')].filter(Boolean).join(' '),
115
+ owner_email: getStr(owner, 'email'),
116
+ owner_phone: getStr(owner, 'phone_number'),
117
+ },
118
+ event: event
119
+ ? {
120
+ _id: event._id,
121
+ date: getStr(event, 'event_date'),
122
+ type: getStr(event, 'type'),
123
+ couple_names: getStr(event, 'couple_names'),
124
+ }
125
+ : null,
126
+ pricing: {
127
+ sub_total: getNum(vp, 'sub_total'),
128
+ total_price: getNum(vp, 'total_price'),
129
+ discount: getNum(vp, 'discount')
130
+ ? { value: getNum(vp, 'discount'), type: getStr(vp, 'discount_type') }
131
+ : null,
132
+ tax: getNum(vp, 'tax') ? { value: getNum(vp, 'tax'), type: getStr(vp, 'tax_type') } : null,
133
+ svc: getNum(vp, 'svc') ? { value: getNum(vp, 'svc'), type: getStr(vp, 'svc_type') } : null,
134
+ total_tax: getNum(vp, 'total_tax'),
135
+ total_svc: getNum(vp, 'total_svc'),
136
+ packages,
137
+ services,
138
+ },
139
+ payments: {
140
+ total: paymentsTotal,
141
+ paid: paymentsPaid,
142
+ remaining: paymentsTotal - paymentsPaid,
143
+ count: payments.length,
144
+ schedule: payments.map((p) => ({
145
+ due_date: getStr(p, 'due_date'),
146
+ amount: getNum(p, 'amount'),
147
+ count_description: getStr(p, 'count_description'),
148
+ invoice: getStr(p, 'invoice'),
149
+ is_paid: p.is_paid === true,
150
+ is_pending: p.is_pending === true,
151
+ is_milestone: p.is_milestone === true,
152
+ })),
153
+ },
154
+ agreement: agreement
155
+ ? {
156
+ present: true,
157
+ signatures: getArr(agreement, 'contract_signatures').map((s) => ({
158
+ signed_by: getStr(s, 'signer_email') ?? getStr(s, 'signer_name'),
159
+ signed_at: getStr(s, 'signed_at') ?? getStr(s, 'signature_date'),
160
+ is_vendor: s.is_vendor === true,
161
+ })),
162
+ html_length: (getStr(agreement, 'html_body') ?? '').length,
163
+ }
164
+ : { present: false },
165
+ sections_available: WORKSPACE_FILE_SECTIONS,
166
+ hint: 'Call get_workspace_file again with section="pricing" (full line items + tax/svc detail), "agreement" (contract HTML + signatures), "payments" (full payment-schedule detail), "all" (pruned full response), or "raw" (full unpruned response — may exceed MCP size limit).',
167
+ };
168
+ }
169
+ function extractPricing(file) {
170
+ return {
171
+ _id: file._id,
172
+ file_title: file.file_title,
173
+ currency: file.currency,
174
+ vendor_proposal: file.vendor_proposal,
175
+ };
176
+ }
177
+ function extractAgreement(file) {
178
+ return {
179
+ _id: file._id,
180
+ file_title: file.file_title,
181
+ agreement: file.agreement,
182
+ };
183
+ }
184
+ function extractPayments(file) {
185
+ return {
186
+ _id: file._id,
187
+ file_title: file.file_title,
188
+ currency: file.currency,
189
+ payment_type: file.payment_type,
190
+ has_milestone_payment: file.has_milestone_payment,
191
+ has_pending_payment: file.has_pending_payment,
192
+ payments_container: file.payments_container,
193
+ };
194
+ }
37
195
  export async function listWorkspaceFiles(args) {
38
196
  const client = await getActiveClient(args.origin);
39
197
  const res = await client.request('GET', `/api/v2/users/${client.scope.userId}/workspace_files`);
@@ -46,9 +204,31 @@ export async function listWorkspaceFiles(args) {
46
204
  return { content: [{ type: 'text', text: prefix + JSON.stringify(filtered, null, 2) }] };
47
205
  }
48
206
  export async function getWorkspaceFile(args) {
207
+ const section = args.section ?? 'summary';
49
208
  const client = await getActiveClient(args.origin);
50
- const res = await client.request('GET', `/api/v2/workspace_files/${args.file_id}`);
51
- const body = args.include_raw ? res : pruneWorkspaceFile(res);
209
+ const raw = await client.request('GET', `/api/v2/workspace_files/${args.file_id}`);
210
+ let body;
211
+ switch (section) {
212
+ case 'raw':
213
+ body = raw;
214
+ break;
215
+ case 'all':
216
+ body = pruneWorkspaceFile(raw);
217
+ break;
218
+ case 'pricing':
219
+ body = extractPricing(raw);
220
+ break;
221
+ case 'agreement':
222
+ body = extractAgreement(raw);
223
+ break;
224
+ case 'payments':
225
+ body = extractPayments(raw);
226
+ break;
227
+ case 'summary':
228
+ default:
229
+ body = buildSummary(raw);
230
+ break;
231
+ }
52
232
  return { content: [{ type: 'text', text: JSON.stringify(body, null, 2) }] };
53
233
  }
54
234
  export function registerWorkspaceFileTools(server) {
@@ -67,17 +247,17 @@ export function registerWorkspaceFileTools(server) {
67
247
  annotations: { readOnlyHint: true },
68
248
  }, listWorkspaceFiles);
69
249
  server.registerTool('get_workspace_file', {
70
- description: 'Get full detail for one workspace file by its _id. The vendor-side admin blob inside `company` (email templates, brochure templates, etc., which can be >1 MB) is stripped by default; pass include_raw:true to keep it.',
250
+ description: 'Get detail for one workspace file. Returns a compact summary by default (metadata, vendor, event, pricing totals, payment schedule, agreement presence). Use `section` to drill into a specific part of the file: "pricing" for full line items + tax/svc detail, "agreement" for contract HTML + signatures, "payments" for full payment-schedule detail, "all" for the pruned full response, or "raw" for the entirely-unpruned response (may exceed MCP size limits on proposal-class files).',
71
251
  inputSchema: {
72
252
  file_id: z.string().describe('The file _id from list_workspace_files.'),
73
253
  origin: z
74
254
  .string()
75
255
  .optional()
76
256
  .describe('Portal origin (e.g. https://<vendor>.hbportal.co). Optional when only one session is active.'),
77
- include_raw: z
78
- .boolean()
257
+ section: z
258
+ .enum(WORKSPACE_FILE_SECTIONS)
79
259
  .optional()
80
- .describe('If true, return the full unpruned response. Default false — strips heavy vendor-side fields under `company`.'),
260
+ .describe('Which view to return. Default "summary" (~5-15 kB). Others return focused sections of the raw response.'),
81
261
  },
82
262
  annotations: { readOnlyHint: true },
83
263
  }, getWorkspaceFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "honeybook-mcp",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "mcpName": "io.github.chrischall/honeybook-mcp",
5
5
  "description": "HoneyBook client-portal MCP server for Claude",
6
6
  "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/honeybook-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.7",
9
+ "version": "0.1.9",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "honeybook-mcp",
14
- "version": "0.1.7",
14
+ "version": "0.1.9",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },