@trylighthouse/mcp-server 0.1.0 → 0.1.2

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 (2) hide show
  1. package/build/index.js +418 -35
  2. package/package.json +1 -1
package/build/index.js CHANGED
@@ -126,13 +126,11 @@ server.registerTool("lighthouse_crm_get_attribute", {
126
126
  description: "Get a single custom field definition by its field_permalink.",
127
127
  inputSchema: {
128
128
  field_permalink: z.string().describe("Field permalink (unique key) of the attribute"),
129
- entity_type: z.enum(["company", "person", "deal"]).optional().describe("Record type context"),
129
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type (required)"),
130
130
  },
131
131
  }, async (params) => {
132
132
  try {
133
- const res = await api.get(`/attributes/${params.field_permalink}`, {
134
- entity_type: params.entity_type,
135
- });
133
+ const res = await api.get(`/attributes/${params.entity_type}/${params.field_permalink}`);
136
134
  return text(res);
137
135
  }
138
136
  catch (e) {
@@ -142,15 +140,16 @@ server.registerTool("lighthouse_crm_get_attribute", {
142
140
  server.registerTool("lighthouse_crm_create_attribute", {
143
141
  title: "Create CRM Attribute",
144
142
  description: "Create a new custom field in your CRM workspace. " +
145
- "Common types: short_text, long_text, number, currency, date, select, multi_select, boolean, url, email, phone, rating.",
143
+ "Types: text, number, currency, percentage, date, datetimez, select, multi_select, status, checkbox, url, email, phone, rating, record, multi_record, user, multi_user.",
146
144
  inputSchema: {
147
- name: z.string().describe("Display name for the field"),
148
- entity_type: z.enum(["company", "person", "deal"]).describe("Record type this field belongs to"),
149
- data_type: z.string().describe("Field type (short_text, long_text, number, currency, date, select, multi_select, boolean, url, email, phone, rating)"),
145
+ label: z.string().describe("Display label for the field"),
146
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type this field belongs to"),
147
+ data_type: z.string().describe("Field type (text, number, currency, percentage, date, datetimez, select, multi_select, status, checkbox, url, email, phone, rating, record, multi_record, user, multi_user)"),
148
+ record_type: z.enum(["company", "person", "deal", "task"]).optional().describe("For record/multi_record fields: which record type to link to"),
150
149
  options: z
151
150
  .array(z.record(z.unknown()))
152
151
  .optional()
153
- .describe("For select/multi_select fields: array of {value, label} option objects"),
152
+ .describe("For select/multi_select/status fields: array of {value, label} option objects"),
154
153
  },
155
154
  }, async (params) => {
156
155
  try {
@@ -163,16 +162,47 @@ server.registerTool("lighthouse_crm_create_attribute", {
163
162
  });
164
163
  server.registerTool("lighthouse_crm_update_attribute", {
165
164
  title: "Update CRM Attribute",
166
- description: "Update a custom field's name or configuration.",
165
+ description: "Update a custom field's label or configuration.",
167
166
  inputSchema: {
168
167
  field_permalink: z.string().describe("Field permalink of the attribute to update"),
169
- entity_type: z.enum(["company", "person", "deal"]).optional().describe("Record type context"),
170
- name: z.string().optional().describe("New display name"),
168
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type (required)"),
169
+ label: z.string().optional().describe("New display label"),
171
170
  },
172
171
  }, async (params) => {
173
172
  try {
174
- const { field_permalink, ...body } = params;
175
- const res = await api.patch(`/attributes/${field_permalink}`, body);
173
+ const { field_permalink, entity_type, ...body } = params;
174
+ const res = await api.patch(`/attributes/${entity_type}/${field_permalink}`, body);
175
+ return text(res);
176
+ }
177
+ catch (e) {
178
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
179
+ }
180
+ });
181
+ server.registerTool("lighthouse_crm_delete_attribute", {
182
+ title: "Delete CRM Attribute",
183
+ description: "Delete (archive) a custom field. Only custom fields can be deleted, not system fields.",
184
+ inputSchema: {
185
+ field_permalink: z.string().describe("Field permalink of the attribute to delete"),
186
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type (required)"),
187
+ },
188
+ }, async (params) => {
189
+ try {
190
+ const res = await api.delete(`/attributes/${params.entity_type}/${params.field_permalink}`);
191
+ return text(res);
192
+ }
193
+ catch (e) {
194
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
195
+ }
196
+ });
197
+ server.registerTool("lighthouse_crm_get_color_palette", {
198
+ title: "Get Color Palette",
199
+ description: "Get the available color palette for select/multi_select/status field options. " +
200
+ "Returns an array of {hex_code, label} objects. " +
201
+ "Use this to discover valid hex color codes when creating or updating select options.",
202
+ inputSchema: {},
203
+ }, async () => {
204
+ try {
205
+ const res = await api.get("/attributes/colors");
176
206
  return text(res);
177
207
  }
178
208
  catch (e) {
@@ -185,12 +215,12 @@ server.registerTool("lighthouse_crm_list_options", {
185
215
  description: "Get select/multi_select field options. " +
186
216
  "Use this to discover available values for select fields before setting them on records.",
187
217
  inputSchema: {
188
- field_permalink: z.string().optional().describe("Filter by field permalink"),
189
- entity_type: z.enum(["company", "person", "deal"]).optional().describe("Filter by record type"),
218
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
219
+ field_permalink: z.string().describe("Field permalink of the select/multi_select field"),
190
220
  },
191
221
  }, async (params) => {
192
222
  try {
193
- const res = await api.get("/options", params);
223
+ const res = await api.get(`/options/${params.entity_type}/${params.field_permalink}`);
194
224
  return text(res);
195
225
  }
196
226
  catch (e) {
@@ -199,17 +229,34 @@ server.registerTool("lighthouse_crm_list_options", {
199
229
  });
200
230
  server.registerTool("lighthouse_crm_create_option", {
201
231
  title: "Create CRM Field Option",
202
- description: "Add a new option value to a select/multi_select field.",
232
+ description: "Add a new option to a select/multi_select field. The option value is auto-generated from the label. Returns the updated list of options for the field.",
233
+ inputSchema: {
234
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
235
+ field_permalink: z.string().describe("Field permalink of the select/multi_select field"),
236
+ label: z.string().describe("Option display label"),
237
+ color: z.string().optional().describe("Option color (hex code from palette, auto-assigned if omitted)"),
238
+ },
239
+ }, async (params) => {
240
+ try {
241
+ const { entity_type, field_permalink, ...body } = params;
242
+ const res = await api.post(`/options/${entity_type}/${field_permalink}`, body);
243
+ return text(res);
244
+ }
245
+ catch (e) {
246
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
247
+ }
248
+ });
249
+ server.registerTool("lighthouse_crm_delete_option", {
250
+ title: "Delete CRM Field Option",
251
+ description: "Delete an option value from a select/multi_select field. Cannot delete system default options.",
203
252
  inputSchema: {
253
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
204
254
  field_permalink: z.string().describe("Field permalink of the select/multi_select field"),
205
- entity_type: z.enum(["company", "person", "deal"]).describe("Record type"),
206
- value: z.string().describe("Option value (stored value)"),
207
- label: z.string().describe("Option label (display text)"),
208
- color: z.string().optional().describe("Option color (hex code)"),
255
+ value: z.string().describe("Option value to delete"),
209
256
  },
210
257
  }, async (params) => {
211
258
  try {
212
- const res = await api.post("/options", params);
259
+ const res = await api.delete(`/options/${params.entity_type}/${params.field_permalink}/${params.value}`);
213
260
  return text(res);
214
261
  }
215
262
  catch (e) {
@@ -225,7 +272,7 @@ server.registerTool("lighthouse_crm_search_records", {
225
272
  "for the entity_type, then use those keys in your filter conditions. " +
226
273
  "NOT for discovering new companies/people — use lighthouse_discovery_* tools for that.",
227
274
  inputSchema: {
228
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
275
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
229
276
  filters: z
230
277
  .record(z.unknown())
231
278
  .optional()
@@ -261,7 +308,7 @@ server.registerTool("lighthouse_crm_get_record", {
261
308
  title: "Get CRM Record",
262
309
  description: "Get a single CRM record by ID with all fields and relationships.",
263
310
  inputSchema: {
264
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
311
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
265
312
  id: z.string().uuid().describe("Record ID"),
266
313
  },
267
314
  }, async (params) => {
@@ -278,7 +325,7 @@ server.registerTool("lighthouse_crm_create_record", {
278
325
  description: "Create a new CRM record. Required fields: " +
279
326
  "company (name, domain), person (first_name, last_name), deal (name).",
280
327
  inputSchema: {
281
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
328
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
282
329
  data: z.record(z.unknown()).describe("Record fields (name, domain, first_name, last_name, etc.)"),
283
330
  attributes: z
284
331
  .record(z.unknown())
@@ -301,7 +348,7 @@ server.registerTool("lighthouse_crm_update_record", {
301
348
  title: "Update CRM Record",
302
349
  description: "Update an existing CRM record's fields.",
303
350
  inputSchema: {
304
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
351
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
305
352
  id: z.string().uuid().describe("Record ID"),
306
353
  data: z.record(z.unknown()).describe("Fields to update"),
307
354
  attributes: z.record(z.unknown()).optional().describe("Custom field values to update (keyed by field_permalink)"),
@@ -322,7 +369,7 @@ server.registerTool("lighthouse_crm_delete_record", {
322
369
  title: "Delete CRM Record",
323
370
  description: "Permanently delete a CRM record.",
324
371
  inputSchema: {
325
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
372
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
326
373
  id: z.string().uuid().describe("Record ID"),
327
374
  },
328
375
  }, async (params) => {
@@ -340,7 +387,9 @@ server.registerTool("lighthouse_crm_create_list", {
340
387
  description: "Create a new CRM list to organize records.",
341
388
  inputSchema: {
342
389
  name: z.string().describe("List name"),
343
- entity_type: z.enum(["company", "person", "deal"]).describe("Record type for this list"),
390
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type for this list"),
391
+ sharing: z.enum(["private", "workspace"]).default("private").optional().describe("Visibility: 'private' or 'workspace'"),
392
+ color: z.string().optional().describe("Hex color code for the list"),
344
393
  },
345
394
  }, async (params) => {
346
395
  try {
@@ -385,6 +434,8 @@ server.registerTool("lighthouse_crm_update_list", {
385
434
  inputSchema: {
386
435
  id: z.string().uuid().describe("List ID"),
387
436
  name: z.string().optional().describe("New list name"),
437
+ sharing: z.enum(["private", "workspace"]).optional().describe("New visibility setting"),
438
+ color: z.string().optional().describe("New hex color code"),
388
439
  },
389
440
  }, async (params) => {
390
441
  try {
@@ -439,7 +490,7 @@ server.registerTool("lighthouse_crm_add_to_list", {
439
490
  inputSchema: {
440
491
  list_id: z.string().uuid().describe("List ID"),
441
492
  record_id: z.string().uuid().describe("Record ID to add"),
442
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
493
+ type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
443
494
  },
444
495
  }, async (params) => {
445
496
  try {
@@ -474,8 +525,8 @@ server.registerTool("lighthouse_crm_list_notes", {
474
525
  title: "List Notes",
475
526
  description: "Get notes for a CRM record.",
476
527
  inputSchema: {
477
- record_id: z.string().uuid().describe("Record ID to get notes for"),
478
- record_type: z.enum(["company", "person", "deal"]).describe("Record type"),
528
+ record_id: z.string().uuid().optional().describe("Record ID to filter notes by (required)"),
529
+ record_type: z.enum(["company", "person", "deal", "task"]).optional().describe("Record type: company, person, or deal (required)"),
479
530
  limit: z.number().min(1).max(100).default(25),
480
531
  offset: z.number().min(0).default(0),
481
532
  },
@@ -500,7 +551,7 @@ server.registerTool("lighthouse_crm_create_note", {
500
551
  "Tagged users receive an in-app notification.",
501
552
  inputSchema: {
502
553
  record_id: z.string().uuid().describe("Record ID to attach the note to"),
503
- record_type: z.enum(["company", "person", "deal"]).describe("Record type"),
554
+ record_type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
504
555
  title: z.string().describe("Note title"),
505
556
  content: z.string().describe("Note content (plain text or HTML). Use <p> tags for paragraphs. For @mentions, include mention spans (see description)."),
506
557
  tagged_users: z
@@ -699,7 +750,7 @@ server.registerTool("lighthouse_crm_list_views", {
699
750
  title: "List CRM Views",
700
751
  description: "Get all saved CRM views. Views store filter/sort configurations that can be loaded in search_records.",
701
752
  inputSchema: {
702
- entity_type: z.enum(["company", "person", "deal"]).optional().describe("Filter by record type"),
753
+ entity_type: z.enum(["company", "person", "deal", "task"]).optional().describe("Filter by record type"),
703
754
  },
704
755
  }, async (params) => {
705
756
  try {
@@ -720,7 +771,7 @@ server.registerTool("lighthouse_crm_create_view", {
720
771
  "The saved filters will auto-apply when search_records is called with this view_id.",
721
772
  inputSchema: {
722
773
  name: z.string().describe("View name"),
723
- entity_type: z.enum(["company", "person", "deal"]).describe("Record type"),
774
+ entity_type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
724
775
  filters: z
725
776
  .record(z.unknown())
726
777
  .optional()
@@ -729,6 +780,7 @@ server.registerTool("lighthouse_crm_create_view", {
729
780
  view_type: z.enum(["table", "kanban"]).default("table").describe("View layout type"),
730
781
  grouped_by: z.string().optional().describe("Field key to group by (required for kanban views)"),
731
782
  sharing: z.enum(["private", "workspace"]).default("private").describe("Visibility"),
783
+ team_ids: z.array(z.string().uuid()).optional().describe("Team UUIDs to share the view with. Replaces any existing team shares."),
732
784
  },
733
785
  }, async (params) => {
734
786
  try {
@@ -751,6 +803,7 @@ server.registerTool("lighthouse_crm_update_view", {
751
803
  view_type: z.enum(["table", "kanban"]).optional(),
752
804
  grouped_by: z.string().optional().describe("Field key to group by (required for kanban views)"),
753
805
  sharing: z.enum(["private", "workspace"]).optional(),
806
+ team_ids: z.array(z.string().uuid()).optional().describe("Team UUIDs to share the view with. Replaces any existing team shares."),
754
807
  },
755
808
  }, async (params) => {
756
809
  try {
@@ -945,6 +998,336 @@ server.registerTool("lighthouse_discovery_search_people", {
945
998
  return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
946
999
  }
947
1000
  });
1001
+ // ─── Reports: Dashboards ─────────────────────────────────
1002
+ const WIDGET_CONFIG_DESCRIPTION = "Widget config object. Structure depends on report_type: " +
1003
+ "STANDARD: {data_source:'companies'|'deals'|'people', report_type:'standard', " +
1004
+ "dimension:{field:'<field_key>', is_custom_field:boolean, time_grouping?:'day'|'week'|'month'|'quarter'|'year'}, " +
1005
+ "measure:{aggregation:'COUNT'|'SUM'|'AVG'|'MIN'|'MAX', field:'*'|'<field_key>', is_custom_field:boolean}, " +
1006
+ "segment_by?:{field:'<field_key>', is_custom_field:boolean}, " +
1007
+ "filter?:{operator:'AND'|'OR', conditions:[...], groups:[...]}, " +
1008
+ "date_range?:{type:'relative', relative_period:'all_time'|'last_7_days'|'last_30_days'|'last_90_days'|'this_month'|'last_month'|'this_quarter'|'last_quarter'|'this_year'|'last_year'} " +
1009
+ "OR {type:'absolute', start_date:'YYYY-MM-DD', end_date:'YYYY-MM-DD'}, " +
1010
+ "included_stages?:['value1','value2'], show_axis_labels?:boolean, show_zeros?:boolean, sort_by_stage_order?:boolean}. " +
1011
+ "FUNNEL: {report_type:'funnel', entity_type:'company'|'deal'|'person', stage_field:'<field_key>', " +
1012
+ "included_stages?:['stage1','stage2'], date_range?:{...}, filter?:{...}}. " +
1013
+ "HISTORICAL: {report_type:'stage_changes', entity_type:'company'|'deal'|'person', " +
1014
+ "field_permalink:'<field_key>', time_grouping?:'day'|'week'|'month'|'quarter'|'year', date_range?:{...}}. " +
1015
+ "ALWAYS call lighthouse_crm_get_attributes first to discover valid field keys for dimension, measure, and stage_field.";
1016
+ server.registerTool("lighthouse_reports_list_dashboards", {
1017
+ title: "List Dashboards",
1018
+ description: "List all report dashboards accessible to the current user. " +
1019
+ "Returns dashboards you own, workspace-shared dashboards, and team-shared dashboards. " +
1020
+ "Each dashboard includes: id, name, description, sharing (private/workspace), is_owner, " +
1021
+ "owner {id, first_name, last_name, full_name}, widget_count, shared_with_teams, created_at, updated_at.",
1022
+ inputSchema: {},
1023
+ }, async () => {
1024
+ try {
1025
+ const res = await api.get("/dashboards");
1026
+ return text(res);
1027
+ }
1028
+ catch (e) {
1029
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1030
+ }
1031
+ });
1032
+ server.registerTool("lighthouse_reports_create_dashboard", {
1033
+ title: "Create Dashboard",
1034
+ description: "Create a new report dashboard. Dashboards contain report widgets (charts, tables, KPIs). " +
1035
+ "Requires reports.create permission. Returns the full dashboard object (same format as get dashboard).",
1036
+ inputSchema: {
1037
+ name: z.string().describe("Dashboard name"),
1038
+ description: z.string().optional().describe("Dashboard description"),
1039
+ sharing: z
1040
+ .enum(["private", "workspace"])
1041
+ .default("private")
1042
+ .describe("Visibility: 'private' (only you) or 'workspace' (all members)"),
1043
+ team_ids: z
1044
+ .array(z.string().uuid())
1045
+ .optional()
1046
+ .describe("Team IDs to share this dashboard with"),
1047
+ },
1048
+ }, async (params) => {
1049
+ try {
1050
+ const res = await api.post("/dashboards", params);
1051
+ return text(res);
1052
+ }
1053
+ catch (e) {
1054
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1055
+ }
1056
+ });
1057
+ server.registerTool("lighthouse_reports_get_dashboard", {
1058
+ title: "Get Dashboard",
1059
+ description: "Get a single dashboard by ID with all its widgets. " +
1060
+ "Returns: id, name, description, sharing, is_owner, owner {id, first_name, last_name, full_name}, " +
1061
+ "widget_count, shared_with_teams, widgets [{id, name, widget_type, config, created_at, updated_at}], created_at, updated_at.",
1062
+ inputSchema: {
1063
+ id: z.string().uuid().describe("Dashboard ID"),
1064
+ },
1065
+ }, async (params) => {
1066
+ try {
1067
+ const res = await api.get(`/dashboards/${params.id}`);
1068
+ return text(res);
1069
+ }
1070
+ catch (e) {
1071
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1072
+ }
1073
+ });
1074
+ server.registerTool("lighthouse_reports_update_dashboard", {
1075
+ title: "Update Dashboard",
1076
+ description: "Update a dashboard's name, description, or sharing. Only the dashboard owner can update. " +
1077
+ "Returns the full dashboard object (same format as get dashboard).",
1078
+ inputSchema: {
1079
+ id: z.string().uuid().describe("Dashboard ID"),
1080
+ name: z.string().optional().describe("New dashboard name"),
1081
+ description: z.string().optional().describe("New description"),
1082
+ sharing: z.enum(["private", "workspace"]).optional().describe("New visibility setting"),
1083
+ },
1084
+ }, async (params) => {
1085
+ try {
1086
+ const { id, ...body } = params;
1087
+ const res = await api.patch(`/dashboards/${id}`, body);
1088
+ return text(res);
1089
+ }
1090
+ catch (e) {
1091
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1092
+ }
1093
+ });
1094
+ server.registerTool("lighthouse_reports_delete_dashboard", {
1095
+ title: "Delete Dashboard",
1096
+ description: "Permanently delete a dashboard and all its widgets. Only the dashboard owner can delete.",
1097
+ inputSchema: {
1098
+ id: z.string().uuid().describe("Dashboard ID"),
1099
+ },
1100
+ }, async (params) => {
1101
+ try {
1102
+ const res = await api.delete(`/dashboards/${params.id}`);
1103
+ return text(res);
1104
+ }
1105
+ catch (e) {
1106
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1107
+ }
1108
+ });
1109
+ server.registerTool("lighthouse_reports_refresh_dashboard", {
1110
+ title: "Refresh Dashboard Data",
1111
+ description: "Refresh/fetch report data for all widgets in a dashboard. " +
1112
+ "Returns a map of { widget_id: report_data } for each widget.",
1113
+ inputSchema: {
1114
+ id: z.string().uuid().describe("Dashboard ID"),
1115
+ },
1116
+ }, async (params) => {
1117
+ try {
1118
+ const res = await api.post(`/dashboards/${params.id}/refresh`);
1119
+ return text(res);
1120
+ }
1121
+ catch (e) {
1122
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1123
+ }
1124
+ });
1125
+ // ─── Reports: Widgets ────────────────────────────────────
1126
+ server.registerTool("lighthouse_reports_list_widgets", {
1127
+ title: "List Dashboard Widgets",
1128
+ description: "Get all widgets for a dashboard. " +
1129
+ "Each widget includes: id, name, widget_type, config, created_at, updated_at.",
1130
+ inputSchema: {
1131
+ id: z.string().uuid().describe("Dashboard ID"),
1132
+ },
1133
+ }, async (params) => {
1134
+ try {
1135
+ const res = await api.get(`/dashboards/${params.id}/widgets`);
1136
+ return text(res);
1137
+ }
1138
+ catch (e) {
1139
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1140
+ }
1141
+ });
1142
+ server.registerTool("lighthouse_reports_create_widget", {
1143
+ title: "Create Report Widget",
1144
+ description: "Create a report widget (chart/table/KPI) on a dashboard. " +
1145
+ "WORKFLOW: 1) Call lighthouse_crm_get_attributes to discover field keys for the entity type, " +
1146
+ "2) Build a config object using those keys, 3) Create the widget. " +
1147
+ "Widget types: bar, line, pie, table, kpi, funnel, area. " +
1148
+ "Report types: standard (aggregate by field), funnel (pipeline conversion), stage_changes (historical transitions). " +
1149
+ "Returns: {id, name, widget_type, config, created_at, updated_at}.",
1150
+ inputSchema: {
1151
+ dashboard_id: z.string().uuid().describe("Dashboard ID to add widget to"),
1152
+ name: z.string().describe("Widget/report name"),
1153
+ widget_type: z
1154
+ .enum(["bar", "line", "pie", "table", "kpi", "funnel", "area"])
1155
+ .describe("Visualization type"),
1156
+ config: z.record(z.unknown()).describe(WIDGET_CONFIG_DESCRIPTION),
1157
+ position: z
1158
+ .record(z.unknown())
1159
+ .optional()
1160
+ .describe("Grid position: {x: number, y: number, w: number, h: number}"),
1161
+ },
1162
+ }, async (params) => {
1163
+ try {
1164
+ const { dashboard_id, ...body } = params;
1165
+ const res = await api.post(`/dashboards/${dashboard_id}/widgets`, body);
1166
+ return text(res);
1167
+ }
1168
+ catch (e) {
1169
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1170
+ }
1171
+ });
1172
+ server.registerTool("lighthouse_reports_update_widget", {
1173
+ title: "Update Report Widget",
1174
+ description: "Update a report widget's name, type, config, or position. " +
1175
+ "Only provide fields you want to change — others are preserved. " +
1176
+ "Returns: {id, name, widget_type, config, created_at, updated_at}.",
1177
+ inputSchema: {
1178
+ dashboard_id: z.string().uuid().describe("Dashboard ID"),
1179
+ widget_id: z.string().uuid().describe("Widget ID"),
1180
+ name: z.string().optional().describe("New widget name"),
1181
+ widget_type: z
1182
+ .enum(["bar", "line", "pie", "table", "kpi", "funnel", "area"])
1183
+ .optional()
1184
+ .describe("New visualization type"),
1185
+ config: z.record(z.unknown()).optional().describe(WIDGET_CONFIG_DESCRIPTION),
1186
+ position: z.record(z.unknown()).optional().describe("New grid position: {x, y, w, h}"),
1187
+ },
1188
+ }, async (params) => {
1189
+ try {
1190
+ const { dashboard_id, widget_id, ...body } = params;
1191
+ const res = await api.patch(`/dashboards/${dashboard_id}/widgets/${widget_id}`, body);
1192
+ return text(res);
1193
+ }
1194
+ catch (e) {
1195
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1196
+ }
1197
+ });
1198
+ server.registerTool("lighthouse_reports_delete_widget", {
1199
+ title: "Delete Report Widget",
1200
+ description: "Permanently delete a report widget from a dashboard.",
1201
+ inputSchema: {
1202
+ dashboard_id: z.string().uuid().describe("Dashboard ID"),
1203
+ widget_id: z.string().uuid().describe("Widget ID"),
1204
+ },
1205
+ }, async (params) => {
1206
+ try {
1207
+ const res = await api.delete(`/dashboards/${params.dashboard_id}/widgets/${params.widget_id}`);
1208
+ return text(res);
1209
+ }
1210
+ catch (e) {
1211
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1212
+ }
1213
+ });
1214
+ server.registerTool("lighthouse_reports_get_widget_data", {
1215
+ title: "Get Widget Report Data",
1216
+ description: "Fetch the aggregated report data for a specific widget. " +
1217
+ "Returns the computed data based on the widget's config (dimensions, measures, segments, filters). " +
1218
+ "For standard reports: array of {dimension, measure, segment?} rows. " +
1219
+ "For funnel reports: array of {stage, count} rows. " +
1220
+ "For historical reports: array of {period, from_stage, to_stage, transition_count} rows.",
1221
+ inputSchema: {
1222
+ dashboard_id: z.string().uuid().describe("Dashboard ID"),
1223
+ widget_id: z.string().uuid().describe("Widget ID"),
1224
+ },
1225
+ }, async (params) => {
1226
+ try {
1227
+ const res = await api.get(`/dashboards/${params.dashboard_id}/widgets/${params.widget_id}/data`);
1228
+ return text(res);
1229
+ }
1230
+ catch (e) {
1231
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1232
+ }
1233
+ });
1234
+ // ─── Sharing: Dashboard Teams ─────────────────────────────
1235
+ server.registerTool("lighthouse_reports_share_dashboard_with_teams", {
1236
+ title: "Share Dashboard with Teams",
1237
+ description: "Grant team-level access to a dashboard. Only the dashboard owner can share. " +
1238
+ "Use lighthouse_workspace_list_teams to find team IDs first. " +
1239
+ "Requires reports.edit permission.",
1240
+ inputSchema: {
1241
+ id: z.string().uuid().describe("Dashboard ID"),
1242
+ team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to grant access to"),
1243
+ },
1244
+ }, async (params) => {
1245
+ try {
1246
+ const { id, ...body } = params;
1247
+ const res = await api.post(`/dashboards/${id}/teams`, body);
1248
+ return text(res);
1249
+ }
1250
+ catch (e) {
1251
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1252
+ }
1253
+ });
1254
+ server.registerTool("lighthouse_reports_revoke_dashboard_from_teams", {
1255
+ title: "Revoke Dashboard Team Access",
1256
+ description: "Revoke team-level access from a dashboard. Only the dashboard owner can revoke. " +
1257
+ "Requires reports.edit permission.",
1258
+ inputSchema: {
1259
+ id: z.string().uuid().describe("Dashboard ID"),
1260
+ team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to revoke access from"),
1261
+ },
1262
+ }, async (params) => {
1263
+ try {
1264
+ const { id, ...body } = params;
1265
+ const res = await api.delete(`/dashboards/${id}/teams`, body);
1266
+ return text(res);
1267
+ }
1268
+ catch (e) {
1269
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1270
+ }
1271
+ });
1272
+ // ─── Sharing: View Teams ──────────────────────────────────
1273
+ server.registerTool("lighthouse_crm_share_view_with_teams", {
1274
+ title: "Share View with Teams",
1275
+ description: "Grant team-level access to a CRM view. Only the view owner can share. " +
1276
+ "Use lighthouse_workspace_list_teams to find team IDs first. " +
1277
+ "Requires views.share permission. " +
1278
+ "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
1279
+ inputSchema: {
1280
+ id: z.string().uuid().describe("View ID"),
1281
+ team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to grant access to"),
1282
+ },
1283
+ }, async (params) => {
1284
+ try {
1285
+ const { id, ...body } = params;
1286
+ const res = await api.post(`/views/${id}/teams`, body);
1287
+ return text(res);
1288
+ }
1289
+ catch (e) {
1290
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1291
+ }
1292
+ });
1293
+ server.registerTool("lighthouse_crm_revoke_view_from_teams", {
1294
+ title: "Revoke View Team Access",
1295
+ description: "Revoke team-level access from a CRM view. Only the view owner can revoke. " +
1296
+ "Requires views.share permission. " +
1297
+ "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
1298
+ inputSchema: {
1299
+ id: z.string().uuid().describe("View ID"),
1300
+ team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to revoke access from"),
1301
+ },
1302
+ }, async (params) => {
1303
+ try {
1304
+ const { id, ...body } = params;
1305
+ const res = await api.delete(`/views/${id}/teams`, body);
1306
+ return text(res);
1307
+ }
1308
+ catch (e) {
1309
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1310
+ }
1311
+ });
1312
+ server.registerTool("lighthouse_crm_update_view_sharing", {
1313
+ title: "Update View Sharing Status",
1314
+ description: "Change a CRM view's sharing visibility. Setting to 'private' also removes all team permissions. " +
1315
+ "Only the view owner can change sharing. Requires views.share permission. " +
1316
+ "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
1317
+ inputSchema: {
1318
+ id: z.string().uuid().describe("View ID"),
1319
+ sharing: z.enum(["private", "workspace"]).describe("New sharing visibility"),
1320
+ },
1321
+ }, async (params) => {
1322
+ try {
1323
+ const { id, ...body } = params;
1324
+ const res = await api.patch(`/views/${id}/sharing`, body);
1325
+ return text(res);
1326
+ }
1327
+ catch (e) {
1328
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1329
+ }
1330
+ });
948
1331
  // ─── Start ───────────────────────────────────────────────
949
1332
  async function main() {
950
1333
  const transport = new StdioServerTransport();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trylighthouse/mcp-server",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "MCP server for the Lighthouse CRM API",
5
5
  "type": "module",
6
6
  "bin": {