@jclvsh/iris 0.19.1 → 0.20.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/README.md CHANGED
@@ -61,10 +61,10 @@ iris campaigns --help
61
61
 
62
62
  | Group | Alias | Commands | Count |
63
63
  | ------------- | ------ | ------------------------------------------------------------- | ----- |
64
- | campaigns | `c` | list, get, create, update, delete, usage, analyze | 7 |
64
+ | campaigns | `c` | list, get, create, update, delete, usage, analyze, contacts, add-contacts, chats, chat-messages, rename-chat, delete-chat | 13 |
65
65
  | contacts | `ct` | list, get, create, update, delete, usage | 6 |
66
66
  | messages | `m` | pending, approve, reject, retry, feedback, bulk | 6 |
67
- | conversations | `conv` | list, get, messages, read | 4 |
67
+ | conversations | `conv` | list, get, create, send, messages, read, update | 7 |
68
68
  | discovery | `d` | search, search-campaign, sources, job | 4 |
69
69
  | ai | — | generate, generate-campaign, analyze, usage | 4 |
70
70
  | keys | `k` | me, list, create, rename, revoke | 5 |
@@ -136,6 +136,11 @@ Add to `.cursor/mcp.json`:
136
136
  | `update_campaign` | Update a campaign's name, status, or follow-up settings |
137
137
  | `delete_campaign` | Soft-delete a campaign (sets status to `deleted`) |
138
138
  | `list_campaign_contacts` | List contacts in a campaign with optional status filter |
139
+ | `add_campaign_contacts` | Add contacts to a campaign by id or inline; needed before activation |
140
+ | `list_campaign_chats` | List iris chat sessions (the conversational campaign builder) |
141
+ | `get_campaign_chat_messages` | List the messages in a chat session, oldest first |
142
+ | `rename_campaign_chat` | Rename a chat session |
143
+ | `delete_campaign_chat` | Soft-delete a chat session |
139
144
  | `get_campaign_usage` | Get usage limits and current counts for a campaign |
140
145
  | `analyze_campaign` | Trigger AI analysis to generate target profiles |
141
146
  | `list_target_profiles` | List target profiles for a campaign |
@@ -225,6 +230,7 @@ Add to `.cursor/mcp.json`:
225
230
  | --------------------------- | ------------------------------------------------------- |
226
231
  | `list_conversations` | List conversations with optional channel/status filters |
227
232
  | `get_conversation` | Get a conversation by ID |
233
+ | `update_conversation` | Update a conversation's status (active, archived, closed) |
228
234
  | `get_conversation_messages` | List messages in a conversation |
229
235
  | `mark_conversation_read` | Mark a conversation as read |
230
236
  | `get_engagement_metrics` | Get engagement metrics across conversations |
@@ -120,6 +120,93 @@ export function registerCampaignCommands(program, client) {
120
120
  handleError(error);
121
121
  }
122
122
  });
