@mentoringo/vantage-ops-mcp 1.3.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.
@@ -0,0 +1,214 @@
1
+ import { z } from "zod";
2
+ import { tables, scanTable, getItem, apiHealth, describeTable, readsConfigured } from "../dynamo.js";
3
+ import { clientEntity, userEntity, listOutput, getOutput, healthOutput, tableCountsOutput, } from "../schemas.js";
4
+ export function registerAdminTools(server) {
5
+ server.registerTool("vantage_health", {
6
+ description: "Check Vantage API health and MCP read-surface auth",
7
+ inputSchema: {},
8
+ outputSchema: healthOutput,
9
+ }, async () => {
10
+ try {
11
+ const api = await apiHealth();
12
+ const keyConfigured = readsConfigured();
13
+ const summary = [
14
+ "Vantage Health Check",
15
+ "",
16
+ `API: ${api.healthy ? "OK" : "DOWN"} — ${api.message}`,
17
+ `MCP key: ${keyConfigured ? "configured" : "NOT SET (reads will fail)"}`,
18
+ ].join("\n");
19
+ return {
20
+ content: [{ type: "text", text: summary }],
21
+ structuredContent: {
22
+ healthy: api.healthy && keyConfigured,
23
+ details: {
24
+ api_healthy: api.healthy,
25
+ api_message: api.message,
26
+ key_configured: keyConfigured,
27
+ },
28
+ summary,
29
+ },
30
+ };
31
+ }
32
+ catch (e) {
33
+ const summary = `Health check failed: ${e.message}`;
34
+ return {
35
+ content: [{ type: "text", text: summary }],
36
+ structuredContent: {
37
+ healthy: false,
38
+ details: { error: e.message },
39
+ summary,
40
+ },
41
+ isError: true,
42
+ };
43
+ }
44
+ });
45
+ server.registerTool("vantage_clients_list", {
46
+ description: "List all Vantage clients/tenants",
47
+ inputSchema: {
48
+ status: z
49
+ .string()
50
+ .optional()
51
+ .describe("Filter by client status (e.g. active, inactive)"),
52
+ },
53
+ outputSchema: listOutput(clientEntity),
54
+ }, async ({ status }) => {
55
+ try {
56
+ let items;
57
+ if (status) {
58
+ // Use scan with filter since client table is small
59
+ const result = await scanTable(tables.clients, {
60
+ limit: 50,
61
+ filterExpression: "#s = :s",
62
+ expressionValues: { ":s": { S: status } },
63
+ expressionNames: { "#s": "status" },
64
+ });
65
+ items = result.items;
66
+ }
67
+ else {
68
+ const result = await scanTable(tables.clients, { limit: 50 });
69
+ items = result.items;
70
+ }
71
+ if (items.length === 0) {
72
+ const summary = "No clients found.";
73
+ return {
74
+ content: [{ type: "text", text: summary }],
75
+ structuredContent: { items: [], count: 0, summary },
76
+ };
77
+ }
78
+ const lines = items.map((c) => {
79
+ return `${c.name || c.id}\n id: ${c.id} | status: ${c.status || "—"} | slug: ${c.slug || "—"} | industry: ${c.industry || "—"}`;
80
+ });
81
+ const summary = `${items.length} clients:\n\n${lines.join("\n\n")}`;
82
+ return {
83
+ content: [{ type: "text", text: summary }],
84
+ structuredContent: { items, count: items.length, summary },
85
+ };
86
+ }
87
+ catch (e) {
88
+ const summary = `Error listing clients: ${e.stderr?.trim() || e.message}`;
89
+ return {
90
+ content: [{ type: "text", text: summary }],
91
+ structuredContent: { items: [], count: 0, summary },
92
+ isError: true,
93
+ };
94
+ }
95
+ });
96
+ server.registerTool("vantage_client_get", {
97
+ description: "Get full details for a single Vantage client by ID",
98
+ inputSchema: {
99
+ id: z.string().describe("Client ID"),
100
+ },
101
+ outputSchema: getOutput(clientEntity),
102
+ }, async ({ id }) => {
103
+ try {
104
+ const item = await getItem(tables.clients, { id: { S: id } });
105
+ if (!item) {
106
+ const summary = `Client ${id} not found.`;
107
+ return {
108
+ content: [{ type: "text", text: summary }],
109
+ structuredContent: { found: false, summary },
110
+ };
111
+ }
112
+ const summary = JSON.stringify(item, null, 2);
113
+ return {
114
+ content: [{ type: "text", text: summary }],
115
+ structuredContent: { found: true, item, summary },
116
+ };
117
+ }
118
+ catch (e) {
119
+ const summary = `Error fetching client: ${e.stderr?.trim() || e.message}`;
120
+ return {
121
+ content: [{ type: "text", text: summary }],
122
+ structuredContent: { found: false, summary },
123
+ isError: true,
124
+ };
125
+ }
126
+ });
127
+ server.registerTool("vantage_users_list", {
128
+ description: "List all Vantage platform users",
129
+ inputSchema: {
130
+ client_id: z.string().optional().describe("Filter by client ID"),
131
+ },
132
+ outputSchema: listOutput(userEntity),
133
+ }, async ({ client_id }) => {
134
+ try {
135
+ let items;
136
+ if (client_id) {
137
+ const result = await scanTable(tables.users, {
138
+ limit: 50,
139
+ filterExpression: "client_id = :c",
140
+ expressionValues: { ":c": { S: client_id } },
141
+ });
142
+ items = result.items;
143
+ }
144
+ else {
145
+ const result = await scanTable(tables.users, { limit: 50 });
146
+ items = result.items;
147
+ }
148
+ if (items.length === 0) {
149
+ const summary = "No users found.";
150
+ return {
151
+ content: [{ type: "text", text: summary }],
152
+ structuredContent: { items: [], count: 0, summary },
153
+ };
154
+ }
155
+ const lines = items.map((u) => {
156
+ const name = u.name || [u.first_name, u.last_name].filter(Boolean).join(" ") || u.id;
157
+ return `${name}\n email: ${u.email || "—"} | role: ${u.role || "—"} | client: ${u.client_name || u.client_id || "—"}`;
158
+ });
159
+ const summary = `${items.length} users:\n\n${lines.join("\n\n")}`;
160
+ return {
161
+ content: [{ type: "text", text: summary }],
162
+ structuredContent: { items, count: items.length, summary },
163
+ };
164
+ }
165
+ catch (e) {
166
+ const summary = `Error listing users: ${e.stderr?.trim() || e.message}`;
167
+ return {
168
+ content: [{ type: "text", text: summary }],
169
+ structuredContent: { items: [], count: 0, summary },
170
+ isError: true,
171
+ };
172
+ }
173
+ });
174
+ server.registerTool("vantage_table_counts", {
175
+ description: "Get item counts across all Vantage DynamoDB tables for a quick health overview",
176
+ inputSchema: {},
177
+ outputSchema: tableCountsOutput,
178
+ }, async () => {
179
+ try {
180
+ const aliases = Object.entries(tables);
181
+ const tableMap = {};
182
+ const lineResults = await Promise.all(aliases.map(async ([label, alias]) => {
183
+ try {
184
+ const { itemCount, tableSizeBytes } = await describeTable(alias);
185
+ tableMap[label] = {
186
+ item_count: itemCount,
187
+ size_bytes: tableSizeBytes,
188
+ available: true,
189
+ };
190
+ const sizeMB = (tableSizeBytes / (1024 * 1024)).toFixed(1);
191
+ return ` ${label}: ${itemCount.toLocaleString()} items (${sizeMB} MB)`;
192
+ }
193
+ catch {
194
+ tableMap[label] = { available: false };
195
+ return ` ${label}: unavailable`;
196
+ }
197
+ }));
198
+ const summary = ["Vantage Table Overview", "", ...lineResults].join("\n");
199
+ return {
200
+ content: [{ type: "text", text: summary }],
201
+ structuredContent: { tables: tableMap, summary },
202
+ };
203
+ }
204
+ catch (e) {
205
+ const summary = `Error: ${e.message}`;
206
+ return {
207
+ content: [{ type: "text", text: summary }],
208
+ structuredContent: { tables: {}, summary },
209
+ isError: true,
210
+ };
211
+ }
212
+ });
213
+ }
214
+ //# sourceMappingURL=admin-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-tools.js","sourceRoot":"","sources":["../../src/tools/admin-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCampaignTools(server: McpServer): void;
@@ -0,0 +1,219 @@
1
+ import { z } from "zod";
2
+ import { tables, queryByIndex, queryTable, scanTable, getItem } from "../dynamo.js";
3
+ import { campaignEntity, campaignTargetEntity, listOutput, getOutput, statsOutput, } from "../schemas.js";
4
+ export function registerCampaignTools(server) {
5
+ server.registerTool("vantage_campaigns_list", {
6
+ description: "List Vantage campaigns with optional status or client filter",
7
+ inputSchema: {
8
+ status: z
9
+ .enum(["draft", "active", "paused", "completed"])
10
+ .optional()
11
+ .describe("Filter by campaign status (uses by-status GSI)"),
12
+ client_id: z
13
+ .string()
14
+ .optional()
15
+ .describe("Filter by client ID (uses by-client GSI)"),
16
+ vertical: z
17
+ .string()
18
+ .optional()
19
+ .describe("Filter by vertical (uses by-vertical GSI)"),
20
+ limit: z.number().optional().default(25).describe("Max results (default 25, max 50)"),
21
+ },
22
+ outputSchema: listOutput(campaignEntity),
23
+ }, async ({ status, client_id, vertical, limit }) => {
24
+ try {
25
+ const cap = Math.min(limit || 25, 50);
26
+ let items;
27
+ if (status) {
28
+ const result = await queryByIndex(tables.campaigns, "by-status", "#s = :s", { ":s": { S: status } }, { limit: cap, scanForward: false, expressionNames: { "#s": "status" } });
29
+ items = result.items;
30
+ }
31
+ else if (client_id) {
32
+ const result = await queryByIndex(tables.campaigns, "by-client", "client_id = :c", { ":c": { S: client_id } }, { limit: cap, scanForward: false });
33
+ items = result.items;
34
+ }
35
+ else if (vertical) {
36
+ const result = await queryByIndex(tables.campaigns, "by-vertical", "vertical = :v", { ":v": { S: vertical } }, { limit: cap, scanForward: false });
37
+ items = result.items;
38
+ }
39
+ else {
40
+ const result = await scanTable(tables.campaigns, { limit: cap });
41
+ items = result.items;
42
+ }
43
+ if (items.length === 0) {
44
+ const summary = "No campaigns found.";
45
+ return {
46
+ content: [{ type: "text", text: summary }],
47
+ structuredContent: { items: [], count: 0, summary },
48
+ };
49
+ }
50
+ const lines = items.map((c) => {
51
+ const recipients = c.recipient_count || c.total_recipients || "—";
52
+ return `${c.name || c.id}\n id: ${c.id || "—"} | status: ${c.status || "—"} | type: ${c.campaign_type || "—"} | vertical: ${c.vertical || "—"} | recipients: ${recipients} | created: ${c.created_at || "—"}`;
53
+ });
54
+ const summary = `${items.length} campaigns:\n\n${lines.join("\n\n")}`;
55
+ return {
56
+ content: [{ type: "text", text: summary }],
57
+ structuredContent: { items, count: items.length, summary },
58
+ };
59
+ }
60
+ catch (e) {
61
+ const summary = `Error listing campaigns: ${e.stderr?.trim() || e.message}`;
62
+ return {
63
+ content: [{ type: "text", text: summary }],
64
+ structuredContent: { items: [], count: 0, summary },
65
+ isError: true,
66
+ };
67
+ }
68
+ });
69
+ server.registerTool("vantage_campaign_get", {
70
+ description: "Get full details for a single Vantage campaign by ID. " +
71
+ "Enriches total_targets and target_company_ids from the " +
72
+ "campaign-targets junction table — the values stored on the " +
73
+ "campaign record drift to 0 / [] because add_targets writes only " +
74
+ "to the junction. The REST GET /campaigns/{id} endpoint does the " +
75
+ "same enrichment server-side; this tool mirrors it for MCP " +
76
+ "callers who go through /v2/mcp-reads.",
77
+ inputSchema: {
78
+ id: z.string().describe("Campaign ID"),
79
+ },
80
+ outputSchema: getOutput(campaignEntity),
81
+ }, async ({ id }) => {
82
+ try {
83
+ const item = await getItem(tables.campaigns, { id: { S: id } });
84
+ if (!item) {
85
+ const summary = `Campaign ${id} not found.`;
86
+ return {
87
+ content: [{ type: "text", text: summary }],
88
+ structuredContent: { found: false, summary },
89
+ };
90
+ }
91
+ // Enrich with live junction data. Failure here shouldn't fail the
92
+ // whole get_campaign call — better to return the campaign with the
93
+ // stored (possibly stale) counters than to 500.
94
+ try {
95
+ const targets = await queryTable(tables.campaignTargets, "campaign_id = :c", { ":c": { S: id } }, { limit: 500 });
96
+ item.total_targets = targets.count;
97
+ item.target_company_ids = targets.items
98
+ .map((t) => t.company_id)
99
+ .filter(Boolean);
100
+ }
101
+ catch {
102
+ // Leave the stored values in place if the junction query fails.
103
+ }
104
+ const summary = JSON.stringify(item, null, 2);
105
+ return {
106
+ content: [{ type: "text", text: summary }],
107
+ structuredContent: { found: true, item, summary },
108
+ };
109
+ }
110
+ catch (e) {
111
+ const summary = `Error fetching campaign: ${e.stderr?.trim() || e.message}`;
112
+ return {
113
+ content: [{ type: "text", text: summary }],
114
+ structuredContent: { found: false, summary },
115
+ isError: true,
116
+ };
117
+ }
118
+ });
119
+ server.registerTool("vantage_campaigns_stats", {
120
+ description: "Get aggregate campaign statistics — counts by status, type, and vertical",
121
+ inputSchema: {},
122
+ outputSchema: statsOutput,
123
+ }, async () => {
124
+ try {
125
+ const result = await scanTable(tables.campaigns, { limit: 200 });
126
+ const items = result.items;
127
+ const byStatus = {};
128
+ const byType = {};
129
+ const byVertical = {};
130
+ for (const c of items) {
131
+ const status = c.status || "unknown";
132
+ const type = c.campaign_type || "unknown";
133
+ const vertical = c.vertical || "unknown";
134
+ byStatus[status] = (byStatus[status] || 0) + 1;
135
+ byType[type] = (byType[type] || 0) + 1;
136
+ byVertical[vertical] = (byVertical[vertical] || 0) + 1;
137
+ }
138
+ const fmt = (obj) => Object.entries(obj)
139
+ .sort((a, b) => b[1] - a[1])
140
+ .map(([k, v]) => ` ${k}: ${v}`)
141
+ .join("\n");
142
+ const summary = [
143
+ `Total campaigns: ${items.length}`,
144
+ "",
145
+ "By Status:",
146
+ fmt(byStatus),
147
+ "",
148
+ "By Type:",
149
+ fmt(byType),
150
+ "",
151
+ "By Vertical:",
152
+ fmt(byVertical),
153
+ ].join("\n");
154
+ return {
155
+ content: [{ type: "text", text: summary }],
156
+ structuredContent: {
157
+ total: items.length,
158
+ buckets: {
159
+ status: byStatus,
160
+ type: byType,
161
+ vertical: byVertical,
162
+ },
163
+ summary,
164
+ },
165
+ };
166
+ }
167
+ catch (e) {
168
+ const summary = `Error fetching stats: ${e.stderr?.trim() || e.message}`;
169
+ return {
170
+ content: [{ type: "text", text: summary }],
171
+ structuredContent: { total: 0, buckets: {}, summary },
172
+ isError: true,
173
+ };
174
+ }
175
+ });
176
+ server.registerTool("vantage_campaign_list_targets", {
177
+ description: "List the companies attached to a campaign (campaign-targets " +
178
+ "junction). Returns the per-link metadata — priority, added_by, " +
179
+ "added_at — that vantage_campaign_get's target_company_ids field " +
180
+ "doesn't carry. Use this when you need to audit how a campaign's " +
181
+ "target list was built, not just count it.",
182
+ inputSchema: {
183
+ campaign_id: z.string().describe("Campaign ID"),
184
+ limit: z.number().optional().default(100).describe("Max results (default 100, max 500)"),
185
+ },
186
+ outputSchema: listOutput(campaignTargetEntity),
187
+ }, async ({ campaign_id, limit }) => {
188
+ try {
189
+ const cap = Math.min(limit || 100, 500);
190
+ const result = await queryTable(tables.campaignTargets, "campaign_id = :c", { ":c": { S: campaign_id } }, { limit: cap });
191
+ if (result.items.length === 0) {
192
+ const summary = `No targets attached to campaign ${campaign_id}.`;
193
+ return {
194
+ content: [{ type: "text", text: summary }],
195
+ structuredContent: { items: [], count: 0, summary },
196
+ };
197
+ }
198
+ const lines = result.items.map((t) => {
199
+ const priority = t.priority || "—";
200
+ const added = t.added_at || "—";
201
+ return `${t.company_id}\n priority: ${priority} | added: ${added} | by: ${t.added_by || "—"}`;
202
+ });
203
+ const summary = `${result.items.length} targets:\n\n${lines.join("\n\n")}`;
204
+ return {
205
+ content: [{ type: "text", text: summary }],
206
+ structuredContent: { items: result.items, count: result.items.length, summary },
207
+ };
208
+ }
209
+ catch (e) {
210
+ const summary = `Error listing campaign targets: ${e.stderr?.trim() || e.message}`;
211
+ return {
212
+ content: [{ type: "text", text: summary }],
213
+ structuredContent: { items: [], count: 0, summary },
214
+ isError: true,
215
+ };
216
+ }
217
+ });
218
+ }
219
+ //# sourceMappingURL=campaign-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"campaign-tools.js","sourceRoot":"","sources":["../../src/tools/campaign-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EACL,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,SAAS,EACT,WAAW,GACZ,MAAM,eAAe,CAAC;AAEvB,MAAM,UAAU,qBAAqB,CAAC,MAAiB;IACrD,MAAM,CAAC,YAAY,CACjB,wBAAwB,EACxB;QACE,WAAW,EAAE,8DAA8D;QAC3E,WAAW,EAAE;YACX,MAAM,EAAE,CAAC;iBACN,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;iBAChD,QAAQ,EAAE;iBACV,QAAQ,CAAC,gDAAgD,CAAC;YAC7D,SAAS,EAAE,CAAC;iBACT,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,0CAA0C,CAAC;YACvD,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,2CAA2C,CAAC;YACxD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,kCAAkC,CAAC;SACtF;QACD,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC;KACzC,EACD,KAAK,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QAC/C,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;YACtC,IAAI,KAA4B,CAAC;YAEjC,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,MAAM,CAAC,SAAS,EAAE,WAAW,EAC7B,SAAS,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAClC,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CACxE,CAAC;gBACF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;iBAAM,IAAI,SAAS,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,MAAM,CAAC,SAAS,EAAE,WAAW,EAC7B,gBAAgB,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAC5C,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,CACnC,CAAC;gBACF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;iBAAM,IAAI,QAAQ,EAAE,CAAC;gBACpB,MAAM,MAAM,GAAG,MAAM,YAAY,CAC/B,MAAM,CAAC,SAAS,EAAE,aAAa,EAC/B,eAAe,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAC1C,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,CACnC,CAAC;gBACF,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBACjE,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;YAED,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,qBAAqB,CAAC;gBACtC,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE;iBACpD,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC5B,MAAM,UAAU,GAAG,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,gBAAgB,IAAI,GAAG,CAAC;gBAClE,OAAO,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,EAAE,IAAI,GAAG,cAAc,CAAC,CAAC,MAAM,IAAI,GAAG,YAAY,CAAC,CAAC,aAAa,IAAI,GAAG,gBAAgB,CAAC,CAAC,QAAQ,IAAI,GAAG,kBAAkB,UAAU,eAAe,CAAC,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACjN,CAAC,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,GAAG,KAAK,CAAC,MAAM,kBAAkB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YACtE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;aAC3D,CAAC;QACJ,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,4BAA4B,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5E,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EACT,wDAAwD;YACxD,yDAAyD;YACzD,6DAA6D;YAC7D,kEAAkE;YAClE,kEAAkE;YAClE,4DAA4D;YAC5D,uCAAuC;QACzC,WAAW,EAAE;YACX,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;SACvC;QACD,YAAY,EAAE,SAAS,CAAC,cAAc,CAAC;KACxC,EACD,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;QACf,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;YAEhE,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,MAAM,OAAO,GAAG,YAAY,EAAE,aAAa,CAAC;gBAC5C,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;iBAC7C,CAAC;YACJ,CAAC;YAED,kEAAkE;YAClE,mEAAmE;YACnE,gDAAgD;YAChD,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAC9B,MAAM,CAAC,eAAe,EACtB,kBAAkB,EAClB,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EACnB,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;gBACF,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,KAAK,CAAC;gBACnC,IAAI,CAAC,kBAAkB,GAAG,OAAO,CAAC,KAAK;qBACpC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;qBACxB,MAAM,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,gEAAgE;YAClE,CAAC;YAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;aAClD,CAAC;QACJ,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,4BAA4B,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YAC5E,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE;gBAC5C,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,yBAAyB,EACzB;QACE,WAAW,EAAE,0EAA0E;QACvF,WAAW,EAAE,EAAE;QACf,YAAY,EAAE,WAAW;KAC1B,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YACjE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAE3B,MAAM,QAAQ,GAA2B,EAAE,CAAC;YAC5C,MAAM,MAAM,GAA2B,EAAE,CAAC;YAC1C,MAAM,UAAU,GAA2B,EAAE,CAAC;YAE9C,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,SAAS,CAAC;gBACrC,MAAM,IAAI,GAAG,CAAC,CAAC,aAAa,IAAI,SAAS,CAAC;gBAC1C,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,SAAS,CAAC;gBACzC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC/C,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;gBACvC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YACzD,CAAC;YAED,MAAM,GAAG,GAAG,CAAC,GAA2B,EAAE,EAAE,CAC1C,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;iBAChB,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;iBAC3B,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;iBAC/B,IAAI,CAAC,IAAI,CAAC,CAAC;YAEhB,MAAM,OAAO,GAAG;gBACd,oBAAoB,KAAK,CAAC,MAAM,EAAE;gBAClC,EAAE;gBACF,YAAY;gBACZ,GAAG,CAAC,QAAQ,CAAC;gBACb,EAAE;gBACF,UAAU;gBACV,GAAG,CAAC,MAAM,CAAC;gBACX,EAAE;gBACF,cAAc;gBACd,GAAG,CAAC,UAAU,CAAC;aAChB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEb,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE;oBACjB,KAAK,EAAE,KAAK,CAAC,MAAM;oBACnB,OAAO,EAAE;wBACP,MAAM,EAAE,QAAQ;wBAChB,IAAI,EAAE,MAAM;wBACZ,QAAQ,EAAE,UAAU;qBACrB;oBACD,OAAO;iBACR;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,yBAAyB,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACzE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE;gBACrD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,+BAA+B,EAC/B;QACE,WAAW,EACT,8DAA8D;YAC9D,iEAAiE;YACjE,kEAAkE;YAClE,kEAAkE;YAClE,2CAA2C;QAC7C,WAAW,EAAE;YACX,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,CAAC;YAC/C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;SACzF;QACD,YAAY,EAAE,UAAU,CAAC,oBAAoB,CAAC;KAC/C,EACD,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,EAAE;QAC/B,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;YACxC,MAAM,MAAM,GAAG,MAAM,UAAU,CAC7B,MAAM,CAAC,eAAe,EACtB,kBAAkB,EAClB,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAC5B,EAAE,KAAK,EAAE,GAAG,EAAE,CACf,CAAC;YAEF,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,MAAM,OAAO,GAAG,mCAAmC,WAAW,GAAG,CAAC;gBAClE,OAAO;oBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;oBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE;iBACpD,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACnC,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;gBACnC,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,IAAI,GAAG,CAAC;gBAChC,OAAO,GAAG,CAAC,CAAC,UAAU,iBAAiB,QAAQ,aAAa,KAAK,UAAU,CAAC,CAAC,QAAQ,IAAI,GAAG,EAAE,CAAC;YACjG,CAAC,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,gBAAgB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;YAE3E,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;aAChF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,MAAM,OAAO,GAAG,mCAAmC,CAAC,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACnF,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;gBACnD,iBAAiB,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE;gBACnD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCompanyTools(server: McpServer): void;
@@ -0,0 +1,194 @@
1
+ import { z } from "zod";
2
+ import { tables, queryByIndex, scanTable, scanAll, getItem } from "../dynamo.js";
3
+ import { companyEntity, listOutput, getOutput, statsOutput, } from "../schemas.js";
4
+ export function registerCompanyTools(server) {
5
+ server.registerTool("vantage_companies_search", {
6
+ description: "Search Vantage companies with optional filters. Returns up to 50 results. Use GSI filters for efficient queries or scan with text filter for fuzzy matching.",
7
+ inputSchema: {
8
+ filter: z
9
+ .string()
10
+ .optional()
11
+ .describe("Text filter — matches against company name (scans table, slower)"),
12
+ tier: z
13
+ .enum(["enterprise", "large", "mid-market", "smb", "emerging"])
14
+ .optional()
15
+ .describe("Filter by tier (uses by-tier GSI)"),
16
+ status: z
17
+ .enum([
18
+ "new",
19
+ "prospect",
20
+ "target",
21
+ "active",
22
+ "qualified",
23
+ "engaged",
24
+ "opportunity",
25
+ "customer",
26
+ "churned",
27
+ "disqualified",
28
+ ])
29
+ .optional()
30
+ .describe("Filter by status (uses by-status GSI)"),
31
+ client_id: z
32
+ .string()
33
+ .optional()
34
+ .describe("Filter by client ID (uses by-client GSI)"),
35
+ limit: z.number().optional().default(25).describe("Max results (default 25, max 50)"),
36
+ },
37
+ outputSchema: listOutput(companyEntity),
38
+ }, async ({ filter, tier, status, client_id, limit }) => {
39
+ try {
40
+ const cap = Math.min(limit || 25, 50);
41
+ let items;
42
+ if (tier) {
43
+ const result = await queryByIndex(tables.companies, "by-tier", "tier = :t", { ":t": { S: tier } }, { limit: cap, scanForward: false });
44
+ items = result.items;
45
+ }
46
+ else if (status) {
47
+ const result = await queryByIndex(tables.companies, "by-status", "#s = :s", { ":s": { S: status } }, { limit: cap, scanForward: false, expressionNames: { "#s": "status" } });
48
+ items = result.items;
49
+ }
50
+ else if (client_id) {
51
+ const result = await queryByIndex(tables.companies, "by-client", "client_id = :c", { ":c": { S: client_id } }, { limit: cap, scanForward: false });
52
+ items = result.items;
53
+ }
54
+ else {
55
+ const result = await scanTable(tables.companies, { limit: cap });
56
+ items = result.items;
57
+ }
58
+ if (filter) {
59
+ const lower = filter.toLowerCase();
60
+ items = items.filter((c) => (c.name || "").toLowerCase().includes(lower) ||
61
+ (c.domain || "").toLowerCase().includes(lower));
62
+ }
63
+ if (items.length === 0) {
64
+ const summary = "No companies found.";
65
+ return {
66
+ content: [{ type: "text", text: summary }],
67
+ structuredContent: { items: [], count: 0, summary },
68
+ };
69
+ }
70
+ const lines = items.map((c) => {
71
+ const score = c.lead_score ? ` | score: ${c.lead_score}` : "";
72
+ const t = c.tier ? ` | ${c.tier}` : "";
73
+ return `${c.name || c.id}${t}${score}\n status: ${c.status || "—"} | vertical: ${c.vertical || "—"} | domain: ${c.domain || "—"}`;
74
+ });
75
+ const summary = `${items.length} companies:\n\n${lines.join("\n\n")}`;
76
+ return {
77
+ content: [{ type: "text", text: summary }],
78
+ structuredContent: {
79
+ items,
80
+ count: items.length,
81
+ summary,
82
+ },
83
+ };
84
+ }
85
+ catch (e) {
86
+ const summary = `Error searching companies: ${e.stderr?.trim() || e.message}`;
87
+ return {
88
+ content: [{ type: "text", text: summary }],
89
+ structuredContent: { items: [], count: 0, summary },
90
+ isError: true,
91
+ };
92
+ }
93
+ });
94
+ server.registerTool("vantage_company_get", {
95
+ description: "Get full details for a single Vantage company by ID and vertical",
96
+ inputSchema: {
97
+ id: z.string().describe("Company ID (partition key)"),
98
+ vertical: z.string().describe("Company vertical (sort key)"),
99
+ },
100
+ outputSchema: getOutput(companyEntity),
101
+ }, async ({ id, vertical }) => {
102
+ try {
103
+ const item = await getItem(tables.companies, {
104
+ id: { S: id },
105
+ vertical: { S: vertical },
106
+ });
107
+ if (!item) {
108
+ const summary = `Company ${id} not found in vertical ${vertical}.`;
109
+ return {
110
+ content: [{ type: "text", text: summary }],
111
+ structuredContent: { found: false, summary },
112
+ };
113
+ }
114
+ const summary = JSON.stringify(item, null, 2);
115
+ return {
116
+ content: [{ type: "text", text: summary }],
117
+ structuredContent: { found: true, item, summary },
118
+ };
119
+ }
120
+ catch (e) {
121
+ const summary = `Error fetching company: ${e.stderr?.trim() || e.message}`;
122
+ return {
123
+ content: [{ type: "text", text: summary }],
124
+ structuredContent: { found: false, summary },
125
+ isError: true,
126
+ };
127
+ }
128
+ });
129
+ server.registerTool("vantage_companies_stats", {
130
+ description: "Get aggregate company statistics — counts by tier, status, and vertical",
131
+ inputSchema: {},
132
+ outputSchema: statsOutput,
133
+ }, async () => {
134
+ try {
135
+ // Project only the three facets we aggregate on. Smaller pages
136
+ // come back over the wire faster, and scanAll walks every page
137
+ // so counts reflect the whole table (not the 1 MB first page).
138
+ const result = await scanAll(tables.companies, {
139
+ projectionExpression: "tier, #s, vertical",
140
+ expressionNames: { "#s": "status" },
141
+ });
142
+ const items = result.items;
143
+ const byTier = {};
144
+ const byStatus = {};
145
+ const byVertical = {};
146
+ for (const c of items) {
147
+ const tier = c.tier || "unknown";
148
+ const status = c.status || "unknown";
149
+ const vertical = c.vertical || "unknown";
150
+ byTier[tier] = (byTier[tier] || 0) + 1;
151
+ byStatus[status] = (byStatus[status] || 0) + 1;
152
+ byVertical[vertical] = (byVertical[vertical] || 0) + 1;
153
+ }
154
+ const fmt = (obj) => Object.entries(obj)
155
+ .sort((a, b) => b[1] - a[1])
156
+ .map(([k, v]) => ` ${k}: ${v}`)
157
+ .join("\n");
158
+ const summary = [
159
+ `Total companies: ${items.length}${result.truncated ? " (truncated — try narrower scope)" : ""}`,
160
+ "",
161
+ "By Tier:",
162
+ fmt(byTier),
163
+ "",
164
+ "By Status:",
165
+ fmt(byStatus),
166
+ "",
167
+ "By Vertical:",
168
+ fmt(byVertical),
169
+ ].join("\n");
170
+ return {
171
+ content: [{ type: "text", text: summary }],
172
+ structuredContent: {
173
+ total: items.length,
174
+ truncated: result.truncated,
175
+ buckets: {
176
+ tier: byTier,
177
+ status: byStatus,
178
+ vertical: byVertical,
179
+ },
180
+ summary,
181
+ },
182
+ };
183
+ }
184
+ catch (e) {
185
+ const summary = `Error fetching stats: ${e.stderr?.trim() || e.message}`;
186
+ return {
187
+ content: [{ type: "text", text: summary }],
188
+ structuredContent: { total: 0, buckets: {}, summary },
189
+ isError: true,
190
+ };
191
+ }
192
+ });
193
+ }
194
+ //# sourceMappingURL=company-tools.js.map