@webbula/mcp 1.0.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.
Files changed (56) hide show
  1. package/README.md +150 -0
  2. package/dist/api/enums.d.ts +80 -0
  3. package/dist/api/enums.js +230 -0
  4. package/dist/api/enums.js.map +1 -0
  5. package/dist/api/types.d.ts +163 -0
  6. package/dist/api/types.js +8 -0
  7. package/dist/api/types.js.map +1 -0
  8. package/dist/api/webbula-client.d.ts +116 -0
  9. package/dist/api/webbula-client.js +249 -0
  10. package/dist/api/webbula-client.js.map +1 -0
  11. package/dist/config.d.ts +70 -0
  12. package/dist/config.js +90 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.js +23 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/server/http.d.ts +4 -0
  18. package/dist/server/http.js +76 -0
  19. package/dist/server/http.js.map +1 -0
  20. package/dist/server/stdio.d.ts +12 -0
  21. package/dist/server/stdio.js +30 -0
  22. package/dist/server/stdio.js.map +1 -0
  23. package/dist/tools/download-results.d.ts +35 -0
  24. package/dist/tools/download-results.js +62 -0
  25. package/dist/tools/download-results.js.map +1 -0
  26. package/dist/tools/errors.d.ts +10 -0
  27. package/dist/tools/errors.js +16 -0
  28. package/dist/tools/errors.js.map +1 -0
  29. package/dist/tools/file-status.d.ts +35 -0
  30. package/dist/tools/file-status.js +54 -0
  31. package/dist/tools/file-status.js.map +1 -0
  32. package/dist/tools/get-credits.d.ts +31 -0
  33. package/dist/tools/get-credits.js +47 -0
  34. package/dist/tools/get-credits.js.map +1 -0
  35. package/dist/tools/hygiene-check.d.ts +49 -0
  36. package/dist/tools/hygiene-check.js +84 -0
  37. package/dist/tools/hygiene-check.js.map +1 -0
  38. package/dist/tools/persona-append.d.ts +62 -0
  39. package/dist/tools/persona-append.js +123 -0
  40. package/dist/tools/persona-append.js.map +1 -0
  41. package/dist/tools/register.d.ts +15 -0
  42. package/dist/tools/register.js +23 -0
  43. package/dist/tools/register.js.map +1 -0
  44. package/dist/tools/upload-file.d.ts +58 -0
  45. package/dist/tools/upload-file.js +119 -0
  46. package/dist/tools/upload-file.js.map +1 -0
  47. package/dist/tools/validate-lead.d.ts +102 -0
  48. package/dist/tools/validate-lead.js +192 -0
  49. package/dist/tools/validate-lead.js.map +1 -0
  50. package/dist/tools/verify-email.d.ts +41 -0
  51. package/dist/tools/verify-email.js +74 -0
  52. package/dist/tools/verify-email.js.map +1 -0
  53. package/dist/trust.d.ts +28 -0
  54. package/dist/trust.js +23 -0
  55. package/dist/trust.js.map +1 -0
  56. package/package.json +67 -0
