@breaknorm_hu/mcp-server 0.1.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/dist/index.d.ts +1 -0
- package/dist/index.js +430 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/api-client.ts
|
|
8
|
+
var BreakNormClient = class {
|
|
9
|
+
baseUrl;
|
|
10
|
+
apiKey;
|
|
11
|
+
constructor(apiKey, baseUrl) {
|
|
12
|
+
this.apiKey = apiKey;
|
|
13
|
+
this.baseUrl = (baseUrl || "https://breaknorm.com").replace(/\/$/, "");
|
|
14
|
+
}
|
|
15
|
+
async request(method, path, options) {
|
|
16
|
+
const url = new URL(`/api/v1${path}`, this.baseUrl);
|
|
17
|
+
if (options?.params) {
|
|
18
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
19
|
+
if (value !== void 0 && value !== "") {
|
|
20
|
+
url.searchParams.set(key, value);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const res = await fetch(url.toString(), {
|
|
25
|
+
method,
|
|
26
|
+
headers: {
|
|
27
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"User-Agent": "@breaknorm/mcp-server"
|
|
30
|
+
},
|
|
31
|
+
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
32
|
+
});
|
|
33
|
+
const json = await res.json();
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const errorMsg = json?.error?.message || json?.error || `API error ${res.status}`;
|
|
36
|
+
throw new Error(`Breaknorm API error (${res.status}): ${errorMsg}`);
|
|
37
|
+
}
|
|
38
|
+
return json;
|
|
39
|
+
}
|
|
40
|
+
async get(path, params) {
|
|
41
|
+
return this.request("GET", path, { params });
|
|
42
|
+
}
|
|
43
|
+
async post(path, body) {
|
|
44
|
+
return this.request("POST", path, { body });
|
|
45
|
+
}
|
|
46
|
+
async delete(path, body) {
|
|
47
|
+
return this.request("DELETE", path, { body });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/tools.ts
|
|
52
|
+
function arrayParam(val) {
|
|
53
|
+
if (Array.isArray(val) && val.length > 0) return val.join(",");
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
function str(val) {
|
|
57
|
+
if (val === void 0 || val === null || val === "") return void 0;
|
|
58
|
+
return String(val);
|
|
59
|
+
}
|
|
60
|
+
var tools = [
|
|
61
|
+
// ── Search ─────────────────────────────────
|
|
62
|
+
{
|
|
63
|
+
name: "search_contacts",
|
|
64
|
+
description: "Search the Breaknorm contact database. Returns contacts matching filters. Use get_search_filters first to discover available filter values. Results paginated (max 25/page). For page 2+, pass searchToken from page 1.",
|
|
65
|
+
inputSchema: {
|
|
66
|
+
type: "object",
|
|
67
|
+
properties: {
|
|
68
|
+
q: { type: "string", description: "Free text search (name, title, company)" },
|
|
69
|
+
industry: { type: "array", items: { type: "string" }, description: "Filter by industry names" },
|
|
70
|
+
city: { type: "array", items: { type: "string" }, description: "Filter by city names" },
|
|
71
|
+
county: { type: "array", items: { type: "string" }, description: "Filter by county (megye)" },
|
|
72
|
+
country: { type: "string", description: "2-letter country code (default: HU)" },
|
|
73
|
+
seniority: {
|
|
74
|
+
type: "array",
|
|
75
|
+
items: { type: "string", enum: ["c_level", "vp", "director", "manager", "head", "senior", "entry", "intern", "founder", "partner", "managing_director", "individual_contributor"] },
|
|
76
|
+
description: "Filter by seniority level"
|
|
77
|
+
},
|
|
78
|
+
department: {
|
|
79
|
+
type: "array",
|
|
80
|
+
items: { type: "string", enum: ["sales", "marketing", "it", "hr", "finance", "operations", "legal", "engineering", "product", "customer_success", "c_suite", "design", "other"] }
|
|
81
|
+
},
|
|
82
|
+
employee_min: { type: "integer", description: "Minimum company employee count" },
|
|
83
|
+
employee_max: { type: "integer", description: "Maximum company employee count" },
|
|
84
|
+
revenue_min: { type: "number", description: "Minimum annual revenue (HUF)" },
|
|
85
|
+
revenue_max: { type: "number", description: "Maximum annual revenue (HUF)" },
|
|
86
|
+
has_email: { type: "string", enum: ["yes", "maybe", "no"], description: "Filter by email availability" },
|
|
87
|
+
has_phone: { type: "string", enum: ["yes", "maybe", "no"] },
|
|
88
|
+
title: { type: "string", description: "Job title keyword search" },
|
|
89
|
+
page: { type: "integer", default: 1 },
|
|
90
|
+
limit: { type: "integer", default: 25, maximum: 25 },
|
|
91
|
+
searchToken: { type: "string", description: "Required for page 2+. From page 1 response." }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
handler: async (client, args) => {
|
|
95
|
+
return client.get("/contacts/search", {
|
|
96
|
+
q: str(args.q),
|
|
97
|
+
industry: arrayParam(args.industry),
|
|
98
|
+
city: arrayParam(args.city),
|
|
99
|
+
county: arrayParam(args.county),
|
|
100
|
+
country: str(args.country),
|
|
101
|
+
seniority: arrayParam(args.seniority),
|
|
102
|
+
department: arrayParam(args.department),
|
|
103
|
+
employee_min: str(args.employee_min),
|
|
104
|
+
employee_max: str(args.employee_max),
|
|
105
|
+
revenue_min: str(args.revenue_min),
|
|
106
|
+
revenue_max: str(args.revenue_max),
|
|
107
|
+
has_email: str(args.has_email),
|
|
108
|
+
has_phone: str(args.has_phone),
|
|
109
|
+
title: str(args.title),
|
|
110
|
+
page: str(args.page),
|
|
111
|
+
limit: str(args.limit),
|
|
112
|
+
searchToken: str(args.searchToken)
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "search_companies",
|
|
118
|
+
description: "Search companies in the Breaknorm database. Returns companies matching filters.",
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
q: { type: "string", description: "Free text company name search" },
|
|
123
|
+
industry: { type: "array", items: { type: "string" } },
|
|
124
|
+
city: { type: "array", items: { type: "string" } },
|
|
125
|
+
county: { type: "array", items: { type: "string" } },
|
|
126
|
+
country: { type: "string" },
|
|
127
|
+
employee_min: { type: "integer" },
|
|
128
|
+
employee_max: { type: "integer" },
|
|
129
|
+
revenue_min: { type: "number" },
|
|
130
|
+
revenue_max: { type: "number" },
|
|
131
|
+
has_website: { type: "boolean" },
|
|
132
|
+
page: { type: "integer", default: 1 },
|
|
133
|
+
limit: { type: "integer", default: 25, maximum: 25 },
|
|
134
|
+
sort: { type: "string", enum: ["relevance", "name", "employee_count", "revenue", "icp_score"], default: "relevance" },
|
|
135
|
+
searchToken: { type: "string" }
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
handler: async (client, args) => {
|
|
139
|
+
return client.get("/companies/search", {
|
|
140
|
+
q: str(args.q),
|
|
141
|
+
industry: arrayParam(args.industry),
|
|
142
|
+
city: arrayParam(args.city),
|
|
143
|
+
county: arrayParam(args.county),
|
|
144
|
+
country: str(args.country),
|
|
145
|
+
employee_min: str(args.employee_min),
|
|
146
|
+
employee_max: str(args.employee_max),
|
|
147
|
+
revenue_min: str(args.revenue_min),
|
|
148
|
+
revenue_max: str(args.revenue_max),
|
|
149
|
+
has_website: str(args.has_website),
|
|
150
|
+
page: str(args.page),
|
|
151
|
+
limit: str(args.limit),
|
|
152
|
+
sort: str(args.sort),
|
|
153
|
+
searchToken: str(args.searchToken)
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "get_search_filters",
|
|
159
|
+
description: "Get all available filter values with counts (industries, cities, counties, seniorities, departments, etc.). Call this FIRST before searching to understand what filter values are available.",
|
|
160
|
+
inputSchema: { type: "object", properties: {} },
|
|
161
|
+
handler: async (client) => client.get("/search/filters")
|
|
162
|
+
},
|
|
163
|
+
// ── Contacts ───────────────────────────────
|
|
164
|
+
{
|
|
165
|
+
name: "get_contact",
|
|
166
|
+
description: "Get details of a single contact (public fields, no revealed data).",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: { contactId: { type: "string", description: "Contact UUID" } },
|
|
170
|
+
required: ["contactId"]
|
|
171
|
+
},
|
|
172
|
+
handler: async (client, args) => client.get(`/contacts/${args.contactId}`)
|
|
173
|
+
},
|
|
174
|
+
// ── Cost Estimation ────────────────────────
|
|
175
|
+
{
|
|
176
|
+
name: "estimate_cost",
|
|
177
|
+
description: "IMPORTANT: Call this BEFORE any paid operation (reveal, ICP scoring). Returns exact credit cost, current balance, and whether you can afford it. Present the cost to the user for approval before proceeding.",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
operations: {
|
|
182
|
+
type: "array",
|
|
183
|
+
items: {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: {
|
|
186
|
+
type: { type: "string", enum: ["reveal", "icp_score"] },
|
|
187
|
+
contactIds: { type: "array", items: { type: "string" }, description: "For reveal operations" },
|
|
188
|
+
revealType: { type: "string", enum: ["email", "phone", "all"], description: "For reveal: what to reveal" },
|
|
189
|
+
companyIds: { type: "array", items: { type: "string" }, description: "For icp_score operations" },
|
|
190
|
+
icpProfileId: { type: "string", description: "For icp_score: specific profile ID (optional)" }
|
|
191
|
+
},
|
|
192
|
+
required: ["type"]
|
|
193
|
+
},
|
|
194
|
+
description: "List of operations to estimate"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
required: ["operations"]
|
|
198
|
+
},
|
|
199
|
+
handler: async (client, args) => client.post("/estimate", { operations: args.operations })
|
|
200
|
+
},
|
|
201
|
+
// ── Reveal ─────────────────────────────────
|
|
202
|
+
{
|
|
203
|
+
name: "reveal_contact",
|
|
204
|
+
description: "Reveal email and/or phone for a single contact. Costs credits (email=1, phone=10, all=11). ALWAYS call estimate_cost first and get user approval.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
type: "object",
|
|
207
|
+
properties: {
|
|
208
|
+
contactId: { type: "string", description: "Contact UUID" },
|
|
209
|
+
type: { type: "string", enum: ["email", "phone", "all"], description: "What to reveal" }
|
|
210
|
+
},
|
|
211
|
+
required: ["contactId", "type"]
|
|
212
|
+
},
|
|
213
|
+
handler: async (client, args) => client.post(`/contacts/${args.contactId}/reveal`, { type: args.type })
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "bulk_reveal_contacts",
|
|
217
|
+
description: "Reveal data for multiple contacts at once. Creates an async job. Returns jobId \u2014 use check_job_status to poll progress. ALWAYS call estimate_cost first.",
|
|
218
|
+
inputSchema: {
|
|
219
|
+
type: "object",
|
|
220
|
+
properties: {
|
|
221
|
+
contactIds: { type: "array", items: { type: "string" }, description: "Contact UUIDs (max 1000)" },
|
|
222
|
+
type: { type: "string", enum: ["email", "phone", "all"] },
|
|
223
|
+
idempotencyKey: { type: "string", description: "UUID to prevent duplicate charges on retry" }
|
|
224
|
+
},
|
|
225
|
+
required: ["contactIds", "type"]
|
|
226
|
+
},
|
|
227
|
+
handler: async (client, args) => client.post("/contacts/bulk-reveal", {
|
|
228
|
+
contactIds: args.contactIds,
|
|
229
|
+
type: args.type,
|
|
230
|
+
idempotencyKey: args.idempotencyKey
|
|
231
|
+
})
|
|
232
|
+
},
|
|
233
|
+
// ── Jobs ───────────────────────────────────
|
|
234
|
+
{
|
|
235
|
+
name: "check_job_status",
|
|
236
|
+
description: "Check the status of an async job (bulk reveal, export, ICP scoring). Returns progress (processedItems/totalItems) and status (pending/processing/completed/failed).",
|
|
237
|
+
inputSchema: {
|
|
238
|
+
type: "object",
|
|
239
|
+
properties: { jobId: { type: "string", description: "Job UUID" } },
|
|
240
|
+
required: ["jobId"]
|
|
241
|
+
},
|
|
242
|
+
handler: async (client, args) => client.get(`/jobs/${args.jobId}`)
|
|
243
|
+
},
|
|
244
|
+
// ── Credits ────────────────────────────────
|
|
245
|
+
{
|
|
246
|
+
name: "get_credits",
|
|
247
|
+
description: "Check current credit balance and subscription tier.",
|
|
248
|
+
inputSchema: { type: "object", properties: {} },
|
|
249
|
+
handler: async (client) => client.get("/credits")
|
|
250
|
+
},
|
|
251
|
+
// ── Lists ──────────────────────────────────
|
|
252
|
+
{
|
|
253
|
+
name: "list_saved_lists",
|
|
254
|
+
description: "List all saved contact lists.",
|
|
255
|
+
inputSchema: {
|
|
256
|
+
type: "object",
|
|
257
|
+
properties: {
|
|
258
|
+
page: { type: "integer", default: 1 },
|
|
259
|
+
limit: { type: "integer", default: 20, maximum: 100 },
|
|
260
|
+
sort: { type: "string", enum: ["name", "created_at", "contact_count"], default: "created_at" }
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
handler: async (client, args) => client.get("/lists", { page: str(args.page), limit: str(args.limit), sort: str(args.sort) })
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: "create_list",
|
|
267
|
+
description: "Create a new saved contact list.",
|
|
268
|
+
inputSchema: {
|
|
269
|
+
type: "object",
|
|
270
|
+
properties: {
|
|
271
|
+
name: { type: "string", description: "List name (1-200 chars)" },
|
|
272
|
+
description: { type: "string", description: "Optional description (max 1000 chars)" }
|
|
273
|
+
},
|
|
274
|
+
required: ["name"]
|
|
275
|
+
},
|
|
276
|
+
handler: async (client, args) => client.post("/lists", { name: args.name, description: args.description })
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: "add_contacts_to_list",
|
|
280
|
+
description: "Add contacts to an existing list. Duplicates are automatically skipped.",
|
|
281
|
+
inputSchema: {
|
|
282
|
+
type: "object",
|
|
283
|
+
properties: {
|
|
284
|
+
listId: { type: "string", description: "List UUID" },
|
|
285
|
+
contactIds: { type: "array", items: { type: "string" }, description: "Contact UUIDs (max 500)" }
|
|
286
|
+
},
|
|
287
|
+
required: ["listId", "contactIds"]
|
|
288
|
+
},
|
|
289
|
+
handler: async (client, args) => client.post(`/lists/${args.listId}/contacts`, { contactIds: args.contactIds })
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
name: "export_list",
|
|
293
|
+
description: "Export a saved list as CSV. Creates async job \u2014 use check_job_status to poll.",
|
|
294
|
+
inputSchema: {
|
|
295
|
+
type: "object",
|
|
296
|
+
properties: {
|
|
297
|
+
listId: { type: "string", description: "List UUID" }
|
|
298
|
+
},
|
|
299
|
+
required: ["listId"]
|
|
300
|
+
},
|
|
301
|
+
handler: async (client, args) => client.post(`/lists/${args.listId}/export`)
|
|
302
|
+
},
|
|
303
|
+
// ── ICP ────────────────────────────────────
|
|
304
|
+
{
|
|
305
|
+
name: "list_icp_profiles",
|
|
306
|
+
description: "List user's ICP (Ideal Customer Profile) definitions.",
|
|
307
|
+
inputSchema: { type: "object", properties: {} },
|
|
308
|
+
handler: async (client) => client.get("/icp-profiles")
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "create_icp_profile",
|
|
312
|
+
description: "Create an ICP profile from a natural language description. This defines what your ideal customer looks like, used for AI scoring.",
|
|
313
|
+
inputSchema: {
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
name: { type: "string", description: "Profile name (e.g., 'SaaS c\xE9gek Budapest')" },
|
|
317
|
+
description: {
|
|
318
|
+
type: "string",
|
|
319
|
+
description: "Detailed ICP description in natural language (1-2000 chars). Example: 'IT \xE9s SaaS c\xE9gek, 10-200 alkalmazott, Budapest, \xE9ves bev\xE9tel 100M-1B HUF, akt\xEDv LinkedIn jelenl\xE9t'"
|
|
320
|
+
},
|
|
321
|
+
isDefault: { type: "boolean", description: "Set as default profile for scoring", default: false }
|
|
322
|
+
},
|
|
323
|
+
required: ["name", "description"]
|
|
324
|
+
},
|
|
325
|
+
handler: async (client, args) => client.post("/icp-profiles", {
|
|
326
|
+
name: args.name,
|
|
327
|
+
description: args.description,
|
|
328
|
+
isDefault: args.isDefault
|
|
329
|
+
})
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
name: "score_companies",
|
|
333
|
+
description: "Score companies against an ICP profile using AI. Creates async job. Costs 1 credit per company. ALWAYS call estimate_cost first.",
|
|
334
|
+
inputSchema: {
|
|
335
|
+
type: "object",
|
|
336
|
+
properties: {
|
|
337
|
+
companyIds: { type: "array", items: { type: "string" }, description: "Company UUIDs (max 500)" },
|
|
338
|
+
icpProfileId: { type: "string", description: "ICP profile UUID (uses default if omitted)" },
|
|
339
|
+
idempotencyKey: { type: "string", description: "UUID to prevent duplicate charges" }
|
|
340
|
+
},
|
|
341
|
+
required: ["companyIds"]
|
|
342
|
+
},
|
|
343
|
+
handler: async (client, args) => client.post("/icp-scores/bulk", {
|
|
344
|
+
companyIds: args.companyIds,
|
|
345
|
+
icpProfileId: args.icpProfileId,
|
|
346
|
+
idempotencyKey: args.idempotencyKey
|
|
347
|
+
})
|
|
348
|
+
}
|
|
349
|
+
];
|
|
350
|
+
|
|
351
|
+
// src/index.ts
|
|
352
|
+
var SERVER_NAME = "breaknorm";
|
|
353
|
+
var SERVER_VERSION = "0.1.0";
|
|
354
|
+
var INSTRUCTIONS = `Breaknorm MCP Server \u2014 B2B contact database for the Hungarian market.
|
|
355
|
+
|
|
356
|
+
IMPORTANT RULES:
|
|
357
|
+
1. Before ANY paid operation (reveal_contact, bulk_reveal_contacts, score_companies),
|
|
358
|
+
ALWAYS call estimate_cost first and present the cost to the user for approval.
|
|
359
|
+
Never execute paid operations without explicit user confirmation.
|
|
360
|
+
|
|
361
|
+
2. Recommended workflow for building a lead list:
|
|
362
|
+
a. get_credits \u2014 check current balance
|
|
363
|
+
b. get_search_filters \u2014 discover available filter values
|
|
364
|
+
c. search_companies \u2014 find target companies matching criteria
|
|
365
|
+
d. search_contacts \u2014 find people at those companies
|
|
366
|
+
e. estimate_cost \u2014 calculate total cost for reveals
|
|
367
|
+
f. [Wait for user approval of the cost]
|
|
368
|
+
g. bulk_reveal_contacts \u2014 get email/phone data
|
|
369
|
+
h. create_list + add_contacts_to_list \u2014 organize results
|
|
370
|
+
i. export_list \u2014 export as CSV if needed
|
|
371
|
+
|
|
372
|
+
3. Credit costs:
|
|
373
|
+
- Email reveal: 1 credit per contact
|
|
374
|
+
- Phone reveal: 10 credits per contact
|
|
375
|
+
- Both (email + phone): 11 credits per contact
|
|
376
|
+
- ICP company scoring: 1 credit per company
|
|
377
|
+
|
|
378
|
+
4. Search results are paginated (max 25 per page). For page 2+,
|
|
379
|
+
you must pass the searchToken from the page 1 response.
|
|
380
|
+
|
|
381
|
+
5. Bulk operations (reveal, ICP scoring) are async. They return a jobId.
|
|
382
|
+
Use check_job_status to poll for completion.`;
|
|
383
|
+
async function main() {
|
|
384
|
+
const apiKey = process.env.BREAKNORM_API_KEY;
|
|
385
|
+
if (!apiKey) {
|
|
386
|
+
console.error("Error: BREAKNORM_API_KEY environment variable is required.");
|
|
387
|
+
console.error("Get your API key at https://breaknorm.com/app/settings/developers");
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
const baseUrl = process.env.BREAKNORM_BASE_URL || "https://breaknorm.com";
|
|
391
|
+
const client = new BreakNormClient(apiKey, baseUrl);
|
|
392
|
+
const server = new McpServer({
|
|
393
|
+
name: SERVER_NAME,
|
|
394
|
+
version: SERVER_VERSION
|
|
395
|
+
}, {
|
|
396
|
+
instructions: INSTRUCTIONS
|
|
397
|
+
});
|
|
398
|
+
for (const tool of tools) {
|
|
399
|
+
server.tool(
|
|
400
|
+
tool.name,
|
|
401
|
+
tool.description,
|
|
402
|
+
tool.inputSchema,
|
|
403
|
+
async (args) => {
|
|
404
|
+
try {
|
|
405
|
+
const result = await tool.handler(client, args);
|
|
406
|
+
return {
|
|
407
|
+
content: [
|
|
408
|
+
{
|
|
409
|
+
type: "text",
|
|
410
|
+
text: JSON.stringify(result, null, 2)
|
|
411
|
+
}
|
|
412
|
+
]
|
|
413
|
+
};
|
|
414
|
+
} catch (error) {
|
|
415
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
418
|
+
isError: true
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
const transport = new StdioServerTransport();
|
|
425
|
+
await server.connect(transport);
|
|
426
|
+
}
|
|
427
|
+
main().catch((err) => {
|
|
428
|
+
console.error("Fatal error:", err);
|
|
429
|
+
process.exit(1);
|
|
430
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@breaknorm_hu/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Breaknorm MCP Server — AI agent interface for B2B contact database",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"breaknorm-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.12.1"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"tsup": "^8.4.0",
|
|
20
|
+
"tsx": "^4.19.4",
|
|
21
|
+
"typescript": "^5.8.3"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"breaknorm",
|
|
30
|
+
"ai",
|
|
31
|
+
"agents",
|
|
32
|
+
"b2b",
|
|
33
|
+
"leads"
|
|
34
|
+
],
|
|
35
|
+
"license": "UNLICENSED",
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|