@glie/mcp-polaris 0.8.0 → 0.9.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 +90 -16
- package/package.json +3 -14
package/dist/index.js
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
|
|
4
4
|
// ../../src/mcp/index.ts
|
|
5
5
|
import { createInterface } from "readline";
|
|
6
|
+
|
|
7
|
+
// ../../src/lib/media-types.ts
|
|
8
|
+
var MEDIA_TYPES = [
|
|
9
|
+
{ mime: "image/png", kind: "image", ext: ".png", aliases: [] },
|
|
10
|
+
{ mime: "image/jpeg", kind: "image", ext: ".jpg", aliases: [".jpeg"] },
|
|
11
|
+
{ mime: "image/webp", kind: "image", ext: ".webp", aliases: [] },
|
|
12
|
+
{ mime: "video/mp4", kind: "video", ext: ".mp4", aliases: [] },
|
|
13
|
+
{ mime: "video/quicktime", kind: "video", ext: ".mov", aliases: [] },
|
|
14
|
+
{ mime: "video/webm", kind: "video", ext: ".webm", aliases: [] }
|
|
15
|
+
];
|
|
16
|
+
function allMimes() {
|
|
17
|
+
return MEDIA_TYPES.map((t) => t.mime);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ../../src/mcp/index.ts
|
|
6
21
|
var BASE = process.env.POLARIS_URL ?? "http://127.0.0.1:7843";
|
|
7
22
|
var TOKEN = process.env.POLARIS_TOKEN ?? "";
|
|
8
23
|
var PROJECT = process.env.POLARIS_PROJECT ?? "";
|
|
@@ -123,13 +138,20 @@ var TOOLS = [
|
|
|
123
138
|
},
|
|
124
139
|
{
|
|
125
140
|
name: "polaris.project.create",
|
|
126
|
-
description: "Create a project (human-only).",
|
|
127
|
-
inputSchema: objSchema({
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
141
|
+
description: "Create a project (human-only). With GitHub connected a repository is required \u2014 pick one from polaris.github.repositories. One repository holds one project.",
|
|
142
|
+
inputSchema: objSchema({
|
|
143
|
+
key: strSchema(),
|
|
144
|
+
name: strSchema(),
|
|
145
|
+
repo_url: strSchema("https://github.com/owner/repo \u2014 required when GitHub is connected")
|
|
146
|
+
}, ["key", "name"]),
|
|
131
147
|
call: async (args) => (await apiCall("POST", "/api/projects", args)).body
|
|
132
148
|
},
|
|
149
|
+
{
|
|
150
|
+
name: "polaris.github.repositories",
|
|
151
|
+
description: "Repositories a project may live in (human-only): whether GitHub is connected, and every repository the GitHub App reaches \u2014 available, or why not (already another project's, or archived).",
|
|
152
|
+
inputSchema: objSchema({}),
|
|
153
|
+
call: async () => (await apiCall("GET", "/api/github/repositories")).body
|
|
154
|
+
},
|
|
133
155
|
{
|
|
134
156
|
name: "polaris.project.arm",
|
|
135
157
|
description: "Arm a project for auto-approval (human-only): while armed, dev work that reaches `approval` CLEAN is approved on your behalf \u2014 no per-task click \u2014 and follows the normal path to release. Escalated tasks, mkt creatives, and support work still wait for a person; a merge that fails leaves the task in `approval`. Idempotent.",
|
|
@@ -213,7 +235,7 @@ var TOOLS = [
|
|
|
213
235
|
description: "Step 1 of attaching an image to a milestone: reserve an upload key. Returns file_key + upload_url \u2014 PUT the raw image bytes to upload_url YOURSELF (curl/fetch from your shell; this call does not carry bytes), then call polaris.milestone.attachment_create with the same file_key. A link isn't an attachment at all \u2014 just write `[text](url)` straight into the milestone's description_md.",
|
|
214
236
|
inputSchema: objSchema({
|
|
215
237
|
id: intSchema("milestone id"),
|
|
216
|
-
mime: strSchema("
|
|
238
|
+
mime: strSchema(allMimes().join(" | ")),
|
|
217
239
|
size_bytes: intSchema()
|
|
218
240
|
}, ["id", "mime", "size_bytes"]),
|
|
219
241
|
call: async (args) => (await apiCall("POST", `/api/milestones/${args.id}/attachments/presign`, {
|
|
@@ -325,7 +347,7 @@ var TOOLS = [
|
|
|
325
347
|
description: "Step 1 of attaching an image to a task: reserve an upload key. Returns file_key + upload_url \u2014 PUT the raw image bytes to upload_url YOURSELF (curl/fetch from your shell; this call does not carry bytes), then call polaris.task.attachment_create with the same file_key. A link isn't an attachment at all \u2014 just write `[text](url)` straight into the task's description_md.",
|
|
326
348
|
inputSchema: objSchema({
|
|
327
349
|
id: intSchema("task id"),
|
|
328
|
-
mime: strSchema("
|
|
350
|
+
mime: strSchema(allMimes().join(" | ")),
|
|
329
351
|
size_bytes: intSchema()
|
|
330
352
|
}, ["id", "mime", "size_bytes"]),
|
|
331
353
|
call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/attachments/presign`, {
|
|
@@ -393,9 +415,16 @@ var TOOLS = [
|
|
|
393
415
|
},
|
|
394
416
|
{
|
|
395
417
|
name: "polaris.task.transition",
|
|
396
|
-
description:
|
|
397
|
-
inputSchema: objSchema({ id: intSchema(), status: strSchema() }, ["id", "status"]),
|
|
398
|
-
call: async (args) =>
|
|
418
|
+
description: 'Move a task to its next status when you finish a stage (e.g. dev\u2192test once your PR is up). Enforced flow: draft\u2192backlog\u2192dev\u2192test\u2192approval\u2192release\u2192done (any\u2192canceled). Illegal jumps are rejected. Neither terminal state is a dead end: canceled\u2192draft and done\u2192approval undo the last step, so a mistaken close is recoverable without touching the database. The approval\u2192release gate is polaris.task.approve (human-only).\n\nThree of these moves hand the work to someone else, and `note` is REQUIRED on them \u2014 it is the message that travels with it, and it lands in the task chat as a handover: `dev\u2192test` (what you did and what to look at first), `test\u2192dev` (why it comes back \u2014 it is what the next attempt is made from), and `test\u2192approval`, which also requires `verdict` ("approved" or "rejected": a person is about to decide, and they need to know whether this passed or ran out of rounds). Every other move takes no note.',
|
|
419
|
+
inputSchema: objSchema({ id: intSchema(), status: strSchema(), note: strSchema(), verdict: strSchema() }, ["id", "status"]),
|
|
420
|
+
call: async (args) => {
|
|
421
|
+
const body = { status: args.status };
|
|
422
|
+
if (typeof args.note === "string")
|
|
423
|
+
body.note = args.note;
|
|
424
|
+
if (typeof args.verdict === "string")
|
|
425
|
+
body.verdict = args.verdict;
|
|
426
|
+
return (await apiCall("POST", `/api/tasks/${args.id}/state`, body)).body;
|
|
427
|
+
}
|
|
399
428
|
},
|
|
400
429
|
{
|
|
401
430
|
name: "polaris.task.approve",
|
|
@@ -729,22 +758,54 @@ var TOOLS = [
|
|
|
729
758
|
},
|
|
730
759
|
{
|
|
731
760
|
name: "polaris.presence.list",
|
|
732
|
-
description: "Where a product exists in the world: the channels it publishes on and the directories it is listed in, each with its state (missing \u2192 exists \u2192 connected). Read this before writing a campaign \u2014 a channel that is not connected cannot be published to.",
|
|
761
|
+
description: "Where a product exists in the world: the channels it publishes on and the directories it is listed in, each with its state (missing \u2192 exists \u2192 connected). Read this before writing a campaign \u2014 a channel that is not connected cannot be published to. Each row carries what the catalogue knows in `place`: read `place.actor` before planning work there \u2014 `agent` means there is a publishing tool, `human` means a person does it and records where, and `place.final_state` says how far this particular place can ever get.",
|
|
733
762
|
inputSchema: objSchema({ key: strSchema("project key") }, ["key"]),
|
|
734
763
|
call: async (args) => (await apiCall("GET", `/api/projects/${args.key}/presences`)).body
|
|
735
764
|
},
|
|
765
|
+
{
|
|
766
|
+
name: "polaris.place.list",
|
|
767
|
+
description: "The catalogue of places a product can exist in \u2014 every social network, paid surface, syndication platform, app store and software directory the studio knows about. The world's list, not ours: read it to decide WHERE a product should be. Each entry says what the place is, who acts there (`actor`: agent = there is a publishing tool; human = a person does it and records where), how far it can get (`final_state`), WHY it is worth being there (`note`), and the URL that was verified (`source`). Whether we are actually there is a PRESENCE \u2014 polaris.presence.list.",
|
|
768
|
+
inputSchema: objSchema({}),
|
|
769
|
+
call: async () => (await apiCall("GET", "/api/places")).body
|
|
770
|
+
},
|
|
771
|
+
{
|
|
772
|
+
name: "polaris.org_credential.list",
|
|
773
|
+
description: "The accounts that serve MANY products at once \u2014 Google Play Console, App Store Connect, a vendor login that covers several listings. Read it to know whether an entry can be opened at all, and by whom: the row names the owner, and the password never leaves the server. A presence points at one of these when the credential is the org's rather than the product's.",
|
|
774
|
+
inputSchema: objSchema({}),
|
|
775
|
+
call: async () => (await apiCall("GET", "/api/org-credentials")).body
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
name: "polaris.org_credential.add",
|
|
779
|
+
description: "Register an org-wide credential (human-only). Idempotent on slug + login: the same Play Console twice is the same Play Console, and a DIFFERENT login on the same site is a different credential. A place reached by OAuth is refused \u2014 that credential already lives in social accounts. Never send a password you were not given.",
|
|
780
|
+
inputSchema: objSchema({
|
|
781
|
+
slug: strSchema("play-store \xB7 app-store \xB7 capterra \u2026"),
|
|
782
|
+
label: strSchema("human name; defaults to the catalogue's"),
|
|
783
|
+
url: strSchema("where you log in"),
|
|
784
|
+
login_email: strSchema(),
|
|
785
|
+
notes: strSchema("who holds the second factor, and anything the next person needs")
|
|
786
|
+
}, ["slug"]),
|
|
787
|
+
call: async (args) => (await apiCall("POST", "/api/org-credentials", args)).body
|
|
788
|
+
},
|
|
789
|
+
{
|
|
790
|
+
name: "polaris.org_credential.verify",
|
|
791
|
+
description: "Stamp an org credential as CHECKED \u2014 it still opens the door (human-only). A credential does not announce that it expired: that is discovered on the day it is needed, which is always the worst day.",
|
|
792
|
+
inputSchema: objSchema({ id: intSchema("credential id") }, ["id"]),
|
|
793
|
+
call: async (args) => (await apiCall("POST", `/api/org-credentials/${args.id}/verify`, {})).body
|
|
794
|
+
},
|
|
736
795
|
{
|
|
737
796
|
name: "polaris.presence.add",
|
|
738
|
-
description: "Add a place to a project (human-only). kind: channel (we publish there) | listing (we keep an entry there). Idempotent on slug: adding the same
|
|
797
|
+
description: "Add a place to a project (human-only). kind: channel (we publish there) | listing (we keep an entry there). Idempotent on slug + MARKET: adding Instagram BR twice is the same Instagram BR, and Instagram US is a different presence. Never send a password you were not given.",
|
|
739
798
|
inputSchema: objSchema({
|
|
740
799
|
key: strSchema("project key"),
|
|
741
800
|
kind: strSchema("channel | listing"),
|
|
742
801
|
slug: strSchema("instagram \xB7 linkedin \xB7 reddit \xB7 g2 \xB7 capterra \u2026"),
|
|
743
802
|
label: strSchema("human name, e.g. Instagram"),
|
|
803
|
+
market: strSchema("the market this one serves (BR \xB7 US \xB7 \u2026) \u2014 it is half the identity, so a product with an account per market gets one presence per market. Omit where the place has no market (a blog, a store entry)."),
|
|
744
804
|
url: strSchema("public profile or listing URL"),
|
|
745
805
|
handle: strSchema("@handle or account name"),
|
|
746
|
-
state: strSchema("missing | exists | connected
|
|
806
|
+
state: strSchema("missing | exists | connected \u2014 connected means there IS a link"),
|
|
747
807
|
login_email: strSchema(),
|
|
808
|
+
org_credential_id: intSchema("the org-wide account that opens this entry, when there is one"),
|
|
748
809
|
notes: strSchema()
|
|
749
810
|
}, ["key", "kind", "slug", "label"]),
|
|
750
811
|
call: async (args) => {
|
|
@@ -763,6 +824,7 @@ var TOOLS = [
|
|
|
763
824
|
handle: strSchema(),
|
|
764
825
|
state: strSchema("missing | exists | connected"),
|
|
765
826
|
login_email: strSchema(),
|
|
827
|
+
org_credential_id: intSchema("the org-wide account that opens this entry; null unlinks"),
|
|
766
828
|
notes: strSchema()
|
|
767
829
|
}, ["key", "id"]),
|
|
768
830
|
call: async (args) => {
|
|
@@ -907,11 +969,23 @@ var TOOLS = [
|
|
|
907
969
|
inputSchema: objSchema({ id: intSchema("stack id"), task_id: intSchema() }, ["id", "task_id"]),
|
|
908
970
|
call: async (args) => (await apiCall("DELETE", `/api/stacks/${args.id}/tasks`, { task_id: args.task_id })).body
|
|
909
971
|
},
|
|
972
|
+
{
|
|
973
|
+
name: "polaris.task.escalate",
|
|
974
|
+
description: "You are making a piece and something needs a HUMAN DECISION before it can be made \u2014 a premise in the brief that turned out false, two ways forward and no ground to choose between them, a collision with something already live. Stops the piece and puts it in front of a person, in `approval`, with your question attached.\n\nNot for a piece you could not finish: that is what ending the run does. This is for one you are not going to decide alone.\n\nThe `note` is the whole point \u2014 what you found, what the options are, and what you are not choosing on your own. Without it this is an abandoned piece with a flag on it.",
|
|
975
|
+
inputSchema: objSchema({
|
|
976
|
+
id: intSchema(),
|
|
977
|
+
note: strSchema("the question \u2014 what was found, the options, and what you will not decide")
|
|
978
|
+
}, ["id", "note"]),
|
|
979
|
+
call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/escalate`, { note: args.note })).body
|
|
980
|
+
},
|
|
910
981
|
{
|
|
911
982
|
name: "polaris.task.return_to_backlog",
|
|
912
|
-
description: "Send an approval task back for rework (human-only) \u2014 to its sticky developer (dev) or backlog. Clears escalation.",
|
|
913
|
-
inputSchema: objSchema({
|
|
914
|
-
|
|
983
|
+
description: "Send an approval task back for rework (human-only) \u2014 to its sticky developer (dev) or backlog. Clears escalation.\n\n**Write the `note`.** On a rework the agent does NOT re-read the brief: the request it gets says *you already have the brief*, and the note is the only thing that reaches it. Sending a piece back without one asks for the same piece again \u2014 and an agent that finds the field empty concludes the worker restarted and changes nothing, which is what it should do.",
|
|
984
|
+
inputSchema: objSchema({
|
|
985
|
+
id: intSchema(),
|
|
986
|
+
note: strSchema("why it is coming back \u2014 this is the brief for the next attempt")
|
|
987
|
+
}, ["id"]),
|
|
988
|
+
call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/return-to-backlog`, args.note ? { note: args.note } : undefined)).body
|
|
915
989
|
},
|
|
916
990
|
{
|
|
917
991
|
name: "polaris.chat.post",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glie/mcp-polaris",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
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>",
|
|
@@ -8,11 +8,7 @@
|
|
|
8
8
|
"bin": {
|
|
9
9
|
"mcp-polaris": "dist/index.js"
|
|
10
10
|
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"README.md",
|
|
14
|
-
"LICENSE"
|
|
15
|
-
],
|
|
11
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
16
12
|
"engines": {
|
|
17
13
|
"node": ">=18"
|
|
18
14
|
},
|
|
@@ -25,14 +21,7 @@
|
|
|
25
21
|
"directory": "packages/mcp-polaris"
|
|
26
22
|
},
|
|
27
23
|
"homepage": "https://polaris.glie.ai",
|
|
28
|
-
"keywords": [
|
|
29
|
-
"mcp",
|
|
30
|
-
"model-context-protocol",
|
|
31
|
-
"claude",
|
|
32
|
-
"claude-code",
|
|
33
|
-
"polaris",
|
|
34
|
-
"glie"
|
|
35
|
-
],
|
|
24
|
+
"keywords": ["mcp", "model-context-protocol", "claude", "claude-code", "polaris", "glie"],
|
|
36
25
|
"scripts": {
|
|
37
26
|
"build": "bun run build.ts",
|
|
38
27
|
"prepublishOnly": "bun run build.ts"
|