@trylighthouse/mcp-server 0.1.4 → 0.1.7

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/server.js +158 -86
  2. package/package.json +4 -1
package/build/server.js CHANGED
@@ -16,7 +16,7 @@ export function createServer(apiKey) {
16
16
  const api = new LighthouseAPI(apiKey);
17
17
  const server = new McpServer({
18
18
  name: "lighthouse",
19
- version: "0.1.4",
19
+ version: "0.1.7",
20
20
  });
21
21
  // ─────────────────────────────────────────────────────────
22
22
  // FILTER FORMAT REFERENCE (used in tool descriptions)
@@ -326,21 +326,17 @@ export function createServer(apiKey) {
326
326
  });
327
327
  server.registerTool("lighthouse_crm_create_record", {
328
328
  title: "Create CRM Record",
329
- description: "Create a new CRM record. Required fields: " +
330
- "company (name, domain), person (first_name, last_name), deal (name).",
329
+ description: "Create a new CRM record. Pass all fields (standard + custom) in a single flat data object. " +
330
+ "Required: company (name), person (first_name or fn), deal (name). " +
331
+ "Relations: company (domain, team, deals), person (emails, phones, companies, deals), deal (companies, people).",
331
332
  inputSchema: {
332
- type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
333
- data: z.record(z.unknown()).describe("Record fields (name, domain, first_name, last_name, etc.)"),
334
- attributes: z
335
- .record(z.unknown())
336
- .optional()
337
- .describe("Custom field values keyed by field_permalink (from lighthouse_crm_get_attributes)"),
333
+ type: z.enum(["company", "person", "deal"]).describe("Record type"),
334
+ data: z.record(z.unknown()).describe("All record fields standard, custom, and relations in a single flat object"),
338
335
  },
339
336
  }, async (params) => {
340
337
  try {
341
338
  const res = await api.post(`/records/${params.type}`, {
342
339
  data: params.data,
343
- attributes: params.attributes,
344
340
  });
345
341
  return text(res);
346
342
  }
@@ -350,18 +346,17 @@ export function createServer(apiKey) {
350
346
  });
351
347
  server.registerTool("lighthouse_crm_update_record", {
352
348
  title: "Update CRM Record",
353
- description: "Update an existing CRM record's fields.",
349
+ description: "Update an existing CRM record. Pass all fields to update (standard + custom) in a single flat data object. " +
350
+ "Relation fields (domains, emails, phones, team, companies, deals, people) are replaced entirely when provided.",
354
351
  inputSchema: {
355
- type: z.enum(["company", "person", "deal", "task"]).describe("Record type"),
352
+ type: z.enum(["company", "person", "deal"]).describe("Record type"),
356
353
  id: z.string().uuid().describe("Record ID"),
357
- data: z.record(z.unknown()).describe("Fields to update"),
358
- attributes: z.record(z.unknown()).optional().describe("Custom field values to update (keyed by field_permalink)"),
354
+ data: z.record(z.unknown()).describe("Fields to update — standard, custom, and relations in a single flat object"),
359
355
  },
360
356
  }, async (params) => {
361
357
  try {
362
358
  const res = await api.patch(`/records/${params.type}/${params.id}`, {
363
359
  data: params.data,
364
- attributes: params.attributes,
365
360
  });
366
361
  return text(res);
367
362
  }
@@ -492,17 +487,15 @@ export function createServer(apiKey) {
492
487
  });
493
488
  server.registerTool("lighthouse_crm_add_to_list", {
494
489
  title: "Add Record to List",
495
- description: "Add a CRM record to a list.",
490
+ description: "Add a CRM record to a list. The record type is automatically inferred from the list type.",
496
491
  inputSchema: {
497
492
  list_id: z.string().uuid().describe("List ID"),
498
493
  record_id: z.string().uuid().describe("Record ID to add"),
499
- type: z.enum(["company", "person", "deal"]).describe("Record type"),
500
494
  },
501
495
  }, async (params) => {
502
496
  try {
503
497
  const res = await api.post(`/lists/${params.list_id}/records`, {
504
498
  record_id: params.record_id,
505
- type: params.type,
506
499
  });
507
500
  return text(res);
508
501
  }
@@ -590,8 +583,7 @@ export function createServer(apiKey) {
590
583
  title: "List Notes",
591
584
  description: "Get notes for a CRM record.",
592
585
  inputSchema: {
593
- record_id: z.string().uuid().optional().describe("Record ID to filter notes by (required)"),
594
- record_type: z.enum(["company", "person", "deal", "task"]).optional().describe("Record type: company, person, or deal (required)"),
586
+ record_id: z.string().uuid().describe("Record ID to list notes for"),
595
587
  limit: z.number().min(1).max(100).default(25),
596
588
  offset: z.number().min(0).default(0),
597
589
  },
@@ -667,18 +659,12 @@ export function createServer(apiKey) {
667
659
  server.registerTool("lighthouse_crm_list_tasks", {
668
660
  title: "List Tasks",
669
661
  description: "List CRM tasks with optional filtering and sorting. " +
670
- "The filters object supports: " +
671
- 'filter (CRM-style): {"operator":"AND","conditions":[{"field":"status","operator":"contains_any","value":["pending"]}]}; ' +
662
+ 'filters (CRM-style): {"operator":"AND","conditions":[{"field":"status","operator":"contains_any","value":["pending"]}]}; ' +
672
663
  'sort: [{"field":"due_date","direction":"asc"}]; ' +
673
664
  "Filterable fields: title, status, priority, due_date, description, assigned_to, records (person/company/deal), plus custom fields.",
674
665
  inputSchema: {
675
- filters: z
676
- .object({
677
- filter: z.record(z.any()).optional().describe("CRM filter object with operator/conditions/groups"),
678
- sort: z.array(z.record(z.any())).optional().describe('Sort array: [{"field":"due_date","direction":"asc"}]'),
679
- })
680
- .optional()
681
- .describe("Filter and sort options"),
666
+ filters: z.record(z.any()).optional().describe("CRM filter object with operator/conditions/groups"),
667
+ sort: z.array(z.record(z.any())).optional().describe('Sort array: [{"field":"due_date","direction":"asc"}]'),
682
668
  limit: z.number().min(1).max(100).default(25),
683
669
  offset: z.number().min(0).default(0),
684
670
  },
@@ -708,8 +694,7 @@ export function createServer(apiKey) {
708
694
  });
709
695
  server.registerTool("lighthouse_crm_create_task", {
710
696
  title: "Create Task",
711
- description: "Create a CRM task, optionally linked to records and assigned to users. " +
712
- "The SQL expects assigned_to as [{id: uuid}] and records as {company: [{id}], person: [{id}], deal: [{id}]}.",
697
+ description: "Create a CRM task, optionally linked to records and assigned to users.",
713
698
  inputSchema: {
714
699
  title: z.string().describe("Task title"),
715
700
  description: z.string().optional().describe("Task description"),
@@ -732,18 +717,8 @@ export function createServer(apiKey) {
732
717
  },
733
718
  }, async (params) => {
734
719
  try {
735
- const { attributes, assigned_to, records, ...rest } = params;
736
- const data = { ...rest };
737
- if (assigned_to)
738
- data.assigned_to = assigned_to.map((id) => ({ id }));
739
- if (records) {
740
- data.records = {
741
- company: records.company?.map((id) => ({ id })) || [],
742
- person: records.person?.map((id) => ({ id })) || [],
743
- deal: records.deal?.map((id) => ({ id })) || [],
744
- };
745
- }
746
- const res = await api.post("/tasks", { data, attributes });
720
+ const { attributes, ...rest } = params;
721
+ const res = await api.post("/tasks", { data: rest, attributes });
747
722
  return text(res);
748
723
  }
749
724
  catch (e) {
@@ -777,18 +752,8 @@ export function createServer(apiKey) {
777
752
  },
778
753
  }, async (params) => {
779
754
  try {
780
- const { id, attributes, assigned_to, records, ...rest } = params;
781
- const data = { ...rest };
782
- if (assigned_to)
783
- data.assigned_to = assigned_to.map((id) => ({ id }));
784
- if (records) {
785
- data.records = {
786
- company: records.company?.map((id) => ({ id })) || [],
787
- person: records.person?.map((id) => ({ id })) || [],
788
- deal: records.deal?.map((id) => ({ id })) || [],
789
- };
790
- }
791
- const res = await api.patch(`/tasks/${id}`, { data, attributes });
755
+ const { id, attributes, ...rest } = params;
756
+ const res = await api.patch(`/tasks/${id}`, { data: rest, attributes });
792
757
  return text(res);
793
758
  }
794
759
  catch (e) {
@@ -813,9 +778,11 @@ export function createServer(apiKey) {
813
778
  // ─── CRM: Views ──────────────────────────────────────────
814
779
  server.registerTool("lighthouse_crm_list_views", {
815
780
  title: "List CRM Views",
816
- description: "Get all saved CRM views. Views store filter/sort configurations that can be loaded in search_records.",
781
+ description: "Get all saved CRM views, or list views scoped to a specific list. " +
782
+ "Views store filter/sort configurations that can be loaded in search_records.",
817
783
  inputSchema: {
818
784
  entity_type: z.enum(["company", "person", "deal"]).optional().describe("Filter by record type"),
785
+ list_id: z.string().uuid().optional().describe("Filter views by list ID. When provided, returns only views scoped to that list."),
819
786
  },
820
787
  }, async (params) => {
821
788
  try {
@@ -828,8 +795,9 @@ export function createServer(apiKey) {
828
795
  });
829
796
  server.registerTool("lighthouse_crm_create_view", {
830
797
  title: "Create CRM View",
831
- description: "Create a saved CRM view with filters and sort configuration. " +
798
+ description: "Create a saved CRM view (or list view) with filters and sort configuration. " +
832
799
  "Views are for YOUR CRM data only (not discovery). " +
800
+ "Pass list_id to create a view scoped to a specific list. " +
833
801
  "WORKFLOW: 1) Call lighthouse_crm_get_attributes to get field keys, " +
834
802
  "2) Build a filter object using those keys, " +
835
803
  "3) Create the view with the filter. " +
@@ -846,6 +814,7 @@ export function createServer(apiKey) {
846
814
  grouped_by: z.string().optional().describe("Field key to group by (required for kanban views)"),
847
815
  sharing: z.enum(["private", "workspace"]).default("private").describe("Visibility"),
848
816
  team_ids: z.array(z.string().uuid()).optional().describe("Team UUIDs to share the view with. Replaces any existing team shares."),
817
+ list_id: z.string().uuid().optional().describe("List ID to scope this view to a specific list. The entity_type must match the list type."),
849
818
  },
850
819
  }, async (params) => {
851
820
  try {
@@ -858,7 +827,8 @@ export function createServer(apiKey) {
858
827
  });
859
828
  server.registerTool("lighthouse_crm_update_view", {
860
829
  title: "Update CRM View",
861
- description: "Update a saved CRM view's name, filters, sort, or sharing settings. " +
830
+ description: "Update a saved CRM view or list view's name, filters, sort, or sharing settings. " +
831
+ "Automatically detects whether it's a CRM view or list view. " +
862
832
  "Uses CRM filter format — call lighthouse_crm_get_attributes first if modifying filters.",
863
833
  inputSchema: {
864
834
  id: z.string().uuid().describe("View ID"),
@@ -882,7 +852,7 @@ export function createServer(apiKey) {
882
852
  });
883
853
  server.registerTool("lighthouse_crm_get_view", {
884
854
  title: "Get CRM View",
885
- description: "Get a single saved CRM view by ID.",
855
+ description: "Get a single saved CRM view or list view by ID. Automatically detects the view type.",
886
856
  inputSchema: {
887
857
  id: z.string().uuid().describe("View ID"),
888
858
  },
@@ -897,7 +867,7 @@ export function createServer(apiKey) {
897
867
  });
898
868
  server.registerTool("lighthouse_crm_delete_view", {
899
869
  title: "Delete CRM View",
900
- description: "Permanently delete a saved CRM view.",
870
+ description: "Permanently delete a saved CRM view or list view. Automatically detects the view type.",
901
871
  inputSchema: {
902
872
  id: z.string().uuid().describe("View ID"),
903
873
  },
@@ -1051,6 +1021,7 @@ export function createServer(apiKey) {
1051
1021
  .record(z.unknown())
1052
1022
  .optional()
1053
1023
  .describe(DISCOVERY_FILTER_DESCRIPTION),
1024
+ sort: z.record(z.unknown()).optional().describe(DISCOVERY_SORT_DESCRIPTION),
1054
1025
  skip: z.number().min(0).default(0).describe("Number of results to skip"),
1055
1026
  limit: z.number().min(1).max(100).default(25).describe("Results per page"),
1056
1027
  },
@@ -1063,9 +1034,131 @@ export function createServer(apiKey) {
1063
1034
  return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1064
1035
  }
1065
1036
  });
1037
+ // ─── Discovery: Lookup ──────────────────────────────────
1038
+ server.registerTool("lighthouse_discovery_lookup_by_domain", {
1039
+ title: "Lookup Companies by Domain",
1040
+ description: "Look up companies in Lighthouse's global database by their website domain(s). " +
1041
+ "Returns enriched company profiles for matching domains. Max 1000 domains per request. " +
1042
+ "Consumes API entity credits for each new company returned.",
1043
+ inputSchema: {
1044
+ domains: z
1045
+ .array(z.string())
1046
+ .describe("Array of domains to look up (e.g. ['google.com', 'stripe.com']). Max 1000."),
1047
+ },
1048
+ }, async (params) => {
1049
+ try {
1050
+ const res = await api.post("/database/get-companies-by-domain", 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_discovery_lookup_by_linkedin", {
1058
+ title: "Lookup Companies by LinkedIn",
1059
+ description: "Look up companies in Lighthouse's global database by LinkedIn URL(s). " +
1060
+ "Returns enriched company profiles for matching LinkedIn company pages. Max 1000 URLs per request. " +
1061
+ "Consumes API entity credits for each new company returned.",
1062
+ inputSchema: {
1063
+ linkedin_urls: z
1064
+ .array(z.string())
1065
+ .describe("Array of LinkedIn company URLs (e.g. ['https://linkedin.com/company/stripe']). Max 1000."),
1066
+ },
1067
+ }, async (params) => {
1068
+ try {
1069
+ const res = await api.post("/database/get-companies-by-linkedin", params);
1070
+ return text(res);
1071
+ }
1072
+ catch (e) {
1073
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1074
+ }
1075
+ });
1076
+ server.registerTool("lighthouse_discovery_get_saved_searches", {
1077
+ title: "List Saved Searches",
1078
+ description: "List all saved discovery searches in the workspace. " +
1079
+ "Returns saved searches you own and workspace-shared searches. " +
1080
+ "Each search includes its filters and type (company or person). " +
1081
+ "Use the search ID with lighthouse_discovery_get_search_results to run a saved search.",
1082
+ inputSchema: {},
1083
+ }, async () => {
1084
+ try {
1085
+ const res = await api.get("/database/get-saved-searches");
1086
+ return text(res);
1087
+ }
1088
+ catch (e) {
1089
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1090
+ }
1091
+ });
1092
+ server.registerTool("lighthouse_discovery_get_search_results", {
1093
+ title: "Get Saved Search Results",
1094
+ description: "Run a saved discovery search and get its results. " +
1095
+ "The search's saved filters are applied automatically. " +
1096
+ "Optionally filter by signal creation date range. " +
1097
+ "Consumes API entity credits for new entities returned.",
1098
+ inputSchema: {
1099
+ search_id: z.string().uuid().describe("Saved search ID (from lighthouse_discovery_get_saved_searches)"),
1100
+ signal_created_after: z.string().optional().describe("Only include results with signals created after this ISO timestamp"),
1101
+ signal_created_before: z.string().optional().describe("Only include results with signals created before this ISO timestamp"),
1102
+ skip: z.number().min(0).default(0).describe("Number of results to skip"),
1103
+ limit: z.number().min(1).max(100).default(25).describe("Results per page"),
1104
+ },
1105
+ }, async (params) => {
1106
+ try {
1107
+ const res = await api.post("/database/get-search-results", params);
1108
+ return text(res);
1109
+ }
1110
+ catch (e) {
1111
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1112
+ }
1113
+ });
1114
+ server.registerTool("lighthouse_discovery_get_locations", {
1115
+ title: "Get Location Options",
1116
+ description: "Get available location options (countries and regions) for discovery filters. " +
1117
+ "Returns {countries: [{name, value, flag}], regions: [{name, value, flag}]}. " +
1118
+ "Use 'value' (alpha-2 codes for countries, IDs for regions) in filter conditions like company_hq_country.",
1119
+ inputSchema: {},
1120
+ }, async () => {
1121
+ try {
1122
+ const res = await api.get("/database/get-locations");
1123
+ return text(res);
1124
+ }
1125
+ catch (e) {
1126
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1127
+ }
1128
+ });
1129
+ server.registerTool("lighthouse_discovery_get_industries", {
1130
+ title: "Get Industry Options",
1131
+ description: "Get available industry/category options for discovery filters. " +
1132
+ "Returns {categories: [{name, value}], sdg_categories: [{name, value}]}. " +
1133
+ "Use 'value' (permalink) in filter conditions like company_categories.",
1134
+ inputSchema: {},
1135
+ }, async () => {
1136
+ try {
1137
+ const res = await api.get("/database/get-industries");
1138
+ return text(res);
1139
+ }
1140
+ catch (e) {
1141
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1142
+ }
1143
+ });
1144
+ server.registerTool("lighthouse_discovery_get_tags", {
1145
+ title: "Get Tag Options",
1146
+ description: "Get available tag options for discovery filters. " +
1147
+ "Returns {company_tags: [{value, name}], person_tags: [{value, name}]}. " +
1148
+ "Use 'value' in filter conditions like company_highlights or person_highlights.",
1149
+ inputSchema: {},
1150
+ }, async () => {
1151
+ try {
1152
+ const res = await api.get("/database/get-tags");
1153
+ return text(res);
1154
+ }
1155
+ catch (e) {
1156
+ return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1157
+ }
1158
+ });
1066
1159
  // ─── Reports: Dashboards ─────────────────────────────────
1067
1160
  const WIDGET_CONFIG_DESCRIPTION = "Widget config object. Structure depends on report_type: " +
1068
- "STANDARD: {data_source:'companies'|'deals'|'people', report_type:'standard', " +
1161
+ "STANDARD: {data_source:'company'|'deal'|'person', report_type:'standard', " +
1069
1162
  "dimension:{field:'<field_key>', is_custom_field:boolean, time_grouping?:'day'|'week'|'month'|'quarter'|'year'}, " +
1070
1163
  "measure:{aggregation:'COUNT'|'SUM'|'AVG'|'MIN'|'MAX', field:'*'|'<field_key>', is_custom_field:boolean}, " +
1071
1164
  "segment_by?:{field:'<field_key>', is_custom_field:boolean}, " +
@@ -1171,22 +1264,6 @@ export function createServer(apiKey) {
1171
1264
  return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1172
1265
  }
1173
1266
  });
1174
- server.registerTool("lighthouse_reports_refresh_dashboard", {
1175
- title: "Refresh Dashboard Data",
1176
- description: "Refresh/fetch report data for all widgets in a dashboard. " +
1177
- "Returns a map of { widget_id: report_data } for each widget.",
1178
- inputSchema: {
1179
- id: z.string().uuid().describe("Dashboard ID"),
1180
- },
1181
- }, async (params) => {
1182
- try {
1183
- const res = await api.post(`/dashboards/${params.id}/refresh`);
1184
- return text(res);
1185
- }
1186
- catch (e) {
1187
- return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
1188
- }
1189
- });
1190
1267
  // ─── Reports: Widgets ────────────────────────────────────
1191
1268
  server.registerTool("lighthouse_reports_list_widgets", {
1192
1269
  title: "List Dashboard Widgets",
@@ -1219,10 +1296,6 @@ export function createServer(apiKey) {
1219
1296
  .enum(["bar", "line", "pie", "table", "kpi", "funnel", "area"])
1220
1297
  .describe("Visualization type"),
1221
1298
  config: z.record(z.unknown()).describe(WIDGET_CONFIG_DESCRIPTION),
1222
- position: z
1223
- .record(z.unknown())
1224
- .optional()
1225
- .describe("Grid position: {x: number, y: number, w: number, h: number}"),
1226
1299
  },
1227
1300
  }, async (params) => {
1228
1301
  try {
@@ -1248,7 +1321,6 @@ export function createServer(apiKey) {
1248
1321
  .optional()
1249
1322
  .describe("New visualization type"),
1250
1323
  config: z.record(z.unknown()).optional().describe(WIDGET_CONFIG_DESCRIPTION),
1251
- position: z.record(z.unknown()).optional().describe("New grid position: {x, y, w, h}"),
1252
1324
  },
1253
1325
  }, async (params) => {
1254
1326
  try {
@@ -1337,7 +1409,7 @@ export function createServer(apiKey) {
1337
1409
  // ─── Sharing: View Teams ──────────────────────────────────
1338
1410
  server.registerTool("lighthouse_crm_share_view_with_teams", {
1339
1411
  title: "Share View with Teams",
1340
- description: "Grant team-level access to a CRM view. Only the view owner can share. " +
1412
+ description: "Grant team-level access to a CRM view or list view. Only the view owner can share. " +
1341
1413
  "Use lighthouse_workspace_list_teams to find team IDs first. " +
1342
1414
  "Requires views.share permission. " +
1343
1415
  "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
@@ -1357,7 +1429,7 @@ export function createServer(apiKey) {
1357
1429
  });
1358
1430
  server.registerTool("lighthouse_crm_revoke_view_from_teams", {
1359
1431
  title: "Revoke View Team Access",
1360
- description: "Revoke team-level access from a CRM view. Only the view owner can revoke. " +
1432
+ description: "Revoke team-level access from a CRM view or list view. Only the view owner can revoke. " +
1361
1433
  "Requires views.share permission. " +
1362
1434
  "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
1363
1435
  inputSchema: {
@@ -1376,8 +1448,8 @@ export function createServer(apiKey) {
1376
1448
  });
1377
1449
  server.registerTool("lighthouse_crm_update_view_sharing", {
1378
1450
  title: "Update View Sharing Status",
1379
- description: "Change a CRM view's sharing visibility. Setting to 'private' also removes all team permissions. " +
1380
- "Only the view owner can change sharing. Requires views.share permission. " +
1451
+ description: "Change a CRM view or list view's sharing visibility. Setting to 'private' also removes all team permissions. " +
1452
+ "Automatically detects view type. Only the view owner can change sharing. Requires views.share permission. " +
1381
1453
  "Returns formatted view: {id, name, entity_type, view_type, filters, sort, grouped_by, sharing, owner, shared_with_teams, created_at}.",
1382
1454
  inputSchema: {
1383
1455
  id: z.string().uuid().describe("View ID"),
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@trylighthouse/mcp-server",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
4
4
  "description": "MCP server for the Lighthouse CRM API",
5
5
  "type": "module",
6
6
  "main": "./build/server.js",
7
+ "exports": {
8
+ ".": "./build/server.js"
9
+ },
7
10
  "bin": {
8
11
  "lighthouse-mcp": "./build/index.js"
9
12
  },