123
+ campaigns
124
+ .command("contacts <id>")
125
+ .description("list the contacts in a campaign")
126
+ .option("-p, --page <n>", "page number", "1")
127
+ .option("-s, --page-size <n>", "items per page (max 100)", "50")
128
+ .option("--status <status>", "filter by contact status")
129
+ .action(async (id, opts) => {
130
+ try {
131
+ const params = {};
132
+ if (opts.page)
133
+ params.page = opts.page;
134
+ if (opts.pageSize)
135
+ params.page_size = opts.pageSize;
136
+ if (opts.status)
137
+ params.status = opts.status;
138
+ const result = await client.get(`/campaigns/${id}/contacts`, params);
139
+ formatOutput(result, getFormat(program.opts()));
140
+ }
141
+ catch (error) {
142
+ handleError(error);
143
+ }
144
+ });
145
+ campaigns
146
+ .command("add-contacts <id>")
147
+ .description("link existing contacts to a campaign (a campaign needs at least one before it can start)")
148
+ .requiredOption("--contact-ids <ids>", "comma-separated contact IDs to link")
149
+ .option("--no-deduplicate", "link even if already linked (default skips duplicates)")
150
+ .action(async (id, opts) => {
151
+ try {
152
+ const body = { contact_ids: parseList(opts.contactIds) };
153
+ if (opts.deduplicate === false)
154
+ body.deduplicate = false;
155
+ const result = await client.post(`/campaigns/${id}/contacts`, body);
156
+ formatOutput(result, getFormat(program.opts()));
157
+ }
158
+ catch (error) {
159
+ handleError(error);
160
+ }
161
+ });
162
+ campaigns
163
+ .command("chats")
164
+ .description("list iris chat sessions, most recently updated first")
165
+ .action(async () => {
166
+ try {
167
+ const result = await client.get("/campaigns/chats");
168
+ formatOutput(result, getFormat(program.opts()));
169
+ }
170
+ catch (error) {
171
+ handleError(error);
172
+ }
173
+ });
174
+ campaigns
175
+ .command("chat-messages <chat-id>")
176
+ .description("list the messages in a chat session, oldest first")
177
+ .action(async (chatId) => {
178
+ try {
179
+ const result = await client.get(`/campaigns/chats/${chatId}/messages`);
180
+ formatOutput(result, getFormat(program.opts()));
181
+ }
182
+ catch (error) {
183
+ handleError(error);
184
+ }
185
+ });
186
+ campaigns
187
+ .command("rename-chat <chat-id> <title>")
188
+ .description("rename a chat session")
189
+ .action(async (chatId, title) => {
190
+ try {
191
+ const result = await client.patch(`/campaigns/chats/${chatId}`, { title });
192
+ formatOutput(result, getFormat(program.opts()));
193
+ }
194
+ catch (error) {
195
+ handleError(error);
196
+ }
197
+ });
198
+ campaigns
199
+ .command("delete-chat <chat-id>")
200
+ .description("soft-delete a chat session")
201
+ .action(async (chatId) => {
202
+ try {
203
+ await client.delete(`/campaigns/chats/${chatId}`);
204
+ console.log("chat deleted");
205
+ }
206
+ catch (error) {
207
+ handleError(error);
208
+ }
209
+ });
123
210
  const targetProfiles = campaigns
124
211
  .command("target-profiles")
125
212
  .alias("tp")
@@ -110,4 +110,17 @@ export function registerConversationCommands(program, client) {
110
110
  handleError(error);
111
111
  }
112
112
  });
113
+ conversations
114
+ .command("update <id>")
115
+ .description("update a conversation's status")
116
+ .requiredOption("--status <status>", "one of: active, archived, closed")
117
+ .action(async (id, opts) => {
118
+ try {
119
+ const result = await client.patch(`/conversations/${id}`, { status: opts.status });
120
+ formatOutput(result, getFormat(program.opts()));
121
+ }
122
+ catch (error) {
123
+ handleError(error);
124
+ }
125
+ });
113
126
  }
@@ -115,6 +115,73 @@ export function registerCampaignTools(server, client) {
115
115
  return formatMcpError(error);
116
116
  }
117
117
  });
