@glie/mcp-polaris 0.13.0 → 0.18.2
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 +20 -8
- package/dist/index.js +230 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,8 +48,10 @@ it. The same token authenticates this server and the `polaris` CLI as you.
|
|
|
48
48
|
## Tools
|
|
49
49
|
|
|
50
50
|
**Roadmap.** `polaris.me`, `polaris.project.*`, `polaris.milestone.*` (incl.
|
|
51
|
-
`update` to refine a spec, `
|
|
52
|
-
|
|
51
|
+
`update` to refine a spec or move its dates, `demote` to send one with no tasks back to the
|
|
52
|
+
idea list, `attachment_*` to list/attach/remove the visual
|
|
53
|
+
references anchored in its body), `polaris.idea.*` (incl. `promote` into a milestone and
|
|
54
|
+
`promote_task` into a standalone task with no milestone), `polaris.task.*` (incl. `update`,
|
|
53
55
|
`transition`, `approve`, `schedule`, `attachment_*` — same as the milestone's,
|
|
54
56
|
plus how a milestone reference gets forwarded here via
|
|
55
57
|
`source_attachment_id`), `polaris.stack.*` (`list` / `get` / `release` for the
|
|
@@ -57,12 +59,22 @@ approval Release Candidate flow), `polaris.agent.*`, `polaris.team.*`,
|
|
|
57
59
|
`polaris.claude_md.get`, `polaris.framework.list`.
|
|
58
60
|
|
|
59
61
|
**Helpdesk.** `polaris.ticket.*` (the customer conversation: `reply`, `pass`,
|
|
60
|
-
`frame_issue`, `link_issue`) and `polaris.issue.*` (the problem:
|
|
61
|
-
`transition`, `add_task`). N tickets can dedup onto one issue, and one issue
|
|
62
|
-
fans out into N tasks.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
62
|
+
`frame_issue`, `link_issue`, `attachment_list`) and `polaris.issue.*` (the problem:
|
|
63
|
+
`open`, `transition`, `add_task`). N tickets can dedup onto one issue, and one issue
|
|
64
|
+
fans out into N tasks. A reply carries files through `attachment_ids`: upload each
|
|
65
|
+
one to `POST /api/tickets/:id/attachments` first (this server does not carry bytes),
|
|
66
|
+
then paste its `marker` into `body_md` to draw it inside the message.
|
|
67
|
+
|
|
68
|
+
**Context, read-only.** `polaris.activity.list` (what happened),
|
|
69
|
+
`polaris.chat.get` / `polaris.chat.messages` (comments agents left on a scope),
|
|
70
|
+
and `polaris.task.run_lines` (the thread of an agent's run on a task: what it was
|
|
71
|
+
asked, said, ran and got back; human tokens only).
|
|
72
|
+
|
|
73
|
+
**What a creative delivered.** `polaris.task.creatives` lists every version of a
|
|
74
|
+
creative task with its files and the words that go out beside them (caption,
|
|
75
|
+
title, copy). `polaris.task.creative_download` saves one version to this machine
|
|
76
|
+
as a zip of its slides, so a video can be opened, its frames pulled and its
|
|
77
|
+
sound measured.
|
|
66
78
|
|
|
67
79
|
Run `tools/list` against the server for the full, current set.
|
|
68
80
|
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// ../../src/mcp/index.ts
|
|
5
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
6
|
+
import { tmpdir } from "os";
|
|
7
|
+
import { join, resolve } from "path";
|
|
5
8
|
import { createInterface } from "readline";
|
|
6
9
|
|
|
7
10
|
// ../../src/lib/media-types.ts
|
|
@@ -13,11 +16,22 @@ var MEDIA_TYPES = [
|
|
|
13
16
|
{ mime: "video/quicktime", kind: "video", ext: ".mov", aliases: [] },
|
|
14
17
|
{ mime: "video/webm", kind: "video", ext: ".webm", aliases: [] }
|
|
15
18
|
];
|
|
19
|
+
function mimesOfKind(kind) {
|
|
20
|
+
return MEDIA_TYPES.filter((t) => t.kind === kind).map((t) => t.mime);
|
|
21
|
+
}
|
|
16
22
|
function allMimes() {
|
|
17
23
|
return MEDIA_TYPES.map((t) => t.mime);
|
|
18
24
|
}
|
|
25
|
+
var RECEIVED_IMAGE_MIMES = ["image/gif"];
|
|
26
|
+
var INLINE_MIMES = new Set([
|
|
27
|
+
...mimesOfKind("image"),
|
|
28
|
+
...mimesOfKind("video"),
|
|
29
|
+
...RECEIVED_IMAGE_MIMES
|
|
30
|
+
]);
|
|
19
31
|
|
|
20
32
|
// ../../src/mcp/index.ts
|
|
33
|
+
var VERSION = "0.18.2";
|
|
34
|
+
var FRAMEWORK_KEY_DOC = "optional \u2014 a key from polaris.framework.list. Leave it out when none applies: a framework is for founding a large domain, and most milestones take none. An unknown key is refused (400) with the keys that exist.";
|
|
21
35
|
var BASE = process.env.POLARIS_URL ?? "http://127.0.0.1:7843";
|
|
22
36
|
var TOKEN = process.env.POLARIS_TOKEN ?? "";
|
|
23
37
|
var PROJECT = process.env.POLARIS_PROJECT ?? "";
|
|
@@ -84,17 +98,24 @@ var INSTRUCTIONS = [
|
|
|
84
98
|
"\u2022 N tickets \u2192 1 issue (duplicates dedup onto one problem) \xB7 1 ticket \u2192 N issues \xB7 1 issue \u2192 N tasks (iterative fix).",
|
|
85
99
|
"\u2022 Flow: ticket.create \u2192 ticket.reply / ticket.pass (communicate) \u2192 ticket.frame_issue (or issue.open standalone; ticket.link_issue to dedup)",
|
|
86
100
|
" \u2192 issue.add_task \xD7N (a planner decomposes) \u2192 task.transition the tasks through dev\u2192test\u2192approval\u2192release.",
|
|
87
|
-
"\u2022 The ball: ticket.pass sets whose move it is (customer|team)
|
|
101
|
+
"\u2022 The ball: ticket.pass sets whose move it is (customer|team). A team message never moves it; a customer reply brings it back to the team and auto-reopens a resolved/closed ticket.",
|
|
88
102
|
"",
|
|
89
103
|
"WHICH TOOL WHEN:",
|
|
90
104
|
`\u2022 "What's mine / what next?" \u2192 polaris.task.list (assignee \u2026="self", status=\u2026)`,
|
|
91
105
|
'\u2022 "Read X in full" \u2192 polaris.task.get \xB7 milestone.get \xB7 project.get \xB7 stack.get',
|
|
92
106
|
'\u2022 "Fix an unclear brief" \u2192 polaris.task.update / milestone.update (PATCH description_md)',
|
|
93
107
|
'\u2022 "Catch up \u2014 what happened/said?"\u2192 polaris.activity.list (events) \xB7 polaris.chat.messages (comments left by agents)',
|
|
108
|
+
`\u2022 "What is the agent doing / why?" \u2192 polaris.task.run_lines (the run's thread: prompt, reasoning, commands, results; human-only)`,
|
|
109
|
+
'\u2022 "What did this creative deliver?" \u2192 polaris.task.creatives (versions, files, caption) \xB7 polaris.task.creative_download (the file, saved locally)',
|
|
94
110
|
'\u2022 "Move my task forward" \u2192 polaris.task.transition(id, status)',
|
|
95
111
|
`\u2022 "It's reviewed, ship it" \u2192 polaris.task.approve (human-only); ship several together via polaris.stack.* \u2192 stack.release`,
|
|
96
112
|
`\u2022 "My task is stuck in a blocked stack" \u2192 fix the conflict on YOUR branch (rebase agent/task-N onto dev, push) \u2014 the rc PR flips to ready automatically. A stacked task can't advance individually; never merge the rc PR by hand.`,
|
|
97
113
|
'\u2022 "Break a milestone into tasks" \u2192 polaris.milestone.qa_list / qa_answer (while in discovery)',
|
|
114
|
+
'\u2022 "Jot something down for later" \u2192 polaris.idea.create (a title is enough). NO dates: an idea is not a milestone.',
|
|
115
|
+
'\u2022 "What have we already thought of?" \u2192 polaris.idea.list \u2014 read it before proposing work; it also holds what was turned down',
|
|
116
|
+
'\u2022 "This idea is now real" \u2192 polaris.idea.promote (needs end_date + team) \u2014 the one door from the idea list to the timeline',
|
|
117
|
+
'\u2022 "This idea is one small piece of dev work" \u2192 polaris.idea.promote_task \u2014 a standalone task, no milestone, straight into the dev backlog (human-only)',
|
|
118
|
+
'\u2022 "This milestone was only a hunch" \u2192 polaris.milestone.demote \u2014 back to the idea list, only while it has no tasks (human-only)',
|
|
98
119
|
`\u2022 "Triage support / what's waiting?" \u2192 polaris.ticket.list (status, project_key) \u2014 P1 first`,
|
|
99
120
|
'\u2022 "Reply to a customer / hand off" \u2192 polaris.ticket.reply then polaris.ticket.pass(to)',
|
|
100
121
|
'\u2022 "Turn a report into work" \u2192 polaris.ticket.frame_issue \u2192 polaris.issue.add_task \xD7N (framing is HUMAN-only; the helpdesker proposes it)',
|
|
@@ -162,16 +183,111 @@ var TOOLS = [
|
|
|
162
183
|
},
|
|
163
184
|
{
|
|
164
185
|
name: "polaris.project.arm",
|
|
165
|
-
description: "Arm a project for auto-approval (human-only): while armed,
|
|
186
|
+
description: "Arm a project for auto-approval (human-only): while armed, a task an agent writes in `draft` (discovery's batch, a campaign's pieces) is approved into `backlog` on your behalf, with no per-task click. Tasks a person creates by hand and support work still wait in `draft`. `approval \u2192 release` is never automatic. Not retroactive: drafts already there stay. Idempotent.",
|
|
166
187
|
inputSchema: objSchema({ key: strSchema("project key (e.g. saasA)") }, ["key"]),
|
|
167
188
|
call: async (args) => (await apiCall("POST", `/api/projects/${args.key}/arm`)).body
|
|
168
189
|
},
|
|
169
190
|
{
|
|
170
191
|
name: "polaris.project.disarm",
|
|
171
|
-
description: "Disarm a project (human-only). Immediate effect: the next task
|
|
192
|
+
description: "Disarm a project (human-only). Immediate effect: the next task an agent writes in `draft` waits for a person again. Tasks already moved to `backlog` stay there. Idempotent.",
|
|
172
193
|
inputSchema: objSchema({ key: strSchema("project key (e.g. saasA)") }, ["key"]),
|
|
173
194
|
call: async (args) => (await apiCall("POST", `/api/projects/${args.key}/disarm`)).body
|
|
174
195
|
},
|
|
196
|
+
{
|
|
197
|
+
name: "polaris.idea.list",
|
|
198
|
+
description: "List a project's IDEAS \u2014 what might matter later, before it has a date. An idea is not a milestone: no dates, no tasks, never on the timeline. Read this before proposing new work: it is where the things already thought of (and the things already turned down) live. Optional status filter: open|promoted|dropped.",
|
|
199
|
+
inputSchema: objSchema({
|
|
200
|
+
project_key: strSchema("project key (e.g. saasA)"),
|
|
201
|
+
status: strSchema("open|promoted|dropped")
|
|
202
|
+
}, ["project_key"]),
|
|
203
|
+
call: async (args) => {
|
|
204
|
+
const qs = typeof args.status === "string" ? `?status=${args.status}` : "";
|
|
205
|
+
return (await apiCall("GET", `/api/projects/${args.project_key}/ideas${qs}`)).body;
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: "polaris.idea.create",
|
|
210
|
+
description: "Capture an idea in a project. The title is the only required field \u2014 capturing has to cost less than losing the idea. No dates: if it has a deadline it is a milestone, so use polaris.milestone.create instead. Needs a token.",
|
|
211
|
+
inputSchema: objSchema({
|
|
212
|
+
project_key: strSchema("project key (e.g. saasA)"),
|
|
213
|
+
title: strSchema("one line \u2014 what the idea is"),
|
|
214
|
+
note_md: strSchema("optional: what it is and why now; becomes the milestone's description on promotion"),
|
|
215
|
+
team: strSchema("optional: dev|mkt \u2014 leave empty if you don't know yet")
|
|
216
|
+
}, ["project_key", "title"]),
|
|
217
|
+
call: async (args) => {
|
|
218
|
+
const { project_key, ...body } = args;
|
|
219
|
+
return (await apiCall("POST", `/api/projects/${project_key}/ideas`, body)).body;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "polaris.idea.update",
|
|
224
|
+
description: "Refine an open idea \u2014 its title, note or team. A promoted idea is refused: it became a milestone, and the milestone is the truth from then on (use polaris.milestone.update). Needs a token.",
|
|
225
|
+
inputSchema: objSchema({
|
|
226
|
+
project_key: strSchema(),
|
|
227
|
+
id: intSchema("idea id"),
|
|
228
|
+
title: strSchema(),
|
|
229
|
+
note_md: strSchema(),
|
|
230
|
+
team: strSchema("dev|mkt")
|
|
231
|
+
}, ["project_key", "id"]),
|
|
232
|
+
call: async (args) => {
|
|
233
|
+
const { project_key, id, ...body } = args;
|
|
234
|
+
return (await apiCall("PATCH", `/api/projects/${project_key}/ideas/${id}`, body)).body;
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
name: "polaris.idea.promote",
|
|
239
|
+
description: "Promote an idea into a milestone \u2014 the one door between the idea list and the timeline. Demands what a milestone demands: end_date, and a team (unless the idea already carries one). The milestone is created and the idea stamped in ONE transaction; promoting twice returns the first milestone and writes nothing. Needs a token.",
|
|
240
|
+
inputSchema: objSchema({
|
|
241
|
+
project_key: strSchema(),
|
|
242
|
+
id: intSchema("idea id"),
|
|
243
|
+
end_date: strSchema("YYYY-MM-DD \u2014 the deadline the milestone commits to"),
|
|
244
|
+
team: strSchema("dev|mkt \u2014 required unless the idea already has one"),
|
|
245
|
+
start_date: strSchema("YYYY-MM-DD, optional"),
|
|
246
|
+
name: strSchema("optional: overrides the idea's title"),
|
|
247
|
+
description_md: strSchema("optional: overrides the idea's note"),
|
|
248
|
+
framework_key: strSchema(FRAMEWORK_KEY_DOC)
|
|
249
|
+
}, ["project_key", "id", "end_date"]),
|
|
250
|
+
call: async (args) => {
|
|
251
|
+
const { project_key, id, ...body } = args;
|
|
252
|
+
return (await apiCall("POST", `/api/projects/${project_key}/ideas/${id}/promote`, body)).body;
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: "polaris.idea.promote_task",
|
|
257
|
+
description: "Promote a dev idea into a STANDALONE task \u2014 one piece of work with no milestone and no deadline, born in the idea's project and straight into the dev backlog, where the project's developer agents pick it up. For work that is a goal with several tasks, use polaris.idea.promote (a milestone). A mkt idea is refused: a creative needs a campaign. Promoting twice returns the first task and writes nothing. Human-only.",
|
|
258
|
+
inputSchema: objSchema({
|
|
259
|
+
project_key: strSchema(),
|
|
260
|
+
id: intSchema("idea id"),
|
|
261
|
+
name: strSchema("optional: overrides the idea's title"),
|
|
262
|
+
description_md: strSchema("optional: overrides the idea's note (the developer's brief)")
|
|
263
|
+
}, ["project_key", "id"]),
|
|
264
|
+
call: async (args) => {
|
|
265
|
+
const { project_key, id, ...body } = args;
|
|
266
|
+
return (await apiCall("POST", `/api/projects/${project_key}/ideas/${id}/promote-task`, body)).body;
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: "polaris.idea.drop",
|
|
271
|
+
description: "Drop an idea, with the reason it was dropped. Reversible (polaris.idea.reopen) \u2014 dropping is not a one-way door, because almost every good idea looks bad at first. Idempotent: dropping twice keeps the first reason. Needs a token.",
|
|
272
|
+
inputSchema: objSchema({
|
|
273
|
+
project_key: strSchema(),
|
|
274
|
+
id: intSchema("idea id"),
|
|
275
|
+
reason: strSchema("why it was dropped")
|
|
276
|
+
}, ["project_key", "id"]),
|
|
277
|
+
call: async (args) => {
|
|
278
|
+
const { project_key, id, ...body } = args;
|
|
279
|
+
return (await apiCall("POST", `/api/projects/${project_key}/ideas/${id}/drop`, body)).body;
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
name: "polaris.idea.reopen",
|
|
284
|
+
description: "Put a dropped idea back on the open list, clearing its reason. Required before promoting a dropped idea \u2014 resurrecting is a decision, not a side effect. Needs a token.",
|
|
285
|
+
inputSchema: objSchema({ project_key: strSchema(), id: intSchema("idea id") }, [
|
|
286
|
+
"project_key",
|
|
287
|
+
"id"
|
|
288
|
+
]),
|
|
289
|
+
call: async (args) => (await apiCall("POST", `/api/projects/${args.project_key}/ideas/${args.id}/reopen`, {})).body
|
|
290
|
+
},
|
|
175
291
|
{
|
|
176
292
|
name: "polaris.project.pause_publishing",
|
|
177
293
|
description: "Pause publishing through Polaris for a product (human-only). While paused, no creative of this product goes out at its scheduled hour. It does NOT hold the clock: a piece whose hour passes meanwhile comes back to the person in `release`, like any other late piece \u2014 that is what pausing is for. Idempotent: a second call keeps the original pause, and the original reason. `reason` is optional and is what whoever finds it paused reads.",
|
|
@@ -191,9 +307,21 @@ var TOOLS = [
|
|
|
191
307
|
},
|
|
192
308
|
{
|
|
193
309
|
name: "polaris.milestone.list",
|
|
194
|
-
description: "List milestones in a project.",
|
|
195
|
-
inputSchema: objSchema({
|
|
196
|
-
|
|
310
|
+
description: "List milestones in a project. Each one comes with its whole description, so a project with a long history answers with a lot: narrow it with `status` (e.g. loop for the campaigns running now) and `team` (dev|mkt).",
|
|
311
|
+
inputSchema: objSchema({
|
|
312
|
+
project_key: strSchema(),
|
|
313
|
+
status: strSchema("draft|discovery|loop|release|done|canceled"),
|
|
314
|
+
team: strSchema("dev|mkt")
|
|
315
|
+
}, ["project_key"]),
|
|
316
|
+
call: async (args) => {
|
|
317
|
+
const qs = new URLSearchParams({ limit: "200" });
|
|
318
|
+
for (const k of ["status", "team"]) {
|
|
319
|
+
const v = args[k];
|
|
320
|
+
if (v !== undefined && v !== null)
|
|
321
|
+
qs.set(k, String(v));
|
|
322
|
+
}
|
|
323
|
+
return (await apiCall("GET", `/api/projects/${args.project_key}/milestones?${qs}`)).body;
|
|
324
|
+
}
|
|
197
325
|
},
|
|
198
326
|
{
|
|
199
327
|
name: "polaris.milestone.get",
|
|
@@ -203,11 +331,13 @@ var TOOLS = [
|
|
|
203
331
|
},
|
|
204
332
|
{
|
|
205
333
|
name: "polaris.milestone.update",
|
|
206
|
-
description: "Correct or refine a milestone
|
|
334
|
+
description: "Correct or refine a milestone \u2014 PATCH its description_md, name, or dates (start_date / end_date move it on the timeline; send only the one that changes, start can never fall after end), same idea as polaris.task.update. On a mkt campaign the market can also be corrected, while it is still in draft or discovery: after that the creatives are already bound to accounts. The campaign's rhythm, budget and channels live in the description \u2014 write them there and the briefer reads them. Needs a token.",
|
|
207
335
|
inputSchema: objSchema({
|
|
208
336
|
id: intSchema(),
|
|
209
337
|
name: strSchema(),
|
|
210
338
|
description_md: strSchema("full markdown brief; replaces the current description"),
|
|
339
|
+
start_date: strSchema("ISO date, e.g. 2026-06-01"),
|
|
340
|
+
end_date: strSchema("ISO date, the deadline, e.g. 2026-06-30"),
|
|
211
341
|
market: {
|
|
212
342
|
type: "array",
|
|
213
343
|
items: { type: "string", enum: ["BR", "US", "EU"] },
|
|
@@ -220,6 +350,10 @@ var TOOLS = [
|
|
|
220
350
|
body.name = args.name;
|
|
221
351
|
if (typeof args.description_md === "string")
|
|
222
352
|
body.description_md = args.description_md;
|
|
353
|
+
if (typeof args.start_date === "string")
|
|
354
|
+
body.start_date = args.start_date;
|
|
355
|
+
if (typeof args.end_date === "string")
|
|
356
|
+
body.end_date = args.end_date;
|
|
223
357
|
if ("market" in args)
|
|
224
358
|
body.market = args.market;
|
|
225
359
|
return (await apiCall("PATCH", `/api/milestones/${args.id}`, body)).body;
|
|
@@ -231,6 +365,12 @@ var TOOLS = [
|
|
|
231
365
|
inputSchema: objSchema({ id: intSchema(), status: strSchema() }, ["id", "status"]),
|
|
232
366
|
call: async (args) => (await apiCall("POST", `/api/milestones/${args.id}/state`, { status: args.status })).body
|
|
233
367
|
},
|
|
368
|
+
{
|
|
369
|
+
name: "polaris.milestone.demote",
|
|
370
|
+
description: "Send a milestone back to the idea list. Only a milestone with NO tasks (not even canceled ones), still in draft, discovery or loop, and not while a planner is breaking it into tasks. The idea it was promoted from reopens (or a new idea is captured from it) carrying the milestone's name and context; the milestone is canceled and cannot be reopened \u2014 promote the idea again instead. Demoting twice returns the first idea. Human-only.",
|
|
371
|
+
inputSchema: objSchema({ id: intSchema("milestone id") }, ["id"]),
|
|
372
|
+
call: async (args) => (await apiCall("POST", `/api/milestones/${args.id}/demote`, {})).body
|
|
373
|
+
},
|
|
234
374
|
{
|
|
235
375
|
name: "polaris.milestone.qa_list",
|
|
236
376
|
description: "List framework Q&A rows for a milestone.",
|
|
@@ -305,10 +445,10 @@ var TOOLS = [
|
|
|
305
445
|
},
|
|
306
446
|
{
|
|
307
447
|
name: "polaris.task.list",
|
|
308
|
-
description: `Find tasks \u2014 the agent's entry point. Pass assignee_agent_id="self" (or assignee_human_id="self" for a human) to get what is YOURS; add status=dev (etc.) to see only active work. Other filters: milestone_id, product_key, team.`,
|
|
448
|
+
description: `Find tasks \u2014 the agent's entry point. Pass assignee_agent_id="self" (or assignee_human_id="self" for a human) to get what is YOURS; add status=dev (etc.) to see only active work. Other filters: milestone_id (or "none" for the standalone tasks, which have no milestone), product_key, team.`,
|
|
309
449
|
inputSchema: objSchema({
|
|
310
450
|
status: strSchema("comma-separated subset of: draft,backlog,dev,test,approval,release,done,canceled"),
|
|
311
|
-
milestone_id:
|
|
451
|
+
milestone_id: strSchema('a milestone id, or "none" for the standalone tasks (no milestone)'),
|
|
312
452
|
product_key: strSchema("project key, e.g. saasA"),
|
|
313
453
|
team: strSchema("dev|mkt"),
|
|
314
454
|
assignee_agent_id: strSchema('agent id, or "self" for the calling agent'),
|
|
@@ -342,9 +482,10 @@ var TOOLS = [
|
|
|
342
482
|
},
|
|
343
483
|
{
|
|
344
484
|
name: "polaris.task.create",
|
|
345
|
-
description: "Create a task within a milestone. A marketing CREATIVE (a task in an mkt campaign) carries WHERE IT GOES: `presence_id`, one of the destinations from polaris.destination.list. The channel and the market are read from that destination, and sending them here is refused. It also takes format + scheduled_date (the publish day / calendar slot) + platform (if this creative is individually paid). A CAROUSEL (format=carrossel) also requires `slides` (2..10) \u2014 without it the call is refused, because a carousel with no declared size ships one image.",
|
|
485
|
+
description: "Create a task within a milestone \u2014 or, for one small piece of DEV work with no milestone, a STANDALONE task in a project: send project_key instead of milestone_id (human-only, team dev, no creative fields). A marketing CREATIVE (a task in an mkt campaign) carries WHERE IT GOES: `presence_id`, one of the destinations from polaris.destination.list. The channel and the market are read from that destination, and sending them here is refused. It also takes format + scheduled_date (the publish day / calendar slot) + platform (if this creative is individually paid). A CAROUSEL (format=carrossel) also requires `slides` (2..10) \u2014 without it the call is refused, because a carousel with no declared size ships one image.",
|
|
346
486
|
inputSchema: objSchema({
|
|
347
|
-
milestone_id: intSchema(),
|
|
487
|
+
milestone_id: intSchema("the milestone it belongs to \u2014 or omit it and send project_key"),
|
|
488
|
+
project_key: strSchema("instead of milestone_id: a standalone dev task in this project, with no milestone (human-only)"),
|
|
348
489
|
name: strSchema(),
|
|
349
490
|
description_md: strSchema(),
|
|
350
491
|
status: strSchema("draft \u2014 the only status a task can be created in; it reaches the rest by being moved, one legal step at a time (polaris.task.transition)"),
|
|
@@ -359,7 +500,7 @@ var TOOLS = [
|
|
|
359
500
|
scheduled_date: strSchema("mkt: publish day (ISO date) \u2014 the calendar slot"),
|
|
360
501
|
platform: strSchema("mkt: ad platform if this creative is individually paid"),
|
|
361
502
|
asset_url: strSchema("mkt: URL of the produced creative asset (image/video) for the reviewer")
|
|
362
|
-
}, ["
|
|
503
|
+
}, ["name"]),
|
|
363
504
|
call: async (args) => (await apiCall("POST", "/api/tasks", args)).body
|
|
364
505
|
},
|
|
365
506
|
{
|
|
@@ -535,13 +676,29 @@ var TOOLS = [
|
|
|
535
676
|
},
|
|
536
677
|
{
|
|
537
678
|
name: "polaris.ticket.reply",
|
|
538
|
-
description: "Add a message to a ticket's conversation. Default side=team (you replying); an adapter relaying a customer message passes side=customer. Sending a message does NOT pass the ball.",
|
|
539
|
-
inputSchema: objSchema({
|
|
679
|
+
description: "Add a message to a ticket's conversation. Default side=team (you replying); an adapter relaying a customer message passes side=customer. Sending a message does NOT pass the ball. To send FILES with the reply (a screenshot, a screencast of the thing you are describing): upload each one first \u2014 `curl -sS -H \"Authorization: Bearer $POLARIS_TOKEN\" -F file=@clip.mp4 $POLARIS_URL/api/tickets/<id>/attachments` \u2014 then pass the ids it returns as attachment_ids. Paste each one's `marker` into body_md where it belongs and the image or video is drawn THERE, between your paragraphs; an attachment whose marker you never placed falls to a strip at the foot of the message, which for a screencast is the wrong place. Up to 15MB per file.",
|
|
680
|
+
inputSchema: objSchema({
|
|
681
|
+
id: intSchema(),
|
|
682
|
+
body_md: strSchema(),
|
|
683
|
+
side: strSchema("customer|team"),
|
|
684
|
+
attachment_ids: {
|
|
685
|
+
type: "array",
|
|
686
|
+
items: { type: "integer" },
|
|
687
|
+
description: "Ids from the attachment upload above (or from ticket.attachment_list, where sent=false). They ride on THIS message and become visible with it."
|
|
688
|
+
}
|
|
689
|
+
}, ["id", "body_md"]),
|
|
540
690
|
call: async (args) => (await apiCall("POST", `/api/tickets/${args.id}/messages`, {
|
|
541
691
|
body_md: args.body_md,
|
|
542
|
-
side: args.side
|
|
692
|
+
side: args.side,
|
|
693
|
+
attachment_ids: args.attachment_ids
|
|
543
694
|
})).body
|
|
544
695
|
},
|
|
696
|
+
{
|
|
697
|
+
name: "polaris.ticket.attachment_list",
|
|
698
|
+
description: 'Every file on this ticket \u2014 what the customer sent and what we have attached. Each row carries a `marker` (`` for an image, `[name](ref:N)` for a video) to paste into a reply\'s body_md so the file is drawn inside the message instead of in a list under it, and `sent` (false while it has been uploaded and no message names it yet \u2014 nobody sees it until a reply does). A file (PDF, log) has no marker: there is nothing to draw, and it rides as a download chip. Upload with `curl -sS -H "Authorization: Bearer $POLARIS_TOKEN" -F file=@shot.png $POLARIS_URL/api/tickets/<id>/attachments` \u2014 this call does not carry bytes.',
|
|
699
|
+
inputSchema: objSchema({ id: intSchema() }, ["id"]),
|
|
700
|
+
call: async (args) => (await apiCall("GET", `/api/tickets/${args.id}/attachments`)).body
|
|
701
|
+
},
|
|
545
702
|
{
|
|
546
703
|
name: "polaris.ticket.pass",
|
|
547
704
|
description: "Pass the ball \u2014 whose move it is. to=customer after you reply; to=team to pull it back. Manual + idempotent.",
|
|
@@ -752,7 +909,7 @@ var TOOLS = [
|
|
|
752
909
|
},
|
|
753
910
|
{
|
|
754
911
|
name: "polaris.chat.messages",
|
|
755
|
-
description: "Read the comments/context agents and humans left on a scope (e.g. task 42) \u2014 free-text notes, decisions, @mentions. Pass the scope; the chat id is resolved for you.
|
|
912
|
+
description: "Read the comments/context agents and humans left on a scope (e.g. task 42) \u2014 free-text notes, decisions, @mentions, and the handover notes that travel with a task's moves (what the maker delivered, why a reviewer sent it back). Pass the scope; the chat id is resolved for you. To write one, polaris.chat.post.",
|
|
756
913
|
inputSchema: objSchema({
|
|
757
914
|
scope_type: strSchema("task|milestone|project|agent_dm|human_dm|general"),
|
|
758
915
|
scope_id: intSchema(),
|
|
@@ -767,6 +924,58 @@ var TOOLS = [
|
|
|
767
924
|
return (await apiCall("GET", `/api/chats/${chatId}/messages${qs}`)).body;
|
|
768
925
|
}
|
|
769
926
|
},
|
|
927
|
+
{
|
|
928
|
+
name: "polaris.task.run_lines",
|
|
929
|
+
description: "Watch an agent work: the THREAD of one run on a task \u2014 what it was asked (`prompt`), what it said (`said`), what it ran (`ran`, tool + full input), what came back (`back`), and the rest (`other`). This is the backstage of a run; for the comments left on the task use polaris.chat.messages. HUMAN ONLY: an agent token gets 403 (one agent reading another's reasoning changes what it can judge). A task has one run per agent session (the brief, whoever made the piece, the reviewer); the response's `runs` lists them, newest first, and without `session` you get the newest. Bodies are whole, never truncated, and the first line is the entire prompt \u2014 page with `limit`, then pass the response's `next_after` as `after` to read on or to follow a live run.",
|
|
930
|
+
inputSchema: objSchema({
|
|
931
|
+
id: intSchema("task id"),
|
|
932
|
+
session: strSchema("a session_id from `runs` \u2014 which run to read; omit for the newest"),
|
|
933
|
+
after: intSchema("return only lines with seq greater than this (the previous `next_after`)"),
|
|
934
|
+
limit: intSchema("how many lines (default 500, max 2000)")
|
|
935
|
+
}, ["id"]),
|
|
936
|
+
call: async (args) => {
|
|
937
|
+
const qs = new URLSearchParams;
|
|
938
|
+
for (const k of ["session", "after", "limit"]) {
|
|
939
|
+
const v = args[k];
|
|
940
|
+
if (v !== undefined && v !== null)
|
|
941
|
+
qs.set(k, String(v));
|
|
942
|
+
}
|
|
943
|
+
const r = await apiCall("GET", `/api/tasks/${args.id}/run-lines${qs.toString() ? `?${qs}` : ""}`);
|
|
944
|
+
return r.body;
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
{
|
|
948
|
+
name: "polaris.task.creatives",
|
|
949
|
+
description: "What a creative task has actually delivered: every version, newest first, each slot with its file (`mime`, `url`, `status`) and the words that go out beside it (`caption`, `title`, `copy`, `description`). Read this to judge a piece; the maker's handover note is in polaris.chat.messages on the task. The files sit behind the same login as the API, so to look at one, polaris.task.creative_download.",
|
|
950
|
+
inputSchema: objSchema({ id: intSchema("task id") }, ["id"]),
|
|
951
|
+
call: async (args) => (await apiCall("GET", `/api/tasks/${args.id}/creatives`)).body
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
name: "polaris.task.creative_download",
|
|
955
|
+
description: "Save one version of a creative to THIS machine, as a zip of its slides in slot order (slide-01.mp4, slide-02.png, ...), so its frames can be pulled and its sound measured. Newest version unless `version` is given; only a version whose slides are all ready exists to download (404 otherwise). Writes into `dir` (default: the system temp folder) and returns the file's absolute path and size; unzip it to look inside.",
|
|
956
|
+
inputSchema: objSchema({
|
|
957
|
+
id: intSchema("task id"),
|
|
958
|
+
version: intSchema("which version (polaris.task.creatives lists them); omit for the newest"),
|
|
959
|
+
dir: strSchema("folder to save into; created if missing. Default: the system temp folder")
|
|
960
|
+
}, ["id"]),
|
|
961
|
+
call: async (args) => {
|
|
962
|
+
const qs = typeof args.version === "number" ? `?version=${args.version}` : "";
|
|
963
|
+
const headers = {};
|
|
964
|
+
if (TOKEN)
|
|
965
|
+
headers.Authorization = `Bearer ${TOKEN}`;
|
|
966
|
+
const res = await fetch(`${BASE}/api/tasks/${args.id}/creative.zip${qs}`, { headers });
|
|
967
|
+
if (!res.ok) {
|
|
968
|
+
return { status: res.status, body: await res.text() };
|
|
969
|
+
}
|
|
970
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
971
|
+
const dir = resolve(typeof args.dir === "string" && args.dir ? args.dir : tmpdir());
|
|
972
|
+
mkdirSync(dir, { recursive: true });
|
|
973
|
+
const name = `task-${args.id}-${typeof args.version === "number" ? `v${args.version}` : "latest"}.zip`;
|
|
974
|
+
const path = join(dir, name);
|
|
975
|
+
writeFileSync(path, bytes);
|
|
976
|
+
return { data: { path, bytes: bytes.length } };
|
|
977
|
+
}
|
|
978
|
+
},
|
|
770
979
|
{
|
|
771
980
|
name: "polaris.project.update",
|
|
772
981
|
description: "Update a project (human-only): repo_url, name, status (active|paused|archived), owner_human_id. Brand/context live in the CLAUDE.md \u2014 use polaris.project.claude_md_set.",
|
|
@@ -987,7 +1196,7 @@ var TOOLS = [
|
|
|
987
1196
|
team: strSchema("dev|mkt"),
|
|
988
1197
|
end_date: strSchema("ISO date, e.g. 2026-06-30"),
|
|
989
1198
|
start_date: strSchema("ISO date (optional)"),
|
|
990
|
-
framework_key: strSchema(),
|
|
1199
|
+
framework_key: strSchema(FRAMEWORK_KEY_DOC),
|
|
991
1200
|
description_md: strSchema(),
|
|
992
1201
|
market: {
|
|
993
1202
|
type: "array",
|
|
@@ -1094,7 +1303,7 @@ var TOOLS = [
|
|
|
1094
1303
|
items: { type: "string" },
|
|
1095
1304
|
description: "developer|tester|planner|helpdesker"
|
|
1096
1305
|
},
|
|
1097
|
-
framework_key: strSchema()
|
|
1306
|
+
framework_key: strSchema("optional \u2014 the team's default framework: a key from polaris.framework.list")
|
|
1098
1307
|
}, ["key", "name"]),
|
|
1099
1308
|
call: async (args) => (await apiCall("POST", "/api/teams", args)).body
|
|
1100
1309
|
},
|
|
@@ -1106,7 +1315,7 @@ var TOOLS = [
|
|
|
1106
1315
|
name: strSchema(),
|
|
1107
1316
|
requires: { type: "array", items: { type: "string" } },
|
|
1108
1317
|
functions: { type: "array", items: { type: "string" } },
|
|
1109
|
-
framework_key: strSchema()
|
|
1318
|
+
framework_key: strSchema("the team's default framework: a key from polaris.framework.list")
|
|
1110
1319
|
}, ["key"]),
|
|
1111
1320
|
call: async (args) => {
|
|
1112
1321
|
const { key, ...patch } = args;
|
|
@@ -1132,7 +1341,7 @@ async function handle(req) {
|
|
|
1132
1341
|
return ok(id, {
|
|
1133
1342
|
protocolVersion: "2024-11-05",
|
|
1134
1343
|
capabilities: { tools: {} },
|
|
1135
|
-
serverInfo: { name: "mcp-polaris", version:
|
|
1344
|
+
serverInfo: { name: "mcp-polaris", version: VERSION },
|
|
1136
1345
|
instructions: INSTRUCTIONS
|
|
1137
1346
|
});
|
|
1138
1347
|
case "initialized":
|
|
@@ -1192,5 +1401,6 @@ if (true) {
|
|
|
1192
1401
|
rl.on("close", () => log("stdin closed, exiting"));
|
|
1193
1402
|
}
|
|
1194
1403
|
export {
|
|
1404
|
+
VERSION,
|
|
1195
1405
|
TOOLS
|
|
1196
1406
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glie/mcp-polaris",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.2",
|
|
4
4
|
"description": "Model Context Protocol (stdio) server exposing Polaris, glie's ops platform, as native tools in Claude Code and any MCP client.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "glie <carlos@glie.ai>",
|