@@ -0,0 +1,116 @@
1
+ import type { AccountInfoResponse, BatchRunResponse, BatchTaskResponse, ExpressCompoundResponse, ExpressEmailResponseV5, PersonaAppendResponse } from "./types.js";
2
+ /** Batch submission parameters — exactly one of `inline` / `dataSource` is set. */
3
+ export interface BatchRunParams {
4
+ profile: string;
5
+ /** Inline base64 submission (JSON `file_base64` mode). */
6
+ inline?: {
7
+ filename: string;
8
+ content_base64: string;
9
+ };
10
+ /** Remote data-source submission (form mode). */
11
+ dataSource?: {
12
+ data_source: "web" | "ftp" | "ssh";
13
+ url?: string;
14
+ host?: string;
15
+ port?: number;
16
+ username?: string;
17
+ password?: string;
18
+ path?: string;
19
+ secure?: boolean;
20
+ filename?: string;
21
+ };
22
+ }
23
+ /** Raw result-file body returned by `getResultFile` — bytes + the upstream content-type. */
24
+ export interface ResultFileBytes {
25
+ bytes: Buffer;
26
+ contentType?: string;
27
+ }
28
+ /**
29
+ * Lead attributes accepted by v4 `task/lead`; only supplied fields are sent.
30
+ * The full street/city/state/ZIP goes in `full_address` (mapped to the upstream
31
+ * `address` form param).
32
+ */
33
+ export interface LeadValidateParams {
34
+ profile: string;
35
+ first_name?: string;
36
+ last_name?: string;
37
+ full_address?: string;
38
+ phone?: string;
39
+ emails?: string[];
40
+ }
41
+ /** Which data point a persona append should resolve and return. */
42
+ export type PersonaFilter = "Email" | "Telephone" | "Postal" | "Automotive";
43
+ /** Identity-resolution scope for a persona append (CSV: default "both"). */
44
+ export type PersonaScope = "individual" | "household" | "both";
45
+ /**
46
+ * Input to the persona data-append endpoint (feature 006). `record` carries the
47
+ * known PII used as the match key; only supplied fields are sent. `filter`
48
+ * selects the data point to append; `scope` selects the identity-resolution
49
+ * level. The wrapper never invents PII — it forwards exactly what the caller
50
+ * provided (constitution principle I).
51
+ */
52
+ export interface PersonaAppendParams {
53
+ filter: PersonaFilter;
54
+ scope: PersonaScope;
55
+ record: {
56
+ first_name?: string;
57
+ last_name?: string;
58
+ email?: string;
59
+ address?: string;
60
+ city?: string;
61
+ state?: string;
62
+ zip5?: string;
63
+ phone?: string;
64
+ };
65
+ }
66
+ /** Failure categories surfaced to tool handlers (data-model ToolError). */
67
+ export type ErrorCategory = "missing_credential" | "validation" | "auth" | "upstream" | "network";
68
+ /**
69
+ * A structured client error. The message is safe to surface to an agent and
70
+ * NEVER contains the API key (principle V). Tool handlers turn this into an MCP
71
+ * tool error (`isError: true`).
72
+ */
73
+ export declare class WebbulaApiError extends Error {
74
+ readonly category: ErrorCategory;
75
+ constructor(category: ErrorCategory, message: string);
76
+ }
77
+ export interface WebbulaClientOptions {
78
+ /** Override the base URL (defaults to config). */
79
+ baseUrl?: string;
80
+ /** Override the persona-append base URL (defaults to config — feature 006). */
81
+ personaBaseUrl?: string;
82
+ /** Override key resolution (mainly for tests). Defaults to `resolveApiKey`. */
83
+ resolveKey?: () => string | undefined;
84
+ }
85
+ export interface WebbulaClient {
86
+ /** `POST /api.v4/task/express` (form) — single address (verify_email). */
87
+ expressHygiene(params: {
88
+ emails: string[];
89
+ profile: string;
90
+ }): Promise<ExpressEmailResponseV5>;
91
+ /**
92
+ * `POST /api/v5/task/express` (JSON) — multi-address hygiene (hygiene_check).
93
+ * v4 express is single-email only, so the synchronous list path uses v5, which
94
+ * accepts an `emails` array and returns one result per address.
95
+ */
96
+ expressHygieneV5(params: {
97
+ emails: string[];
98
+ profile: string;
99
+ }): Promise<ExpressEmailResponseV5>;
100
+ /** `POST /api.v4/account/info` (form, `mode=short`) — `key` never returned (principle V). */
101
+ accountInfo(): Promise<AccountInfoResponse>;
102
+ /** `POST /api.v4/task/lead` (form) — validate a lead record (feature 002). */
103
+ leadValidate(params: LeadValidateParams): Promise<ExpressCompoundResponse>;
104
+ /** `POST /api.v4/task/run` — submit a batch file task (multipart or form). */
105
+ runBatch(params: BatchRunParams): Promise<BatchRunResponse>;
106
+ /** `POST /api.v4/task/info` (form) — poll a batch task by `package_name`. */
107
+ taskInfo(packageName: string): Promise<BatchTaskResponse>;
108
+ /** `POST /api.v4/task/get_file` (form) — download a result file (raw bytes). */
109
+ getResultFile(params: {
110
+ package_name: string;
111
+ filename: string;
112
+ }): Promise<ResultFileBytes>;
113
+ /** `POST {persona}/v1/append` — append a data point via identity resolution (feature 006). */
114
+ personaAppend(params: PersonaAppendParams): Promise<PersonaAppendResponse>;
115
+ }
116
+ export declare function createWebbulaClient(options?: WebbulaClientOptions): WebbulaClient;
@@ -0,0 +1,249 @@
1
+ /**
2
+ * The single HTTP boundary to the Webbula Email Hygiene API (constitution
3
+ * principle I). Native `fetch`, `X-API-KEY` header auth (research D3), `/api/v5`
4
+ * base path (research D2). All error handling is centralized here so every tool
5
+ * handler surfaces a structured, secret-free failure (principle V, research D7).
6
+ */
7
+ import { createHash } from "node:crypto";
8
+ import { resolveApiKey, WEBBULA_API_BASE_URL, WEBBULA_API_PATH, WEBBULA_DEBUG, WEBBULA_PERSONA_BASE_URL, } from "../config.js";
9
+ /** Webbula API version path segment (v4 — form-encoded endpoints). */
10
+ const API_PATH = WEBBULA_API_PATH;
11
+ /**
12
+ * v5 path, used ONLY by `hygiene_check` — v4 express is single-email, so the
13
+ * synchronous multi-address path stays on v5 (JSON array body).
14
+ */
15
+ const API_PATH_V5 = "/api/v5";
16
+ /** Persona data-append path on the persona host (feature 006). */
17
+ const PERSONA_APPEND_PATH = "/v1/append";
18
+ /** Build an `application/x-www-form-urlencoded` request init from string params. */
19
+ function formInit(params) {
20
+ const form = new URLSearchParams();
21
+ for (const [key, value] of Object.entries(params)) {
22
+ if (value !== undefined)
23
+ form.set(key, value);
24
+ }
25
+ return {
26
+ method: "POST",
27
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
28
+ body: form.toString(),
29
+ };
30
+ }
31
+ /**
32
+ * A structured client error. The message is safe to surface to an agent and
33
+ * NEVER contains the API key (principle V). Tool handlers turn this into an MCP
34
+ * tool error (`isError: true`).
35
+ */
36
+ export class WebbulaApiError extends Error {
37
+ category;
38
+ constructor(category, message) {
39
+ super(message);
40
+ this.name = "WebbulaApiError";
41
+ this.category = category;
42
+ }
43
+ }
44
+ /** Pull verbatim upstream message(s) out of a legacy error envelope. */
45
+ function extractErrorMessages(body) {
46
+ const errors = body?.errors;
47
+ if (Array.isArray(errors))
48
+ return errors.filter((e) => typeof e === "string");
49
+ if (typeof errors === "string")
50
+ return [errors];
51
+ const message = errors?.message;
52
+ return typeof message === "string" ? [message] : [];
53
+ }
54
+ /** Describe a thrown transport error without leaking internals. */
55
+ function networkReason(error) {
56
+ if (error instanceof Error && error.message)
57
+ return error.message;
58
+ return "connection failed";
59
+ }
60
+ /**
61
+ * Log the raw upstream response to stderr when `WEBBULA_DEBUG` is on. A `key`
62
+ * field is redacted before logging so the API token can never leak (principle
63
+ * V). Logging goes to stderr only — never stdout (stdio JSON-RPC channel).
64
+ */
65
+ function debugLogResponse(method, url, status, body) {
66
+ if (!WEBBULA_DEBUG)
67
+ return;
68
+ let printable = body;
69
+ if (body !== null && typeof body === "object" && "key" in body) {
70
+ printable = { ...body, key: "[redacted]" };
71
+ }
72
+ console.error(`[webbula-debug] ${method.toUpperCase()} ${url} → HTTP ${status}\n` +
73
+ JSON.stringify(printable, null, 2));
74
+ }
75
+ export function createWebbulaClient(options = {}) {
76
+ const baseUrl = (options.baseUrl ?? WEBBULA_API_BASE_URL).replace(/\/+$/, "");
77
+ const personaBaseUrl = (options.personaBaseUrl ?? WEBBULA_PERSONA_BASE_URL).replace(/\/+$/, "");
78
+ const resolveKey = options.resolveKey ?? resolveApiKey;
79
+ /** Resolve the key or fail loud (pre-flight, principle V). */
80
+ function requireKey() {
81
+ const key = resolveKey();
82
+ if (!key) {
83
+ throw new WebbulaApiError("missing_credential", "Webbula API key required (set WEBBULA_API_KEY)");
84
+ }
85
+ return key;
86
+ }
87
+ /**
88
+ * Perform a request and map every failure path to a `WebbulaApiError`.
89
+ * `base` overrides the default API host (the persona append endpoint lives on
90
+ * its own host — feature 006).
91
+ */
92
+ async function request(path, init, base = baseUrl) {
93
+ const key = requireKey();
94
+ const headers = new Headers(init.headers);
95
+ headers.set("X-API-KEY", key);
96
+ headers.set("Accept", "application/json");
97
+ let response;
98
+ try {
99
+ response = await fetch(`${base}${path}`, { ...init, headers });
100
+ }
101
+ catch (error) {
102
+ throw new WebbulaApiError("network", `Webbula API unreachable: ${networkReason(error)}`);
103
+ }
104
+ if (response.status === 401) {
105
+ throw new WebbulaApiError("auth", "Authentication failed — check WEBBULA_API_KEY");
106
+ }
107
+ let body;
108
+ try {
109
+ body = await response.json();
110
+ }
111
+ catch {
112
+ body = undefined;
113
+ }
114
+ debugLogResponse(init.method ?? "GET", `${base}${path}`, response.status, body);
115
+ // The legacy envelope uses `success: 0` even on HTTP 200 in some cases, so
116
+ // inspect the body, not just the status (research D7).
117
+ const legacyFailure = body !== undefined && body.success === 0;
118
+ if (!response.ok || legacyFailure) {
119
+ const messages = extractErrorMessages(body);
120
+ throw new WebbulaApiError("upstream", messages.length > 0
121
+ ? messages.join("; ")
122
+ : `Webbula API error (HTTP ${response.status})`);
123
+ }
124
+ return body;
125
+ }
126
+ return {
127
+ async expressHygiene({ emails, profile }) {
128
+ // v4 `task/express` is form-encoded and single-email (verify_email passes
129
+ // exactly one address). The response `emails` is still an array.
130
+ return request(`${API_PATH}/task/express`, formInit({ profile, emails: emails.join(",") }));
131
+ },
132
+ async expressHygieneV5({ emails, profile }) {
133
+ // hygiene_check stays on v5: JSON `emails` array, one result per address.
134
+ return request(`${API_PATH_V5}/task/express`, {
135
+ method: "POST",
136
+ headers: { "Content-Type": "application/json" },
137
+ body: JSON.stringify({ profile, emails }),
138
+ });
139
+ },
140
+ async accountInfo() {
141
+ // v4 `account/info`: form POST with `mode=short` (never `full` — keeps the
142
+ // API key out of the response, principle V).
143
+ return request(`${API_PATH}/account/info`, formInit({ mode: "short" }));
144
+ },
145
+ async leadValidate(params) {
146
+ // Map our tool-facing fields to the v4 `task/lead` form params
147
+ // (firstname/lastname/address/phone/emails). Only supplied fields are sent
148
+ // (FR-003); `emails` is a single value, so a list is joined comma-separated.
149
+ return request(`${API_PATH}/task/lead`, formInit({
150
+ profile: params.profile,
151
+ firstname: params.first_name,
152
+ lastname: params.last_name,
153
+ address: params.full_address,
154
+ phone: params.phone,
155
+ emails: params.emails && params.emails.length > 0
156
+ ? params.emails.join(",")
157
+ : undefined,
158
+ }));
159
+ },
160
+ async runBatch(params) {
161
+ if (params.inline !== undefined) {
162
+ // v4 `task/run` raw-file mode: multipart/form-data with `data_file` and
163
+ // its `md5` checksum (required when `data_file` is present).
164
+ const bytes = Buffer.from(params.inline.content_base64, "base64");
165
+ const md5 = createHash("md5").update(bytes).digest("hex");
166
+ const form = new FormData();
167
+ form.set("profile", params.profile);
168
+ form.set("md5", md5);
169
+ form.set("data_file", new Blob([new Uint8Array(bytes)]), params.inline.filename);
170
+ // Do not set Content-Type — fetch adds the multipart boundary itself.
171
+ return request(`${API_PATH}/task/run`, {
172
+ method: "POST",
173
+ body: form,
174
+ });
175
+ }
176
+ // Remote data source → form-encoded mode.
177
+ const fields = { profile: params.profile };
178
+ if (params.dataSource !== undefined) {
179
+ for (const [key, value] of Object.entries(params.dataSource)) {
180
+ if (value !== undefined)
181
+ fields[key] = String(value);
182
+ }
183
+ }
184
+ return request(`${API_PATH}/task/run`, formInit(fields));
185
+ },
186
+ async taskInfo(packageName) {
187
+ // v4 `task/info`: form POST with `package_name`.
188
+ return request(`${API_PATH}/task/info`, formInit({ package_name: packageName }));
189
+ },
190
+ async getResultFile({ package_name, filename }) {
191
+ // The first non-JSON client path: the body is the file content (text or
192
+ // binary), but the API returns a JSON error envelope on failure. Read the
193
+ // bytes, then surface a JSON error as a structured error — never as content
194
+ // (FR-007).
195
+ const key = requireKey();
196
+ const form = new URLSearchParams({ package_name, filename });
197
+ const headers = new Headers();
198
+ headers.set("X-API-KEY", key);
199
+ headers.set("Content-Type", "application/x-www-form-urlencoded");
200
+ let response;
201
+ try {
202
+ response = await fetch(`${baseUrl}${API_PATH}/task/get_file`, {
203
+ method: "POST",
204
+ headers,
205
+ body: form.toString(),
206
+ });
207
+ }
208
+ catch (error) {
209
+ throw new WebbulaApiError("network", `Webbula API unreachable: ${networkReason(error)}`);
210
+ }
211
+ if (response.status === 401) {
212
+ throw new WebbulaApiError("auth", "Authentication failed — check WEBBULA_API_KEY");
213
+ }
214
+ const contentType = response.headers.get("content-type") ?? undefined;
215
+ const bytes = Buffer.from(await response.arrayBuffer());
216
+ // A JSON body (or any non-2xx) is an error envelope, not file content.
217
+ const looksJson = contentType?.includes("json") ?? false;
218
+ if (!response.ok || looksJson) {
219
+ let parsed;
220
+ try {
221
+ parsed = JSON.parse(bytes.toString("utf8"));
222
+ }
223
+ catch {
224
+ parsed = undefined;
225
+ }
226
+ const messages = extractErrorMessages(parsed);
227
+ throw new WebbulaApiError("upstream", messages.length > 0
228
+ ? messages.join("; ")
229
+ : `Webbula API error (HTTP ${response.status})`);
230
+ }
231
+ return { bytes, contentType };
232
+ },
233
+ async personaAppend({ filter, scope, record }) {
234
+ // Forward only the PII the caller supplied (omit undefined) plus the
235
+ // append filter + scope. The persona endpoint lives on its own host.
236
+ const body = { filter, scope };
237
+ for (const [key, value] of Object.entries(record)) {
238
+ if (value !== undefined)
239
+ body[key] = value;
240
+ }
241
+ return request(`${PERSONA_APPEND_PATH}`, {
242
+ method: "POST",
243
+ headers: { "Content-Type": "application/json" },
244
+ body: JSON.stringify(body),
245
+ }, personaBaseUrl);
246
+ },
247
+ };
248
+ }
249
+ //# sourceMappingURL=webbula-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webbula-client.js","sourceRoot":"","sources":["../../src/api/webbula-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Shared, transport-agnostic configuration for the Webbula MCP server.
3
+ *
4
+ * All configuration is passed through the environment. The Webbula API key is
5
+ * read from the `WEBBULA_API_KEY` env var and held in memory only — never logged
6
+ * or persisted.
7
+ */
8
+ export declare const SERVER_NAME = "webbula-mcp";
9
+ export declare const SERVER_VERSION = "0.1.0";
10
+ /** Default Webbula API base URL; override with WEBBULA_API_BASE_URL. */
11
+ export declare const WEBBULA_API_BASE_URL: string;
12
+ /**
13
+ * API version path segment. The current Webbula contract is **v4** (`/api.v4`,
14
+ * note the dot, not a slash); endpoints take `application/x-www-form-urlencoded`
15
+ * bodies. Override with `WEBBULA_API_PATH`.
16
+ */
17
+ export declare const WEBBULA_API_PATH: string;
18
+ /**
19
+ * Base URL for the persona data-append API (feature 006). The append product
20
+ * lives on a separate host from the hygiene API; override with
21
+ * `WEBBULA_PERSONA_BASE_URL`. The default is a placeholder — the real host must
22
+ * be supplied per deployment before `append_*` tools can succeed.
23
+ */
24
+ export declare const WEBBULA_PERSONA_BASE_URL: string;
25
+ /**
26
+ * Default identity-resolution scope for the `append_*` tools when a call omits
27
+ * one (CSV note: "Assume Both"). Override with `WEBBULA_PERSONA_SCOPE`; an
28
+ * unrecognized value falls back to `both`.
29
+ */
30
+ export declare const WEBBULA_PERSONA_DEFAULT_SCOPE: string;
31
+ /** HTTP transport listen port; override with PORT. */
32
+ export declare const HTTP_PORT: number;
33
+ /**
34
+ * When set (`WEBBULA_DEBUG=1`), the client logs each raw upstream response to
35
+ * **stderr** (stdout is reserved for the stdio JSON-RPC stream). A `key` field,
36
+ * if ever present, is redacted before logging (principle V). Off by default.
37
+ */
38
+ export declare const WEBBULA_DEBUG: boolean;
39
+ /** `verify_email` → `task/express` verification preset (CSV: `_Verification_Only`). */
40
+ export declare const WEBBULA_VERIFICATION_PROFILE: string;
41
+ /** `hygiene_check` → `task/express` email-hygiene preset (CSV: `_B2C_HVI`). */
42
+ export declare const WEBBULA_HYGIENE_PROFILE: string;
43
+ /** `validate_lead` → `task/lead` lead-hygiene preset (CSV: `_Default_LHVI`). */
44
+ export declare const WEBBULA_LEAD_PROFILE: string;
45
+ /**
46
+ * `upload_file` → `task/run` batch preset. The CSV lists three batch presets
47
+ * (verification / hygiene / lead) for the one batch endpoint; this defaults to
48
+ * the verification preset and is overridden per call (or via env) for hygiene or
49
+ * lead batches.
50
+ */
51
+ export declare const WEBBULA_BATCH_PROFILE: string;
52
+ /**
53
+ * Upper bound on addresses per `hygiene_check` call (research D8 / FR-010). The
54
+ * express endpoint is synchronous, so large lists belong in the batch flow.
55
+ * Falls back to 100; a non-positive or non-numeric override is ignored.
56
+ */
57
+ export declare const WEBBULA_MAX_EMAILS: number;
58
+ /**
59
+ * Maximum decoded size (bytes) of an inline base64 upload for `upload_file`
60
+ * (feature 003, FR-003). The JSON `file_base64` mode is documented at ~1 MiB;
61
+ * larger files must use a remote `data_source`. Override with
62
+ * `WEBBULA_MAX_INLINE_BYTES`; a non-positive/non-numeric override falls back.
63
+ */
64
+ export declare const WEBBULA_MAX_INLINE_BYTES: number;
65
+ /**
66
+ * Resolve the Webbula API key from the environment. Returns `undefined` when
67
+ * `WEBBULA_API_KEY` is unset — callers that require a key (tool handlers) surface
68
+ * a structured error per constitution principle V (Fail Loud, Fail Structured).
69
+ */
70
+ export declare function resolveApiKey(): string | undefined;
package/dist/config.js ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Shared, transport-agnostic configuration for the Webbula MCP server.
3
+ *
4
+ * All configuration is passed through the environment. The Webbula API key is
5
+ * read from the `WEBBULA_API_KEY` env var and held in memory only — never logged
6
+ * or persisted.
7
+ */
8
+ export const SERVER_NAME = "webbula-mcp";
9
+ export const SERVER_VERSION = "0.1.0";
10
+ /** Default Webbula API base URL; override with WEBBULA_API_BASE_URL. */
11
+ export const WEBBULA_API_BASE_URL = process.env.WEBBULA_API_BASE_URL ?? "http://eh-api-dev3.us-east-1.elasticbeanstalk.com";
12
+ /**
13
+ * API version path segment. The current Webbula contract is **v4** (`/api.v4`,
14
+ * note the dot, not a slash); endpoints take `application/x-www-form-urlencoded`
15
+ * bodies. Override with `WEBBULA_API_PATH`.
16
+ */
17
+ export const WEBBULA_API_PATH = process.env.WEBBULA_API_PATH ?? "/api.v4";
18
+ /**
19
+ * Base URL for the persona data-append API (feature 006). The append product
20
+ * lives on a separate host from the hygiene API; override with
21
+ * `WEBBULA_PERSONA_BASE_URL`. The default is a placeholder — the real host must
22
+ * be supplied per deployment before `append_*` tools can succeed.
23
+ */
24
+ export const WEBBULA_PERSONA_BASE_URL = process.env.WEBBULA_PERSONA_BASE_URL ?? "https://persona.webbula.com";
25
+ /**
26
+ * Default identity-resolution scope for the `append_*` tools when a call omits
27
+ * one (CSV note: "Assume Both"). Override with `WEBBULA_PERSONA_SCOPE`; an
28
+ * unrecognized value falls back to `both`.
29
+ */
30
+ export const WEBBULA_PERSONA_DEFAULT_SCOPE = (() => {
31
+ const raw = process.env.WEBBULA_PERSONA_SCOPE;
32
+ return raw === "individual" || raw === "household" ? raw : "both";
33
+ })();
34
+ /** HTTP transport listen port; override with PORT. */
35
+ export const HTTP_PORT = Number(process.env.PORT ?? 3000);
36
+ /**
37
+ * When set (`WEBBULA_DEBUG=1`), the client logs each raw upstream response to
38
+ * **stderr** (stdout is reserved for the stdio JSON-RPC stream). A `key` field,
39
+ * if ever present, is redacted before logging (principle V). Off by default.
40
+ */
41
+ export const WEBBULA_DEBUG = process.env.WEBBULA_DEBUG === "1" || process.env.WEBBULA_DEBUG === "true";
42
+ /*
43
+ * Per-tool default profile presets, hardcoded to the values from the MCP tool
44
+ * list ("Filter Profile or Preset" column). The account owns the real set of
45
+ * profile names (they are typically prefixed with the company name upstream,
46
+ * e.g. `Webbula_B2C_HVI`); these bare presets are the out-of-the-box defaults a
47
+ * call uses when it omits `profile`. Each stays env-overridable (undocumented)
48
+ * for accounts whose preset names differ.
49
+ */
50
+ /** `verify_email` → `task/express` verification preset (CSV: `_Verification_Only`). */
51
+ export const WEBBULA_VERIFICATION_PROFILE = process.env.WEBBULA_VERIFICATION_PROFILE ?? "_Verification_Only";
52
+ /** `hygiene_check` → `task/express` email-hygiene preset (CSV: `_B2C_HVI`). */
53
+ export const WEBBULA_HYGIENE_PROFILE = process.env.WEBBULA_HYGIENE_PROFILE ?? "_B2C_HVI";
54
+ /** `validate_lead` → `task/lead` lead-hygiene preset (CSV: `_Default_LHVI`). */
55
+ export const WEBBULA_LEAD_PROFILE = process.env.WEBBULA_LEAD_PROFILE ?? "_Default_LHVI";
56
+ /**
57
+ * `upload_file` → `task/run` batch preset. The CSV lists three batch presets
58
+ * (verification / hygiene / lead) for the one batch endpoint; this defaults to
59
+ * the verification preset and is overridden per call (or via env) for hygiene or
60
+ * lead batches.
61
+ */
62
+ export const WEBBULA_BATCH_PROFILE = process.env.WEBBULA_BATCH_PROFILE ?? "_Verification_Only";
63
+ /**
64
+ * Upper bound on addresses per `hygiene_check` call (research D8 / FR-010). The
65
+ * express endpoint is synchronous, so large lists belong in the batch flow.
66
+ * Falls back to 100; a non-positive or non-numeric override is ignored.
67
+ */
68
+ export const WEBBULA_MAX_EMAILS = (() => {
69
+ const raw = Number(process.env.WEBBULA_MAX_EMAILS);
70
+ return Number.isInteger(raw) && raw > 0 ? raw : 100;
71
+ })();
72
+ /**
73
+ * Maximum decoded size (bytes) of an inline base64 upload for `upload_file`
74
+ * (feature 003, FR-003). The JSON `file_base64` mode is documented at ~1 MiB;
75
+ * larger files must use a remote `data_source`. Override with
76
+ * `WEBBULA_MAX_INLINE_BYTES`; a non-positive/non-numeric override falls back.
77
+ */
78
+ export const WEBBULA_MAX_INLINE_BYTES = (() => {
79
+ const raw = Number(process.env.WEBBULA_MAX_INLINE_BYTES);
80
+ return Number.isInteger(raw) && raw > 0 ? raw : 1_048_576;
81
+ })();
82
+ /**
83
+ * Resolve the Webbula API key from the environment. Returns `undefined` when
84
+ * `WEBBULA_API_KEY` is unset — callers that require a key (tool handlers) surface
85
+ * a structured error per constitution principle V (Fail Loud, Fail Structured).
86
+ */
87
+ export function resolveApiKey() {
88
+ return process.env.WEBBULA_API_KEY || undefined;
89
+ }
90
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AACzC,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AAEtC,wEAAwE;AACxE,MAAM,CAAC,MAAM,oBAAoB,GAC/B,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,mDAAmD,CAAC;AAE1F;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,SAAS,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GACnC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,6BAA6B,CAAC;AAExE;;;;GAIG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,GAAG,EAAE;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAC9C,OAAO,GAAG,KAAK,YAAY,IAAI,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACpE,CAAC,CAAC,EAAE,CAAC;AAEL,sDAAsD;AACtD,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,MAAM,CAAC;AAE5E;;;;;;;GAOG;AAEH,uFAAuF;AACvF,MAAM,CAAC,MAAM,4BAA4B,GACvC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,oBAAoB,CAAC;AAEnE,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAClC,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,UAAU,CAAC;AAEpD,gFAAgF;AAChF,MAAM,CAAC,MAAM,oBAAoB,GAC/B,OAAO,CAAC,GAAG,CAAC,oBAAoB,IAAI,eAAe,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAChC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,oBAAoB,CAAC;AAE5D;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAG,EAAE;IACtC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;IACnD,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACtD,CAAC,CAAC,EAAE,CAAC;AAEL;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACzD,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;AAC5D,CAAC,CAAC,EAAE,CAAC;AAEL;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,SAAS,CAAC;AAClD,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Webbula MCP server entry point.
4
+ *
5
+ * Dual-transport bootstrap (constitution principle IV):
6
+ * - default → local stdio transport (the `webbula-mcp` npm binary)
7
+ * - `--http` / MCP_HTTP → remote HTTP transport (`/mcp`) plus `/health`
8
+ *
9
+ * Both transports expose the identical tool set via `registerTools()`.
10
+ */
11
+ import { startHttpServer } from "./server/http.js";
12
+ import { startStdioServer } from "./server/stdio.js";
13
+ const useHttp = process.argv.includes("--http") || process.env.MCP_HTTP === "1";
14
+ if (useHttp) {
15
+ startHttpServer();
16
+ }
17
+ else {
18
+ startStdioServer().catch((error) => {
19
+ console.error("webbula-mcp: failed to start stdio transport:", error);
20
+ process.exit(1);
21
+ });
22
+ }
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,OAAO,GACX,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC;AAElE,IAAI,OAAO,EAAE,CAAC;IACZ,eAAe,EAAE,CAAC;AACpB,CAAC;KAAM,CAAC;IACN,gBAAgB,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACjC,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;QACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ /** Build the Express app. Exported so it can be exercised in tests. */
2
+ export declare function createHttpApp(): import("express-serve-static-core").Express;
3
+ /** Start the HTTP server and resolve once it is listening. */
4
+ export declare function startHttpServer(port?: number): import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Remote HTTP transport entry point for the Webbula MCP server.
3
+ *
4
+ * Hosts the operational `GET /health` liveness/readiness endpoint plus the MCP
5
+ * Streamable HTTP transport at `/mcp`. The MCP server is built per request in
6
+ * stateless mode (`sessionIdGenerator: undefined`) via the shared
7
+ * `buildMcpServer()`, so the tool set is identical to the stdio transport
8
+ * (constitution principle IV).
9
+ */
10
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
11
+ import express from "express";
12
+ import { HTTP_PORT, SERVER_NAME, SERVER_VERSION } from "../config.js";
13
+ import { buildMcpServer } from "./stdio.js";
14
+ const startedAt = Date.now();
15
+ /** Build the Express app. Exported so it can be exercised in tests. */
16
+ export function createHttpApp() {
17
+ const app = express();
18
+ app.use(express.json());
19
+ // Unauthenticated health endpoint — operational contract for probes.
20
+ app.get("/health", (_req, res) => {
21
+ const uptimeSeconds = Math.floor((Date.now() - startedAt) / 1000);
22
+ res.status(200).json({
23
+ status: "ok",
24
+ service: SERVER_NAME,
25
+ version: SERVER_VERSION,
26
+ uptime_seconds: uptimeSeconds,
27
+ timestamp: new Date().toISOString(),
28
+ });
29
+ });
30
+ // MCP Streamable HTTP transport — stateless: a fresh server + transport per
31
+ // request, with no session persistence (suits the synchronous, short tool calls).
32
+ app.post("/mcp", async (req, res) => {
33
+ const server = buildMcpServer();
34
+ const transport = new StreamableHTTPServerTransport({
35
+ sessionIdGenerator: undefined,
36
+ enableJsonResponse: true,
37
+ });
38
+ res.on("close", () => {
39
+ void transport.close();
40
+ void server.close();
41
+ });
42
+ try {
43
+ await server.connect(transport);
44
+ await transport.handleRequest(req, res, req.body);
45
+ }
46
+ catch (error) {
47
+ console.error(`${SERVER_NAME}: /mcp request failed:`, error);
48
+ if (!res.headersSent) {
49
+ res.status(500).json({
50
+ jsonrpc: "2.0",
51
+ error: { code: -32603, message: "Internal server error" },
52
+ id: null,
53
+ });
54
+ }
55
+ }
56
+ });
57
+ // Stateless transport: session-based GET/DELETE are not supported.
58
+ const methodNotAllowed = (_req, res) => {
59
+ res.status(405).json({
60
+ jsonrpc: "2.0",
61
+ error: { code: -32000, message: "Method not allowed (stateless transport)" },
62
+ id: null,
63
+ });
64
+ };
65
+ app.get("/mcp", methodNotAllowed);
66
+ app.delete("/mcp", methodNotAllowed);
67
+ return app;
68
+ }
69
+ /** Start the HTTP server and resolve once it is listening. */
70
+ export function startHttpServer(port = HTTP_PORT) {
71
+ const app = createHttpApp();
72
+ return app.listen(port, () => {
73
+ console.error(`${SERVER_NAME} v${SERVER_VERSION} HTTP transport listening on port ${port} (GET /health)`);
74
+ });
75
+ }
76
+ //# sourceMappingURL=http.js.map