@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.
Files changed (2) hide show
  1. package/dist/index.js +90 -16
  2. 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({ key: strSchema(), name: strSchema(), description_md: strSchema() }, [
128
- "key",
129
- "name"
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("image/png | image/jpeg | image/webp"),
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("image/png | image/jpeg | image/webp"),
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: "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).",
397
- inputSchema: objSchema({ id: intSchema(), status: strSchema() }, ["id", "status"]),
398
- call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/state`, { status: args.status })).body
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 place twice fills in what was left empty. Never send a password you were not given.",
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 (channel only)"),
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({ id: intSchema() }, ["id"]),
914
- call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/return-to-backlog`)).body
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.8.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"