@agifyai/leadify-mcp 7.1.3 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -192,6 +192,7 @@ Conséquences pratiques :
|
|
|
192
192
|
| `delete_campaign_log` | Supprimer une entrée de log de campagne. |
|
|
193
193
|
| `update_campaign_stats` | Mettre à jour les statistiques d'email d'une campagne pour un groupe de leads. |
|
|
194
194
|
| `create_campaign` | Créer une nouvelle campagne rattachée à un groupe de leads (statut DRAFT). |
|
|
195
|
+
| `list_campaigns` | Lister compactement les campagnes d'une organisation explicitement sélectionnée et leur statut courant. |
|
|
195
196
|
| `get_campaign` | Récupérer les détails d'une campagne et ses KPIs temps réel. |
|
|
196
197
|
| `update_campaign_status` | Changer le statut d'une campagne (DRAFT, ACTIVE, PAUSED, COMPLETED). |
|
|
197
198
|
| `export_campaign` | Exporter les statistiques complètes d'une campagne en CSV. |
|
|
@@ -200,9 +201,11 @@ Conséquences pratiques :
|
|
|
200
201
|
| `signal_disable` | Désactiver un signal (faux positif / écarté manuellement). Flip de `state` uniquement. |
|
|
201
202
|
| `signal_delete` | Supprimer définitivement un signal (cas rare : donnée erronée, doublon). |
|
|
202
203
|
| `add_activity` | Journaliser une interaction prospect (LinkedIn, email, call) dans le feed du lead. |
|
|
203
|
-
| `
|
|
204
|
+
| `get_context_workspace` | Lire le Context Workspace canonique versionné (`company_brain` ou `gtm_playbook`) d'une organisation explicitement sélectionnée. Retourne le brouillon et la dernière version publiée. |
|
|
205
|
+
| `update_company_brain_sections` | Modifier uniquement les sections indiquées du Company Brain : le MCP relit le brouillon ou la dernière publication, fusionne les sections ciblées, puis sauvegarde avec révision attendue et clé d'idempotence. |
|
|
206
|
+
| `update_gtm_playbook_sections` | Modifier uniquement les sections indiquées du GTM Playbook, avec la même lecture-fusion-révision atomiquement contrôlée. |
|
|
207
|
+
| `publish_context_workspace` | Publier une révision prête du Context Workspace après confirmation explicite (`confirm_publish: true`). Admin de l'organisation requis ; les raisons de non-readiness sont renvoyées par le serveur. |
|
|
204
208
|
| `describe_company_info_schema` | Lire le schéma companyInfo v2 mirroré côté MCP (enums, max-lengths, sections, patch tool par section). À appeler avant la première update. |
|
|
205
|
-
| `update_data_room` | Update WHOLESALE (escape hatch) — remplace l'intégralité de companyInfo. Requis pour le bootstrap initial. Préférer les tools granulaires pour l'incrémental. |
|
|
206
209
|
| `update_company_info_identity` | Patch name + website. Reads + merges + PUTs le full v2 payload. |
|
|
207
210
|
| `update_company_info_snapshot` | Patch du bloc snapshot (pitchOneLiner, marketSpecialty, stage, salesTeamSizeFR, geo, constraints). geo merge shallow. |
|
|
208
211
|
| `update_company_info_constraint` | Add / replace / remove une seule contrainte dans snapshot.constraints[]. label + type enum + note≤150. Cap 10. |
|
|
@@ -210,7 +213,6 @@ Conséquences pratiques :
|
|
|
210
213
|
| `update_company_info_economics` | Patch du bloc economics (pricingModel, ticketRange, salesCycleMonths, triggers, defaultBaseline). |
|
|
211
214
|
| `update_company_info_product_icp` | Add / replace / remove un seul ICP dans productICPs[]. Lookup par productId (must match a products[].name). Cap 5. |
|
|
212
215
|
| `update_company_info_proof_wording` | Patch du bloc proofWording (keyMetrics, miniStories, forbiddenWords). |
|
|
213
|
-
| `add_data_room_document` | Ajouter un document textuel à la data room avec catégorie. |
|
|
214
216
|
| `upsert_persona` | Créer ou mettre à jour un persona (ciblage, messaging, outils actifs). |
|
|
215
217
|
| `get_persona` | Récupérer un persona par son ID. |
|
|
216
218
|
| `get_lead_group_persona` | Récupérer le persona assigné à un groupe de leads. |
|
package/dist/server.js
CHANGED
|
@@ -6,17 +6,17 @@ import { registerLogTools } from "./tools/logs.js";
|
|
|
6
6
|
import { registerCampaignTools } from "./tools/campaigns.js";
|
|
7
7
|
import { registerSignalTools } from "./tools/signals.js";
|
|
8
8
|
import { registerActivityTools } from "./tools/activities.js";
|
|
9
|
-
import { registerDataRoomTools } from "./tools/dataroom.js";
|
|
10
9
|
import { registerDataSourceTools } from "./tools/datasources.js";
|
|
11
10
|
import { registerPersonaTools } from "./tools/personas.js";
|
|
12
11
|
import { registerLeadGroupTools } from "./tools/lead_groups.js";
|
|
13
12
|
import { registerOrganizationTools } from "./tools/organizations.js";
|
|
14
13
|
import { registerPipelineTools } from "./tools/pipeline.js";
|
|
15
14
|
import { registerFineTuningTools } from "./tools/fine_tuning.js";
|
|
15
|
+
import { registerContextWorkspaceTools } from "./tools/context_workspace.js";
|
|
16
16
|
export function createServer() {
|
|
17
17
|
const server = new McpServer({
|
|
18
18
|
name: "leadify",
|
|
19
|
-
version: "
|
|
19
|
+
version: "8.1.0",
|
|
20
20
|
});
|
|
21
21
|
registerAuthTools(server);
|
|
22
22
|
registerOrganizationTools(server);
|
|
@@ -27,7 +27,7 @@ export function createServer() {
|
|
|
27
27
|
registerCampaignTools(server);
|
|
28
28
|
registerSignalTools(server);
|
|
29
29
|
registerActivityTools(server);
|
|
30
|
-
|
|
30
|
+
registerContextWorkspaceTools(server);
|
|
31
31
|
registerDataSourceTools(server);
|
|
32
32
|
registerPersonaTools(server);
|
|
33
33
|
registerPipelineTools(server);
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
-
|
|
2
|
+
import { type LeadifyClient } from "../client.js";
|
|
3
|
+
type CampaignReadClient = Pick<LeadifyClient, "get">;
|
|
4
|
+
export declare function registerCampaignTools(server: McpServer, readClient?: CampaignReadClient): void;
|
|
5
|
+
export {};
|
package/dist/tools/campaigns.js
CHANGED
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { getClient } from "../client.js";
|
|
3
3
|
import { toolResult, handleToolError } from "../types.js";
|
|
4
|
-
|
|
4
|
+
const CAMPAIGN_STATUSES = ["DRAFT", "ACTIVE", "PAUSED", "COMPLETED"];
|
|
5
|
+
function assertCampaignBelongsToOrganization(data, organizationId) {
|
|
6
|
+
if (!data || typeof data !== "object") {
|
|
7
|
+
throw new Error("Leadify returned an invalid campaign response");
|
|
8
|
+
}
|
|
9
|
+
const campaign = data;
|
|
10
|
+
const leadGroup = campaign.leadGroup;
|
|
11
|
+
const actualOrganizationId = campaign.organizationId ?? (leadGroup && typeof leadGroup === "object"
|
|
12
|
+
? leadGroup.organizationId
|
|
13
|
+
: undefined);
|
|
14
|
+
if (actualOrganizationId !== organizationId) {
|
|
15
|
+
throw new Error("Campaign does not belong to the explicitly selected organization");
|
|
16
|
+
}
|
|
17
|
+
if (!CAMPAIGN_STATUSES.includes(campaign.status)) {
|
|
18
|
+
throw new Error("Leadify returned a campaign with an invalid current status");
|
|
19
|
+
}
|
|
20
|
+
return campaign;
|
|
21
|
+
}
|
|
22
|
+
export function registerCampaignTools(server, readClient) {
|
|
5
23
|
// ── update_campaign_stats ──────────────────────────────────────────────
|
|
6
24
|
server.tool("update_campaign_stats", "Update email campaign statistics for a lead group. If the given campaignSlug " +
|
|
7
25
|
"doesn't exist in the group's stats yet, it is created; otherwise provided fields " +
|
|
@@ -98,14 +116,63 @@ export function registerCampaignTools(server) {
|
|
|
98
116
|
}
|
|
99
117
|
});
|
|
100
118
|
// ── get_campaign ───────────────────────────────────────────────────────
|
|
101
|
-
server.tool("get_campaign", "Retrieve
|
|
102
|
-
"
|
|
103
|
-
"
|
|
119
|
+
server.tool("get_campaign", "Retrieve a campaign chosen from list_campaigns, scoped to the explicitly selected " +
|
|
120
|
+
"organization. Includes its normalized current status (DRAFT, ACTIVE, PAUSED, or COMPLETED) " +
|
|
121
|
+
"and real-time KPIs. Never use campaign logs as the source of status.", {
|
|
104
122
|
id: z.string().describe("Campaign ID."),
|
|
105
|
-
|
|
123
|
+
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
124
|
+
}, async ({ id, organization_id }) => {
|
|
106
125
|
try {
|
|
107
|
-
const data = await getClient().get(`/api/campaign/${encodeURIComponent(id)}`);
|
|
108
|
-
return toolResult(data);
|
|
126
|
+
const data = await (readClient ?? getClient()).get(`/api/campaign/${encodeURIComponent(id)}`);
|
|
127
|
+
return toolResult(assertCampaignBelongsToOrganization(data, organization_id));
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
return handleToolError(error);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
// ── list_campaigns ─────────────────────────────────────────────────────
|
|
134
|
+
server.tool("list_campaigns", "List campaigns in one explicitly selected organization with compact current status metadata. " +
|
|
135
|
+
"Use this after discover_leadify_context to identify ACTIVE, DRAFT, PAUSED, or COMPLETED " +
|
|
136
|
+
"campaigns without reading historical logs.", {
|
|
137
|
+
organization_id: z.string().describe("Organization selected explicitly via discover_leadify_context or list_organizations."),
|
|
138
|
+
status: z.enum(CAMPAIGN_STATUSES).optional().describe("Optional current-status filter."),
|
|
139
|
+
limit: z.number().int().min(1).max(100).optional().default(50).describe("Maximum campaigns returned, bounded to 100."),
|
|
140
|
+
}, async ({ organization_id, status, limit }) => {
|
|
141
|
+
try {
|
|
142
|
+
const data = await (readClient ?? getClient()).get("/api/campaigns", new URLSearchParams({ organizationId: organization_id }));
|
|
143
|
+
if (!data || typeof data !== "object" || !Array.isArray(data.campaigns)) {
|
|
144
|
+
throw new Error("Leadify returned an invalid campaign list response");
|
|
145
|
+
}
|
|
146
|
+
const response = data;
|
|
147
|
+
if (response.organizationId !== organization_id) {
|
|
148
|
+
throw new Error("Leadify returned campaigns from a different organization");
|
|
149
|
+
}
|
|
150
|
+
const campaigns = response.campaigns.map((item) => {
|
|
151
|
+
if (!item || typeof item !== "object")
|
|
152
|
+
throw new Error("Leadify returned an invalid campaign");
|
|
153
|
+
const campaign = item;
|
|
154
|
+
if (!CAMPAIGN_STATUSES.includes(campaign.status)) {
|
|
155
|
+
throw new Error("Leadify returned a campaign with an invalid current status");
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
id: campaign.id,
|
|
159
|
+
name: campaign.name,
|
|
160
|
+
status: campaign.status,
|
|
161
|
+
channel: campaign.channel,
|
|
162
|
+
leadGroupId: campaign.leadGroupId,
|
|
163
|
+
createdAt: campaign.createdAt,
|
|
164
|
+
updatedAt: campaign.updatedAt,
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
const filtered = status ? campaigns.filter((campaign) => campaign.status === status) : campaigns;
|
|
168
|
+
const statusCounts = Object.fromEntries(CAMPAIGN_STATUSES.map((value) => [value, campaigns.filter((campaign) => campaign.status === value).length]));
|
|
169
|
+
return toolResult({
|
|
170
|
+
organizationId: organization_id,
|
|
171
|
+
campaigns: filtered.slice(0, limit),
|
|
172
|
+
total: filtered.length,
|
|
173
|
+
truncated: filtered.length > limit,
|
|
174
|
+
statusCounts,
|
|
175
|
+
});
|
|
109
176
|
}
|
|
110
177
|
catch (error) {
|
|
111
178
|
return handleToolError(error);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
export interface ContextWorkspaceClient {
|
|
3
|
+
get(path: string, params?: URLSearchParams): Promise<unknown>;
|
|
4
|
+
put(path: string, body: unknown): Promise<unknown>;
|
|
5
|
+
post(path: string, body: unknown): Promise<unknown>;
|
|
6
|
+
}
|
|
7
|
+
/** Bounded projection for agents: the active draft and latest published version only. */
|
|
8
|
+
export declare function projectContextWorkspaceRead(data: unknown): Record<string, unknown>;
|
|
9
|
+
export declare function registerContextWorkspaceTools(server: McpServer, client?: ContextWorkspaceClient): void;
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { getClient } from "../client.js";
|
|
3
|
+
import { handleToolError, toolResult } from "../types.js";
|
|
4
|
+
const CONTEXT_KINDS = ["company_brain", "gtm_playbook"];
|
|
5
|
+
const MAX_READINESS_ISSUES = 20;
|
|
6
|
+
const kindSchema = z.enum(CONTEXT_KINDS);
|
|
7
|
+
const sectionSchema = z.object({
|
|
8
|
+
value: z.unknown(),
|
|
9
|
+
evidenceIds: z.array(z.string().min(1)).max(100),
|
|
10
|
+
approved: z.boolean(),
|
|
11
|
+
}).strict();
|
|
12
|
+
const sectionPatchSchema = z.record(z.string().min(1), sectionSchema).refine(sections => Object.keys(sections).length > 0, "At least one section is required");
|
|
13
|
+
function compactReadiness(readiness) {
|
|
14
|
+
if (!readiness || typeof readiness !== "object" || Array.isArray(readiness))
|
|
15
|
+
return readiness;
|
|
16
|
+
const source = readiness;
|
|
17
|
+
const issues = Array.isArray(source.issues) ? source.issues : [];
|
|
18
|
+
return {
|
|
19
|
+
ready: source.ready,
|
|
20
|
+
evaluated_at: source.evaluatedAt,
|
|
21
|
+
issue_count: issues.length,
|
|
22
|
+
issues: issues.slice(0, MAX_READINESS_ISSUES),
|
|
23
|
+
...(issues.length > MAX_READINESS_ISSUES
|
|
24
|
+
? { issues_truncated: true }
|
|
25
|
+
: {}),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function compactDraft(draft) {
|
|
29
|
+
if (!draft || typeof draft !== "object" || Array.isArray(draft))
|
|
30
|
+
return draft;
|
|
31
|
+
const source = draft;
|
|
32
|
+
return {
|
|
33
|
+
revision: source.revision,
|
|
34
|
+
base_version: source.baseVersion,
|
|
35
|
+
rollback_from_version: source.rollbackFromVersion,
|
|
36
|
+
readiness: compactReadiness(source.readiness),
|
|
37
|
+
content: source.content,
|
|
38
|
+
created_at: source.createdAt,
|
|
39
|
+
updated_at: source.updatedAt,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function compactPublished(published) {
|
|
43
|
+
if (!published || typeof published !== "object" || Array.isArray(published))
|
|
44
|
+
return published;
|
|
45
|
+
const source = published;
|
|
46
|
+
return {
|
|
47
|
+
version: source.version,
|
|
48
|
+
source_draft_id: source.sourceDraftId,
|
|
49
|
+
readiness: compactReadiness(source.readiness),
|
|
50
|
+
content: source.content,
|
|
51
|
+
published_at: source.publishedAt,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Bounded projection for agents: the active draft and latest published version only. */
|
|
55
|
+
export function projectContextWorkspaceRead(data) {
|
|
56
|
+
const source = data && typeof data === "object" && !Array.isArray(data)
|
|
57
|
+
? data
|
|
58
|
+
: {};
|
|
59
|
+
const published = Array.isArray(source.published) ? source.published : [];
|
|
60
|
+
return {
|
|
61
|
+
kind: source.kind,
|
|
62
|
+
draft: compactDraft(source.draft),
|
|
63
|
+
latest_published: compactPublished(published[0] ?? null),
|
|
64
|
+
published_version_count: published.length,
|
|
65
|
+
...(published.length > 1 ? { historical_versions_omitted: published.length - 1 } : {}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function record(value) {
|
|
69
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
70
|
+
? value
|
|
71
|
+
: {};
|
|
72
|
+
}
|
|
73
|
+
function currentRevision(draft) {
|
|
74
|
+
const revision = record(draft).revision;
|
|
75
|
+
return typeof revision === "number" && Number.isInteger(revision) && revision >= 0
|
|
76
|
+
? revision
|
|
77
|
+
: 0;
|
|
78
|
+
}
|
|
79
|
+
function mergeSections(read, sections) {
|
|
80
|
+
const state = record(read);
|
|
81
|
+
const draft = record(state.draft);
|
|
82
|
+
const published = Array.isArray(state.published) ? state.published : [];
|
|
83
|
+
// Start a new draft from the latest published document, then change only the
|
|
84
|
+
// named sections. This preserves every untouched section across revisions.
|
|
85
|
+
const base = Object.keys(draft).length > 0
|
|
86
|
+
? record(draft.content)
|
|
87
|
+
: record(record(published[0]).content);
|
|
88
|
+
return { ...base, ...sections };
|
|
89
|
+
}
|
|
90
|
+
async function updateSections(client, input) {
|
|
91
|
+
const read = await client.get(`/api/context-workspace/${input.kind}`, new URLSearchParams({ organizationId: input.organizationId }));
|
|
92
|
+
const actualRevision = currentRevision(record(read).draft);
|
|
93
|
+
if (actualRevision !== input.expectedRevision) {
|
|
94
|
+
throw new Error(`Revision conflict before write: expected ${input.expectedRevision}, current ${actualRevision}. Re-read and retry without overwriting concurrent sections.`);
|
|
95
|
+
}
|
|
96
|
+
return client.put("/api/context-workspace/draft", {
|
|
97
|
+
organizationId: input.organizationId,
|
|
98
|
+
kind: input.kind,
|
|
99
|
+
content: mergeSections(read, input.sections),
|
|
100
|
+
expectedRevision: input.expectedRevision,
|
|
101
|
+
idempotencyKey: input.idempotencyKey,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
export function registerContextWorkspaceTools(server, client = getClient()) {
|
|
105
|
+
server.registerTool("get_context_workspace", {
|
|
106
|
+
title: "Read canonical versioned Context Workspace",
|
|
107
|
+
description: "Read the canonical Leadify Context Workspace for one explicitly selected organization and kind. " +
|
|
108
|
+
"Returns the current draft plus the latest published Company Brain or GTM Playbook, with bounded readiness metadata. " +
|
|
109
|
+
"Use this for every Leadify workflow. The Data Room is no longer exposed by this MCP.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
organization_id: z.string().trim().min(1).describe("Required organization ID. Always select it explicitly; there is no default tenant."),
|
|
112
|
+
kind: kindSchema.describe("Canonical workspace document: `company_brain` or `gtm_playbook`."),
|
|
113
|
+
},
|
|
114
|
+
annotations: { readOnlyHint: true },
|
|
115
|
+
}, async ({ organization_id, kind }) => {
|
|
116
|
+
try {
|
|
117
|
+
const params = new URLSearchParams({ organizationId: organization_id });
|
|
118
|
+
const data = await client.get(`/api/context-workspace/${kind}`, params);
|
|
119
|
+
return toolResult(projectContextWorkspaceRead(data));
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
return handleToolError(error);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
const sectionTools = [
|
|
126
|
+
{ name: "update_company_brain_sections", kind: "company_brain", title: "Update Company Brain sections" },
|
|
127
|
+
{ name: "update_gtm_playbook_sections", kind: "gtm_playbook", title: "Update GTM Playbook sections" },
|
|
128
|
+
];
|
|
129
|
+
for (const tool of sectionTools) {
|
|
130
|
+
server.registerTool(tool.name, {
|
|
131
|
+
title: tool.title,
|
|
132
|
+
description: "Update only the named canonical sections. The MCP re-reads the current draft (or latest published version), merges only those sections, " +
|
|
133
|
+
"and saves with optimistic concurrency. Unnamed sections are preserved; a concurrent revision is never overwritten.",
|
|
134
|
+
inputSchema: {
|
|
135
|
+
organization_id: z.string().trim().min(1).describe("Required organization ID; section writes require admin access."),
|
|
136
|
+
sections: sectionPatchSchema.describe("Only sections to change. Each section must include `value`, `evidenceIds`, and `approved`."),
|
|
137
|
+
expected_revision: z.number().int().nonnegative().describe("Current draft revision, or 0 when no draft exists. Re-read first."),
|
|
138
|
+
idempotency_key: z.string().trim().min(1).max(200).describe("Stable unique key for this exact section patch."),
|
|
139
|
+
},
|
|
140
|
+
annotations: { idempotentHint: true },
|
|
141
|
+
}, async ({ organization_id, sections, expected_revision, idempotency_key }) => {
|
|
142
|
+
try {
|
|
143
|
+
const data = await updateSections(client, { organizationId: organization_id, kind: tool.kind, sections, expectedRevision: expected_revision, idempotencyKey: idempotency_key });
|
|
144
|
+
return toolResult(compactDraft(data));
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
return handleToolError(error);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
server.registerTool("publish_context_workspace", {
|
|
152
|
+
title: "Publish a ready Context Workspace draft",
|
|
153
|
+
description: "Publish exactly one ready draft revision of the canonical Context Workspace. This is a consequential action: it requires " +
|
|
154
|
+
"an organization-admin write key, the precise current draft revision, and `confirm_publish: true`. The server rejects incomplete " +
|
|
155
|
+
"content with its readiness reasons and rejects stale revisions without overwriting anything.",
|
|
156
|
+
inputSchema: {
|
|
157
|
+
organization_id: z.string().trim().min(1).describe("Required organization ID; publication is restricted to an admin key for this tenant."),
|
|
158
|
+
kind: kindSchema,
|
|
159
|
+
expected_revision: z.number().int().positive().describe("Exact ready draft revision to publish, obtained from `get_context_workspace`."),
|
|
160
|
+
idempotency_key: z.string().trim().min(1).max(200).describe("Stable unique key for this exact publication request."),
|
|
161
|
+
confirm_publish: z.literal(true).describe("Must be explicitly true after the user has confirmed this publication."),
|
|
162
|
+
},
|
|
163
|
+
annotations: { destructiveHint: true, idempotentHint: true },
|
|
164
|
+
}, async ({ organization_id, kind, expected_revision, idempotency_key }) => {
|
|
165
|
+
try {
|
|
166
|
+
const data = await client.post("/api/context-workspace/publish", {
|
|
167
|
+
organizationId: organization_id,
|
|
168
|
+
kind,
|
|
169
|
+
expectedRevision: expected_revision,
|
|
170
|
+
idempotencyKey: idempotency_key,
|
|
171
|
+
});
|
|
172
|
+
return toolResult(compactPublished(data));
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
return handleToolError(error);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
@@ -67,7 +67,7 @@ export function registerOrganizationTools(server) {
|
|
|
67
67
|
server.tool("discover_leadify_context", "Safe zero-ID entry point for Leadify. With no organization_id, lists accessible organizations " +
|
|
68
68
|
"and never chooses one when several are available. With an explicit organization_id, returns " +
|
|
69
69
|
"a compact list of its lead groups. Use the returned lead_group_id with lead, campaign, persona " +
|
|
70
|
-
"and
|
|
70
|
+
"and Context Workspace tools.", {
|
|
71
71
|
organization_id: z.string().optional().describe("Organization selected explicitly after discovery. Omit only for the first step."),
|
|
72
72
|
limit: z.number().int().min(1).max(100).optional().default(50).describe("Maximum lead groups returned after selection."),
|
|
73
73
|
}, async ({ organization_id, limit }) => {
|
|
@@ -92,7 +92,7 @@ export function registerOrganizationTools(server) {
|
|
|
92
92
|
leadGroups: groups.slice(0, limit).map((group) => ({ id: group.id, name: group.name, organizationId: group.organizationId, leadCount: group._count?.leads ?? 0, published: group.published ?? false })),
|
|
93
93
|
total: groups.length,
|
|
94
94
|
truncated: groups.length > limit,
|
|
95
|
-
next: "Choose a lead_group_id, then use get_leads,
|
|
95
|
+
next: "Choose a lead_group_id, then use get_leads, get_context_workspace, or get_lead_group_persona.",
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
98
|
catch (error) {
|