@opennous/mcp 0.21.0 → 0.23.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/package.json +1 -1
- package/src/server.js +54 -3
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -30,13 +30,14 @@
|
|
|
30
30
|
* list_triggers — list the workspace's event triggers + available events
|
|
31
31
|
* lead_list_operations — the operations trail of a lead list (imports/enrich/push/replies), filterable
|
|
32
32
|
* check_leads — pre-spend coverage check: which candidates you already own / should re-enrich
|
|
33
|
+
* lead_coverage — attribute coverage estimate ("how many agency founders do we have, by freshness")
|
|
33
34
|
*/
|
|
34
35
|
|
|
35
36
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
36
37
|
import { z } from "zod";
|
|
37
38
|
import { get, post } from "./client.js";
|
|
38
39
|
|
|
39
|
-
export const SERVER_VERSION = "0.
|
|
40
|
+
export const SERVER_VERSION = "0.23.0";
|
|
40
41
|
|
|
41
42
|
// ─── helpers ──────────────────────────────────────────────────────────────────
|
|
42
43
|
|
|
@@ -520,6 +521,8 @@ export function createServer() {
|
|
|
520
521
|
|
|
521
522
|
const ws = s.workspace ?? {};
|
|
522
523
|
lines.push(`WORKSPACE: ${ws.name || "(unnamed)"}${ws.website ? ` · ${ws.website}` : ""}${ws.business_type ? ` · ${ws.business_type}` : ""}`);
|
|
524
|
+
const pl = s.plan ?? {};
|
|
525
|
+
lines.push(`PLAN: ${pl.name || pl.id || "free"}${pl.crm_sync === false ? " (CRM sync not included — do not offer it)" : ""}`);
|
|
523
526
|
lines.push("");
|
|
524
527
|
|
|
525
528
|
const mark = (b) => (b ? "✓" : "✗");
|
|
@@ -529,7 +532,11 @@ export function createServer() {
|
|
|
529
532
|
const ints = setup.integrations?.connected ?? [];
|
|
530
533
|
lines.push(` ${mark((setup.integrations?.count ?? 0) > 0)} Integrations (${setup.integrations?.count ?? 0})${ints.length ? `: ${ints.map((i) => i.name).join(", ")}` : ""}`);
|
|
531
534
|
const crm = setup.crm_sync ?? {};
|
|
532
|
-
|
|
535
|
+
if (crm.available === false) {
|
|
536
|
+
lines.push(` – CRM sync (not on the ${pl.name || pl.id || "current"} plan)`);
|
|
537
|
+
} else {
|
|
538
|
+
lines.push(` ${mark(crm.configured)} CRM sync${crm.configured ? `: ${(crm.providers ?? []).map((p) => p.provider).join(", ")}` : ""}${crm.pending_hygiene_proposals ? ` · ${crm.pending_hygiene_proposals} hygiene proposal(s) to review` : ""}`);
|
|
539
|
+
}
|
|
533
540
|
lines.push(` ${mark(setup.enrichment?.connected)} Enrichment${setup.enrichment?.provider ? `: ${setup.enrichment.provider}` : ""}`);
|
|
534
541
|
lines.push(` ${mark((setup.webhooks?.count ?? 0) > 0 || (setup.triggers?.count ?? 0) > 0)} Events — ${setup.webhooks?.count ?? 0} webhook(s), ${setup.triggers?.count ?? 0} trigger(s)`);
|
|
535
542
|
|
|
@@ -606,7 +613,10 @@ export function createServer() {
|
|
|
606
613
|
"update_gtm_profile, then call this to translate that context into a weighted set of scoring " +
|
|
607
614
|
"signals so accounts get scored for fit. If a model already exists it is left alone unless you " +
|
|
608
615
|
"pass force:true (use that when the GTM context has changed and the model should be rebuilt). If " +
|
|
609
|
-
"it reports no GTM context yet, record some with update_gtm_profile first, then call this again."
|
|
616
|
+
"it reports no GTM context yet, record some with update_gtm_profile first, then call this again. " +
|
|
617
|
+
"When building the playbook during onboarding, also ASK the user for a few closed-WON customer " +
|
|
618
|
+
"domains and closed-LOST domains — real outcomes sharpen the ICP. Record them with update_gtm_profile " +
|
|
619
|
+
"(e.g. section 'ICP': 'Closed-won: acme.com, globex.com; closed-lost: tinyco.io') so the model reflects who actually buys.",
|
|
610
620
|
{
|
|
611
621
|
force: z.boolean().optional()
|
|
612
622
|
.describe("Rebuild the model even if one already exists — use when the GTM context has changed."),
|
|
@@ -867,5 +877,46 @@ export function createServer() {
|
|
|
867
877
|
}
|
|
868
878
|
);
|
|
869
879
|
|
|
880
|
+
// ===========================================================================
|
|
881
|
+
// TOOL: lead_coverage — GET /v2/people/coverage
|
|
882
|
+
// The attribute-based planning check: "how many <agency founders> do we already
|
|
883
|
+
// have, and how fresh?" — answered WITHOUT pasting identifiers. Use it before
|
|
884
|
+
// building a list elsewhere to see how much you already cover.
|
|
885
|
+
// ===========================================================================
|
|
886
|
+
server.tool(
|
|
887
|
+
"lead_coverage",
|
|
888
|
+
"Estimate how many people you ALREADY have matching a role/keyword, bucketed by enrichment " +
|
|
889
|
+
"freshness — the planning question before building a list (no identifiers to paste). E.g. " +
|
|
890
|
+
"title='founder', keyword='agency' → how many agency founders are already in your workspace, " +
|
|
891
|
+
"how many were never enriched or are stale (>90d, so re-enrich), and how many have a fresh " +
|
|
892
|
+
"verified email. Rough by design (title is precise; keyword matches title/company/department). " +
|
|
893
|
+
"For an exact net-new check against specific candidates, use check_leads with their identifiers.",
|
|
894
|
+
{
|
|
895
|
+
title: z.string().optional().describe("Role match, e.g. 'founder', 'VP Sales' (matches job_title)."),
|
|
896
|
+
keyword: z.string().optional().describe("Extra match across title/company/department, e.g. 'agency'."),
|
|
897
|
+
stale_days: z.number().optional().describe("Days after which enrichment counts as stale (default 90)."),
|
|
898
|
+
},
|
|
899
|
+
async ({ title, keyword, stale_days }) => {
|
|
900
|
+
if (!title && !keyword) {
|
|
901
|
+
return { content: [{ type: "text", text: "Pass a title and/or keyword, e.g. title='founder', keyword='agency'." }] };
|
|
902
|
+
}
|
|
903
|
+
const r = await get("/v2/people/coverage", { title, keyword, stale_days });
|
|
904
|
+
const lines = [
|
|
905
|
+
`COVERAGE — ${[title && `title~"${title}"`, keyword && `keyword~"${keyword}"`].filter(Boolean).join(" + ")}`,
|
|
906
|
+
` ${r.total ?? 0} already in your workspace`,
|
|
907
|
+
` ${r.needs_enrichment ?? 0} need (re-)enrichment (${r.never_enriched ?? 0} never enriched · ${r.stale ?? 0} stale >90d)`,
|
|
908
|
+
` ${r.fresh_verified ?? 0} have a fresh verified email`,
|
|
909
|
+
];
|
|
910
|
+
const sample = r.sample || [];
|
|
911
|
+
if (sample.length) {
|
|
912
|
+
lines.push("", "SAMPLE (oldest first):");
|
|
913
|
+
for (const s of sample.slice(0, 12)) {
|
|
914
|
+
lines.push(` ${[s.job_title, s.company].filter(Boolean).join(" @ ") || s.entity_id} [${s.enriched_at ? `enriched ${relAge(s.enriched_at)}` : "never enriched"}]`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
918
|
+
}
|
|
919
|
+
);
|
|
920
|
+
|
|
870
921
|
return server;
|
|
871
922
|
}
|