@edda-business/mcp 0.57.0 → 0.58.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 +35 -53
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -26,7 +26,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
26
26
|
import { z } from "zod";
|
|
27
27
|
import { get, post, del } from "./client.js";
|
|
28
28
|
|
|
29
|
-
export const SERVER_VERSION = "0.
|
|
29
|
+
export const SERVER_VERSION = "0.58.0";
|
|
30
30
|
|
|
31
31
|
// ─── helpers ──────────────────────────────────────────────────────────────────
|
|
32
32
|
|
|
@@ -91,13 +91,12 @@ export function createServer() {
|
|
|
91
91
|
server.tool(
|
|
92
92
|
"get_context",
|
|
93
93
|
"Get engineered context for a specific task about a person or company. Pass their email (or " +
|
|
94
|
-
"entity id) and the intent. Returns a focused, ranked context block: the
|
|
95
|
-
"
|
|
96
|
-
"
|
|
97
|
-
"
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"act on it, 'suspect'/'expired' verify first.",
|
|
94
|
+
"entity id) and the intent. Returns a focused, ranked context block: the durable FACTS we've " +
|
|
95
|
+
"learned about them (each with a confidence and a freshness), the recent timeline, the other " +
|
|
96
|
+
"people at their company and how they relate, and the relevant notes and records. Call this before " +
|
|
97
|
+
"a meeting or any decision about a person or company, so you act on what we actually know rather " +
|
|
98
|
+
"than generic guesses. A fact's freshness tells you whether to trust it: 'fresh' act on it, " +
|
|
99
|
+
"'suspect'/'expired' verify first.",
|
|
101
100
|
{
|
|
102
101
|
focus: z.string().describe("Who to look up — an email, a LinkedIn URL, a domain, an entity UUID, or a name. A name may match several people; you'll get candidates to choose from."),
|
|
103
102
|
intent: z.enum(["follow_up", "meeting_prep", "call_prep", "account_review"])
|
|
@@ -131,11 +130,6 @@ export function createServer() {
|
|
|
131
130
|
}
|
|
132
131
|
lines.push("");
|
|
133
132
|
}
|
|
134
|
-
if (ctx.workspace?.length) {
|
|
135
|
-
lines.push("YOUR CONTEXT (ICP / product / positioning):");
|
|
136
|
-
for (const w of ctx.workspace) lines.push(` ${w.property}: ${fmtVal(w.value)}`);
|
|
137
|
-
lines.push("");
|
|
138
|
-
}
|
|
139
133
|
if (ctx.timeline?.length) {
|
|
140
134
|
lines.push("TIMELINE:");
|
|
141
135
|
for (const t of ctx.timeline) {
|
|
@@ -157,23 +151,18 @@ export function createServer() {
|
|
|
157
151
|
lines.push("");
|
|
158
152
|
}
|
|
159
153
|
if (ctx.stakeholders?.length) {
|
|
160
|
-
// The
|
|
161
|
-
//
|
|
162
|
-
// whole committee, not one person.
|
|
154
|
+
// The people connected to this company and how they relate — the local map, so
|
|
155
|
+
// the agent sees the whole picture, not one person in isolation.
|
|
163
156
|
const c = ctx.committee;
|
|
164
|
-
lines.push(c?.company ? `
|
|
157
|
+
lines.push(c?.company ? `PEOPLE AT ${c.company}:` : "RELATED PEOPLE:");
|
|
165
158
|
for (const s of ctx.stakeholders) {
|
|
166
159
|
if (s.role === "company") continue; // the company is the header
|
|
167
160
|
const bits = [];
|
|
168
|
-
if (s.committee_role && s.committee_role !== "contact") bits.push(s.committee_role.replace(/_/g, " "));
|
|
169
161
|
if (s.role) bits.push(s.role);
|
|
170
|
-
bits.push(s.engaged ? "engaged" : "not yet engaged");
|
|
171
162
|
if (s.confirmed === false) bits.push("mentioned, unconfirmed");
|
|
172
163
|
const rel = s.relationships?.length ? ` — ${s.relationships.join("; ")}` : "";
|
|
173
|
-
lines.push(` ${s.name ?? "—"} (${bits.join(", ")})${rel}`);
|
|
164
|
+
lines.push(` ${s.name ?? "—"}${bits.length ? ` (${bits.join(", ")})` : ""}${rel}`);
|
|
174
165
|
}
|
|
175
|
-
if (c?.champion) lines.push(` champion: ${c.champion}`);
|
|
176
|
-
if (c?.gaps?.length) for (const g of c.gaps) lines.push(` ⚠ ${g}`);
|
|
177
166
|
lines.push("");
|
|
178
167
|
}
|
|
179
168
|
return {
|
|
@@ -188,8 +177,8 @@ export function createServer() {
|
|
|
188
177
|
// ===========================================================================
|
|
189
178
|
server.tool(
|
|
190
179
|
"get_account",
|
|
191
|
-
"Get the full
|
|
192
|
-
"
|
|
180
|
+
"Get the full record for a person or company — the durable FACTS we've learned about them (their " +
|
|
181
|
+
"role, company, history, and anything else worth remembering), every attribute (claim) with its " +
|
|
193
182
|
"confidence and freshness, plus what they actually SAID and did, ranked by how much it tells you. " +
|
|
194
183
|
"Pass an email or entity UUID, and the intent you're working toward so the record is shaped for it.",
|
|
195
184
|
{
|
|
@@ -243,23 +232,18 @@ export function createServer() {
|
|
|
243
232
|
}
|
|
244
233
|
lines.push("");
|
|
245
234
|
}
|
|
246
|
-
// The
|
|
247
|
-
// engaged them, and how they relate. Same structure get_context surfaces.
|
|
235
|
+
// The people connected to this company and how they relate. Same structure get_context surfaces.
|
|
248
236
|
if (rec.stakeholders?.length) {
|
|
249
237
|
const c = rec.committee;
|
|
250
|
-
lines.push(c?.company ? `
|
|
238
|
+
lines.push(c?.company ? `PEOPLE AT ${c.company}:` : "RELATED PEOPLE:");
|
|
251
239
|
for (const s of rec.stakeholders) {
|
|
252
240
|
if (s.role === "company") continue;
|
|
253
241
|
const bits = [];
|
|
254
|
-
if (s.committee_role && s.committee_role !== "contact") bits.push(s.committee_role.replace(/_/g, " "));
|
|
255
242
|
if (s.role) bits.push(s.role);
|
|
256
|
-
bits.push(s.engaged ? "engaged" : "not yet engaged");
|
|
257
243
|
if (s.confirmed === false) bits.push("mentioned, unconfirmed");
|
|
258
244
|
const rel = s.relationships?.length ? ` — ${s.relationships.join("; ")}` : "";
|
|
259
|
-
lines.push(` ${s.name ?? "—"} (${bits.join(", ")})${rel}`);
|
|
245
|
+
lines.push(` ${s.name ?? "—"}${bits.length ? ` (${bits.join(", ")})` : ""}${rel}`);
|
|
260
246
|
}
|
|
261
|
-
if (c?.champion) lines.push(` champion: ${c.champion}`);
|
|
262
|
-
if (c?.gaps?.length) for (const g of c.gaps) lines.push(` ⚠ ${g}`);
|
|
263
247
|
lines.push("");
|
|
264
248
|
}
|
|
265
249
|
const claims = Object.values(rec.claims ?? {});
|
|
@@ -421,22 +405,21 @@ export function createServer() {
|
|
|
421
405
|
// ===========================================================================
|
|
422
406
|
server.tool(
|
|
423
407
|
"query",
|
|
424
|
-
"Retrieve and summarise activity across many people.
|
|
408
|
+
"Retrieve and summarise activity across many people and companies. Powers:\n" +
|
|
425
409
|
" 1. return:'entities' groups results by person/company (one row per entity, ranked by " +
|
|
426
|
-
"most-recent matching activity). Use for '
|
|
427
|
-
"'
|
|
428
|
-
"
|
|
429
|
-
"'
|
|
430
|
-
"
|
|
431
|
-
"(use scope.property='stage' for funnel reports).\n" +
|
|
410
|
+
"most-recent matching activity). Use for 'who we talked to this week', 'who replied recently'.\n" +
|
|
411
|
+
" 2. `without` subtracts entities — 'sent in 5d MINUS replied in 5d' = 'wrote to, no reply back'. " +
|
|
412
|
+
"'activity in 30d MINUS activity in 5d' = 'people who've gone quiet'.\n" +
|
|
413
|
+
" 3. rollups.by_value appears when scope.kind='state' — counts entities by a current value " +
|
|
414
|
+
"(scope.property='<state property>').\n" +
|
|
432
415
|
" 4. Scheduled meetings/calls are events with property 'interaction.meeting_scheduled' and a " +
|
|
433
416
|
"future-dated `when`. For 'what's booked today/this week', set property:'interaction.meeting_scheduled' " +
|
|
434
417
|
"with from/to bounding the day or week (since_days only looks backward and can't reach them), and " +
|
|
435
418
|
"order:'asc' to list soonest-first. Meeting rows render the absolute date and time.\n" +
|
|
436
|
-
" 5. scope.facts:true + question searches the FACTS corpus (durable
|
|
437
|
-
"instead of activity — cross-
|
|
438
|
-
"
|
|
439
|
-
"
|
|
419
|
+
" 5. scope.facts:true + question searches the FACTS corpus (durable facts about people/companies) " +
|
|
420
|
+
"instead of activity — cross-record semantic fact search like 'who is hiring' or 'who mentioned " +
|
|
421
|
+
"switching tools'. return:'entities' gives the single best-matching fact per record. (A single " +
|
|
422
|
+
"record's facts already come back inline with get_account.)",
|
|
440
423
|
{
|
|
441
424
|
scope: z.object({
|
|
442
425
|
kind: z.enum(["event", "state"]).optional(),
|
|
@@ -448,7 +431,7 @@ export function createServer() {
|
|
|
448
431
|
to: z.string().optional().describe("ISO timestamp — only activity at/before this (absolute upper bound). Combine from+to for a window; future-dated for upcoming meetings"),
|
|
449
432
|
order: z.enum(["asc", "desc"]).optional().describe("observed_at order (default desc, newest first). Use 'asc' for an upcoming-meeting schedule (soonest first)"),
|
|
450
433
|
limit: z.number().optional().describe("max items (default 50, cap 200)"),
|
|
451
|
-
facts: z.boolean().optional().describe("search the FACTS corpus (durable
|
|
434
|
+
facts: z.boolean().optional().describe("search the FACTS corpus (durable facts about people/companies) instead of activity. Needs `question` — a cross-record semantic fact search, e.g. 'who is hiring'. return:'entities' = the best matching fact per record."),
|
|
452
435
|
}).describe("Corpus filter"),
|
|
453
436
|
without: z.object({
|
|
454
437
|
kind: z.enum(["event", "state"]).optional(),
|
|
@@ -537,12 +520,12 @@ export function createServer() {
|
|
|
537
520
|
// ===========================================================================
|
|
538
521
|
server.tool(
|
|
539
522
|
"verify",
|
|
540
|
-
"Re-check a specific fact before you act on it — e.g. an email or a
|
|
523
|
+
"Re-check a specific fact before you act on it — e.g. an email or a job title that looks stale " +
|
|
541
524
|
"in get_context. Pass the person/company and the property name. Returns the fact re-derived from " +
|
|
542
525
|
"current evidence, and tells you whether it is still unverified.",
|
|
543
526
|
{
|
|
544
527
|
focus: z.string().describe("Email, LinkedIn URL, entity UUID, or name"),
|
|
545
|
-
property: z.string().describe("The fact to re-check — e.g. 'email', 'job_title', '
|
|
528
|
+
property: z.string().describe("The fact to re-check — e.g. 'email', 'job_title', 'company'"),
|
|
546
529
|
},
|
|
547
530
|
async ({ focus, property }) => {
|
|
548
531
|
const r = await post("/v2/verify", { focus, property });
|
|
@@ -761,14 +744,13 @@ export function createServer() {
|
|
|
761
744
|
// ===========================================================================
|
|
762
745
|
server.tool(
|
|
763
746
|
"connect_integration",
|
|
764
|
-
"Connect a
|
|
765
|
-
"
|
|
766
|
-
"
|
|
767
|
-
"
|
|
768
|
-
"
|
|
769
|
-
"page. After connecting an enrichment provider, the account record starts filling in.",
|
|
747
|
+
"Connect a data source that feeds the knowledge base — a provider that authenticates with an API " +
|
|
748
|
+
"key or token. Ask the user for the provider's key, then call this; it verifies the credentials " +
|
|
749
|
+
"before saving. Providers that use a browser sign-in (OAuth, e.g. Gmail or calendar) can't be " +
|
|
750
|
+
"connected this way — for those, point the user to the Integrations page. Once connected, the " +
|
|
751
|
+
"source starts feeding the knowledge base.",
|
|
770
752
|
{
|
|
771
|
-
provider: z.string().describe("Provider name, lowercase
|
|
753
|
+
provider: z.string().describe("Provider name, lowercase."),
|
|
772
754
|
credentials: z.record(z.string()).describe("The provider's credentials as key/value, e.g. { api_key: '...' } or { access_token: '...' }."),
|
|
773
755
|
name: z.string().optional().describe("Optional label for the connection."),
|
|
774
756
|
},
|