@aift/engagr-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.
package/dist/client.js ADDED
@@ -0,0 +1,51 @@
1
+ const BASE_URL = process.env.ENGAGR_API_BASE || "https://wvdublbhpwjoaahuzehs.supabase.co/functions/v1/public-api";
2
+ export class EngagrClient {
3
+ apiKey;
4
+ constructor(apiKey) {
5
+ this.apiKey = apiKey || process.env.ENGAGR_API_KEY || "";
6
+ if (!this.apiKey)
7
+ throw new Error("ENGAGR_API_KEY env var requis");
8
+ }
9
+ headers() {
10
+ return {
11
+ "Authorization": `Bearer ${this.apiKey}`,
12
+ "Content-Type": "application/json",
13
+ };
14
+ }
15
+ async get(path) {
16
+ const res = await fetch(`${BASE_URL}/v1/${path}`, { headers: this.headers() });
17
+ const data = await res.json();
18
+ if (!res.ok)
19
+ throw new Error(data.error || `HTTP ${res.status}`);
20
+ return data;
21
+ }
22
+ async post(path, body = {}) {
23
+ const res = await fetch(`${BASE_URL}/v1/${path}`, {
24
+ method: "POST",
25
+ headers: this.headers(),
26
+ body: JSON.stringify(body),
27
+ });
28
+ const data = await res.json();
29
+ if (!res.ok)
30
+ throw new Error(data.error || `HTTP ${res.status}`);
31
+ return data;
32
+ }
33
+ async delete(path) {
34
+ const res = await fetch(`${BASE_URL}/v1/${path}`, { method: "DELETE", headers: this.headers() });
35
+ const data = await res.json();
36
+ if (!res.ok)
37
+ throw new Error(data.error || `HTTP ${res.status}`);
38
+ return data;
39
+ }
40
+ async patch(path, body) {
41
+ const res = await fetch(`${BASE_URL}/v1/${path}`, {
42
+ method: "PATCH",
43
+ headers: this.headers(),
44
+ body: JSON.stringify(body),
45
+ });
46
+ const data = await res.json();
47
+ if (!res.ok)
48
+ throw new Error(data.error || `HTTP ${res.status}`);
49
+ return data;
50
+ }
51
+ }
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { EngagrClient } from "./client.js";
5
+ import { registerSniprTools } from "./tools/snipr.js";
6
+ import { registerInvitrTools } from "./tools/invitr.js";
7
+ import { registerBookrTools } from "./tools/bookr.js";
8
+ const server = new McpServer({
9
+ name: "engagr",
10
+ version: "0.1.0",
11
+ });
12
+ const client = new EngagrClient();
13
+ registerSniprTools(server, client);
14
+ registerInvitrTools(server, client);
15
+ registerBookrTools(server, client);
16
+ const transport = new StdioServerTransport();
17
+ await server.connect(transport);
18
+ console.error("Engagr MCP server running on stdio");
@@ -0,0 +1,24 @@
1
+ import { z } from "zod";
2
+ const CONVERSATION_STATUSES = ["ready_for_draft", "draft_ready", "sent", "waiting_reply", "reply_received", "paused", "closed"];
3
+ export function registerBookrTools(server, client) {
4
+ server.tool("bookr_list_conversations", "Liste les conversations Bookr (outreach LinkedIn)", { status: z.enum(CONVERSATION_STATUSES).optional().describe("Filtrer par statut") }, async ({ status }) => {
5
+ const path = status ? `conversations?status=${status}` : "conversations";
6
+ const data = await client.get(path);
7
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
8
+ });
9
+ server.tool("bookr_generate_draft", "Génère un brouillon de message IA pour une conversation Bookr", { conversation_id: z.string().describe("ID de la conversation Bookr") }, async ({ conversation_id }) => {
10
+ const data = await client.post(`conversations/${conversation_id}/draft`);
11
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
12
+ });
13
+ server.tool("bookr_send_draft", "Envoie le brouillon validé d'une conversation Bookr via LinkedIn", { conversation_id: z.string().describe("ID de la conversation Bookr") }, async ({ conversation_id }) => {
14
+ const data = await client.post(`conversations/${conversation_id}/send`);
15
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
16
+ });
17
+ server.tool("bookr_update_status", "Met à jour le statut d'une conversation Bookr", {
18
+ conversation_id: z.string().describe("ID de la conversation Bookr"),
19
+ status: z.enum(CONVERSATION_STATUSES).describe("Nouveau statut")
20
+ }, async ({ conversation_id, status }) => {
21
+ const data = await client.patch(`conversations/${conversation_id}/status`, { status });
22
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
23
+ });
24
+ }
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ export function registerInvitrTools(server, client) {
3
+ server.tool("invitr_list_invitations", "Liste les invitations LinkedIn (envoyées, acceptées, échouées, annulées).", { status: z.enum(["sent", "accepted", "failed", "cancelled"]).optional().describe("Filtrer par statut") }, async ({ status }) => {
4
+ const path = status ? `invitations?status=${status}` : "invitations";
5
+ const data = await client.get(path);
6
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
7
+ });
8
+ server.tool("invitr_send_invitation", "Envoie une invitation LinkedIn à un prospect", {
9
+ prospect_id: z.string().describe("ID du prospect Engagr"),
10
+ message: z.string().optional().describe("Message personnalisé (optionnel, max ~200 caractères)")
11
+ }, async ({ prospect_id, message }) => {
12
+ const data = await client.post("invitations", { prospect_id, message });
13
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
14
+ });
15
+ server.tool("invitr_cancel_invitation", "Annule une invitation LinkedIn en attente", { prospect_id: z.string().describe("ID du prospect dont l'invitation doit être annulée") }, async ({ prospect_id }) => {
16
+ const data = await client.delete(`invitations/${prospect_id}`);
17
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
18
+ });
19
+ }
@@ -0,0 +1,15 @@
1
+ import { z } from "zod";
2
+ export function registerSniprTools(server, client) {
3
+ server.tool("snipr_list_prospects", "Liste les prospects dans le pipeline Snipr", {}, async () => {
4
+ const data = await client.get("prospects");
5
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
6
+ });
7
+ server.tool("snipr_import_profile", "Importe un profil LinkedIn dans le pipeline Snipr", { url: z.string().describe("URL LinkedIn complète (ex: https://www.linkedin.com/in/nom-prenom/)") }, async ({ url }) => {
8
+ const data = await client.post("prospects", { url });
9
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
10
+ });
11
+ server.tool("snipr_inject_prospects", "Active des prospects dans le pipeline (déclenche le monitoring LinkedIn)", { prospect_ids: z.array(z.string()).optional().describe("IDs des prospects à activer. Si vide, active tous les prospects.") }, async ({ prospect_ids }) => {
12
+ const data = await client.post("prospects/inject", prospect_ids ? { prospect_ids } : {});
13
+ return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
14
+ });
15
+ }
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@aift/engagr-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Engagr — Snipr, Invitr, Bookr",
5
+ "type": "module",
6
+ "bin": {
7
+ "engagr-mcp": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "dev": "tsc --watch",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.12.0",
16
+ "zod": "^4.4.3"
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^20.0.0",
20
+ "typescript": "^5.4.0"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ }
28
+ }