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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Michaeltsg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,424 @@
1
+ # blip-mcp
2
+
3
+ An **MCP server** for the [Take Blip](https://www.blip.ai/) platform. It exposes
4
+ your Blip account to any MCP client (Claude Code, Claude Desktop, Cursor, …)
5
+ over the **stdio** transport, on top of the official Blip **HTTP / Command API**.
6
+
7
+ You can:
8
+
9
+ - **Reach your Blip resources** — contacts (CRM), key/value buckets, broadcast
10
+ lists, schedules, and a generic escape hatch for *anything else*.
11
+ - **Ask your bot's AI / knowledge base** a question (`blip_ask_ai`).
12
+ - Send messages and mutate data — gated behind an explicit safety switch so you
13
+ don't message real users by accident.
14
+
15
+ > Built with `@modelcontextprotocol/sdk`, TypeScript (strict), `zod` validation,
16
+ > per-request timeouts, retry-with-backoff, and **secret redaction everywhere**.
17
+
18
+ ---
19
+
20
+ ## Requirements
21
+
22
+ - **Node.js 18.17+** (uses the built-in global `fetch`).
23
+ - A Take Blip bot and its connection credentials (see below).
24
+
25
+ ---
26
+
27
+ ## Install
28
+
29
+ **Three ways for people to run it:**
30
+
31
+ 1. **Straight from GitHub (no npm needed — works as soon as the repo is pushed):**
32
+ ```bash
33
+ npx -y github:Michaeltsg/mcp-blip --version
34
+ ```
35
+ npm clones, builds (via the `prepare` script) and runs it. This is what the
36
+ `.mcp.json` below uses.
37
+
38
+ 2. **From npm** (smoothest `npx` UX) — once you publish it. Note: the name
39
+ `blip-mcp` is already taken on npm, so publish under a free name (this package
40
+ is set up as `take-blip-mcp`) and use `npx -y take-blip-mcp`.
41
+
42
+ 3. **Clone & build** (for development or a pinned local copy):
43
+ ```bash
44
+ git clone https://github.com/Michaeltsg/mcp-blip.git
45
+ cd mcp-blip && npm install && npm run build
46
+ # then point .mcp.json at: node /abs/path/to/mcp-blip/dist/index.js
47
+ ```
48
+
49
+ > ⚠️ Do **not** use a bare `npx blip-mcp` — that pulls a different, unrelated
50
+ > package from npm. Always use the `github:Michaeltsg/mcp-blip` form (or your own
51
+ > published name).
52
+
53
+ ## Get your Blip credentials (portal step-by-step)
54
+
55
+ 1. Open your bot in the Blip portal (**https://blip.ai** → your bot).
56
+ 2. Go to **Configurações** (Settings) → **Informações de conexão**
57
+ (Connection information).
58
+ 3. Open the **HTTP Endpoints** section.
59
+ 4. Copy the value of **"Cabeçalho de autenticação (Authorization)"**
60
+ (Authorization header). It already looks like:
61
+
62
+ ```
63
+ Key bWV1Ym90OmFiYzEyMy4uLg==
64
+ ```
65
+
66
+ That whole string is your `BLIP_AUTHORIZATION`.
67
+ 5. Your **contract id / shortname** is the bot subdomain — it forms the host
68
+ `https://<contract>.http.msging.net`. Use it as `BLIP_CONTRACT_ID`.
69
+
70
+ > Prefer not to copy the ready-made header? You can instead provide
71
+ > `BLIP_BOT_IDENTIFIER` + `BLIP_ACCESS_KEY` and the server computes
72
+ > `Key base64("<identifier>:<accessKey>")` for you.
73
+
74
+ ---
75
+
76
+ ## Multiple bots (multi-flow)
77
+
78
+ If your account has several bots (a router + sub-bots), add each as a flow.
79
+ For EACH bot, repeat the credential steps above to get its name (the bot Id)
80
+ and an access key:
81
+
82
+ ```
83
+ BLIP_CONTRACT_ID=mycontract
84
+ BLIP_FLOW_NAME=my-router-bot
85
+ BLIP_AUTHORIZATION="Key ..." # default flow
86
+
87
+ BLIP_FLOW_1_NAME=my-subbot
88
+ BLIP_FLOW_1_AUTHORIZATION="Key ..."
89
+ BLIP_FLOW_2_NAME=another-bot
90
+ BLIP_FLOW_2_AUTHORIZATION="Key ..."
91
+ ```
92
+
93
+ `blip_list_flows` shows everything configured (names only). When you map a flow,
94
+ the server checks that each key's real identity matches its configured name.
95
+
96
+ ## Environment variables
97
+
98
+ | Variable | Required | Default | Description |
99
+ |---|---|---|---|
100
+ | `BLIP_AUTHORIZATION` | one of two modes | — | Ready-made header, e.g. `Key <base64>`. **Preferred.** |
101
+ | `BLIP_BOT_IDENTIFIER` | with access key | — | Bot identifier (credential mode 2). |
102
+ | `BLIP_ACCESS_KEY` | with identifier | — | Access key (credential mode 2). |
103
+ | `BLIP_CONTRACT_ID` | no | — | Contract / shortname → host `https://<id>.http.msging.net`. |
104
+ | `BLIP_SHORTNAME` | no | — | Alias for `BLIP_CONTRACT_ID`. |
105
+ | `BLIP_BASE_URL` | no | derived | Full base URL override (rare). |
106
+ | `BLIP_ALLOW_WRITES` | no | `false` | Master switch for side-effecting tools. |
107
+ | `BLIP_REQUEST_TIMEOUT_MS` | no | `30000` | Per-request timeout (ms). |
108
+ | `BLIP_MAX_RETRIES` | no | `3` | Retries on 429/5xx/network errors. |
109
+ | `BLIP_LOG_LEVEL` | no | `info` | `error` \| `warn` \| `info` \| `debug` (stderr only). |
110
+ | `BLIP_FLOW_NAME` | no | `default` | Friendly name for the default flow (e.g. your bot Id). |
111
+ | `BLIP_FLOW_<n>_*` | no | — | Extra flows: `_NAME`, `_AUTHORIZATION` (or `_BOT_IDENTIFIER`/`_ACCESS_KEY`), `_CONTRACT_ID`. |
112
+ | `BLIP_FLOWS_DIR` | no | `flows` | Where `blip_map_flow` writes componentized docs. |
113
+ | `BLIP_ENV_FILE` | no | `blip.env` | Path to an env file to auto-load (real env always wins). |
114
+ | `BLIP_DATA_DIR` | no | `.mcp-blip` | Base folder for everything blip-mcp writes. |
115
+
116
+ If no contract/host is given, the server falls back to the shared host
117
+ `https://http.msging.net` (for accounts not in an organization).
118
+
119
+ ---
120
+
121
+ ## Safety: read-only by default
122
+
123
+ Every tool that **sends a message** or **mutates data** is gated by
124
+ `BLIP_ALLOW_WRITES` (default `false`). While off:
125
+
126
+ - `blip_send_message`, `blip_set_bucket`, `blip_set_contact`,
127
+ `blip_delete_contact` return a **"READ-ONLY MODE"** notice instead of running.
128
+ - `blip_command` refuses any method other than `get`.
129
+
130
+ Start in read-only mode while you explore. Set `BLIP_ALLOW_WRITES=true` (and
131
+ restart the server) only when you're ready to cause real side effects.
132
+
133
+ ---
134
+
135
+ ## Configure your MCP client
136
+
137
+ ### Project file `.mcp.json` (recommended)
138
+
139
+ Drop a `.mcp.json` at your project root (any MCP client picks it up). Keep your
140
+ secrets in a gitignored `blip.env` and point the server at it:
141
+
142
+ ```json
143
+ {
144
+ "mcpServers": {
145
+ "blip": {
146
+ "command": "npx",
147
+ "args": ["-y", "github:Michaeltsg/mcp-blip"],
148
+ "env": { "BLIP_ENV_FILE": "blip.env", "BLIP_ALLOW_WRITES": "false" }
149
+ }
150
+ }
151
+ }
152
+ ```
153
+
154
+ Then create `blip.env` next to it with your credentials. The server also
155
+ auto-loads `./blip.env` even if you omit `BLIP_ENV_FILE`. See
156
+ [.mcp.json.example](.mcp.json.example). (Cursor uses `.cursor/mcp.json`, same shape.)
157
+
158
+ ### Claude Code
159
+
160
+ Local build (works today):
161
+
162
+ ```bash
163
+ claude mcp add blip -s user \
164
+ -e BLIP_AUTHORIZATION="Key xxxxx" \
165
+ -e BLIP_CONTRACT_ID="meu-contrato" \
166
+ -e BLIP_ALLOW_WRITES="false" \
167
+ -- node /absolute/path/to/mcp-blip/dist/index.js
168
+ ```
169
+
170
+ After publishing to npm, the same with `npx`:
171
+
172
+ ```bash
173
+ claude mcp add blip -s user \
174
+ -e BLIP_AUTHORIZATION="Key xxxxx" \
175
+ -e BLIP_CONTRACT_ID="meu-contrato" \
176
+ -e BLIP_ALLOW_WRITES="false" \
177
+ -- npx -y github:Michaeltsg/mcp-blip
178
+ ```
179
+
180
+ ### Claude Desktop / Cursor (JSON)
181
+
182
+ Add to `mcpServers` (Claude Desktop: `claude_desktop_config.json`):
183
+
184
+ ```json
185
+ {
186
+ "mcpServers": {
187
+ "blip": {
188
+ "command": "node",
189
+ "args": ["/absolute/path/to/mcp-blip/dist/index.js"],
190
+ "env": {
191
+ "BLIP_AUTHORIZATION": "Key xxxxx",
192
+ "BLIP_CONTRACT_ID": "meu-contrato",
193
+ "BLIP_ALLOW_WRITES": "false"
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ Once published, swap `command`/`args` for `"command": "npx", "args": ["-y", "github:Michaeltsg/mcp-blip"]`.
201
+
202
+ ---
203
+
204
+ ## Validate credentials (self-test)
205
+
206
+ ```bash
207
+ BLIP_AUTHORIZATION="Key xxxxx" BLIP_CONTRACT_ID="meu-contrato" \
208
+ node dist/index.js --self-test
209
+ ```
210
+
211
+ It performs a tiny `GET /contacts?$take=1` and prints a **masked** config
212
+ summary plus OK/FAILED. Your token is never printed.
213
+
214
+ ---
215
+
216
+ ## Tools
217
+
218
+ ### Read (safe, always available)
219
+
220
+ | Tool | What it does |
221
+ |---|---|
222
+ | `blip_list_contacts` | List CRM contacts. Args: `skip`, `take` (1–100), `filter` (OData). |
223
+ | `blip_get_contact` | Get one contact by `identity`. |
224
+ | `blip_find_contact_by_phone` | Find a contact by phone (tries 55/+/local formats). |
225
+ | `blip_get_bucket` | Read a value from key/value storage by `id`. |
226
+ | `blip_list_broadcast_lists` | List broadcast/distribution lists. *(experimental)* |
227
+ | `blip_list_recipients` | List recipients of a broadcast `list`. *(experimental)* |
228
+ | `blip_list_schedules` | List scheduled messages/commands. *(experimental)* |
229
+ | `blip_ask_ai` | Ask your bot's AI. `mode="knowledge"` (knowledge base) or `"intent"` (intentions/entities). *(experimental)* |
230
+ | `blip_command` | **Escape hatch:** run any `get` command (and writes when enabled). |
231
+ | `blip_get_thread` | Conversation history with a contact (what was said). |
232
+ | `blip_list_threads` | Recent conversations. |
233
+ | `blip_get_context` | A contact's **context variables** — the values flow rules evaluate against. *(experimental)* |
234
+ | `blip_get_context_variable` | One context variable; `stateid@{flowId}` = where the contact is in a flow. *(experimental)* |
235
+ | `blip_get_flow` | The published flow definition (blocks + conditions/rules). *(experimental)* |
236
+ | `blip_list_event_categories` | Event categories your flows track. *(experimental)* |
237
+ | `blip_get_event_track` | Tracked events for a category (optional date range). *(experimental)* |
238
+ | `blip_list_flows` | List configured flows (names/hosts only, never secrets). |
239
+ | `blip_map_flow` | Read a flow and write componentized docs (index + one file per block) under `flows/<flow>/`. |
240
+
241
+ ### Write (require `BLIP_ALLOW_WRITES=true`)
242
+
243
+ | Tool | Side effect |
244
+ |---|---|
245
+ | `blip_send_message` | **Sends a message to a real recipient.** |
246
+ | `blip_set_bucket` | Writes a value to storage. |
247
+ | `blip_set_contact` | Creates/updates a contact (`merge`). |
248
+ | `blip_delete_contact` | Permanently deletes a contact. |
249
+
250
+ ### Examples (natural language to your MCP client)
251
+
252
+ - "List the first 5 Blip contacts that came from WhatsApp."
253
+ → `blip_list_contacts { take: 5, filter: "substringof('WhatsApp',source)" }`
254
+ - "Ask my bot's knowledge base: what are the opening hours?"
255
+ → `blip_ask_ai { text: "what are the opening hours?", mode: "knowledge" }`
256
+ - "Read bucket `onboarding_state`."
257
+ → `blip_get_bucket { id: "onboarding_state" }`
258
+ - "Run a raw command: get the active message templates."
259
+ → `blip_command { method: "get", uri: "/templates" }`
260
+ - (writes on) "Send 'Hi!' to 5511999999999 on WhatsApp."
261
+ → `blip_send_message { to: "5511999999999@wa.gw.msging.net", content: "Hi!" }`
262
+
263
+ ---
264
+
265
+ ## Experimental / unverified endpoints
266
+
267
+ These were implemented from Blip's documented patterns and the official C# SDK,
268
+ but **not verified against a live account**, so they're marked *experimental*:
269
+
270
+ - **AI / knowledge base** (`blip_ask_ai`): `postmaster@ai.msging.net`,
271
+ `SET /content/analysis` (knowledge) and `SET /analysis` (intent), resource
272
+ type `application/vnd.iris.ai.analysis-request+json`. The exact content
273
+ contract can vary per account.
274
+ - **Broadcast lists** (`blip_list_broadcast_lists`, `blip_list_recipients`):
275
+ `postmaster@broadcast.msging.net`, `/lists` and `/lists/{id}/recipients`.
276
+ - **Schedules** (`blip_list_schedules`): `postmaster@scheduler.msging.net`,
277
+ `/schedules`.
278
+
279
+ If any of these don't match your account, use `blip_command` with the exact
280
+ `to`/`uri`/`resource` from your Blip docs.
281
+
282
+ > **Buckets note:** bucket commands are sent to your bot's **own node** (no
283
+ > `to`/postmaster), which is the correct Blip behavior — this differs from the
284
+ > `postmaster@msging.net` mentioned in some notes. Override with `blip_command`
285
+ > if your setup needs it.
286
+
287
+ ---
288
+
289
+ ## Debugging "why did this contact take this path?"
290
+
291
+ Blip's HTTP API does not expose a turn-by-turn condition-evaluation log, but you
292
+ can reconstruct the decision from a few reads and let your MCP client diff them:
293
+
294
+ 1. `blip_get_flow` — the exact condition/rule on the branch in question.
295
+ 2. `blip_get_context { identity }` — the contact's actual variable values that the
296
+ rule evaluated against (and `stateid@{flowId}` for where they are now).
297
+ 3. `blip_get_thread { identity }` — what the user actually said / chose.
298
+ 4. (optional) `blip_get_event_track` — milestones the flow logged.
299
+
300
+ Then ask, e.g.: *"Why did 5511999999999 enter the Support flow instead of Sales?"*
301
+ Your client reads the rule, sees (say) `origem = WhatsApp-Ads` in the context and
302
+ explains the match — or shows that the variable the rule needed was empty.
303
+
304
+ > These journey tools are **experimental** and sent to your bot's own node (no
305
+ > postmaster). The richest turn-by-turn view is still the portal's conversation
306
+ > history / Builder debug tool; the API gives you the raw materials to reconstruct it.
307
+
308
+ ---
309
+ ## Multiple flows & mapping a flow into docs
310
+
311
+ `blip.env` (or your MCP env) can hold several flows (Blip bots) — a parent
312
+ router plus its subflows, each with its own access key:
313
+
314
+ ```
315
+ BLIP_CONTRACT_ID=mycontract
316
+ BLIP_FLOW_NAME=my-router
317
+ BLIP_AUTHORIZATION="Key ..." # default flow (the parent / router)
318
+
319
+ BLIP_FLOW_1_NAME=fila-identificada
320
+ BLIP_FLOW_1_AUTHORIZATION="Key ..." # a subflow bot (its own access key)
321
+ ```
322
+
323
+ - `blip_list_flows` shows what's configured (never the secrets).
324
+ - `blip_map_flow { flow: "my-router" }` reads the flow and writes a
325
+ **componentized** doc tree under `flows/<flow>/`, so a large flow stays browsable:
326
+
327
+ ```
328
+ .mcp-blip/flows/my-router/
329
+ index.md overview + routing graph (block -> who it connects to)
330
+ index.json machine-readable map
331
+ blocks/
332
+ inicio.md what the block does (messages, HTTP/script actions) + its rules
333
+ roteador-de-mensagem.md
334
+ ...
335
+ ```
336
+
337
+ Re-run after editing the flow in Builder (idempotent). CLI: `npm run map-flow -- --flow my-router`.
338
+ The `flows/` folder is gitignored by default (business logic). Mapping a subflow's
339
+ internals needs that subflow's own access key configured as an extra flow.
340
+
341
+ ---
342
+
343
+ ## Where blip-mcp writes (`.mcp-blip/`)
344
+
345
+ Everything the server discovers or documents goes under a single `.mcp-blip/`
346
+ folder in your project root — e.g. `blip_map_flow` writes to
347
+ `.mcp-blip/flows/<flow>/`. This keeps blip-mcp's output namespaced and out of
348
+ the way when you connect it inside a new or existing repo.
349
+
350
+ Add it (and `blip.env`) to your project's `.gitignore`:
351
+
352
+ ```
353
+ .mcp-blip/
354
+ blip.env
355
+ ```
356
+
357
+ Override the base with `BLIP_DATA_DIR`.
358
+
359
+ ---
360
+
361
+ ## How it works
362
+
363
+ Commands `POST` to `https://<contract>.http.msging.net/commands` with header
364
+ `Authorization: Key <token>` and a LIME-style envelope:
365
+
366
+ ```json
367
+ {
368
+ "id": "<uuid>",
369
+ "to": "postmaster@crm.msging.net",
370
+ "method": "get",
371
+ "uri": "/contacts?$skip=0&$take=20"
372
+ }
373
+ ```
374
+
375
+ A response with `status: "failure"` is converted into a clear error carrying
376
+ Blip's `reason.code` and `reason.description`. Messages `POST` to `/messages`.
377
+
378
+ ---
379
+
380
+ ## Publishing to npm
381
+
382
+ A GitHub Action (`.github/workflows/publish.yml`) publishes the package whenever
383
+ you create a GitHub Release. One-time setup:
384
+
385
+ 1. Create an npm account + an **automation** access token (npmjs.com → Access
386
+ Tokens → Generate New Token → Automation).
387
+ 2. Add it as a repo secret named `NPM_TOKEN`:
388
+ ```bash
389
+ gh secret set NPM_TOKEN # paste the token when prompted
390
+ ```
391
+ (or GitHub → Settings → Secrets and variables → Actions).
392
+ 3. Create a release — the Action runs typecheck + tests, builds, and publishes:
393
+ ```bash
394
+ gh release create v0.1.0 --generate-notes
395
+ ```
396
+ For later versions, bump first: `npm version patch && git push --follow-tags`,
397
+ then create the matching release.
398
+
399
+ The first publish claims the `take-blip-mcp` name. After that, users install with
400
+ `npx -y take-blip-mcp`. (A `ci.yml` workflow runs tests on every push/PR.)
401
+
402
+ ## Development
403
+
404
+ ```bash
405
+ npm run dev # run from source via tsx
406
+ npm run build # compile to dist/
407
+ npm run typecheck # tsc --noEmit (strict)
408
+ npm test # vitest (header assembly, sendCommand, failure, guardrails)
409
+ npm run self-test # build first, then validate credentials
410
+ ```
411
+
412
+ ---
413
+
414
+ ## Security notes
415
+
416
+ - Credentials come **only** from environment variables; nothing is hardcoded.
417
+ - The authorization header value is **never logged**. All logs go to **stderr**
418
+ (stdout is reserved for the MCP protocol) and pass through a redactor that
419
+ scrubs the token and any `Key …` pattern — even from error bodies.
420
+ - Writes are off by default (`BLIP_ALLOW_WRITES=false`).
421
+
422
+ ## License
423
+
424
+ MIT © Michaeltsg. See [LICENSE](LICENSE).
@@ -0,0 +1,69 @@
1
+ import type { Logger } from "./logger.js";
2
+ export type CommandMethod = "get" | "set" | "merge" | "delete";
3
+ export interface SendCommandParams {
4
+ method: CommandMethod;
5
+ uri: string;
6
+ to?: string | undefined;
7
+ type?: string | undefined;
8
+ resource?: unknown;
9
+ }
10
+ export interface SendMessageParams {
11
+ to: string;
12
+ content: unknown;
13
+ type?: string | undefined;
14
+ }
15
+ /** Shape of a Blip command response envelope (only the fields we rely on). */
16
+ export interface BlipCommandResponse {
17
+ id?: string;
18
+ from?: string;
19
+ to?: string;
20
+ method?: string;
21
+ status?: "success" | "failure" | "pending";
22
+ uri?: string;
23
+ type?: string;
24
+ resource?: unknown;
25
+ reason?: {
26
+ code?: number;
27
+ description?: string;
28
+ };
29
+ [key: string]: unknown;
30
+ }
31
+ type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
32
+ export interface BlipClientOptions {
33
+ baseUrl: string;
34
+ authorization: string;
35
+ timeoutMs?: number;
36
+ maxRetries?: number;
37
+ fetchImpl?: FetchLike;
38
+ sleepImpl?: (ms: number) => Promise<void>;
39
+ logger?: Logger;
40
+ }
41
+ export declare class BlipClient {
42
+ private readonly baseUrl;
43
+ private readonly authorization;
44
+ private readonly timeoutMs;
45
+ private readonly maxRetries;
46
+ private readonly fetchImpl;
47
+ private readonly sleep;
48
+ private readonly logger;
49
+ private readonly secrets;
50
+ constructor(options: BlipClientOptions);
51
+ /** Send a command to `/commands` and return the response envelope. */
52
+ sendCommand(params: SendCommandParams): Promise<BlipCommandResponse>;
53
+ /** Send a message to `/messages`. Blip replies 2xx with no useful body. */
54
+ sendMessage(params: SendMessageParams): Promise<{
55
+ status: "accepted";
56
+ id: string;
57
+ }>;
58
+ private post;
59
+ private fetchWithTimeout;
60
+ private isRetryableStatus;
61
+ private isNetworkError;
62
+ private backoffDelay;
63
+ private readJson;
64
+ private errorExcerpt;
65
+ private safeText;
66
+ private drain;
67
+ }
68
+ export {};
69
+ //# sourceMappingURL=blip-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blip-client.d.ts","sourceRoot":"","sources":["../src/blip-client.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE/D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,aAAa,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B;AAED,8EAA8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,KAAK,SAAS,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEzE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAW;gBAEvB,OAAO,EAAE,iBAAiB;IAmBtC,sEAAsE;IAChE,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAyB1E,2EAA2E;IACrE,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;YAe3E,IAAI;YA6CJ,gBAAgB;IAa9B,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,YAAY;YAeN,QAAQ;YAUR,YAAY;YAKZ,QAAQ;YAQR,KAAK;CAGpB"}