@glie/mcp-polaris 0.8.0 → 0.10.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 +97 -15
  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,12 +138,27 @@ 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",
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"]),
147
+ call: async (args) => (await apiCall("POST", "/api/projects", args)).body
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
+ },
155
+ {
156
+ name: "polaris.github.repository.create",
157
+ description: "Create a repository ready for Polaris (human-only): private, initialised, Issues on, with a dev branch. Needs the GitHub App's Administration: write permission \u2014 the answer says where to grant it when missing. Then create the project with its URL.",
158
+ inputSchema: objSchema({ name: strSchema("the repository name \u2014 usually the project key") }, [
129
159
  "name"
130
160
  ]),
131
- call: async (args) => (await apiCall("POST", "/api/projects", args)).body
161
+ call: async (args) => (await apiCall("POST", "/api/github/repositories", args)).body
132
162
  },
133
163
  {
134
164
  name: "polaris.project.arm",
@@ -213,7 +243,7 @@ var TOOLS = [
213
243
  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
244
  inputSchema: objSchema({
215
245
  id: intSchema("milestone id"),
216
- mime: strSchema("image/png | image/jpeg | image/webp"),
246
+ mime: strSchema(allMimes().join(" | ")),
217
247
  size_bytes: intSchema()
218
248
  }, ["id", "mime", "size_bytes"]),
219
249
  call: async (args) => (await apiCall("POST", `/api/milestones/${args.id}/attachments/presign`, {
@@ -325,7 +355,7 @@ var TOOLS = [
325
355
  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
356
  inputSchema: objSchema({
327
357
  id: intSchema("task id"),
328
- mime: strSchema("image/png | image/jpeg | image/webp"),
358
+ mime: strSchema(allMimes().join(" | ")),
329
359
  size_bytes: intSchema()
330
360
  }, ["id", "mime", "size_bytes"]),
331
361
  call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/attachments/presign`, {
@@ -393,9 +423,16 @@ var TOOLS = [
393
423
  },
394
424
  {
395
425
  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
426
+ 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.',
427
+ inputSchema: objSchema({ id: intSchema(), status: strSchema(), note: strSchema(), verdict: strSchema() }, ["id", "status"]),
428
+ call: async (args) => {
429
+ const body = { status: args.status };
430
+ if (typeof args.note === "string")
431
+ body.note = args.note;
432
+ if (typeof args.verdict === "string")
433
+ body.verdict = args.verdict;
434
+ return (await apiCall("POST", `/api/tasks/${args.id}/state`, body)).body;
435
+ }
399
436
  },
400
437
  {
401
438
  name: "polaris.task.approve",
@@ -729,22 +766,54 @@ var TOOLS = [
729
766
  },
730
767
  {
731
768
  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.",
769
+ 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
770
  inputSchema: objSchema({ key: strSchema("project key") }, ["key"]),
734
771
  call: async (args) => (await apiCall("GET", `/api/projects/${args.key}/presences`)).body
735
772
  },
773
+ {
774
+ name: "polaris.place.list",
775
+ 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.",
776
+ inputSchema: objSchema({}),
777
+ call: async () => (await apiCall("GET", "/api/places")).body
778
+ },
779
+ {
780
+ name: "polaris.org_credential.list",
781
+ 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.",
782
+ inputSchema: objSchema({}),
783
+ call: async () => (await apiCall("GET", "/api/org-credentials")).body
784
+ },
785
+ {
786
+ name: "polaris.org_credential.add",
787
+ 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.",
788
+ inputSchema: objSchema({
789
+ slug: strSchema("play-store \xB7 app-store \xB7 capterra \u2026"),
790
+ label: strSchema("human name; defaults to the catalogue's"),
791
+ url: strSchema("where you log in"),
792
+ login_email: strSchema(),
793
+ notes: strSchema("who holds the second factor, and anything the next person needs")
794
+ }, ["slug"]),
795
+ call: async (args) => (await apiCall("POST", "/api/org-credentials", args)).body
796
+ },
797
+ {
798
+ name: "polaris.org_credential.verify",
799
+ 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.",
800
+ inputSchema: objSchema({ id: intSchema("credential id") }, ["id"]),
801
+ call: async (args) => (await apiCall("POST", `/api/org-credentials/${args.id}/verify`, {})).body
802
+ },
736
803
  {
737
804
  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.",
805
+ 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
806
  inputSchema: objSchema({
740
807
  key: strSchema("project key"),
741
808
  kind: strSchema("channel | listing"),
742
809
  slug: strSchema("instagram \xB7 linkedin \xB7 reddit \xB7 g2 \xB7 capterra \u2026"),
743
810
  label: strSchema("human name, e.g. Instagram"),
811
+ 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
812
  url: strSchema("public profile or listing URL"),
745
813
  handle: strSchema("@handle or account name"),
746
- state: strSchema("missing | exists | connected (channel only)"),
814
+ state: strSchema("missing | exists | connected \u2014 connected means there IS a link"),
747
815
  login_email: strSchema(),
816
+ org_credential_id: intSchema("the org-wide account that opens this entry, when there is one"),
748
817
  notes: strSchema()
749
818
  }, ["key", "kind", "slug", "label"]),
750
819
  call: async (args) => {
@@ -763,6 +832,7 @@ var TOOLS = [
763
832
  handle: strSchema(),
764
833
  state: strSchema("missing | exists | connected"),
765
834
  login_email: strSchema(),
835
+ org_credential_id: intSchema("the org-wide account that opens this entry; null unlinks"),
766
836
  notes: strSchema()
767
837
  }, ["key", "id"]),
768
838
  call: async (args) => {
@@ -907,11 +977,23 @@ var TOOLS = [
907
977
  inputSchema: objSchema({ id: intSchema("stack id"), task_id: intSchema() }, ["id", "task_id"]),
908
978
  call: async (args) => (await apiCall("DELETE", `/api/stacks/${args.id}/tasks`, { task_id: args.task_id })).body
909
979
  },
980
+ {
981
+ name: "polaris.task.escalate",
982
+ 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.",
983
+ inputSchema: objSchema({
984
+ id: intSchema(),
985
+ note: strSchema("the question \u2014 what was found, the options, and what you will not decide")
986
+ }, ["id", "note"]),
987
+ call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/escalate`, { note: args.note })).body
988
+ },
910
989
  {
911
990
  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
991
+ 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.",
992
+ inputSchema: objSchema({
993
+ id: intSchema(),
994
+ note: strSchema("why it is coming back \u2014 this is the brief for the next attempt")
995
+ }, ["id"]),
996
+ call: async (args) => (await apiCall("POST", `/api/tasks/${args.id}/return-to-backlog`, args.note ? { note: args.note } : undefined)).body
915
997
  },
916
998
  {
917
999
  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.10.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"