@opennous/mcp 0.60.0 → 0.61.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 +59 -5
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -105,15 +105,18 @@ export function createServer() {
|
|
|
105
105
|
],
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
// Nous is fully a revenue plugin: this server exposes EXACTLY
|
|
108
|
+
// Nous is fully a revenue plugin: this server exposes EXACTLY these primitives and
|
|
109
109
|
// nothing else (the ~30 legacy tools were pruned). Writes: record · record_insight.
|
|
110
110
|
// Reads: get_context · get_account · query (search folds in via facts:true) · score.
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
111
|
+
// ICP: set_icp writes the hypothesis; record_closed_deals trains it on real
|
|
112
|
+
// won/lost outcomes (contrastive lift). Identity: whoami (who the key acts as,
|
|
113
|
+
// scope, GTM role[s]) — the governance substrate. record_signal folds into record;
|
|
114
|
+
// save_note is out. The `tool()` guard keeps the surface locked to these nine — a
|
|
115
|
+
// stray non-primitive registration is dropped, not exposed. See
|
|
116
|
+
// docs/revenue-plugin/README.md §2–3.
|
|
115
117
|
const PLUGIN_TOOLS = new Set([
|
|
116
118
|
"record", "record_insight", "get_context", "get_account", "query", "score", "whoami", "set_icp",
|
|
119
|
+
"record_closed_deals",
|
|
117
120
|
]);
|
|
118
121
|
const _tool = server.tool.bind(server);
|
|
119
122
|
const tool = (name, ...rest) => {
|
|
@@ -484,6 +487,57 @@ export function createServer() {
|
|
|
484
487
|
}
|
|
485
488
|
);
|
|
486
489
|
|
|
490
|
+
// ===========================================================================
|
|
491
|
+
// TOOL: record_closed_deals — POST /v2/workspace/closed-deals
|
|
492
|
+
// Train the ICP on REAL outcomes. set_icp writes the hypothesis; this upgrades
|
|
493
|
+
// it to an outcome-graded model: feed the won and lost domains (the CRM/Stripe
|
|
494
|
+
// closed cohorts), the engine runs contrastive lift — what separates wins from
|
|
495
|
+
// losses — links known contacts, resolves their open predictions with the real
|
|
496
|
+
// outcome, and re-scores every open account. The onboarding + win-loss payoff.
|
|
497
|
+
// ===========================================================================
|
|
498
|
+
tool(
|
|
499
|
+
"record_closed_deals",
|
|
500
|
+
"Train this workspace's ICP on REAL closed deals. Pass the won and lost deals (each a bare " +
|
|
501
|
+
"domain, or an object {domain, amount, currency, closed_at}). The engine enriches each domain, " +
|
|
502
|
+
"links the contacts you already have there, resolves their open ICP predictions with the real " +
|
|
503
|
+
"outcome, runs contrastive lift (the signals that separate wins from losses), and re-scores every " +
|
|
504
|
+
"open account against the upgraded model. Use it in onboarding once the backfill has landed the " +
|
|
505
|
+
"closed cohorts (pull them with query scope.property:'stage' — accounts at 'closed_won' and " +
|
|
506
|
+
"'closed_lost'), and whenever new deals close. Needs at least one deal; both sides (some won AND " +
|
|
507
|
+
"some lost) make the lift trustworthy — one side alone is directional only. The ICP is the one " +
|
|
508
|
+
"company model, so this is an admin/founder action.",
|
|
509
|
+
{
|
|
510
|
+
won: z.array(CLOSED_DEAL).optional().describe("Closed-WON deals — bare domains or {domain, amount, currency, closed_at}. Pull from accounts at stage 'closed_won'."),
|
|
511
|
+
lost: z.array(CLOSED_DEAL).optional().describe("Closed-LOST deals — same shape. Pull from accounts at stage 'closed_lost'."),
|
|
512
|
+
},
|
|
513
|
+
async ({ won = [], lost = [] }) => {
|
|
514
|
+
const r = await post("/v2/workspace/closed-deals", { won, lost });
|
|
515
|
+
if (r.need_more_deals || r.error === "need_more_deals") {
|
|
516
|
+
return { content: [{ type: "text", text:
|
|
517
|
+
"Need at least one closed deal (a won or lost domain) to train the ICP. Pull the closed cohorts with query scope.property:'stage', then pass their domains." }] };
|
|
518
|
+
}
|
|
519
|
+
const lines = [
|
|
520
|
+
`ICP trained on real outcomes — ${r.won || 0} won · ${r.lost || 0} lost` +
|
|
521
|
+
(r.enriched ? ` · ${r.enriched} companies enriched` : "") +
|
|
522
|
+
(r.surfaced ? ` · ${r.surfaced} predictions resolved` : "") + ".",
|
|
523
|
+
];
|
|
524
|
+
if ((r.discovered || []).length) {
|
|
525
|
+
lines.push("", "Signals the model learned (contrastive lift, won vs lost):");
|
|
526
|
+
for (const d of r.discovered) {
|
|
527
|
+
const dir = (d.weight ?? 0) > 0 ? "+" : "";
|
|
528
|
+
lines.push(` • ${d.label} [${dir}${d.weight}]${d.note ? ` — ${d.note}` : ""}`);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
if ((r.linked || []).length) {
|
|
532
|
+
const names = r.linked.map(l => l.name).filter(Boolean);
|
|
533
|
+
lines.push("", `Linked ${r.linked.length} known contact${r.linked.length !== 1 ? "s" : ""} to these deals` +
|
|
534
|
+
(names.length ? `: ${names.slice(0, 8).join(", ")}${names.length > 8 ? "…" : ""}` : "") + ".");
|
|
535
|
+
}
|
|
536
|
+
lines.push("", "Open accounts have been re-scored. score / get_context now reflect the outcome-graded ICP.");
|
|
537
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
538
|
+
}
|
|
539
|
+
);
|
|
540
|
+
|
|
487
541
|
// ===========================================================================
|
|
488
542
|
// TOOL: query — POST /v2/query
|
|
489
543
|
// Retrieve a corpus of activity across many people. You do the analysis.
|