@trylighthouse/mcp-server 0.2.6 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/build/server.js +106 -41
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -164,6 +164,10 @@ Files and folders live in the workspace, not on a record. Linking one to a recor
|
|
|
164
164
|
| `lighthouse_workspace_get_user` | Get a workspace user |
|
|
165
165
|
| `lighthouse_workspace_list_teams` | List workspace teams |
|
|
166
166
|
| `lighthouse_workspace_get_team` | Get a workspace team |
|
|
167
|
+
| `lighthouse_workspace_list_webhook_subscriptions` | List your webhook subscriptions (read only) |
|
|
168
|
+
| `lighthouse_workspace_get_webhook_subscription` | Get one of your webhook subscriptions (read only) |
|
|
169
|
+
|
|
170
|
+
Webhook subscriptions are created and deleted with the REST API or the SDKs, with an API key.
|
|
167
171
|
|
|
168
172
|
### Discovery
|
|
169
173
|
|
package/build/server.js
CHANGED
|
@@ -85,7 +85,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
85
85
|
const api = new LighthouseAPI(apiKey);
|
|
86
86
|
const server = new McpServer({
|
|
87
87
|
name: "lighthouse",
|
|
88
|
-
version: "0.2.
|
|
88
|
+
version: "0.2.11",
|
|
89
89
|
});
|
|
90
90
|
const originalRegister = server.registerTool.bind(server);
|
|
91
91
|
server.registerTool = (name, schemaOrConfig, handler) => {
|
|
@@ -117,6 +117,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
117
117
|
"experience / education (a person's work history and schools, read-only multi_records of the GLOBAL DATABASE's companies and schools): contains_any, not_contains_any, contains_all, empty, not_empty; the value is a list of ids from lighthouse_discovery_search_entities with record_type 'company' (record_type 'school' for education), never CRM record ids; " +
|
|
118
118
|
"domain/email/phone: contains_any, not_contains_any, contains_all, empty, not_empty; " +
|
|
119
119
|
"list_entries (the record's list memberships; the value is an array of list ids from lighthouse_crm_list_lists, and the list's record_type must be the type of the record the condition is on: this record type, or the linked record's type on a dotted path): contains_any, not_contains_any, contains_all, empty, not_empty (empty = in no list, not_empty = in at least one list; both take no value). On a dotted path it filters by the linked records' lists, quantified like every dotted field: associated_people.list_entries contains_any [<people list id>] on a company = the companies with at least one person in that list, associated_people.list_entries empty = the companies none of whose people is in a list, companies with no people included; a custom record-link field works the same way (<link field>.list_entries, the lists of the record type it links to). A custom field can belong to ONE list: lighthouse_crm_list_attributes gives it that list's list_id, it can be set only on a record already in that list, its value sits inside that list's entry under the record's list_entries rather than at the top level, and naming it in a condition or a sort answers 'List not found' when you cannot open the list. A question about the records IN a list with further conditions or an order (\"companies in list X created this month, newest first\") is ONE call: either lighthouse_crm_list_list_records with the same filters and sort, or this search with a list_entries contains_any condition on that list's id beside the other conditions; never read the whole list and narrow it by hand; " +
|
|
120
|
+
"sequences (people only: the outbound sequences a person is in, a read-only multi_select whose options are the sequences you can open; the value is an array of sequence ids, from that field's options (lighthouse_crm_list_options) or the id of an entry of a person's sequences): contains_any, not_contains_any, contains_all, empty, not_empty (empty = in none of the sequences you can open, not_empty = in at least one of them; both take no value). The id of a sequence you cannot open, or an id that names no sequence, matches no one, so it adds no one to contains_any, makes contains_all match no one and removes no one with not_contains_any. A value that is not a sequence id is refused. On a company or a deal, associated_people.sequences filters by the sequences of its people, quantified like every dotted field (associated_people.sequences contains_any [<sequence id>] = the records with at least one person in that sequence); " +
|
|
120
121
|
"checkbox: eq (value is true or false); " +
|
|
121
122
|
"interaction (first/last email & linkedin interaction, overall interaction-with): eq, neq, empty, not_empty; " +
|
|
122
123
|
"interaction-type (first/last interaction type): contains_any, not_contains_any, empty, not_empty. " +
|
|
@@ -138,11 +139,27 @@ export function createServer(apiKey, options = {}) {
|
|
|
138
139
|
"Dates have NO range operator, express a window as two conditions (after <start> AND before <end>) in an AND group, computing the bounds from today's date. " +
|
|
139
140
|
"Use the EXACT field keys and option values returned by lighthouse_crm_list_attributes / lighthouse_crm_list_options, never abbreviate or invent them, the chosen values for a select/multi_select condition MUST be a subset of that field's options. If a requested concept has no matching field or option (e.g. a 'competitor'/'portfolio' status that no field actually holds), TELL THE USER rather than dropping the clause or mapping it onto an unrelated field. " +
|
|
140
141
|
"Do NOT use discovery field keys here, CRM fields come from lighthouse_crm_list_attributes. " +
|
|
141
|
-
"RESPONSE VALIDATION META:
|
|
142
|
-
"`ignored_filter_fields` (field keys not
|
|
142
|
+
"RESPONSE VALIDATION META (searching records, and reading a list's records): an unknown field, an operator the field does not take (list_entries included; on a dotted path, the leaf field's) or a group join other than AND/OR does not fail the read; instead the response carries, alongside `records`/`total`, three arrays you MUST inspect (collected recursively across nested groups): " +
|
|
143
|
+
"`ignored_filter_fields` (field keys not recognised; that condition was dropped), " +
|
|
143
144
|
"`invalid_operators` ([{field, operator}] where the operator is not valid for that field's type; that condition was dropped), and " +
|
|
144
145
|
"`invalid_logical_operators` ([{operator}] where a group's join was not AND/OR; treated as AND). " +
|
|
145
|
-
"If ANY are non-empty, part of your filter did NOT apply (results are broader/narrower than intended): fix the offending field/operator and retry, or tell the user which clauses could not be applied. A `warnings` array
|
|
146
|
+
"If ANY are non-empty, part of your filter did NOT apply (results are broader/narrower than intended): fix the offending field/operator and retry, or tell the user which clauses could not be applied. A `warnings` array summarises the same in plain text. " +
|
|
147
|
+
"A dotted path is judged by its leaf, and only for its operator: a relation, or a linked record's field, that does not exist is in none of these arrays, and that condition matches no record, so take every key of a dotted path from lighthouse_crm_list_attributes of the record type it is on. " +
|
|
148
|
+
"The saved filters of a view_id are checked the same way, as if you had sent them. " +
|
|
149
|
+
"A VALUE the field cannot take is never dropped: a date that is neither YYYY-MM-DD nor a rolling period, and every value the field types above say is refused, refuses the whole call with a message that says what is wrong with it.";
|
|
150
|
+
const SAVED_FILTER_REFUSAL = "A filter the record search would not run as written is refused here with a 400 and nothing is saved: an unknown field, an operator the field does not take (list_entries included; on a dotted path, the leaf field's), a group join other than AND/OR, or a value in the wrong shape, such as a sequence name where sequence ids go.";
|
|
151
|
+
const DOTTED_PATH_NAMES_UNCHECKED = "On a dotted path only the operator is checked: a relation, or a linked record's field, that does not exist is neither refused nor reported, and that condition matches no record, so take every dotted path's fields from lighthouse_crm_list_attributes of the linked record type.";
|
|
152
|
+
const LINK_NOT_VIEWABLE_RULE = "A record of a type you cannot view, or of a custom object you cannot open, answers 404 not_found, exactly like an id that does not exist.";
|
|
153
|
+
const NOTE_CREATE_LINK_RULE = "A record of a type you cannot view answers 404 not_found, exactly like an id that does not exist, and a record of a custom object you cannot open answers 400 invalid_input, exactly like a record type that does not exist.";
|
|
154
|
+
const TASK_LINKS_PER_TYPE = "Each type you send with at least one id replaces that type's links, and a type you leave out, or send with no ids, keeps them.";
|
|
155
|
+
const TASK_UNVIEWABLE_LINKS_KEPT = "Links of such a type already on the task are kept as they are.";
|
|
156
|
+
const UNLINK_NOT_VIEWABLE_KEPT = "A link to a record of a type you cannot view, or of a custom object you cannot open, is left as it is, exactly like an id that is not linked to the note: the call still answers 200 with the note.";
|
|
157
|
+
const LINKED_RECORDS_LEFT_OUT = "A record of a type you cannot view, or of a custom object you cannot open, is left out.";
|
|
158
|
+
const ACTIVITY_RELATED_RECORDS_LEFT_OUT = "A record of a type you cannot view, or of a custom object you cannot open, is left out, as if the activity had not touched it.";
|
|
159
|
+
const REPORT_ROLE_SOURCES = "notes, tasks or a meeting transcript their role does not let them read";
|
|
160
|
+
const BOARD_GROUPING_RULE = "Kanban views only: the field the board's columns come from. It must be a status or select field stored on the view's records (lighthouse_crm_list_attributes gives each field's data_type), and a list's own field works only on that list's views. A field Lighthouse computes, such as tags, sequences or the first and last interaction type, cannot group a board, and neither can a text, number, date or user field. Any other key is refused with a 400 and nothing is saved, with the message: \"<field>\" cannot group this kanban view. Use a status or select field stored on its records (a list's own field works on that list's views only). Sending the view's current grouped_by back, or leaving it out, is never checked. A table view ignores it.";
|
|
161
|
+
const SHARE_ALONG_RULE = "Pass share_along: true ONLY when the user asked to share the lists and sequences it filters on as well; a list is shared along only when the user's role can share lists, and a sequence only when it can share sequences. The response always carries shared_along ([{ type, id, name }]) and audience_cannot_open ([{ type, id, name, is_owner, is_parent }]), lists first, then sequences (type list or sequence): for the people it is now shared with, a filter on a list they cannot open is not applied, and a filter on a sequence they cannot open counts it as a sequence nobody is in.";
|
|
162
|
+
const shareAlongInput = (where) => `Also share the lists and sequences you own that ${where} with the same people. Nothing is shared along unless you pass true.`;
|
|
146
163
|
const CRM_SORT_DESCRIPTION = 'CRM sort array. Format: [{"field":"<key>","direction":"asc"|"desc"}]. ' +
|
|
147
164
|
"Multiple entries sort by the first, then tie-break by the next. " +
|
|
148
165
|
"NOT every field is sortable. A field is sortable ONLY when its type (the `data_type` returned by lighthouse_crm_list_attributes) is one of: " +
|
|
@@ -175,7 +192,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
175
192
|
"investor_multi_select: contains_any, not_contains_any, contains_all, empty, not_empty " +
|
|
176
193
|
"(value is array of investor UUIDs or objects [{\"id\":\"uuid\",\"label\":\"Name\"}]. Call lighthouse_discovery_search_entities with record_type 'investor' to find investor IDs by name); " +
|
|
177
194
|
"work_experience_multi_select: contains_any, not_contains_any, contains_all, empty, not_empty " +
|
|
178
|
-
"(value is array of
|
|
195
|
+
"(value is array of organisation UUIDs or objects [{\"id\":\"uuid\",\"label\":\"Name\"}]. Call lighthouse_discovery_search_entities with record_type 'company' to find organisation IDs by name); " +
|
|
179
196
|
"education_multi_select: contains_any, not_contains_any, contains_all, empty, not_empty " +
|
|
180
197
|
"(value is array of institution UUIDs or objects [{\"id\":\"uuid\",\"label\":\"Name\"}]. Call lighthouse_discovery_search_entities with record_type 'school' to find school IDs by name); " +
|
|
181
198
|
"funding_type_multi_select: contains_any, not_contains_any, empty, not_empty; " +
|
|
@@ -195,14 +212,14 @@ export function createServer(apiKey, options = {}) {
|
|
|
195
212
|
"date ops → ISO date string \"YYYY-MM-DD\". " +
|
|
196
213
|
"The `operator` of a block (the top level OR any group) is \"AND\" or \"OR\" and joins that block's conditions AND its nested groups: \"AND\" = every one must match, \"OR\" = at least one matches. Set the top-level operator to \"OR\" to OR the top-level conditions, or nest a group to mix logic, e.g. (A OR B) AND C = {\"operator\":\"AND\",\"conditions\":[C],\"groups\":[{\"operator\":\"OR\",\"conditions\":[A,B],\"groups\":[]}]}. A worked example with real keys is in the field-key list below. " +
|
|
197
214
|
"company_signals value is a plain array of strings; person_signals value is an object {\"signals\":[...],\"registration_countries\"?:[...],\"signal_dates\"?:{...},\"stealth_predicted_areas\"?:[...]}, these are the only contains_any fields whose value is not a bare array. " +
|
|
198
|
-
"Use the company_*/person_* keys listed below VERBATIM, do not invent variants (no _usd suffix, no abbreviation like hq for headquarter) and do not use the bare permalinks returned by lighthouse_discovery_list_attributes; any
|
|
215
|
+
"Use the company_*/person_* keys listed below VERBATIM, do not invent variants (no _usd suffix, no abbreviation like hq for headquarter) and do not use the bare permalinks returned by lighthouse_discovery_list_attributes; any unrecognised key is dropped (the search runs as if that condition were absent) and echoed in the response's `ignored_filter_fields`, so ALWAYS check that array, otherwise a mistyped key returns a result that looks fine but is unfiltered. " +
|
|
199
216
|
"NOT EXPRESSIBLE on this surface (it searches the global database, NOT your CRM): CRM list membership, CRM record owner / unassigned, CRM custom fields, deal stage, role-change dates, and geography finer than country (no US state). If a request needs one of these, apply what you CAN and TELL THE USER which clauses you could not express, do NOT silently drop them or map them onto an unrelated field. " +
|
|
200
217
|
"Do NOT use CRM attribute keys here. The complete set of valid discovery filter keys for this entity is listed at the end of this description. " +
|
|
201
218
|
"RESPONSE VALIDATION META: this search NEVER fails on a bad filter; instead the response carries, alongside `items`/`total`, three arrays you MUST inspect (collected recursively across nested groups): " +
|
|
202
|
-
"`ignored_filter_fields` (keys not
|
|
219
|
+
"`ignored_filter_fields` (keys not recognised; that condition was dropped), " +
|
|
203
220
|
"`invalid_operators` ([{field, operator}] where the operator is not valid for that field's type; that condition was dropped), and " +
|
|
204
221
|
"`invalid_logical_operators` ([{operator}] where a group's join was not AND/OR; treated as AND). " +
|
|
205
|
-
"If ANY are non-empty, part of your filter did NOT apply (results are broader than intended): fix the offending field/operator and retry, or tell the user which clauses could not be applied. A `warnings` array
|
|
222
|
+
"If ANY are non-empty, part of your filter did NOT apply (results are broader than intended): fix the offending field/operator and retry, or tell the user which clauses could not be applied. A `warnings` array summarises the same in plain text.";
|
|
206
223
|
const COMPANY_DISCOVERY_SORT_DESCRIPTION = 'Discovery company sort. Format: {"by":"<field>","dir":"asc"|"desc"}. ' +
|
|
207
224
|
"ONLY two fields are sortable: founded_on (founding date) and total_funding_amount_usd (total funding raised, in USD). " +
|
|
208
225
|
"Omit sort to use the default: relevance score, descending. " +
|
|
@@ -463,7 +480,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
463
480
|
"A search by MEANING over people or company records is a semantic_search condition in filters; it needs the ready vector of a saved view (lighthouse_crm_get_view) or of a condition copied from the app, never text and never a vector you wrote (the filters description has the rule). " +
|
|
464
481
|
"NEVER guess field keys, ALWAYS call lighthouse_crm_list_attributes first to get the exact field keys " +
|
|
465
482
|
"for the record_type, then use those keys in your filter conditions. " +
|
|
466
|
-
"The response's `meta` includes `total` (an integer match count)
|
|
483
|
+
"The response's `meta` includes `total` (an integer match count), `ignored_filter_fields` (any filter key that is not a valid attribute for this record_type, each with a did_you_mean suggestion when a close match can be computed), `invalid_operators` (a condition whose operator its field does not take, left out), `invalid_logical_operators` (a group joined by something other than AND/OR, read as AND) and `invalid_sort_fields`. Check every array to confirm the whole filter and the sort were applied. " +
|
|
484
|
+
DOTTED_PATH_NAMES_UNCHECKED + " " +
|
|
467
485
|
"A list_entries condition naming a list you cannot open answers 'List not found', and so does a condition or a sort on a custom field that belongs to such a list: the field is the membership. " +
|
|
468
486
|
"NOT for discovering new companies/people, use lighthouse_discovery_* tools for that.",
|
|
469
487
|
inputSchema: {
|
|
@@ -509,7 +527,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
509
527
|
"Filter these fields by the signal VALUES (strings). Search results carry the same shape. " +
|
|
510
528
|
"Two more read-only keys on a company: investors ({ company: [{ database_id, crm_id, name, avatar_url, is_lead }], person: [{ database_id, crm_id, first_name, last_name, avatar_url, is_lead }] }: the global database's investors of the company, both keys always present; database_id = the investor's id in the database tools and the value the investors filter takes, crm_id = the workspace's CRM record linked to that investor or null when it is not in the CRM, is_lead = led one of its rounds) and headcount_growth ({ \"1m\", \"3m\", \"6m\", \"1y\" }: the headcount's percent change per window, null when unknown, plus value = the headcount on measured_on; null when the record has no linked profile or no data). " +
|
|
511
529
|
"Two read-only keys on a person: experience and education ([{ database_id, crm_id, name, avatar_url }]: the companies the linked global profile worked at and the schools it studied at, always present, [] without a linked profile; database_id = the id in the database tools and the value the experience / education filters take, crm_id = the workspace's CRM company linked to that company or school, null when it is not in the CRM). " +
|
|
512
|
-
"A third read-only key on every company, person, deal and custom object record: notes ({ count, last_note_at, notes: [ { id, title, type, created_at, created_by, tags, records, meeting } ] }: the notes linked to the record, the newest five with the author, the kind, the tags, the records they link to (the record itself included) and the meeting details of a meeting note; never the body, which lighthouse_crm_get_note reads; count is every note the caller may see). " +
|
|
530
|
+
"A third read-only key on every company, person, deal and custom object record: notes ({ count, last_note_at, notes: [ { id, title, type, created_at, created_by, tags, records, meeting } ] }: the notes linked to the record, the newest five with the author, the kind, the tags, the records they link to that you can view (the record itself included) and the meeting details of a meeting note; never the body, which lighthouse_crm_get_note reads; count is every note the caller may see). " +
|
|
531
|
+
"One more read-only key on a person: sequences ([{ id, name, status, end_reason, current_step, step_count, added_at, started_at, ended_at }]: the outbound sequences the person is in that the API key's user can open, runs still going first, then the most recently added; always present, [] when there are none, or when the user's role cannot view both sequences and people; id = the sequence's id, the value the sequences filter takes; status = the person's own status in that sequence (in_review, ready, active, paused, account_disconnected, completed, stopped, failed, bounced or unsubscribed), never the sequence's; end_reason = why the run ended, a short key such as finished, replied or unsubscribed, null while the run is going; current_step = { number (the step's place, from 1), type (email, linkedin_connection_request or linkedin_message) }, null before the first step or once that step no longer exists; step_count = how many steps the sequence has; added_at, started_at and ended_at are ISO 8601 timestamps, null when unset). " +
|
|
513
532
|
"A read-only duration twin sits beside every status field (time_in_stage on a deal, time_in_status on a task, time_in_<status field> beside a custom status field; lighthouse_crm_list_attributes lists each with its source_field): { days, since } (the whole days the record has held its current status and the ISO timestamp since when), null while the status is empty. " +
|
|
514
533
|
"(Discovery profiles keep their own signals list: one row per feed window with type, created_at, timeframe, percentage_change.)",
|
|
515
534
|
inputSchema: {
|
|
@@ -532,6 +551,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
532
551
|
"source always has { type, label }: type is app / mobile / api / mcp / workflow / ai_assistant / notetaker / email_sync / calendar_sync / linkedin_sync / inbound_email / inbound_whatsapp / lp_portal / sync, and label names the exact origin. The four trace keys are always present and null when the origin did not record them: workflow is { id, name, run_id, node_id } when a workflow made the change (tracing an entry back to the exact run and step), api_key_id / mcp_tool identify the key or AI tool behind an API change, and trigger is automatic or manual where the origin recorded whether a person asked for the change (meeting notes written on their own and notes a member asked for both log that member as the actor, so trigger is what tells them apart). " +
|
|
533
552
|
"changes[] holds the field edits: { field, label, data_type, is_custom, list, previous, current }, where previous and current are each { value, display }: value is what is stored (an option value, a uuid, a number) and display is the same thing resolved for humans (an option's label, a linked record or user object). Consecutive edits to the same field by the same person on the same day are collapsed into one entry, so previous is the value before the burst and current the value after it. " +
|
|
534
553
|
"Every entry has id, activity_id, action, occurred_at, summary, actor, source, target and changes. One save that changed several fields is reported as one entry per field: id stays unique per entry, activity_id is shared by the entries from that same save. The five detail keys are on every entry and null on the entries they do not describe: note and task carry that note's / task's body inline (no follow-up call needed), merge carries { merged_record, kept_record }, document carries { type, id, name } for an attached file or folder, and related_records (an empty array when there are none) lists the OTHER records when one activity spanned several (never the entry's own target, which is already under target, and never the record whose feed this is). " +
|
|
554
|
+
ACTIVITY_RELATED_RECORDS_LEFT_OUT + " " +
|
|
535
555
|
"record_type is company, person, deal, or a custom object permalink; pass task or note to read a task's or note's own history. " +
|
|
536
556
|
"SCOPE: the feed covers records, notes and tasks being created, updated, deleted and merged, plus files and folders being attached. It does NOT record list membership changes or detaching a document, so do not present it to the user as a complete audit trail.",
|
|
537
557
|
inputSchema: {
|
|
@@ -556,7 +576,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
556
576
|
description: "The AI reports generated on one CRM record (any record type: company, person, deal, or a custom object permalink), newest first: the memos, SWOTs, teardowns, pass memos and briefs a member generated from the record's AI Reports tab in Lighthouse. " +
|
|
557
577
|
"WHEN TO USE: the user asks whether a memo or report already exists on a record, what the last one said, wants to compare or build on an earlier report, or asks about a company's \"memo\" / \"analysis\" / \"write-up\" (check here before writing one from scratch). Not for dashboards or charts (those are lighthouse_reports_*), and nothing here generates a report: that happens in the app. " +
|
|
558
578
|
"Each row: id, name, status (processing / success / error), depth (short / standard / extensive), template, partial (true when the run hit a cap), summary (one paragraph of the report's own prose), created_by ({ id, first_name, last_name, full_name, avatar_url, email }, the same member object every other row carries), created_at, completed_at. Only a success report has a document: read it with lighthouse_crm_get_ai_report. " +
|
|
559
|
-
"A report quoting a mailbox, calendar, meeting notes, LinkedIn account or file the API key's user cannot read is left out of the list (its author always sees it).",
|
|
579
|
+
"A report quoting a mailbox, calendar, meeting notes, LinkedIn account or file the API key's user cannot read, or " + REPORT_ROLE_SOURCES + ", is left out of the list (its author always sees it).",
|
|
560
580
|
inputSchema: {
|
|
561
581
|
record_type: z.string().describe("Record type: company, person, deal, or a custom object permalink"),
|
|
562
582
|
record_id: z.string().uuid().optional().describe("Record ID whose reports to list"),
|
|
@@ -578,7 +598,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
578
598
|
title: "Get AI Report",
|
|
579
599
|
description: "One AI report by id (from lighthouse_crm_list_ai_reports): the same metadata plus record_type / record_id and content, the full document as Markdown with its section headings (citation markers removed). " +
|
|
580
600
|
"WHEN TO USE: the user wants what an earlier memo or report actually says (a section, a number, a recommendation), or a comparison with it. A report can be long: quote or summarise the part the question is about rather than echoing it whole. " +
|
|
581
|
-
"content is null while the report is still processing or failed. Answers 403 naming the channel when the report draws on a shared mailbox, calendar, meeting notes, LinkedIn account or document the API key's user cannot read, and 404 for an unknown id.",
|
|
601
|
+
"content is null while the report is still processing or failed. Answers 403 naming the channel when the report draws on a shared mailbox, calendar, meeting notes, LinkedIn account or document the API key's user cannot read, or on " + REPORT_ROLE_SOURCES + " (its author is never refused for its sources), and 404 for an unknown id.",
|
|
582
602
|
inputSchema: {
|
|
583
603
|
id: z.string().uuid().describe("Report ID from lighthouse_crm_list_ai_reports"),
|
|
584
604
|
},
|
|
@@ -901,8 +921,9 @@ export function createServer(apiKey, options = {}) {
|
|
|
901
921
|
server.registerTool("lighthouse_crm_list_list_records", {
|
|
902
922
|
title: "List Records in a List",
|
|
903
923
|
description: "Get records in a CRM list with optional filtering, sorting, and view support. " +
|
|
904
|
-
"Same filter format as
|
|
905
|
-
"The response's `meta` includes `total` (an integer match count), `ignored_filter_fields` (any filter key that is not a valid attribute for this list's record type, each with a did_you_mean suggestion when a close match can be computed) and `invalid_sort_fields` (a sort this list cannot apply, reported rather than refused, so the records come back in their default order). Check
|
|
924
|
+
"Same filter format as lighthouse_crm_search_records. To get valid filter keys, call lighthouse_crm_list_attributes with this list's record_type, which is 'company', 'person', 'deal', or a custom object permalink. " +
|
|
925
|
+
"The response's `meta` includes `total` (an integer match count), `ignored_filter_fields` (any filter key that is not a valid attribute for this list's record type, each with a did_you_mean suggestion when a close match can be computed), `invalid_operators` (a condition whose operator its field does not take, left out of the results), `invalid_logical_operators` (a group joined by something other than AND/OR, read as AND) and `invalid_sort_fields` (a sort this list cannot apply, reported rather than refused, so the records come back in their default order). The saved filters of a view_id are reported on the same way. Check every array to confirm the whole filter and the sort were applied; a value in the wrong shape is refused instead, with a 400. " +
|
|
926
|
+
DOTTED_PATH_NAMES_UNCHECKED + " " +
|
|
906
927
|
"A list you cannot open answers 'List not found', the same as one that does not exist, and so does a condition or a sort on a custom field that belongs to such a list.",
|
|
907
928
|
inputSchema: {
|
|
908
929
|
list_id: z.string().uuid().optional().describe("List ID"),
|
|
@@ -1055,7 +1076,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
1055
1076
|
});
|
|
1056
1077
|
server.registerTool("lighthouse_crm_list_notes", {
|
|
1057
1078
|
title: "List Notes",
|
|
1058
|
-
description: "Get notes for a CRM record. Each note in the response includes a `records` map, every record (company/person/deal/custom object) the note is currently linked to, plus a `tags` array of any workspace tags attached to the note (each with id, label, color). " +
|
|
1079
|
+
description: "Get notes for a CRM record. Each note in the response includes a `records` map, every record (company/person/deal/custom object) the note is currently linked to that you can view, plus a `tags` array of any workspace tags attached to the note (each with id, label, color). " +
|
|
1080
|
+
LINKED_RECORDS_LEFT_OUT + " " +
|
|
1059
1081
|
"`tagged_users` is the teammates the note MENTIONS, each the member object this API answers for a person ({ id, first_name, last_name, full_name, avatar_url, email }), and [] when it mentions nobody. It is read from the mention spans in `content`, not from the ids a write sent, so a note written with tagged_users ids and no mention span reads back []: always write both, as lighthouse_crm_create_note and lighthouse_crm_update_note say. " +
|
|
1060
1082
|
"Every note also carries `type` ('standard' for a note a member wrote, 'meeting' for meeting notes written from a recorded meeting) and `meeting` (null on a standard note; on a meeting note { meeting_key, title, occurred_at, duration_seconds, call_type, ical_uid, attendees, has_transcript, has_recording }, where attendees is [{ email, name }] with name possibly null, call_type is a short category from a fixed list such as 'First founder call' or 'Internal discussion', duration_seconds and ical_uid are null when unknown and attendees is [] when there are none). " + NOTE_MEETING_KEY_RULE + " Meeting notes are included only when the API key's user may read the meeting. " + MEETING_ACCESS_RULE,
|
|
1061
1083
|
inputSchema: {
|
|
@@ -1119,7 +1141,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
1119
1141
|
records: z
|
|
1120
1142
|
.record(z.array(z.string().uuid()))
|
|
1121
1143
|
.optional()
|
|
1122
|
-
.describe("Records to link, keyed by record type. Optional: omit for a standalone note. Any record ids you pass must exist or the call
|
|
1144
|
+
.describe("Records to link, keyed by record type. Optional: omit for a standalone note. Any record ids you pass must exist, or the call answers 404 not_found. " + NOTE_CREATE_LINK_RULE),
|
|
1123
1145
|
title: z.string().describe("Note title"),
|
|
1124
1146
|
content: z.string().describe("Note content (plain text or HTML). Use <p> tags for paragraphs. For @mentions, include mention spans (see description)."),
|
|
1125
1147
|
tagged_users: z
|
|
@@ -1142,7 +1164,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
1142
1164
|
});
|
|
1143
1165
|
server.registerTool("lighthouse_crm_get_note", {
|
|
1144
1166
|
title: "Get Note",
|
|
1145
|
-
description: "Get a single note by ID. Response includes a `records` object keyed by record type (or custom object permalink) with every linked record, plus a `tags` array of any workspace tags attached to the note (each with permalink, label, color). " +
|
|
1167
|
+
description: "Get a single note by ID. Response includes a `records` object keyed by record type (or custom object permalink) with every linked record you can view, plus a `tags` array of any workspace tags attached to the note (each with permalink, label, color). " +
|
|
1168
|
+
LINKED_RECORDS_LEFT_OUT + " " +
|
|
1146
1169
|
"`tagged_users` is the teammates the note MENTIONS, each the member object this API answers for a person ({ id, first_name, last_name, full_name, avatar_url, email }), and [] when it mentions nobody. It is read from the mention spans in `content`, not from the ids a write sent, so a note written with tagged_users ids and no mention span reads back []: always write both, as lighthouse_crm_create_note and lighthouse_crm_update_note say. " +
|
|
1147
1170
|
"`type` is 'standard' for a note a member wrote and 'meeting' for meeting notes written from a recorded meeting; `meeting` is null on a standard note and { meeting_key, title, occurred_at, duration_seconds, call_type, ical_uid, attendees, has_transcript, has_recording } on a meeting note: title and occurred_at come from the meeting itself, attendees is [{ email, name }] with name possibly null, call_type is one of 'First founder call', 'Follow-up founder call', 'Diligence call', 'Re-engagement', 'Portfolio check-in', 'Board meeting', 'Investment committee', 'LP meeting', 'Fundraising call', 'Co-investor or syndication call', 'Internal discussion', 'Hiring or reference call', 'Partnership or vendor call' or 'Other', duration_seconds and ical_uid are null when unknown, and attendees is [] when there are none. " + NOTE_MEETING_KEY_RULE + " A meeting note is returned only when the API key's user may read the meeting; otherwise it is reported as not found. " + MEETING_ACCESS_RULE,
|
|
1148
1171
|
inputSchema: {
|
|
@@ -1206,7 +1229,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
1206
1229
|
title: "Link Record to Note",
|
|
1207
1230
|
description: "Attach a record to an existing note. Call once per record. " +
|
|
1208
1231
|
"The company/person/deal record_id MUST be an EXISTING CRM record id (an id returned by lighthouse_crm_* tools), NOT a discovery id from lighthouse_discovery_* search results. To link a discovery result, first add it to your CRM and use the resulting CRM record id. " +
|
|
1209
|
-
|
|
1232
|
+
LINK_NOT_VIEWABLE_RULE + " " +
|
|
1233
|
+
"Use lighthouse_crm_get_note to see the current set of linked records (the ones you can view).",
|
|
1210
1234
|
inputSchema: {
|
|
1211
1235
|
note_id: z.string().uuid().describe("Note ID"),
|
|
1212
1236
|
record_type: z.string().describe("Record type: company, person, deal, or a custom object permalink"),
|
|
@@ -1224,7 +1248,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
1224
1248
|
server.registerTool("lighthouse_crm_unlink_note_record", {
|
|
1225
1249
|
title: "Unlink Record from Note",
|
|
1226
1250
|
description: "Remove a record link from a note. Call once per record. " +
|
|
1227
|
-
"Removing the last linked record is allowed and leaves a standalone note (it stays in the workspace Notes section)."
|
|
1251
|
+
"Removing the last linked record is allowed and leaves a standalone note (it stays in the workspace Notes section). " +
|
|
1252
|
+
UNLINK_NOT_VIEWABLE_KEPT,
|
|
1228
1253
|
inputSchema: {
|
|
1229
1254
|
note_id: z.string().uuid().describe("Note ID"),
|
|
1230
1255
|
record_type: z.string().describe("Record type: company, person, deal, or a custom object permalink"),
|
|
@@ -1386,7 +1411,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
1386
1411
|
records: z
|
|
1387
1412
|
.record(z.array(z.string().uuid()))
|
|
1388
1413
|
.optional()
|
|
1389
|
-
.describe("Records to link, keyed by type: {company: [ids], person: [ids], deal: [ids]}; a custom object's records go under its permalink (e.g. lp: [ids])"),
|
|
1414
|
+
.describe("Records to link, keyed by type: {company: [ids], person: [ids], deal: [ids]}; a custom object's records go under its permalink (e.g. lp: [ids]). " + LINK_NOT_VIEWABLE_RULE),
|
|
1390
1415
|
},
|
|
1391
1416
|
}, async (params) => {
|
|
1392
1417
|
try {
|
|
@@ -1400,7 +1425,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
1400
1425
|
server.registerTool("lighthouse_crm_update_task", {
|
|
1401
1426
|
title: "Update Task",
|
|
1402
1427
|
description: "Update a CRM task. Provide only fields you want to change. " +
|
|
1403
|
-
"assigned_to replaces all current assignees. records
|
|
1428
|
+
"assigned_to replaces all current assignees. In records, " + TASK_LINKS_PER_TYPE.charAt(0).toLowerCase() + TASK_LINKS_PER_TYPE.slice(1) + " " +
|
|
1404
1429
|
"Status values are configured per workspace and are NOT fixed: call lighthouse_crm_list_options with record_type 'task' and permalink 'status' to get the allowed values, then pass the option's value (not its label) in `status`. " +
|
|
1405
1430
|
"status and priority answer the stored VALUE and nothing else. The words your workspace puts on those values come from lighthouse_crm_list_options(record_type:'task', permalink:'status' | 'priority'), one call that covers every task you will ever read.",
|
|
1406
1431
|
inputSchema: {
|
|
@@ -1417,7 +1442,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
1417
1442
|
records: z
|
|
1418
1443
|
.record(z.array(z.string().uuid()))
|
|
1419
1444
|
.optional()
|
|
1420
|
-
.describe("Records to link
|
|
1445
|
+
.describe("Records to link, keyed by type: {company: [ids], person: [ids], deal: [ids]}; a custom object's records go under its permalink (e.g. lp: [ids]). " + TASK_LINKS_PER_TYPE + " " + LINK_NOT_VIEWABLE_RULE + " " + TASK_UNVIEWABLE_LINKS_KEPT),
|
|
1421
1446
|
},
|
|
1422
1447
|
}, async (params) => {
|
|
1423
1448
|
try {
|
|
@@ -1795,14 +1820,15 @@ export function createServer(apiKey, options = {}) {
|
|
|
1795
1820
|
"To change sharing settings, use lighthouse_crm_update_view_sharing instead. " +
|
|
1796
1821
|
"Uses CRM filter format, call lighthouse_crm_list_attributes first if modifying filters. " +
|
|
1797
1822
|
"A semantic_search condition in the new filters must carry its vector (keep the one the view already has, from lighthouse_crm_get_view, or one copied from the app): this API never builds an embedding from text. " +
|
|
1798
|
-
"Every key is optional and a key you leave out keeps the value the view already has, the board settings included, so changing a filter never reopens the columns someone collapsed."
|
|
1823
|
+
"Every key is optional and a key you leave out keeps the value the view already has, the board settings included, so changing a filter never reopens the columns someone collapsed. " +
|
|
1824
|
+
SAVED_FILTER_REFUSAL + " " + DOTTED_PATH_NAMES_UNCHECKED,
|
|
1799
1825
|
inputSchema: {
|
|
1800
1826
|
id: z.string().uuid().describe("View ID"),
|
|
1801
1827
|
name: z.string().optional(),
|
|
1802
1828
|
filters: z.record(z.unknown()).optional().describe(CRM_FILTER_DESCRIPTION),
|
|
1803
1829
|
sort: z.array(z.record(z.unknown())).optional().describe(CRM_SORT_DESCRIPTION),
|
|
1804
1830
|
view_type: z.enum(["table", "kanban"]).optional(),
|
|
1805
|
-
grouped_by: z.string().optional().describe(
|
|
1831
|
+
grouped_by: z.string().optional().describe(BOARD_GROUPING_RULE),
|
|
1806
1832
|
hidden_kanban_groups: z
|
|
1807
1833
|
.array(z.unknown())
|
|
1808
1834
|
.nullable()
|
|
@@ -1934,6 +1960,47 @@ export function createServer(apiKey, options = {}) {
|
|
|
1934
1960
|
return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1935
1961
|
}
|
|
1936
1962
|
});
|
|
1963
|
+
const WEBHOOK_SUBSCRIPTION_SHAPE = "Each subscription: id, url, record_type, events ('record.created' and/or 'record.updated'), watched_fields (the field permalinks record.updated is limited to, [] when every update is sent), header_names (the names of the custom headers sent with every delivery, never their values), description, " +
|
|
1964
|
+
"status ('active' or 'disabled'), disabled_reason (null, 'endpoint_gone' when the address answered 410 Gone, 'failing' after many failed tries in a row, 'access_lost' when the owner lost developer access or access to the record type), disabled_at, " +
|
|
1965
|
+
"last_delivery (null before the first try, else { attempted_at, status: 'delivered' or 'failed', response_status }), owner (the member it belongs to), created_at and updated_at. The signing secret is never returned.";
|
|
1966
|
+
const WEBHOOK_SUBSCRIPTION_WRITES = "This tool only reads: creating and deleting webhook subscriptions is done with the REST API (POST /v1/webhook-subscriptions and DELETE /v1/webhook-subscriptions/{id}) or the SDKs, with an API key, and a subscription can also be created or deleted in the app, under Settings > Developer > Webhooks. A turned-off subscription is not turned back on: delete it and create a new one.";
|
|
1967
|
+
server.registerTool("lighthouse_workspace_list_webhook_subscriptions", {
|
|
1968
|
+
title: "List Webhook Subscriptions",
|
|
1969
|
+
description: "List the webhook subscriptions of the user this key acts as (only their own), newest first, active and turned off alike: each one sends a signed POST to its url whenever a record of its record_type is created or updated. " +
|
|
1970
|
+
WEBHOOK_SUBSCRIPTION_SHAPE + " " +
|
|
1971
|
+
"WHEN TO USE: \"which webhooks do I have\", \"where are our company updates being sent\", \"why did our integration stop receiving changes\" (read status, disabled_reason and last_delivery). " +
|
|
1972
|
+
"Needs a role with developer access. " + WEBHOOK_SUBSCRIPTION_WRITES,
|
|
1973
|
+
inputSchema: {
|
|
1974
|
+
limit: z.number().min(1).max(100).default(25),
|
|
1975
|
+
offset: z.number().min(0).default(0),
|
|
1976
|
+
},
|
|
1977
|
+
}, async (params) => {
|
|
1978
|
+
try {
|
|
1979
|
+
const res = await api.get("/webhook-subscriptions", params);
|
|
1980
|
+
return text(res);
|
|
1981
|
+
}
|
|
1982
|
+
catch (e) {
|
|
1983
|
+
return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1984
|
+
}
|
|
1985
|
+
});
|
|
1986
|
+
server.registerTool("lighthouse_workspace_get_webhook_subscription", {
|
|
1987
|
+
title: "Get Webhook Subscription",
|
|
1988
|
+
description: "Get one of the webhook subscriptions of the user this key acts as, by id. " +
|
|
1989
|
+
WEBHOOK_SUBSCRIPTION_SHAPE + " " +
|
|
1990
|
+
"WHEN TO USE: \"is that webhook still on\", \"when did it last deliver\", for one subscription whose id you already have (from lighthouse_workspace_list_webhook_subscriptions). " +
|
|
1991
|
+
"Another member's subscription is not found, the same as an id that does not exist. Needs a role with developer access. " + WEBHOOK_SUBSCRIPTION_WRITES,
|
|
1992
|
+
inputSchema: {
|
|
1993
|
+
id: z.string().uuid().describe("Webhook subscription ID"),
|
|
1994
|
+
},
|
|
1995
|
+
}, async (params) => {
|
|
1996
|
+
try {
|
|
1997
|
+
const res = await api.get(`/webhook-subscriptions/${params.id}`);
|
|
1998
|
+
return text(res);
|
|
1999
|
+
}
|
|
2000
|
+
catch (e) {
|
|
2001
|
+
return err(`Failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
1937
2004
|
server.registerTool("lighthouse_discovery_list_attributes", {
|
|
1938
2005
|
title: "List Discovery Data Fields",
|
|
1939
2006
|
description: "Get a lightweight catalog of the DATA FIELDS discovery results carry for companies or people: each field's permalink, label, data_type, links_to, read_only, and has_options. " +
|
|
@@ -1993,7 +2060,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
1993
2060
|
"3) For location filters (company_hq_country, company_hq_region, and any other *_country/*_region key), call lighthouse_discovery_list_locations to get country/region codes. " +
|
|
1994
2061
|
"4) For investor/past-company/education filters, call lighthouse_discovery_search_entities (record_type 'investor', 'company', or 'school') to resolve names to ids. " +
|
|
1995
2062
|
"The COMPLETE, authoritative list of company_* filter keys (every field with its data type and where its values come from) is at the END of the `filters` parameter description below. Read it and use those keys exactly; do not anchor on a subset, invent variants (no _usd suffix), or abbreviate (hq vs headquarter). " +
|
|
1996
|
-
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT
|
|
2063
|
+
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT recognised, each with a did_you_mean suggestion when a close match can be computed). Check `ignored_filter_fields` to confirm every filter was actually applied." +
|
|
1997
2064
|
DISCOVERY_CRM_ID_NOTE,
|
|
1998
2065
|
inputSchema: {
|
|
1999
2066
|
filters: z
|
|
@@ -2023,7 +2090,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
2023
2090
|
"3) For location filters (person_country, person_region, and any other *_country/*_region key), call lighthouse_discovery_list_locations to get country/region codes. " +
|
|
2024
2091
|
"4) For past-company/education filters, call lighthouse_discovery_search_entities (record_type 'company' or 'school') to resolve names to ids. " +
|
|
2025
2092
|
"The COMPLETE, authoritative list of person_* filter keys (every field with its data type and where its values come from) is at the END of the `filters` parameter description below. Read it and use those keys exactly; do not anchor on a subset, invent variants, or abbreviate. " +
|
|
2026
|
-
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT
|
|
2093
|
+
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT recognised, each with a did_you_mean suggestion when a close match can be computed). Check `ignored_filter_fields` to confirm every filter was actually applied. " +
|
|
2027
2094
|
"People results are returned by founder relevance; custom sorting is NOT supported for people, so there is no sort parameter." +
|
|
2028
2095
|
DISCOVERY_CRM_ID_NOTE,
|
|
2029
2096
|
inputSchema: {
|
|
@@ -2050,7 +2117,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
2050
2117
|
"You CAN combine it with structured filters: pass `filters` using the same company_* keys as lighthouse_discovery_search_companies and they apply together (semantic match AND your filters), e.g. semantically-similar companies that are ALSO headquartered in Germany. " +
|
|
2051
2118
|
"PREFER lighthouse_discovery_search_companies (no `query`) when the user only needs exact attribute filters (a specific country, funding stage, headcount range) with no conceptual component. " +
|
|
2052
2119
|
"If the user asks for something this cannot satisfy (an exact field the database does not expose, a guarantee of completeness, or ordering by a specific metric), tell them that plainly and fall back to lighthouse_discovery_search_companies with filters rather than guessing. " +
|
|
2053
|
-
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT
|
|
2120
|
+
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT recognised, each with a did_you_mean suggestion when a close match can be computed). Check `ignored_filter_fields` to confirm every filter was actually applied. " +
|
|
2054
2121
|
"`query` is required; everything else is optional." +
|
|
2055
2122
|
DISCOVERY_CRM_ID_NOTE,
|
|
2056
2123
|
inputSchema: {
|
|
@@ -2084,7 +2151,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
2084
2151
|
"You CAN combine it with structured filters: pass `filters` using the same person_* keys as lighthouse_discovery_search_people and they apply together (semantic match AND your filters), e.g. semantically-similar people who are ALSO based in the UK. " +
|
|
2085
2152
|
"PREFER lighthouse_discovery_search_people (no `query`) when the user only needs exact attribute filters with no conceptual component. " +
|
|
2086
2153
|
"If the user asks for something this cannot satisfy (an exact field the database does not expose, a guarantee of completeness, or ordering by a specific metric), tell them that plainly and fall back to lighthouse_discovery_search_people with filters rather than guessing. " +
|
|
2087
|
-
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT
|
|
2154
|
+
"The response's `meta` includes `total` (the match count, e.g. \"247\", or \"1k+\" when there are 1000+) and `ignored_filter_fields` (any filter key that was NOT recognised, each with a did_you_mean suggestion when a close match can be computed). Check `ignored_filter_fields` to confirm every filter was actually applied. " +
|
|
2088
2155
|
"`query` is required; everything else is optional." +
|
|
2089
2156
|
DISCOVERY_CRM_ID_NOTE,
|
|
2090
2157
|
inputSchema: {
|
|
@@ -2596,8 +2663,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
2596
2663
|
title: "Update Dashboard",
|
|
2597
2664
|
description: "Update a dashboard's name, description, or sharing. Only the dashboard owner can update. " +
|
|
2598
2665
|
"Returns the full dashboard object (same format as get dashboard), plus shared_along and audience_cannot_open. " +
|
|
2599
|
-
|
|
2600
|
-
"the user's role can share lists. The response always carries shared_along and audience_cannot_open.",
|
|
2666
|
+
SHARE_ALONG_RULE,
|
|
2601
2667
|
inputSchema: {
|
|
2602
2668
|
id: z.string().uuid().describe("Dashboard ID"),
|
|
2603
2669
|
name: z.string().optional().describe("New dashboard name"),
|
|
@@ -2606,7 +2672,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
2606
2672
|
share_along: z
|
|
2607
2673
|
.boolean()
|
|
2608
2674
|
.optional()
|
|
2609
|
-
.describe("
|
|
2675
|
+
.describe(shareAlongInput("this dashboard's widgets filter on")),
|
|
2610
2676
|
},
|
|
2611
2677
|
}, async (params) => {
|
|
2612
2678
|
try {
|
|
@@ -2657,6 +2723,8 @@ export function createServer(apiKey, options = {}) {
|
|
|
2657
2723
|
"2) Build a config object using those keys, 3) Create the widget. " +
|
|
2658
2724
|
"Widget types: bar, line, pie, table, kpi, funnel, area. " +
|
|
2659
2725
|
"Report types: standard (aggregate by field), funnel (pipeline conversion), stage_changes (historical transitions). " +
|
|
2726
|
+
"The config's filter (and the older flat filters) uses the CRM filter format of lighthouse_crm_search_records, on the widget's record type. " +
|
|
2727
|
+
SAVED_FILTER_REFUSAL + " " + DOTTED_PATH_NAMES_UNCHECKED + " " +
|
|
2660
2728
|
"Returns: {id, name, widget_type, config, created_at, updated_at}.",
|
|
2661
2729
|
inputSchema: {
|
|
2662
2730
|
dashboard_id: z.string().uuid().describe("Dashboard ID to add widget to"),
|
|
@@ -2739,15 +2807,14 @@ export function createServer(apiKey, options = {}) {
|
|
|
2739
2807
|
description: "Grant team-level access to a dashboard. Only the dashboard owner can share. " +
|
|
2740
2808
|
"Use lighthouse_workspace_list_teams to find team IDs first. " +
|
|
2741
2809
|
"Requires reports.edit permission. " +
|
|
2742
|
-
|
|
2743
|
-
"the user's role can share lists. The response always carries shared_along and audience_cannot_open.",
|
|
2810
|
+
SHARE_ALONG_RULE,
|
|
2744
2811
|
inputSchema: {
|
|
2745
2812
|
id: z.string().uuid().describe("Dashboard ID"),
|
|
2746
2813
|
team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to grant access to"),
|
|
2747
2814
|
share_along: z
|
|
2748
2815
|
.boolean()
|
|
2749
2816
|
.optional()
|
|
2750
|
-
.describe("
|
|
2817
|
+
.describe(shareAlongInput("this dashboard's widgets filter on")),
|
|
2751
2818
|
},
|
|
2752
2819
|
}, async (params) => {
|
|
2753
2820
|
try {
|
|
@@ -2783,15 +2850,14 @@ export function createServer(apiKey, options = {}) {
|
|
|
2783
2850
|
"Use lighthouse_workspace_list_teams to find team IDs first. " +
|
|
2784
2851
|
"Requires views.share permission. " +
|
|
2785
2852
|
"Returns formatted view: {id, name, record_type, list_id, view_type, filters, sort, grouped_by, sharing, owner, is_owner, shared_with_teams, created_at, shared_along, audience_cannot_open}, where is_owner says whether the API key's user owns the view. " +
|
|
2786
|
-
|
|
2787
|
-
"the user's role can share lists. The response always carries shared_along and audience_cannot_open.",
|
|
2853
|
+
SHARE_ALONG_RULE,
|
|
2788
2854
|
inputSchema: {
|
|
2789
2855
|
id: z.string().uuid().describe("View ID"),
|
|
2790
2856
|
team_ids: z.array(z.string().uuid()).describe("Array of team UUIDs to grant access to"),
|
|
2791
2857
|
share_along: z
|
|
2792
2858
|
.boolean()
|
|
2793
2859
|
.optional()
|
|
2794
|
-
.describe("
|
|
2860
|
+
.describe(shareAlongInput("this view filters on")),
|
|
2795
2861
|
},
|
|
2796
2862
|
}, async (params) => {
|
|
2797
2863
|
try {
|
|
@@ -2827,15 +2893,14 @@ export function createServer(apiKey, options = {}) {
|
|
|
2827
2893
|
description: "Change a CRM view or list view's sharing visibility. Setting to 'private' also removes all team permissions. " +
|
|
2828
2894
|
"Automatically detects view type. Only the view owner can change sharing. Requires views.share permission. " +
|
|
2829
2895
|
"Returns formatted view: {id, name, record_type, list_id, view_type, filters, sort, grouped_by, sharing, owner, is_owner, shared_with_teams, created_at, shared_along, audience_cannot_open}, where is_owner says whether the API key's user owns the view. " +
|
|
2830
|
-
|
|
2831
|
-
"the user's role can share lists. The response always carries shared_along and audience_cannot_open.",
|
|
2896
|
+
SHARE_ALONG_RULE,
|
|
2832
2897
|
inputSchema: {
|
|
2833
2898
|
id: z.string().uuid().describe("View ID"),
|
|
2834
2899
|
sharing: z.enum(["private", "workspace"]).describe("New sharing visibility"),
|
|
2835
2900
|
share_along: z
|
|
2836
2901
|
.boolean()
|
|
2837
2902
|
.optional()
|
|
2838
|
-
.describe("
|
|
2903
|
+
.describe(shareAlongInput("this view filters on")),
|
|
2839
2904
|
},
|
|
2840
2905
|
}, async (params) => {
|
|
2841
2906
|
try {
|
|
@@ -3198,7 +3263,7 @@ export function createServer(apiKey, options = {}) {
|
|
|
3198
3263
|
});
|
|
3199
3264
|
server.registerTool("lighthouse_inbox_calendar_meeting_notes", {
|
|
3200
3265
|
title: "Get Meeting Notes",
|
|
3201
|
-
description: "The meeting notes written from one recorded meeting, by meeting_key (" + MEETING_KEY_SOURCES + "): the note in the same shape as lighthouse_crm_get_note (type 'meeting', with the meeting object and the records it is linked to), plus content_markdown (what was generated: Meeting Context first, then the sections) and is_edited (true when a member changed the note since, in which case content is the version to trust). " +
|
|
3266
|
+
description: "The meeting notes written from one recorded meeting, by meeting_key (" + MEETING_KEY_SOURCES + "): the note in the same shape as lighthouse_crm_get_note (type 'meeting', with the meeting object and the records it is linked to that you can view), plus content_markdown (what was generated: Meeting Context first, then the sections) and is_edited (true when a member changed the note since, in which case content is the version to trust). " +
|
|
3202
3267
|
"WHEN TO USE: the user asks what was discussed, decided or agreed in a specific meeting, the next steps from a call, or who attended. Lighter than the transcript: prefer it unless the answer depends on exactly what was said. " +
|
|
3203
3268
|
"Not found when the meeting has no notes yet (see has_notes on the meeting) or the API key's user may not read them; for a recurring meeting the newest occurrence the user may read is returned. " + MEETING_ACCESS_RULE,
|
|
3204
3269
|
inputSchema: {
|