@opennous/mcp 0.58.0 → 0.59.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 +40 -6
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -29,7 +29,7 @@ const APP_URL = () => (process.env.NOUS_APP_URL || "https://app.opennous.cloud")
|
|
|
29
29
|
const OAUTH_CONNECT = { gmail: "gmail", gmail_oauth: "gmail", google: "gmail", "google-mail": "gmail", googlemail: "gmail", linkedin: "linkedin" };
|
|
30
30
|
const connectLink = (slug) => `${APP_URL()}/settings?section=integrations&connect=${slug}`;
|
|
31
31
|
|
|
32
|
-
export const SERVER_VERSION = "0.
|
|
32
|
+
export const SERVER_VERSION = "0.59.0";
|
|
33
33
|
|
|
34
34
|
// ─── helpers ──────────────────────────────────────────────────────────────────
|
|
35
35
|
|
|
@@ -67,6 +67,24 @@ function whenLabel(type, ts) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
const fmtType = (p) => (p || "").replace(/^interaction\./, "").replace(/_/g, " ");
|
|
70
|
+
// The EVIDENCE line under a fact: who said it, their verbatim words, and where it
|
|
71
|
+
// came from. This is what makes a fact checkable instead of something the user has
|
|
72
|
+
// to take on faith — the rep reads the actual sentence rather than trusting a
|
|
73
|
+
// summary. Returns [] when there is nothing to show: facts recorded before evidence
|
|
74
|
+
// capture shipped, and facts with nothing quotable (an attendee list, a CRM field),
|
|
75
|
+
// so the block degrades quietly on historical data instead of looking broken.
|
|
76
|
+
function evidenceLines(f) {
|
|
77
|
+
const out = [];
|
|
78
|
+
const who = f.speaker ? String(f.speaker).trim() : "";
|
|
79
|
+
const quote = f.quote ? String(f.quote).trim() : "";
|
|
80
|
+
const where = [f.source, f.date ? new Date(f.date).toISOString().slice(0, 10) : null]
|
|
81
|
+
.filter(Boolean).join(" \u00b7 ");
|
|
82
|
+
if (quote) out.push(` ${who ? `${who} \u00b7 ` : ""}"${quote}"`);
|
|
83
|
+
else if (who) out.push(` ${who}`);
|
|
84
|
+
if (where && (quote || who)) out.push(` ${where}`);
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
70
88
|
const fmtVal = (v) => (v != null && typeof v === "object") ? JSON.stringify(v) : String(v ?? "");
|
|
71
89
|
const pct = (c) => `${Math.round((c ?? 0) * 100)}%`;
|
|
72
90
|
|
|
@@ -92,7 +110,7 @@ export function createServer() {
|
|
|
92
110
|
// Reads: get_context · get_account · query (search folds in via facts:true) · score.
|
|
93
111
|
// Identity: whoami (who the key acts as, scope, GTM role[s]) — the governance
|
|
94
112
|
// substrate. record_signal folds into record; save_note is out. The `tool()` guard
|
|
95
|
-
// keeps the surface locked to these
|
|
113
|
+
// keeps the surface locked to these eight — a stray non-primitive registration is
|
|
96
114
|
// dropped, not exposed. See docs/revenue-plugin/README.md §2–3.
|
|
97
115
|
const PLUGIN_TOOLS = new Set([
|
|
98
116
|
"record", "record_insight", "get_context", "get_account", "query", "score", "whoami", "set_icp",
|
|
@@ -145,7 +163,10 @@ export function createServer() {
|
|
|
145
163
|
if (ctx.facts?.length) {
|
|
146
164
|
// Atomic memory — the durable, decision-relevant facts learned about them.
|
|
147
165
|
lines.push(`FACTS (${ctx.facts.length} — durable memory about them):`);
|
|
148
|
-
for (const f of ctx.facts)
|
|
166
|
+
for (const f of ctx.facts) {
|
|
167
|
+
lines.push(` [${f.category}] ${f.content}${f.date ? ` (${relAge(f.date)})` : ""}`);
|
|
168
|
+
for (const e of evidenceLines(f)) lines.push(e);
|
|
169
|
+
}
|
|
149
170
|
lines.push("");
|
|
150
171
|
}
|
|
151
172
|
if (ctx.claims?.length) {
|
|
@@ -261,7 +282,10 @@ export function createServer() {
|
|
|
261
282
|
if (rec.facts?.length) {
|
|
262
283
|
// Atomic memory — the durable, decision-relevant facts learned about them.
|
|
263
284
|
lines.push(`FACTS (${rec.facts.length} — durable memory about them):`);
|
|
264
|
-
for (const f of rec.facts)
|
|
285
|
+
for (const f of rec.facts) {
|
|
286
|
+
lines.push(` [${f.category}] ${f.content}${f.date ? ` (${relAge(f.date)})` : ""}`);
|
|
287
|
+
for (const e of evidenceLines(f)) lines.push(e);
|
|
288
|
+
}
|
|
265
289
|
lines.push("");
|
|
266
290
|
}
|
|
267
291
|
const docs = rec.documents ?? [];
|
|
@@ -357,8 +381,17 @@ export function createServer() {
|
|
|
357
381
|
"about the contact — a preference, an objection, a competitor they mentioned, a general note — " +
|
|
358
382
|
"use property:'intel' with a category: {kind:'state',property:'intel',value:{category:'objection'," +
|
|
359
383
|
"content:'skeptical about market demand',label:'Market demand skepticism'}} (category ∈ " +
|
|
360
|
-
"
|
|
361
|
-
"
|
|
384
|
+
"status_quo | goal | pain | objection | authority | budget | timeline | buying_signal | " +
|
|
385
|
+
"preference | competitor | discovery | relationship | general — use the MOST specific one; " +
|
|
386
|
+
"'general' is a last resort, and it's 'timeline', never 'timing'). " +
|
|
387
|
+
"Insights about YOUR OWN business (product/positioning/market/buyer) go to record_insight, not here. " +
|
|
388
|
+
"CLOSING THE LOOP — when you RECOMMEND an action, write the recommendation down before it happens: " +
|
|
389
|
+
"{kind:'state',property:'decision.proposed',value:{decision_id:'<your id>',proposal:'send a follow-up " +
|
|
390
|
+
"naming the security objection',action_type:'email_send',category:'outreach'}}. When the human answers, " +
|
|
391
|
+
"record the verdict: property 'decision.accepted' | 'decision.edited' | 'decision.rejected' with the same " +
|
|
392
|
+
"decision_id (pass drafted_body + sent_body on accepted and the edit is measured for you). Then pass that " +
|
|
393
|
+
"same decision_id on the interaction observation when the action fires. That is what turns advice into " +
|
|
394
|
+
"learnable data — without it a send is an orphan nobody can grade.",
|
|
362
395
|
{
|
|
363
396
|
focus: z.string().describe("Email address, LinkedIn URL, domain, or entity UUID — a precise identifier, never a bare name"),
|
|
364
397
|
observations: z.array(z.object({
|
|
@@ -371,6 +404,7 @@ export function createServer() {
|
|
|
371
404
|
external_id: z.string().optional().describe("idempotency key, unique per (source). Re-recording the same one is a no-op. One raw item → many observations → give each a distinct id, e.g. '<itemId>:<property>'."),
|
|
372
405
|
source_ref: z.string().optional().describe("pointer to the raw this came from, e.g. 'git://<repo>/raw/fireflies/2026-03-14-9182.md'. Keeps provenance without storing raw in Nous."),
|
|
373
406
|
content_hash: z.string().optional().describe("sha256 of the raw file, so a later fetch can be verified untampered. Set when raw lives in git (not sent here)."),
|
|
407
|
+
decision_id: z.string().optional().describe("the recommendation this action came from. Stamp it on the interaction observation you record when the action fires (an email send, a LinkedIn message) and Nous attributes whatever comes back — reply, meeting, bounce, silence — to that decision."),
|
|
374
408
|
})).describe("One or more observations to record"),
|
|
375
409
|
},
|
|
376
410
|
async ({ focus, observations }) => {
|