take-blip-mcp 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.
Files changed (87) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +424 -0
  3. package/dist/blip-client.d.ts +69 -0
  4. package/dist/blip-client.d.ts.map +1 -0
  5. package/dist/blip-client.js +187 -0
  6. package/dist/blip-client.js.map +1 -0
  7. package/dist/config.d.ts +37 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +197 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/env-file.d.ts +16 -0
  12. package/dist/env-file.d.ts.map +1 -0
  13. package/dist/env-file.js +70 -0
  14. package/dist/env-file.js.map +1 -0
  15. package/dist/errors.d.ts +49 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +74 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/flow-map.d.ts +48 -0
  20. package/dist/flow-map.d.ts.map +1 -0
  21. package/dist/flow-map.js +209 -0
  22. package/dist/flow-map.js.map +1 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +143 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/logger.d.ts +18 -0
  28. package/dist/logger.d.ts.map +1 -0
  29. package/dist/logger.js +42 -0
  30. package/dist/logger.js.map +1 -0
  31. package/dist/tools/ai.d.ts +3 -0
  32. package/dist/tools/ai.d.ts.map +1 -0
  33. package/dist/tools/ai.js +51 -0
  34. package/dist/tools/ai.js.map +1 -0
  35. package/dist/tools/broadcast.d.ts +3 -0
  36. package/dist/tools/broadcast.d.ts.map +1 -0
  37. package/dist/tools/broadcast.js +46 -0
  38. package/dist/tools/broadcast.js.map +1 -0
  39. package/dist/tools/buckets.d.ts +3 -0
  40. package/dist/tools/buckets.d.ts.map +1 -0
  41. package/dist/tools/buckets.js +49 -0
  42. package/dist/tools/buckets.js.map +1 -0
  43. package/dist/tools/command.d.ts +3 -0
  44. package/dist/tools/command.d.ts.map +1 -0
  45. package/dist/tools/command.js +41 -0
  46. package/dist/tools/command.js.map +1 -0
  47. package/dist/tools/contacts.d.ts +3 -0
  48. package/dist/tools/contacts.d.ts.map +1 -0
  49. package/dist/tools/contacts.js +119 -0
  50. package/dist/tools/contacts.js.map +1 -0
  51. package/dist/tools/context.d.ts +3 -0
  52. package/dist/tools/context.d.ts.map +1 -0
  53. package/dist/tools/context.js +50 -0
  54. package/dist/tools/context.js.map +1 -0
  55. package/dist/tools/events.d.ts +3 -0
  56. package/dist/tools/events.d.ts.map +1 -0
  57. package/dist/tools/events.js +48 -0
  58. package/dist/tools/events.js.map +1 -0
  59. package/dist/tools/flow-tools.d.ts +3 -0
  60. package/dist/tools/flow-tools.d.ts.map +1 -0
  61. package/dist/tools/flow-tools.js +77 -0
  62. package/dist/tools/flow-tools.js.map +1 -0
  63. package/dist/tools/flow.d.ts +3 -0
  64. package/dist/tools/flow.d.ts.map +1 -0
  65. package/dist/tools/flow.js +33 -0
  66. package/dist/tools/flow.js.map +1 -0
  67. package/dist/tools/index.d.ts +5 -0
  68. package/dist/tools/index.d.ts.map +1 -0
  69. package/dist/tools/index.js +32 -0
  70. package/dist/tools/index.js.map +1 -0
  71. package/dist/tools/messages.d.ts +3 -0
  72. package/dist/tools/messages.d.ts.map +1 -0
  73. package/dist/tools/messages.js +31 -0
  74. package/dist/tools/messages.js.map +1 -0
  75. package/dist/tools/schedules.d.ts +3 -0
  76. package/dist/tools/schedules.d.ts.map +1 -0
  77. package/dist/tools/schedules.js +25 -0
  78. package/dist/tools/schedules.js.map +1 -0
  79. package/dist/tools/shared.d.ts +55 -0
  80. package/dist/tools/shared.d.ts.map +1 -0
  81. package/dist/tools/shared.js +67 -0
  82. package/dist/tools/shared.js.map +1 -0
  83. package/dist/tools/threads.d.ts +3 -0
  84. package/dist/tools/threads.d.ts.map +1 -0
  85. package/dist/tools/threads.js +39 -0
  86. package/dist/tools/threads.js.map +1 -0
  87. package/package.json +60 -0
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Thin client over the Blip HTTP/Command API.
3
+ *
4
+ * Responsibilities:
5
+ * - Build the command/message envelopes and the `Authorization: Key ...` header.
6
+ * - Enforce a per-request timeout (AbortController).
7
+ * - Retry with exponential backoff + jitter on 429 / 5xx / network errors.
8
+ * - Turn `status: "failure"` command responses into a clear thrown error.
9
+ *
10
+ * It never logs the authorization header, and any text it surfaces from the
11
+ * wire is run through redactSecrets first.
12
+ */
13
+ import { randomUUID } from "node:crypto";
14
+ import { BlipCommandError, BlipHttpError, BlipTimeoutError, redactSecrets, } from "./errors.js";
15
+ import { silentLogger } from "./logger.js";
16
+ const MAX_BACKOFF_MS = 8_000;
17
+ const MAX_RETRY_AFTER_MS = 30_000;
18
+ export class BlipClient {
19
+ baseUrl;
20
+ authorization;
21
+ timeoutMs;
22
+ maxRetries;
23
+ fetchImpl;
24
+ sleep;
25
+ logger;
26
+ secrets;
27
+ constructor(options) {
28
+ const resolvedFetch = options.fetchImpl ?? globalThis.fetch;
29
+ if (typeof resolvedFetch !== "function") {
30
+ throw new Error("global fetch is not available. blip-mcp requires Node.js 18+ (or pass fetchImpl).");
31
+ }
32
+ this.baseUrl = options.baseUrl.replace(/\/+$/, "");
33
+ this.authorization = options.authorization;
34
+ this.timeoutMs = options.timeoutMs ?? 30_000;
35
+ this.maxRetries = options.maxRetries ?? 3;
36
+ this.fetchImpl = resolvedFetch;
37
+ this.sleep = options.sleepImpl ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
38
+ this.logger = options.logger ?? silentLogger;
39
+ this.secrets = [this.authorization, this.authorization.replace(/^key\s+/i, "")].filter((value) => value.length >= 4);
40
+ }
41
+ /** Send a command to `/commands` and return the response envelope. */
42
+ async sendCommand(params) {
43
+ const envelope = {
44
+ id: randomUUID(),
45
+ method: params.method,
46
+ uri: params.uri,
47
+ };
48
+ if (params.to !== undefined)
49
+ envelope["to"] = params.to;
50
+ if (params.type !== undefined)
51
+ envelope["type"] = params.type;
52
+ if (params.resource !== undefined)
53
+ envelope["resource"] = params.resource;
54
+ const response = await this.post("/commands", envelope);
55
+ const body = (await this.readJson(response));
56
+ const result = body ?? {};
57
+ if (result.status === "failure") {
58
+ throw new BlipCommandError({
59
+ code: result.reason?.code,
60
+ description: result.reason?.description,
61
+ method: params.method,
62
+ uri: params.uri,
63
+ });
64
+ }
65
+ return result;
66
+ }
67
+ /** Send a message to `/messages`. Blip replies 2xx with no useful body. */
68
+ async sendMessage(params) {
69
+ const id = randomUUID();
70
+ const envelope = {
71
+ id,
72
+ to: params.to,
73
+ type: params.type ?? "text/plain",
74
+ content: params.content,
75
+ };
76
+ const response = await this.post("/messages", envelope);
77
+ await this.drain(response);
78
+ return { status: "accepted", id };
79
+ }
80
+ // --- internals -------------------------------------------------------------
81
+ async post(path, payload) {
82
+ const url = `${this.baseUrl}${path}`;
83
+ const init = {
84
+ method: "POST",
85
+ headers: {
86
+ "Content-Type": "application/json",
87
+ Accept: "application/json",
88
+ Authorization: this.authorization,
89
+ },
90
+ body: JSON.stringify(payload),
91
+ };
92
+ let attempt = 0;
93
+ for (;;) {
94
+ try {
95
+ const response = await this.fetchWithTimeout(url, init);
96
+ if (response.ok)
97
+ return response;
98
+ if (this.isRetryableStatus(response.status) && attempt < this.maxRetries) {
99
+ const delay = this.backoffDelay(attempt, response);
100
+ await this.drain(response);
101
+ this.logger.warn(`HTTP ${response.status} on ${path}; retry ${attempt + 1}/${this.maxRetries} in ${delay}ms`);
102
+ attempt += 1;
103
+ await this.sleep(delay);
104
+ continue;
105
+ }
106
+ throw new BlipHttpError(response.status, await this.errorExcerpt(response));
107
+ }
108
+ catch (err) {
109
+ if (err instanceof BlipHttpError)
110
+ throw err;
111
+ const retryable = err instanceof BlipTimeoutError || this.isNetworkError(err);
112
+ if (retryable && attempt < this.maxRetries) {
113
+ const delay = this.backoffDelay(attempt);
114
+ const label = err instanceof Error ? err.name : "network error";
115
+ this.logger.warn(`${label} on ${path}; retry ${attempt + 1}/${this.maxRetries} in ${delay}ms`);
116
+ attempt += 1;
117
+ await this.sleep(delay);
118
+ continue;
119
+ }
120
+ throw err;
121
+ }
122
+ }
123
+ }
124
+ async fetchWithTimeout(url, init) {
125
+ const controller = new AbortController();
126
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
127
+ try {
128
+ return await this.fetchImpl(url, { ...init, signal: controller.signal });
129
+ }
130
+ catch (err) {
131
+ if (controller.signal.aborted)
132
+ throw new BlipTimeoutError(this.timeoutMs);
133
+ throw err;
134
+ }
135
+ finally {
136
+ clearTimeout(timer);
137
+ }
138
+ }
139
+ isRetryableStatus(status) {
140
+ return status === 429 || (status >= 500 && status <= 599);
141
+ }
142
+ isNetworkError(err) {
143
+ // The fetch standard throws a TypeError for network-level failures.
144
+ return err instanceof TypeError;
145
+ }
146
+ backoffDelay(attempt, response) {
147
+ if (response) {
148
+ const retryAfter = response.headers.get("retry-after");
149
+ if (retryAfter !== null) {
150
+ const seconds = Number(retryAfter);
151
+ if (Number.isFinite(seconds) && seconds >= 0) {
152
+ return Math.min(seconds * 1000, MAX_RETRY_AFTER_MS);
153
+ }
154
+ }
155
+ }
156
+ const base = 500 * 2 ** attempt;
157
+ const jitter = Math.random() * base * 0.25;
158
+ return Math.min(Math.round(base + jitter), MAX_BACKOFF_MS);
159
+ }
160
+ async readJson(response) {
161
+ const text = await this.safeText(response);
162
+ if (!text)
163
+ return undefined;
164
+ try {
165
+ return JSON.parse(text);
166
+ }
167
+ catch {
168
+ return { raw: redactSecrets(text, this.secrets) };
169
+ }
170
+ }
171
+ async errorExcerpt(response) {
172
+ const text = await this.safeText(response);
173
+ return redactSecrets(text.slice(0, 500), this.secrets);
174
+ }
175
+ async safeText(response) {
176
+ try {
177
+ return await response.text();
178
+ }
179
+ catch {
180
+ return "";
181
+ }
182
+ }
183
+ async drain(response) {
184
+ await this.safeText(response);
185
+ }
186
+ }
187
+ //# sourceMappingURL=blip-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blip-client.js","sourceRoot":"","sources":["../src/blip-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { LogLevel } from "./logger.js";
2
+ export type CredentialMode = "authorization" | "identifier+key";
3
+ /** A single named flow (Blip bot) with its own host + credentials. */
4
+ export interface FlowCredential {
5
+ name: string;
6
+ baseUrl: string;
7
+ authorization: string;
8
+ credentialMode: CredentialMode;
9
+ contractId: string | undefined;
10
+ }
11
+ export interface BlipConfig {
12
+ /** Default flow host (mirror of flows[0]). */
13
+ baseUrl: string;
14
+ /** Default flow authorization (mirror of flows[0]). */
15
+ authorization: string;
16
+ credentialMode: CredentialMode;
17
+ contractId: string | undefined;
18
+ /** All configured flows; flows[0] is the default. */
19
+ flows: FlowCredential[];
20
+ defaultFlowName: string;
21
+ /** Base dir for everything blip-mcp discovers/writes (default .mcp-blip). */
22
+ dataDir: string;
23
+ /** Where blip_map_flow writes componentized docs (default <dataDir>/flows). */
24
+ flowsDir: string;
25
+ allowWrites: boolean;
26
+ timeoutMs: number;
27
+ maxRetries: number;
28
+ logLevel: LogLevel;
29
+ }
30
+ export declare function authToken(authorization: string): string;
31
+ export declare function maskAuthorization(authorization: string): string;
32
+ export declare function configSecrets(config: BlipConfig): string[];
33
+ /** Resolve a flow by name (case-insensitive); falls back to the default flow. */
34
+ export declare function resolveFlow(config: BlipConfig, name?: string): FlowCredential;
35
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): BlipConfig;
36
+ export declare function describeConfig(config: BlipConfig): string;
37
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,gBAAgB,CAAC;AAEhE,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,cAAc,CAAC;IAC/B,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,qDAAqD;IACrD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AA0CD,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAO1D;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAQ7E;AA8DD,wBAAgB,UAAU,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,UAAU,CA8D3E;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAiBzD"}
package/dist/config.js ADDED
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Environment-driven configuration for blip-mcp (multi-flow aware).
3
+ *
4
+ * Credentials come ONLY from environment variables. You can configure several
5
+ * "flows" (Blip bots): a default flow from the classic vars, plus numbered
6
+ * extra flows via BLIP_FLOW_<n>_NAME / _AUTHORIZATION / _CONTRACT_ID, etc.
7
+ */
8
+ import { z } from "zod";
9
+ import { BlipConfigError } from "./errors.js";
10
+ const TRUTHY = new Set(["1", "true", "yes", "y", "on"]);
11
+ const FALSY = new Set(["0", "false", "no", "n", "off", ""]);
12
+ function parseBool(raw, fallback) {
13
+ if (raw === undefined)
14
+ return fallback;
15
+ const value = raw.trim().toLowerCase();
16
+ if (TRUTHY.has(value))
17
+ return true;
18
+ if (FALSY.has(value))
19
+ return false;
20
+ throw new BlipConfigError(`Invalid boolean for BLIP_ALLOW_WRITES: "${raw}". Use true/false.`);
21
+ }
22
+ /** Keep only env keys that have a non-empty, trimmed value. */
23
+ function compact(env) {
24
+ const out = {};
25
+ for (const [key, value] of Object.entries(env)) {
26
+ if (typeof value === "string" && value.trim() !== "")
27
+ out[key] = value.trim();
28
+ }
29
+ return out;
30
+ }
31
+ const TuningSchema = z.object({
32
+ BLIP_BASE_URL: z.string().url().optional(),
33
+ BLIP_ALLOW_WRITES: z.string().optional(),
34
+ BLIP_REQUEST_TIMEOUT_MS: z.coerce.number().int().positive().max(600_000).optional(),
35
+ BLIP_MAX_RETRIES: z.coerce.number().int().min(0).max(10).optional(),
36
+ BLIP_LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).optional(),
37
+ BLIP_FLOWS_DIR: z.string().optional(),
38
+ });
39
+ function normalizeAuthorization(raw) {
40
+ const trimmed = raw.trim();
41
+ if (/^key\s+/i.test(trimmed))
42
+ return `Key ${trimmed.replace(/^key\s+/i, "")}`;
43
+ return `Key ${trimmed}`;
44
+ }
45
+ function computeAuthorization(identifier, accessKey) {
46
+ const token = Buffer.from(`${identifier}:${accessKey}`, "utf8").toString("base64");
47
+ return `Key ${token}`;
48
+ }
49
+ export function authToken(authorization) {
50
+ return authorization.replace(/^key\s+/i, "").trim();
51
+ }
52
+ export function maskAuthorization(authorization) {
53
+ return authorization.replace(/^key\s+/i, "") ? "Key ****" : "(empty)";
54
+ }
55
+ export function configSecrets(config) {
56
+ const out = new Set();
57
+ for (const flow of config.flows) {
58
+ out.add(flow.authorization);
59
+ out.add(authToken(flow.authorization));
60
+ }
61
+ return [...out].filter((value) => value.length >= 4);
62
+ }
63
+ /** Resolve a flow by name (case-insensitive); falls back to the default flow. */
64
+ export function resolveFlow(config, name) {
65
+ if (!name)
66
+ return config.flows[0];
67
+ const match = config.flows.find((f) => f.name.toLowerCase() === name.toLowerCase());
68
+ if (!match) {
69
+ const names = config.flows.map((f) => f.name).join(", ");
70
+ throw new BlipConfigError(`Unknown flow "${name}". Configured flows: ${names}.`);
71
+ }
72
+ return match;
73
+ }
74
+ function buildFlow(inputs, fallbackBaseUrl) {
75
+ let authorization;
76
+ let credentialMode;
77
+ if (inputs.authorization) {
78
+ authorization = normalizeAuthorization(inputs.authorization);
79
+ credentialMode = "authorization";
80
+ }
81
+ else if (inputs.botIdentifier && inputs.accessKey) {
82
+ authorization = computeAuthorization(inputs.botIdentifier, inputs.accessKey);
83
+ credentialMode = "identifier+key";
84
+ }
85
+ else {
86
+ throw new BlipConfigError(`Flow "${inputs.name}": missing credentials. Provide an Authorization header or identifier+access key.`);
87
+ }
88
+ let baseUrl;
89
+ if (inputs.baseUrl)
90
+ baseUrl = inputs.baseUrl;
91
+ else if (inputs.contractId)
92
+ baseUrl = `https://${inputs.contractId}.http.msging.net`;
93
+ else if (fallbackBaseUrl)
94
+ baseUrl = fallbackBaseUrl;
95
+ else
96
+ baseUrl = "https://http.msging.net";
97
+ baseUrl = baseUrl.replace(/\/+$/, "");
98
+ return { name: inputs.name, baseUrl, authorization, credentialMode, contractId: inputs.contractId };
99
+ }
100
+ /** Discover extra flows declared as BLIP_FLOW_<n>_*. */
101
+ function parseExtraFlows(raw, fallbackBaseUrl) {
102
+ const indices = new Set();
103
+ for (const key of Object.keys(raw)) {
104
+ const m = /^BLIP_FLOW_(\d+)_[A-Z_]+$/.exec(key);
105
+ if (m)
106
+ indices.add(Number(m[1]));
107
+ }
108
+ const flows = [];
109
+ for (const n of [...indices].sort((a, b) => a - b)) {
110
+ const p = `BLIP_FLOW_${n}_`;
111
+ const name = raw[`${p}NAME`] ?? `flow${n}`;
112
+ flows.push(buildFlow({
113
+ name,
114
+ authorization: raw[`${p}AUTHORIZATION`],
115
+ botIdentifier: raw[`${p}BOT_IDENTIFIER`],
116
+ accessKey: raw[`${p}ACCESS_KEY`],
117
+ contractId: raw[`${p}CONTRACT_ID`] ?? raw[`${p}SHORTNAME`],
118
+ baseUrl: raw[`${p}BASE_URL`],
119
+ }, fallbackBaseUrl));
120
+ }
121
+ return flows;
122
+ }
123
+ export function loadConfig(env = process.env) {
124
+ const raw = compact(env);
125
+ let tuning;
126
+ try {
127
+ tuning = TuningSchema.parse(raw);
128
+ }
129
+ catch (err) {
130
+ if (err instanceof z.ZodError) {
131
+ const issues = err.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
132
+ throw new BlipConfigError(`Invalid environment configuration: ${issues}`);
133
+ }
134
+ throw err;
135
+ }
136
+ const hasDefaultCreds = Boolean(raw["BLIP_AUTHORIZATION"]) ||
137
+ Boolean(raw["BLIP_BOT_IDENTIFIER"] && raw["BLIP_ACCESS_KEY"]);
138
+ if (!hasDefaultCreds) {
139
+ throw new BlipConfigError([
140
+ "Missing Blip credentials. Provide ONE of:",
141
+ ' • BLIP_AUTHORIZATION="Key <token>" (copy from the Blip portal), or',
142
+ " • BLIP_BOT_IDENTIFIER + BLIP_ACCESS_KEY.",
143
+ "See the README for where to find these in the portal.",
144
+ ].join("\n"));
145
+ }
146
+ const defaultFlow = buildFlow({
147
+ name: raw["BLIP_FLOW_NAME"] ?? "default",
148
+ authorization: raw["BLIP_AUTHORIZATION"],
149
+ botIdentifier: raw["BLIP_BOT_IDENTIFIER"],
150
+ accessKey: raw["BLIP_ACCESS_KEY"],
151
+ contractId: raw["BLIP_CONTRACT_ID"] ?? raw["BLIP_SHORTNAME"],
152
+ baseUrl: tuning.BLIP_BASE_URL,
153
+ });
154
+ const extraFlows = parseExtraFlows(raw, defaultFlow.baseUrl);
155
+ const flows = [defaultFlow, ...extraFlows];
156
+ const seen = new Set();
157
+ for (const f of flows) {
158
+ const key = f.name.toLowerCase();
159
+ if (seen.has(key))
160
+ throw new BlipConfigError(`Duplicate flow name "${f.name}".`);
161
+ seen.add(key);
162
+ }
163
+ const dataDir = raw["BLIP_DATA_DIR"] ?? ".mcp-blip";
164
+ return {
165
+ baseUrl: defaultFlow.baseUrl,
166
+ authorization: defaultFlow.authorization,
167
+ credentialMode: defaultFlow.credentialMode,
168
+ contractId: defaultFlow.contractId,
169
+ flows,
170
+ defaultFlowName: defaultFlow.name,
171
+ dataDir,
172
+ flowsDir: raw["BLIP_FLOWS_DIR"] ?? `${dataDir}/flows`,
173
+ allowWrites: parseBool(raw["BLIP_ALLOW_WRITES"], false),
174
+ timeoutMs: tuning.BLIP_REQUEST_TIMEOUT_MS ?? 30_000,
175
+ maxRetries: tuning.BLIP_MAX_RETRIES ?? 3,
176
+ logLevel: tuning.BLIP_LOG_LEVEL ?? "info",
177
+ };
178
+ }
179
+ export function describeConfig(config) {
180
+ const flowList = config.flows
181
+ .map((f, i) => ` ${i === 0 ? "*" : "-"} ${f.name} (${maskAuthorization(f.authorization)}, ${f.baseUrl})`)
182
+ .join("\n");
183
+ return [
184
+ ` default flow : ${config.defaultFlowName}`,
185
+ ` base URL : ${config.baseUrl}`,
186
+ ` authorization : ${maskAuthorization(config.authorization)} (mode: ${config.credentialMode})`,
187
+ ` flows (${config.flows.length}):`,
188
+ flowList,
189
+ ` data dir : ${config.dataDir}`,
190
+ ` flows dir : ${config.flowsDir}`,
191
+ ` writes : ${config.allowWrites ? "ENABLED — side effects allowed" : "read-only (default)"}`,
192
+ ` request timeout : ${config.timeoutMs} ms`,
193
+ ` max retries : ${config.maxRetries}`,
194
+ ` log level : ${config.logLevel}`,
195
+ ].join("\n");
196
+ }
197
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /** Parse `KEY=value` lines, honoring quotes and stripping inline comments. */
2
+ export declare function parseEnvFile(content: string): Record<string, string>;
3
+ /**
4
+ * Apply an env file into `target` (default process.env). Existing keys are kept
5
+ * unless `override` is true. Returns the list of keys that were applied.
6
+ */
7
+ export declare function loadEnvFile(filePath: string, target?: NodeJS.ProcessEnv, options?: {
8
+ override?: boolean;
9
+ }): string[];
10
+ /**
11
+ * Decide which env file to auto-load:
12
+ * - BLIP_ENV_FILE if set (even to a path that doesn't exist → no load), else
13
+ * - ./blip.env if it exists.
14
+ */
15
+ export declare function resolveEnvFile(env?: NodeJS.ProcessEnv): string | undefined;
16
+ //# sourceMappingURL=env-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.d.ts","sourceRoot":"","sources":["../src/env-file.ts"],"names":[],"mappings":"AAaA,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBpE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAM,CAAC,UAAwB,EACvC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAO,GACnC,MAAM,EAAE,CAWV;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAMvF"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Optional `.env`-style file loader.
3
+ *
4
+ * blip-mcp is configured from environment variables. For convenience — and
5
+ * especially for multi-flow setups with many variables — it can also load a
6
+ * `blip.env` file from the working directory (or BLIP_ENV_FILE). Real
7
+ * environment variables always win (the file never overrides them).
8
+ *
9
+ * This keeps all your credentials in ONE gitignored file inside your project,
10
+ * instead of a long `env` block in `.mcp.json`.
11
+ */
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ /** Parse `KEY=value` lines, honoring quotes and stripping inline comments. */
14
+ export function parseEnvFile(content) {
15
+ const out = {};
16
+ for (const raw of content.split(/\r?\n/)) {
17
+ const line = raw.trim();
18
+ if (!line || line.startsWith("#"))
19
+ continue;
20
+ const eq = line.indexOf("=");
21
+ if (eq === -1)
22
+ continue;
23
+ const key = line.slice(0, eq).trim();
24
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
25
+ continue;
26
+ let value = line.slice(eq + 1).trim();
27
+ if ((value.startsWith('"') && value.endsWith('"')) ||
28
+ (value.startsWith("'") && value.endsWith("'"))) {
29
+ value = value.slice(1, -1);
30
+ }
31
+ else {
32
+ value = value.replace(/\s+#.*$/, "").trim();
33
+ }
34
+ out[key] = value;
35
+ }
36
+ return out;
37
+ }
38
+ /**
39
+ * Apply an env file into `target` (default process.env). Existing keys are kept
40
+ * unless `override` is true. Returns the list of keys that were applied.
41
+ */
42
+ export function loadEnvFile(filePath, target = process.env, options = {}) {
43
+ if (!existsSync(filePath))
44
+ return [];
45
+ const parsed = parseEnvFile(readFileSync(filePath, "utf8"));
46
+ const applied = [];
47
+ for (const [key, value] of Object.entries(parsed)) {
48
+ if (value === "")
49
+ continue;
50
+ if (!options.override && target[key] !== undefined)
51
+ continue;
52
+ target[key] = value;
53
+ applied.push(key);
54
+ }
55
+ return applied;
56
+ }
57
+ /**
58
+ * Decide which env file to auto-load:
59
+ * - BLIP_ENV_FILE if set (even to a path that doesn't exist → no load), else
60
+ * - ./blip.env if it exists.
61
+ */
62
+ export function resolveEnvFile(env = process.env) {
63
+ if (typeof env["BLIP_ENV_FILE"] === "string" && env["BLIP_ENV_FILE"] !== "") {
64
+ return env["BLIP_ENV_FILE"];
65
+ }
66
+ if (existsSync("blip.env"))
67
+ return "blip.env";
68
+ return undefined;
69
+ }
70
+ //# sourceMappingURL=env-file.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-file.js","sourceRoot":"","sources":["../src/env-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEnD,8EAA8E;AAC9E,MAAM,UAAU,YAAY,CAAC,OAAe;IAC1C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS;QACxB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACpD,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IACE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC9C,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAC9C,CAAC;YACD,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,QAAgB,EAChB,SAA4B,OAAO,CAAC,GAAG,EACvC,UAAkC,EAAE;IAEpC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IAC5D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IAAI,KAAK,KAAK,EAAE;YAAE,SAAS;QAC3B,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS;YAAE,SAAS;QAC7D,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAyB,OAAO,CAAC,GAAG;IACjE,IAAI,OAAO,GAAG,CAAC,eAAe,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,eAAe,CAAC,KAAK,EAAE,EAAE,CAAC;QAC5E,OAAO,GAAG,CAAC,eAAe,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IAC9C,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Error types and secret redaction for blip-mcp.
3
+ *
4
+ * Redaction is defensive: even if a token somehow lands in an error message or
5
+ * a response body, {@link redactSecrets} scrubs it before it can reach a log
6
+ * line or a tool result.
7
+ */
8
+ /**
9
+ * Remove known secrets and any `Key <token>` authorization header from a string.
10
+ * Safe to call on arbitrary text (log lines, HTTP bodies, error messages).
11
+ */
12
+ export declare function redactSecrets(text: string, secrets?: ReadonlyArray<string>): string;
13
+ /** Base class for every error this package throws on purpose. */
14
+ export declare class BlipError extends Error {
15
+ constructor(message: string, options?: {
16
+ cause?: unknown;
17
+ });
18
+ }
19
+ /** Configuration / environment problem (missing credentials, bad host, etc.). */
20
+ export declare class BlipConfigError extends BlipError {
21
+ }
22
+ /** A request exceeded the configured timeout. */
23
+ export declare class BlipTimeoutError extends BlipError {
24
+ readonly timeoutMs: number;
25
+ constructor(timeoutMs: number);
26
+ }
27
+ /** A non-2xx HTTP response from the Blip API (e.g. 401 auth, 500 server). */
28
+ export declare class BlipHttpError extends BlipError {
29
+ readonly status: number;
30
+ readonly bodyExcerpt: string;
31
+ constructor(status: number, bodyExcerpt?: string);
32
+ }
33
+ /**
34
+ * A command was delivered but Blip answered with `status: "failure"`.
35
+ * Carries the structured `reason` so callers get a clear message.
36
+ */
37
+ export declare class BlipCommandError extends BlipError {
38
+ readonly code: number | undefined;
39
+ readonly description: string | undefined;
40
+ readonly method: string;
41
+ readonly uri: string;
42
+ constructor(params: {
43
+ code?: number | undefined;
44
+ description?: string | undefined;
45
+ method: string;
46
+ uri: string;
47
+ });
48
+ }
49
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,aAAa,CAAC,MAAM,CAAM,GAAG,MAAM,CAUvF;AAED,iEAAiE;AACjE,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED,iFAAiF;AACjF,qBAAa,eAAgB,SAAQ,SAAS;CAAG;AAEjD,iDAAiD;AACjD,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBACf,SAAS,EAAE,MAAM;CAI9B;AAED,6EAA6E;AAC7E,qBAAa,aAAc,SAAQ,SAAS;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;gBACjB,MAAM,EAAE,MAAM,EAAE,WAAW,SAAK;CAK7C;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;gBACT,MAAM,EAAE;QAClB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACjC,MAAM,EAAE,MAAM,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;KACb;CAeF"}