@aioproductoscom/mcp-pm 0.1.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 +61 -0
- package/dist/client.js +60 -0
- package/dist/index.js +52 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# ProductOS PM Helper · MCP server
|
|
2
|
+
|
|
3
|
+
Manage **ProductOS** tickets from your AI host — Claude Code, Cursor, Codex, or any
|
|
4
|
+
MCP client. List, create, update, and comment on tasks, grounded in the ProductOS
|
|
5
|
+
spine (features, insights). Thin and secret-free: the only credential is your
|
|
6
|
+
personal access token, which you paste into *your* host's config.
|
|
7
|
+
|
|
8
|
+
## 1. Get a token
|
|
9
|
+
|
|
10
|
+
In ProductOS → **Settings → Tokens & Agents** → **Generate token**. Copy it (shown
|
|
11
|
+
once) — it looks like `pat_live_…`.
|
|
12
|
+
|
|
13
|
+
## 2. Connect
|
|
14
|
+
|
|
15
|
+
### Claude Code
|
|
16
|
+
```bash
|
|
17
|
+
claude mcp add productos-pm -e PRODUCTOS_TOKEN=pat_live_… -- npx -y @aioproductoscom/mcp-pm
|
|
18
|
+
```
|
|
19
|
+
or in `.mcp.json`:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"productos-pm": {
|
|
24
|
+
"command": "npx",
|
|
25
|
+
"args": ["-y", "@aioproductoscom/mcp-pm"],
|
|
26
|
+
"env": { "PRODUCTOS_TOKEN": "pat_live_…" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Cursor / Codex
|
|
33
|
+
Same shape in their `mcp.json`: `command: "npx"`, `args: ["-y", "@aioproductoscom/mcp-pm"]`,
|
|
34
|
+
`env.PRODUCTOS_TOKEN`.
|
|
35
|
+
|
|
36
|
+
`PRODUCTOS_URL` defaults to `https://platform.aioproductos.com`; set it to point at a
|
|
37
|
+
self-hosted platform.
|
|
38
|
+
|
|
39
|
+
## Tools
|
|
40
|
+
|
|
41
|
+
| Tool | What it does |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `whoami` | the connected org + member |
|
|
44
|
+
| `pm_meta` | lists / statuses / members / features (resolve names → ids) |
|
|
45
|
+
| `list_tasks` | list tasks (filter by `status_id`, `list_id`) |
|
|
46
|
+
| `get_task` | one task + comments + assignees |
|
|
47
|
+
| `create_task` | new task; `feature_id` / `insight_id` link it to the spine |
|
|
48
|
+
| `update_task` | change status, priority, fields, assignees |
|
|
49
|
+
| `comment_on_task` | add a comment |
|
|
50
|
+
|
|
51
|
+
## Local build (until published to npm)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pnpm install
|
|
55
|
+
pnpm --filter @aioproductoscom/mcp-pm build
|
|
56
|
+
```
|
|
57
|
+
Then point the host at the built binary instead of `npx`:
|
|
58
|
+
```json
|
|
59
|
+
{ "command": "node", "args": ["<repo>/packages/mcps/productos-pm/dist/index.js"],
|
|
60
|
+
"env": { "PRODUCTOS_TOKEN": "pat_live_…" } }
|
|
61
|
+
```
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Thin typed HTTP client over the platform's token-authed PM endpoints.
|
|
2
|
+
// Uses global fetch (Node 18+). The PAT is the only credential; nothing is stored.
|
|
3
|
+
export class PlatformClient {
|
|
4
|
+
baseUrl;
|
|
5
|
+
token;
|
|
6
|
+
constructor(baseUrl, token) {
|
|
7
|
+
this.baseUrl = baseUrl;
|
|
8
|
+
this.token = token;
|
|
9
|
+
}
|
|
10
|
+
async req(path, init) {
|
|
11
|
+
const res = await fetch(`${this.baseUrl}${path}`, {
|
|
12
|
+
...init,
|
|
13
|
+
headers: {
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
Authorization: `Bearer ${this.token}`,
|
|
16
|
+
...(init?.headers ?? {}),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
const body = await res.text();
|
|
20
|
+
const json = body ? JSON.parse(body) : {};
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const o = (json ?? {});
|
|
23
|
+
const msg = o.error ? `${o.error}${o.detail ? `: ${o.detail}` : ""}` : `HTTP ${res.status}`;
|
|
24
|
+
const e = new Error(msg);
|
|
25
|
+
e.status = res.status;
|
|
26
|
+
throw e;
|
|
27
|
+
}
|
|
28
|
+
return json;
|
|
29
|
+
}
|
|
30
|
+
whoami() {
|
|
31
|
+
return this.req("/api/me");
|
|
32
|
+
}
|
|
33
|
+
meta() {
|
|
34
|
+
return this.req("/api/pm/meta");
|
|
35
|
+
}
|
|
36
|
+
listTasks(q) {
|
|
37
|
+
const params = new URLSearchParams();
|
|
38
|
+
if (q.status_id)
|
|
39
|
+
params.set("status_id", q.status_id);
|
|
40
|
+
if (q.list_id)
|
|
41
|
+
params.set("list_id", q.list_id);
|
|
42
|
+
const qs = params.toString();
|
|
43
|
+
return this.req(`/api/pm/tasks${qs ? `?${qs}` : ""}`);
|
|
44
|
+
}
|
|
45
|
+
getTask(id) {
|
|
46
|
+
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}`);
|
|
47
|
+
}
|
|
48
|
+
createTask(body) {
|
|
49
|
+
return this.req("/api/pm/tasks", { method: "POST", body: JSON.stringify(body) });
|
|
50
|
+
}
|
|
51
|
+
updateTask(id, body) {
|
|
52
|
+
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}`, { method: "PATCH", body: JSON.stringify(body) });
|
|
53
|
+
}
|
|
54
|
+
comment(id, body) {
|
|
55
|
+
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}/comments`, {
|
|
56
|
+
method: "POST",
|
|
57
|
+
body: JSON.stringify({ body }),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { PlatformClient } from "./client.js";
|
|
6
|
+
const token = process.env.PRODUCTOS_TOKEN;
|
|
7
|
+
const baseUrl = (process.env.PRODUCTOS_URL ?? "https://platform.aioproductos.com").replace(/\/$/, "");
|
|
8
|
+
if (!token) {
|
|
9
|
+
console.error("[productos-pm] PRODUCTOS_TOKEN is required — generate one in ProductOS → Settings → Tokens & Agents.");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const client = new PlatformClient(baseUrl, token);
|
|
13
|
+
function text(data) {
|
|
14
|
+
return {
|
|
15
|
+
content: [
|
|
16
|
+
{ type: "text", text: typeof data === "string" ? data : JSON.stringify(data, null, 2) },
|
|
17
|
+
],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const server = new McpServer({ name: "productos-pm", version: "0.1.0" });
|
|
21
|
+
server.tool("whoami", "Show the connected ProductOS identity (org, member).", {}, async () => text(await client.whoami()));
|
|
22
|
+
server.tool("pm_meta", "List the org's PM lists, statuses, members, and features — use to resolve names to ids before create/update.", {}, async () => text(await client.meta()));
|
|
23
|
+
server.tool("list_tasks", "List tasks in the org. Optional filters: status_id, list_id.", { status_id: z.string().optional(), list_id: z.string().optional() }, async (a) => text(await client.listTasks(a)));
|
|
24
|
+
server.tool("get_task", "Get one task with its comments and assignees.", { id: z.string() }, async (a) => text(await client.getTask(a.id)));
|
|
25
|
+
server.tool("create_task", "Create a task. Omitting list_id uses the org's first list. feature_id / insight_id link it to the ProductOS spine.", {
|
|
26
|
+
title: z.string(),
|
|
27
|
+
description: z.string().optional(),
|
|
28
|
+
priority: z.enum(["urgent", "high", "normal", "low"]).optional(),
|
|
29
|
+
list_id: z.string().optional(),
|
|
30
|
+
status_id: z.string().optional(),
|
|
31
|
+
feature_id: z.string().optional(),
|
|
32
|
+
insight_id: z.string().optional(),
|
|
33
|
+
assignee_member_ids: z.array(z.string()).optional(),
|
|
34
|
+
}, async (a) => text(await client.createTask(a)));
|
|
35
|
+
server.tool("update_task", "Update a task's fields (status, priority, title, description, feature, insight, assignees).", {
|
|
36
|
+
id: z.string(),
|
|
37
|
+
title: z.string().optional(),
|
|
38
|
+
description: z.string().nullable().optional(),
|
|
39
|
+
priority: z.enum(["urgent", "high", "normal", "low"]).nullable().optional(),
|
|
40
|
+
status_id: z.string().nullable().optional(),
|
|
41
|
+
feature_id: z.string().nullable().optional(),
|
|
42
|
+
insight_id: z.string().nullable().optional(),
|
|
43
|
+
assignee_member_ids: z.array(z.string()).optional(),
|
|
44
|
+
}, async ({ id, ...body }) => text(await client.updateTask(id, body)));
|
|
45
|
+
server.tool("comment_on_task", "Add a comment to a task.", { id: z.string(), body: z.string() }, async (a) => text(await client.comment(a.id, a.body)));
|
|
46
|
+
async function main() {
|
|
47
|
+
await server.connect(new StdioServerTransport());
|
|
48
|
+
}
|
|
49
|
+
main().catch((err) => {
|
|
50
|
+
console.error("[productos-pm] fatal:", err);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aioproductoscom/mcp-pm",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "ProductOS PM Helper — an MCP server to manage ProductOS tickets from Claude Code, Cursor, Codex, and any MCP host.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "AIITDEVELOPMENT DOO Novi Sad",
|
|
8
|
+
"homepage": "https://platform.aioproductos.com",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/businesstalksnetwork/productos.git",
|
|
12
|
+
"directory": "packages/mcps/productos-pm"
|
|
13
|
+
},
|
|
14
|
+
"bugs": { "url": "https://github.com/businesstalksnetwork/productos/issues" },
|
|
15
|
+
"keywords": [
|
|
16
|
+
"mcp",
|
|
17
|
+
"model-context-protocol",
|
|
18
|
+
"productos",
|
|
19
|
+
"claude",
|
|
20
|
+
"cursor",
|
|
21
|
+
"codex",
|
|
22
|
+
"project-management",
|
|
23
|
+
"product-management"
|
|
24
|
+
],
|
|
25
|
+
"bin": { "productos-pm": "dist/index.js" },
|
|
26
|
+
"files": ["dist", "README.md"],
|
|
27
|
+
"engines": { "node": ">=18" },
|
|
28
|
+
"publishConfig": { "access": "public" },
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsc",
|
|
31
|
+
"prepublishOnly": "npm run build",
|
|
32
|
+
"start": "node dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
36
|
+
"zod": "^3.23.8"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.7.0",
|
|
40
|
+
"typescript": "^5.6.0"
|
|
41
|
+
}
|
|
42
|
+
}
|