// progression-party-lookup — resolve an inbound sender's email or phone to the // chain link and party role it belongs to. The entry point the progression-inbox // workflow uses to classify a solicitor/buyer/vendor email to its deal. May // return more than one match (a solicitor acting on several chains). Read-only. import { z } from "zod"; import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { lifelineTool } from "../../../../../../../platform/lib/mcp-lifeline/dist/index.js"; import { getSession } from "../lib/neo4j.js"; import { lookupParty } from "../lib/chain-graph.js"; function mask(v?: string): string { if (!v) return "none"; return v.length <= 3 ? "***" : `${v.slice(0, 2)}***${v.slice(-1)}`; } export function registerPartyLookup(server: McpServer, accountId: string): void { lifelineTool( server, "progression-party-lookup", "Resolve an inbound contact (email or phone) to the chain link(s) and party role(s) it belongs to. Use to classify a solicitor/buyer/vendor email to its deal before reading or updating the chain. Read-only.", { email: z.string().optional().describe("Sender email address."), phone: z.string().optional().describe("Sender phone number."), }, async (params: Record) => { const { email, phone } = params as { email?: string; phone?: string }; if (!email && !phone) { return { content: [{ type: "text" as const, text: "Error: one of email or phone is required." }], isError: true, }; } const session = getSession(); try { const matches = await lookupParty(session, accountId, { email, phone }); console.error( `[progression] op=party-lookup email=${mask(email)} phone=${mask(phone)} matches=${matches.length}`, ); return { content: [ { type: "text" as const, text: JSON.stringify({ ok: true, matches }, null, 2) }, ], }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); return { content: [{ type: "text" as const, text: `Error: ${msg}` }], isError: true }; } finally { await session.close(); } }, ); }