118
+ server.tool("add_campaign_contacts", "add contacts to a campaign, by id or by creating them inline. a campaign needs at least one contact before it can be activated. requires write scope.", {
119
+ id: z.string().uuid().describe("campaign ID"),
120
+ contact_ids: z.array(z.string().uuid()).optional().describe("ids of existing contacts to link"),
121
+ contacts: z
122
+ .array(z.record(z.string(), z.unknown()))
123
+ .optional()
124
+ .describe("new contacts to create and link (same shape as create_contact)"),
125
+ deduplicate: z.boolean().optional().describe("skip contacts already linked (default true)"),
126
+ }, async ({ id, contact_ids, contacts, deduplicate }) => {
127
+ try {
128
+ const body = {};
129
+ if (contact_ids)
130
+ body.contact_ids = contact_ids;
131
+ if (contacts)
132
+ body.contacts = contacts;
133
+ if (deduplicate !== undefined)
134
+ body.deduplicate = deduplicate;
135
+ const result = await client.post(`/campaigns/${id}/contacts`, body);
136
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
137
+ }
138
+ catch (error) {
139
+ return formatMcpError(error);
140
+ }
141
+ });
142
+ server.tool("list_campaign_chats", "list your iris chat sessions (the conversational campaign builder), most recently updated first, max 100. the live chat turn itself is a server-sent-events stream outside this tool set. requires read scope.", {}, async () => {
143
+ try {
144
+ const result = await client.get("/campaigns/chats");
145
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
146
+ }
147
+ catch (error) {
148
+ return formatMcpError(error);
149
+ }
150
+ });
151
+ server.tool("get_campaign_chat_messages", "list the messages in a chat session, oldest first. `content` is an array of opaque blocks (text / tool_use / tool_result). requires read scope.", {
152
+ chat_id: z.string().uuid().describe("chat session ID"),
153
+ }, async ({ chat_id }) => {
154
+ try {
155
+ const result = await client.get(`/campaigns/chats/${chat_id}/messages`);
156
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
157
+ }
158
+ catch (error) {
159
+ return formatMcpError(error);
160
+ }
161
+ });
162
+ server.tool("rename_campaign_chat", "rename a chat session. requires write scope.", {
163
+ chat_id: z.string().uuid().describe("chat session ID"),
164
+ title: z.string().min(1).max(200).describe("new title"),
165
+ }, async ({ chat_id, title }) => {
166
+ try {
167
+ const result = await client.patch(`/campaigns/chats/${chat_id}`, { title });
168
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
169
+ }
170
+ catch (error) {
171
+ return formatMcpError(error);
172
+ }
173
+ });
174
+ server.tool("delete_campaign_chat", "soft-delete a chat session. requires write scope.", {
175
+ chat_id: z.string().uuid().describe("chat session ID"),
176
+ }, async ({ chat_id }) => {
177
+ try {
178
+ await client.delete(`/campaigns/chats/${chat_id}`);
179
+ return { content: [{ type: "text", text: `chat ${chat_id} deleted` }] };
180
+ }
181
+ catch (error) {
182
+ return formatMcpError(error);
183
+ }
184
+ });
118
185
  server.tool("get_campaign_usage", "get usage limits and current counts for a campaign. requires read scope.", {
119
186
  id: z.string().uuid().describe("campaign ID"),
120
187
  }, async ({ id }) => {
@@ -106,6 +106,18 @@ export function registerConversationTools(server, client) {
106
106
  return formatMcpError(error);
107
107
  }
108
108
  });
109
+ server.tool("update_conversation", "update a conversation's status: active, archived, or closed. requires write scope.", {
110
+ id: z.string().uuid().describe("conversation ID"),
111
+ status: z.enum(["active", "archived", "closed"]).describe("new conversation status"),
112
+ }, async ({ id, status }) => {
113
+ try {
114
+ const result = await client.patch(`/conversations/${id}`, { status });
115
+ return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
116
+ }
117
+ catch (error) {
118
+ return formatMcpError(error);
119
+ }
120
+ });
109
121
  server.tool("get_engagement_metrics", "get overall engagement metrics across all conversations. requires read scope.", {}, async () => {
110
122
  try {
111
123
  const result = await client.get("/conversations/engagement");
package/dist/types.d.ts CHANGED
@@ -52,6 +52,29 @@ export interface CampaignUsage {
52
52
  discovery_runs: number;
53
53
  discovery_limit: number;
54
54
  }
55
+ /** `POST /campaigns/{id}/contacts` */
56
+ export interface AddCampaignContactsResult {
57
+ added_count: number;
58
+ duplicates_count: number;
59
+ total_contacts: number;
60
+ }
61
+ /** `GET /campaigns/chats`, `PATCH /campaigns/chats/{id}` */
62
+ export interface CampaignChat {
63
+ id: string;
64
+ campaign_id: string | null;
65
+ title: string | null;
66
+ status: string;
67
+ created_at: string;
68
+ updated_at: string;
69
+ }
70
+ /** `GET /campaigns/chats/{id}/messages` — `content` is an opaque block array. */
71
+ export interface CampaignChatMessage {
72
+ id: string;
73
+ chat_id: string;
74
+ role: string;
75
+ content: unknown;
76
+ created_at: string;
77
+ }
55
78
  export interface TargetProfile {
56
79
  id: string;
57
80
  campaign_id: string;
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@jclvsh/iris",
3
- "version": "0.19.1",
3
+ "version": "0.20.0",
4
4
  "description": "MCP server for the Iris API",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/JCLVSH/monorepo.git",
8
+ "directory": "apps/iris/packages/mcp"
9
+ },
5
10
  "type": "module",
6
11
  "main": "dist/index.js",
7
12
  "types": "dist/index.d.ts",