@codespar/mcp-take-blip 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.js ADDED
@@ -0,0 +1,256 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Server for Take Blip — Brazilian chatbot and messaging platform.
4
+ *
5
+ * Tools:
6
+ * - send_message: Send a message to a contact
7
+ * - get_contacts: List contacts
8
+ * - create_contact: Create a contact
9
+ * - get_threads: Get message threads
10
+ * - send_notification: Send a notification/broadcast message
11
+ * - get_analytics: Get chatbot analytics
12
+ * - create_broadcast: Create a broadcast list and send
13
+ * - get_chatbot_flow: Get chatbot flow configuration
14
+ *
15
+ * Environment:
16
+ * TAKE_BLIP_BOT_ID — Bot identifier
17
+ * TAKE_BLIP_ACCESS_KEY — Bot access key
18
+ *
19
+ * Note: Take Blip uses a JSON-based messaging protocol.
20
+ * Requests go as POST to /commands with type/method/uri in body.
21
+ */
22
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
23
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
25
+ const BOT_ID = process.env.TAKE_BLIP_BOT_ID || "";
26
+ const ACCESS_KEY = process.env.TAKE_BLIP_ACCESS_KEY || "";
27
+ const BASE_URL = "https://msging.net";
28
+ function getAuthKey() {
29
+ const raw = `${BOT_ID}:${ACCESS_KEY}`;
30
+ return btoa(raw);
31
+ }
32
+ async function blipCommand(id, method, uri, type, resource) {
33
+ const body = { id, method, uri };
34
+ if (type)
35
+ body.type = type;
36
+ if (resource)
37
+ body.resource = resource;
38
+ const res = await fetch(`${BASE_URL}/commands`, {
39
+ method: "POST",
40
+ headers: {
41
+ "Content-Type": "application/json",
42
+ "Authorization": `Key ${getAuthKey()}`,
43
+ },
44
+ body: JSON.stringify(body),
45
+ });
46
+ if (!res.ok) {
47
+ const err = await res.text();
48
+ throw new Error(`Take Blip API ${res.status}: ${err}`);
49
+ }
50
+ return res.json();
51
+ }
52
+ async function blipMessage(to, type, content) {
53
+ const res = await fetch(`${BASE_URL}/messages`, {
54
+ method: "POST",
55
+ headers: {
56
+ "Content-Type": "application/json",
57
+ "Authorization": `Key ${getAuthKey()}`,
58
+ },
59
+ body: JSON.stringify({ id: crypto.randomUUID(), to, type, content }),
60
+ });
61
+ if (!res.ok) {
62
+ const err = await res.text();
63
+ throw new Error(`Take Blip API ${res.status}: ${err}`);
64
+ }
65
+ return res.json();
66
+ }
67
+ const server = new Server({ name: "mcp-take-blip", version: "0.1.0" }, { capabilities: { tools: {} } });
68
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
69
+ tools: [
70
+ {
71
+ name: "send_message",
72
+ description: "Send a message to a contact via Take Blip",
73
+ inputSchema: {
74
+ type: "object",
75
+ properties: {
76
+ to: { type: "string", description: "Recipient identity (e.g., 5511999999999@wa.gw.msging.net)" },
77
+ content: { type: "string", description: "Message text content" },
78
+ type: { type: "string", description: "Content type (default: text/plain)", enum: ["text/plain", "application/json"] },
79
+ },
80
+ required: ["to", "content"],
81
+ },
82
+ },
83
+ {
84
+ name: "get_contacts",
85
+ description: "List contacts in Take Blip",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ skip: { type: "number", description: "Number of contacts to skip" },
90
+ take: { type: "number", description: "Number of contacts to return (default 20)" },
91
+ },
92
+ },
93
+ },
94
+ {
95
+ name: "create_contact",
96
+ description: "Create a contact in Take Blip",
97
+ inputSchema: {
98
+ type: "object",
99
+ properties: {
100
+ identity: { type: "string", description: "Contact identity (e.g., 5511999999999@wa.gw.msging.net)" },
101
+ name: { type: "string", description: "Contact name" },
102
+ email: { type: "string", description: "Contact email" },
103
+ phoneNumber: { type: "string", description: "Phone number" },
104
+ group: { type: "string", description: "Contact group" },
105
+ },
106
+ required: ["identity", "name"],
107
+ },
108
+ },
109
+ {
110
+ name: "get_threads",
111
+ description: "Get message threads (recent conversations)",
112
+ inputSchema: {
113
+ type: "object",
114
+ properties: {
115
+ skip: { type: "number", description: "Number of threads to skip" },
116
+ take: { type: "number", description: "Number of threads to return" },
117
+ },
118
+ },
119
+ },
120
+ {
121
+ name: "send_notification",
122
+ description: "Send a notification message to a contact",
123
+ inputSchema: {
124
+ type: "object",
125
+ properties: {
126
+ to: { type: "string", description: "Recipient identity" },
127
+ templateName: { type: "string", description: "Message template name" },
128
+ templateNamespace: { type: "string", description: "Template namespace" },
129
+ parameters: {
130
+ type: "array",
131
+ description: "Template parameters",
132
+ items: { type: "string" },
133
+ },
134
+ },
135
+ required: ["to", "templateName"],
136
+ },
137
+ },
138
+ {
139
+ name: "get_analytics",
140
+ description: "Get chatbot analytics and metrics",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {
144
+ startDate: { type: "string", description: "Start date (YYYY-MM-DD)" },
145
+ endDate: { type: "string", description: "End date (YYYY-MM-DD)" },
146
+ event: { type: "string", description: "Event category to track" },
147
+ },
148
+ required: ["startDate", "endDate"],
149
+ },
150
+ },
151
+ {
152
+ name: "create_broadcast",
153
+ description: "Create a broadcast distribution list and send messages",
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {
157
+ listName: { type: "string", description: "Distribution list name" },
158
+ recipients: {
159
+ type: "array",
160
+ description: "List of recipient identities",
161
+ items: { type: "string" },
162
+ },
163
+ message: { type: "string", description: "Message content to broadcast" },
164
+ },
165
+ required: ["listName", "recipients", "message"],
166
+ },
167
+ },
168
+ {
169
+ name: "get_chatbot_flow",
170
+ description: "Get chatbot flow/builder configuration",
171
+ inputSchema: {
172
+ type: "object",
173
+ properties: {
174
+ flowId: { type: "string", description: "Flow ID (optional, returns default flow if omitted)" },
175
+ },
176
+ },
177
+ },
178
+ ],
179
+ }));
180
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
+ const { name, arguments: args } = request.params;
182
+ try {
183
+ switch (name) {
184
+ case "send_message": {
185
+ const contentType = args?.type || "text/plain";
186
+ const content = contentType === "text/plain" ? args?.content : JSON.parse(String(args?.content));
187
+ return { content: [{ type: "text", text: JSON.stringify(await blipMessage(String(args?.to), String(contentType), content), null, 2) }] };
188
+ }
189
+ case "get_contacts": {
190
+ const skip = args?.skip ? `$skip=${args.skip}&` : "";
191
+ const take = args?.take ? `$take=${args.take}` : "$take=20";
192
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", `/contacts?${skip}${take}`), null, 2) }] };
193
+ }
194
+ case "create_contact": {
195
+ const resource = {
196
+ identity: args?.identity,
197
+ name: args?.name,
198
+ email: args?.email,
199
+ phoneNumber: args?.phoneNumber,
200
+ group: args?.group,
201
+ };
202
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "set", "/contacts", "application/vnd.lime.contact+json", resource), null, 2) }] };
203
+ }
204
+ case "get_threads": {
205
+ const skip = args?.skip ? `$skip=${args.skip}&` : "";
206
+ const take = args?.take ? `$take=${args.take}` : "$take=20";
207
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", `/threads?${skip}${take}`), null, 2) }] };
208
+ }
209
+ case "send_notification": {
210
+ const template = {
211
+ name: args?.templateName,
212
+ namespace: args?.templateNamespace,
213
+ language: { code: "pt_BR", policy: "deterministic" },
214
+ };
215
+ if (args?.parameters) {
216
+ template.components = [{ type: "body", parameters: args.parameters.map((p) => ({ type: "text", text: p })) }];
217
+ }
218
+ return { content: [{ type: "text", text: JSON.stringify(await blipMessage(String(args?.to), "application/json", template), null, 2) }] };
219
+ }
220
+ case "get_analytics": {
221
+ const uri = `/event-track/${args?.event || ""}?startDate=${args?.startDate}&endDate=${args?.endDate}`;
222
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", uri), null, 2) }] };
223
+ }
224
+ case "create_broadcast": {
225
+ // Create the distribution list
226
+ const listIdentity = `${args?.listName}@broadcast.msging.net`;
227
+ await blipCommand(crypto.randomUUID(), "set", "/lists", "application/vnd.iris.distribution-list+json", { identity: listIdentity });
228
+ // Add recipients
229
+ for (const recipient of args?.recipients || []) {
230
+ await blipCommand(crypto.randomUUID(), "set", `/lists/${listIdentity}/recipients`, "application/vnd.lime.identity", recipient);
231
+ }
232
+ // Send message
233
+ const result = await blipMessage(listIdentity, "text/plain", args?.message);
234
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
235
+ }
236
+ case "get_chatbot_flow": {
237
+ const uri = args?.flowId ? `/buckets/blip_portal:builder_working_flow_${args.flowId}` : "/buckets/blip_portal:builder_working_flow";
238
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", uri), null, 2) }] };
239
+ }
240
+ default:
241
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
242
+ }
243
+ }
244
+ catch (err) {
245
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
246
+ }
247
+ });
248
+ async function main() {
249
+ if (!BOT_ID || !ACCESS_KEY) {
250
+ console.error("TAKE_BLIP_BOT_ID and TAKE_BLIP_ACCESS_KEY environment variables are required");
251
+ process.exit(1);
252
+ }
253
+ const transport = new StdioServerTransport();
254
+ await server.connect(transport);
255
+ }
256
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@codespar/mcp-take-blip",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Take Blip — chatbots, messaging, contacts, broadcasts",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "mcp-take-blip": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/node": "^25.5.0",
19
+ "typescript": "^5.8.0"
20
+ },
21
+ "license": "MIT",
22
+ "keywords": [
23
+ "mcp",
24
+ "take-blip",
25
+ "chatbot",
26
+ "messaging",
27
+ "whatsapp",
28
+ "brazil"
29
+ ]
30
+ }
package/src/index.ts ADDED
@@ -0,0 +1,272 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MCP Server for Take Blip — Brazilian chatbot and messaging platform.
5
+ *
6
+ * Tools:
7
+ * - send_message: Send a message to a contact
8
+ * - get_contacts: List contacts
9
+ * - create_contact: Create a contact
10
+ * - get_threads: Get message threads
11
+ * - send_notification: Send a notification/broadcast message
12
+ * - get_analytics: Get chatbot analytics
13
+ * - create_broadcast: Create a broadcast list and send
14
+ * - get_chatbot_flow: Get chatbot flow configuration
15
+ *
16
+ * Environment:
17
+ * TAKE_BLIP_BOT_ID — Bot identifier
18
+ * TAKE_BLIP_ACCESS_KEY — Bot access key
19
+ *
20
+ * Note: Take Blip uses a JSON-based messaging protocol.
21
+ * Requests go as POST to /commands with type/method/uri in body.
22
+ */
23
+
24
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
25
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
26
+ import {
27
+ CallToolRequestSchema,
28
+ ListToolsRequestSchema,
29
+ } from "@modelcontextprotocol/sdk/types.js";
30
+
31
+ const BOT_ID = process.env.TAKE_BLIP_BOT_ID || "";
32
+ const ACCESS_KEY = process.env.TAKE_BLIP_ACCESS_KEY || "";
33
+ const BASE_URL = "https://msging.net";
34
+
35
+ function getAuthKey(): string {
36
+ const raw = `${BOT_ID}:${ACCESS_KEY}`;
37
+ return btoa(raw);
38
+ }
39
+
40
+ async function blipCommand(id: string, method: string, uri: string, type?: string, resource?: unknown): Promise<unknown> {
41
+ const body: Record<string, unknown> = { id, method, uri };
42
+ if (type) body.type = type;
43
+ if (resource) body.resource = resource;
44
+
45
+ const res = await fetch(`${BASE_URL}/commands`, {
46
+ method: "POST",
47
+ headers: {
48
+ "Content-Type": "application/json",
49
+ "Authorization": `Key ${getAuthKey()}`,
50
+ },
51
+ body: JSON.stringify(body),
52
+ });
53
+ if (!res.ok) {
54
+ const err = await res.text();
55
+ throw new Error(`Take Blip API ${res.status}: ${err}`);
56
+ }
57
+ return res.json();
58
+ }
59
+
60
+ async function blipMessage(to: string, type: string, content: unknown): Promise<unknown> {
61
+ const res = await fetch(`${BASE_URL}/messages`, {
62
+ method: "POST",
63
+ headers: {
64
+ "Content-Type": "application/json",
65
+ "Authorization": `Key ${getAuthKey()}`,
66
+ },
67
+ body: JSON.stringify({ id: crypto.randomUUID(), to, type, content }),
68
+ });
69
+ if (!res.ok) {
70
+ const err = await res.text();
71
+ throw new Error(`Take Blip API ${res.status}: ${err}`);
72
+ }
73
+ return res.json();
74
+ }
75
+
76
+ const server = new Server(
77
+ { name: "mcp-take-blip", version: "0.1.0" },
78
+ { capabilities: { tools: {} } }
79
+ );
80
+
81
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
82
+ tools: [
83
+ {
84
+ name: "send_message",
85
+ description: "Send a message to a contact via Take Blip",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ to: { type: "string", description: "Recipient identity (e.g., 5511999999999@wa.gw.msging.net)" },
90
+ content: { type: "string", description: "Message text content" },
91
+ type: { type: "string", description: "Content type (default: text/plain)", enum: ["text/plain", "application/json"] },
92
+ },
93
+ required: ["to", "content"],
94
+ },
95
+ },
96
+ {
97
+ name: "get_contacts",
98
+ description: "List contacts in Take Blip",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {
102
+ skip: { type: "number", description: "Number of contacts to skip" },
103
+ take: { type: "number", description: "Number of contacts to return (default 20)" },
104
+ },
105
+ },
106
+ },
107
+ {
108
+ name: "create_contact",
109
+ description: "Create a contact in Take Blip",
110
+ inputSchema: {
111
+ type: "object",
112
+ properties: {
113
+ identity: { type: "string", description: "Contact identity (e.g., 5511999999999@wa.gw.msging.net)" },
114
+ name: { type: "string", description: "Contact name" },
115
+ email: { type: "string", description: "Contact email" },
116
+ phoneNumber: { type: "string", description: "Phone number" },
117
+ group: { type: "string", description: "Contact group" },
118
+ },
119
+ required: ["identity", "name"],
120
+ },
121
+ },
122
+ {
123
+ name: "get_threads",
124
+ description: "Get message threads (recent conversations)",
125
+ inputSchema: {
126
+ type: "object",
127
+ properties: {
128
+ skip: { type: "number", description: "Number of threads to skip" },
129
+ take: { type: "number", description: "Number of threads to return" },
130
+ },
131
+ },
132
+ },
133
+ {
134
+ name: "send_notification",
135
+ description: "Send a notification message to a contact",
136
+ inputSchema: {
137
+ type: "object",
138
+ properties: {
139
+ to: { type: "string", description: "Recipient identity" },
140
+ templateName: { type: "string", description: "Message template name" },
141
+ templateNamespace: { type: "string", description: "Template namespace" },
142
+ parameters: {
143
+ type: "array",
144
+ description: "Template parameters",
145
+ items: { type: "string" },
146
+ },
147
+ },
148
+ required: ["to", "templateName"],
149
+ },
150
+ },
151
+ {
152
+ name: "get_analytics",
153
+ description: "Get chatbot analytics and metrics",
154
+ inputSchema: {
155
+ type: "object",
156
+ properties: {
157
+ startDate: { type: "string", description: "Start date (YYYY-MM-DD)" },
158
+ endDate: { type: "string", description: "End date (YYYY-MM-DD)" },
159
+ event: { type: "string", description: "Event category to track" },
160
+ },
161
+ required: ["startDate", "endDate"],
162
+ },
163
+ },
164
+ {
165
+ name: "create_broadcast",
166
+ description: "Create a broadcast distribution list and send messages",
167
+ inputSchema: {
168
+ type: "object",
169
+ properties: {
170
+ listName: { type: "string", description: "Distribution list name" },
171
+ recipients: {
172
+ type: "array",
173
+ description: "List of recipient identities",
174
+ items: { type: "string" },
175
+ },
176
+ message: { type: "string", description: "Message content to broadcast" },
177
+ },
178
+ required: ["listName", "recipients", "message"],
179
+ },
180
+ },
181
+ {
182
+ name: "get_chatbot_flow",
183
+ description: "Get chatbot flow/builder configuration",
184
+ inputSchema: {
185
+ type: "object",
186
+ properties: {
187
+ flowId: { type: "string", description: "Flow ID (optional, returns default flow if omitted)" },
188
+ },
189
+ },
190
+ },
191
+ ],
192
+ }));
193
+
194
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
195
+ const { name, arguments: args } = request.params;
196
+
197
+ try {
198
+ switch (name) {
199
+ case "send_message": {
200
+ const contentType = args?.type || "text/plain";
201
+ const content = contentType === "text/plain" ? args?.content : JSON.parse(String(args?.content));
202
+ return { content: [{ type: "text", text: JSON.stringify(await blipMessage(String(args?.to), String(contentType), content), null, 2) }] };
203
+ }
204
+ case "get_contacts": {
205
+ const skip = args?.skip ? `$skip=${args.skip}&` : "";
206
+ const take = args?.take ? `$take=${args.take}` : "$take=20";
207
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", `/contacts?${skip}${take}`), null, 2) }] };
208
+ }
209
+ case "create_contact": {
210
+ const resource = {
211
+ identity: args?.identity,
212
+ name: args?.name,
213
+ email: args?.email,
214
+ phoneNumber: args?.phoneNumber,
215
+ group: args?.group,
216
+ };
217
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "set", "/contacts", "application/vnd.lime.contact+json", resource), null, 2) }] };
218
+ }
219
+ case "get_threads": {
220
+ const skip = args?.skip ? `$skip=${args.skip}&` : "";
221
+ const take = args?.take ? `$take=${args.take}` : "$take=20";
222
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", `/threads?${skip}${take}`), null, 2) }] };
223
+ }
224
+ case "send_notification": {
225
+ const template: Record<string, unknown> = {
226
+ name: args?.templateName,
227
+ namespace: args?.templateNamespace,
228
+ language: { code: "pt_BR", policy: "deterministic" },
229
+ };
230
+ if (args?.parameters) {
231
+ template.components = [{ type: "body", parameters: (args.parameters as string[]).map((p: string) => ({ type: "text", text: p })) }];
232
+ }
233
+ return { content: [{ type: "text", text: JSON.stringify(await blipMessage(String(args?.to), "application/json", template), null, 2) }] };
234
+ }
235
+ case "get_analytics": {
236
+ const uri = `/event-track/${args?.event || ""}?startDate=${args?.startDate}&endDate=${args?.endDate}`;
237
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", uri), null, 2) }] };
238
+ }
239
+ case "create_broadcast": {
240
+ // Create the distribution list
241
+ const listIdentity = `${args?.listName}@broadcast.msging.net`;
242
+ await blipCommand(crypto.randomUUID(), "set", "/lists", "application/vnd.iris.distribution-list+json", { identity: listIdentity });
243
+ // Add recipients
244
+ for (const recipient of (args?.recipients as string[]) || []) {
245
+ await blipCommand(crypto.randomUUID(), "set", `/lists/${listIdentity}/recipients`, "application/vnd.lime.identity", recipient);
246
+ }
247
+ // Send message
248
+ const result = await blipMessage(listIdentity, "text/plain", args?.message);
249
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
250
+ }
251
+ case "get_chatbot_flow": {
252
+ const uri = args?.flowId ? `/buckets/blip_portal:builder_working_flow_${args.flowId}` : "/buckets/blip_portal:builder_working_flow";
253
+ return { content: [{ type: "text", text: JSON.stringify(await blipCommand(crypto.randomUUID(), "get", uri), null, 2) }] };
254
+ }
255
+ default:
256
+ return { content: [{ type: "text", text: `Unknown tool: ${name}` }], isError: true };
257
+ }
258
+ } catch (err) {
259
+ return { content: [{ type: "text", text: `Error: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
260
+ }
261
+ });
262
+
263
+ async function main() {
264
+ if (!BOT_ID || !ACCESS_KEY) {
265
+ console.error("TAKE_BLIP_BOT_ID and TAKE_BLIP_ACCESS_KEY environment variables are required");
266
+ process.exit(1);
267
+ }
268
+ const transport = new StdioServerTransport();
269
+ await server.connect(transport);
270
+ }
271
+
272
+ main().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "./dist",
7
+ "rootDir": "./src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "declaration": true
11
+ },
12
+ "include": ["src"]
13
+ }