@aioproductoscom/mcp 0.9.0 → 0.11.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/client.js CHANGED
@@ -61,6 +61,12 @@ export class PlatformClient {
61
61
  const qs = productId ? `?product_id=${encodeURIComponent(productId)}` : "";
62
62
  return this.req(`/api/me/brain${qs}`);
63
63
  }
64
+ captureInsight(body) {
65
+ return this.req("/api/me/insight", { method: "POST", body: JSON.stringify(body) });
66
+ }
67
+ customer360(query) {
68
+ return this.req(`/api/me/customer?q=${encodeURIComponent(query)}`);
69
+ }
64
70
  funnel(opts) {
65
71
  const p = new URLSearchParams();
66
72
  if (opts.productId)
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
4
  import { z } from "zod";
5
5
  import { PlatformClient } from "./client.js";
6
+ import { PM_PLAYBOOK, PM_SKILL_VERSION } from "./pm-skill.js";
6
7
  const token = process.env.PRODUCTOS_TOKEN;
7
8
  const baseUrl = (process.env.PRODUCTOS_URL ?? "https://platform.aioproductos.com").replace(/\/$/, "");
8
9
  if (!token) {
@@ -17,15 +18,16 @@ function text(data) {
17
18
  ],
18
19
  };
19
20
  }
20
- const server = new McpServer({ name: "productos", version: "0.9.0" }, {
21
- instructions: "You manage ProductOS PM tickets on behalf of the connected member — the board is the team's " +
22
- "source of truth, so work precisely. Resolve names to ids with pm_meta before create_task / " +
23
- "update_task; never guess an id. Link a task to the spine (feature_id / insight_id) whenever the " +
24
- "context makes the connection real, so the work traces back to the why. Keep titles and " +
25
- "descriptions concrete and scoped. Confirm what you changed in plain language, and claim only " +
26
- "writes you actually made.",
21
+ const server = new McpServer({ name: "productos", version: "0.11.0" }, {
22
+ instructions: "You manage product work on ProductOS for the connected member — board, insights, features, and " +
23
+ "analytics all hang off one spine. Call get_pm_playbook first for how to operate: ground in " +
24
+ "get_product_brain, keep work tied to the spine (insight→feature→task→outcome), and prioritize on " +
25
+ "evidence (affected accounts + MRR + reach), never an invented score. Resolve names to ids with " +
26
+ "pm_meta before create_task / update_task; never guess an id. Confirm what you changed in plain " +
27
+ "language, and claim only writes you actually made.",
27
28
  });
28
29
  server.tool("whoami", "Show the connected ProductOS identity (org, member).", {}, async () => text(await client.whoami()));
30
+ server.tool("get_pm_playbook", "How to operate as a product manager on ProductOS — ground in the brain, keep work tied to the spine (insight→feature→task→outcome), and prioritize on evidence (affected accounts + MRR + reach), never an invented score. Read this before planning or prioritizing.", {}, async () => text(`${PM_PLAYBOOK}\n\n— pm playbook v${PM_SKILL_VERSION}`));
29
31
  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()));
30
32
  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)));
31
33
  server.tool("get_task", "Get one task with its comments and assignees.", { id: z.string() }, async (a) => text(await client.getTask(a.id)));
@@ -51,6 +53,15 @@ server.tool("update_task", "Update a task's fields (status, priority, title, des
51
53
  }, async ({ id, ...body }) => text(await client.updateTask(id, body)));
52
54
  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)));
53
55
  server.tool("get_product_brain", "Read a grounded, read-only snapshot of the org's product so YOU can reason about it on your own model: revenue + top paying accounts, web + product analytics, features, recent verbatim customer signals, and open work. Optional product_id; omit for the primary product. Returns the brain text plus the list of products you can ask about.", { product_id: z.string().optional() }, async (a) => text(await client.brain(a.product_id)));
56
+ server.tool("get_customer_360", "Everything about ONE customer, resolved by id, email, domain, or company name: profile, subscription + MRR, how many users sit under the account, and their verbatim feedback. The money + people + voice join on one record — use it before answering anything about a specific account.", { query: z.string() }, async (a) => text(await client.customer360(a.query)));
57
+ server.tool("capture_insight", "Write a piece of customer feedback to the spine (the agent's own hand, not just reading). It fires the same insight.created webhook a manual capture does. Tie it to the account it's about (account_id) and the feature it concerns (feature_id) when you know them; kind='opportunity' for a prioritizable ask. Defaults to the primary product.", {
58
+ body: z.string(),
59
+ title: z.string().optional(),
60
+ kind: z.enum(["insight", "opportunity"]).optional(),
61
+ product_id: z.string().optional(),
62
+ feature_id: z.string().optional(),
63
+ account_id: z.string().optional(),
64
+ }, async (a) => text(await client.captureInsight(a)));
54
65
  server.tool("analyze_funnel", "Build a conversion funnel from the product's own events and reason over it on your model. Pass `steps` as an ordered list of event names (2+) to compute the funnel: distinct users per step, conversion, and drop-off. Omit `steps` to get the menu of available event names first. Optional product_id (defaults to the primary product) and window_days (default 30).", {
55
66
  steps: z.array(z.string()).optional(),
56
67
  product_id: z.string().optional(),
@@ -0,0 +1,40 @@
1
+ // The PM operating playbook — OUR product logic, handed to the customer's PM-side
2
+ // agent via get_pm_playbook() so it works like a great PM grounded in ProductOS,
3
+ // not a generic ticket bot. The server's `instructions` points at it; the agent
4
+ // pulls the full doctrine on demand (keeps it out of every session's context).
5
+ //
6
+ // Bump PM_SKILL_VERSION on any behavioural change so a run traces to a playbook.
7
+ export const PM_SKILL_VERSION = "0.1.0";
8
+ export const PM_PLAYBOOK = `# ProductOS · Product Manager
9
+
10
+ You operate the product on ProductOS for the connected member. The board, insights, features, and
11
+ analytics all hang off one spine — your job is to keep work tied to the real why and to decide from evidence.
12
+
13
+ ## Ground yourself first
14
+ Call \`get_product_brain\` before you reason about priorities: a live, read-only snapshot of THIS product —
15
+ revenue and top accounts, web + product analytics, features, recent verbatim customer signals, and the open
16
+ work. Decide from it, not from memory. Use \`pm_meta\` to resolve names → ids before any write; never guess an id.
17
+
18
+ ## The spine logic — keep everything connected
19
+ Customer signal (insight) → the bet (feature) → the work (task) → the result (outcome). Don't create orphans:
20
+ - A task that exists because a customer asked → link its \`insight_id\`. Because it serves a bet → link its \`feature_id\`.
21
+ - When you capture feedback, tie it to the account and the feature it's about, in the customer's own words —
22
+ a specific verbatim beats your paraphrase.
23
+ - Work with no why is the thing to question, not to dutifully schedule.
24
+
25
+ ## Prioritize on evidence — never on a score you invented
26
+ This org has its OWN prioritization framework (RICE, ICE, MoSCoW, WSJF, value/effort, Kano — whatever they set).
27
+ Do NOT assume one, and do NOT compute the score yourself. Attach the EVIDENCE and let their framework do the math:
28
+ - Which accounts are affected, and the MRR behind them (the revenue weight).
29
+ - The reach/usage from analytics (\`analyze_funnel\`, \`get_retention\`, \`analyze_paths\`, the brain).
30
+ - The customer signals that back it.
31
+ Hand work off as an opportunity with that evidence attached — a human (or their configured model) does the ranking.
32
+ You ground the call; you never fake the number.
33
+
34
+ ## Decide from data, not vibes
35
+ Before you assert a priority or a problem, pull the number — funnel drop, retention curve, the path users take,
36
+ the MRR behind a segment — and quote it. "Signups drop 38% on the company-size field" beats "the form feels long".
37
+
38
+ ## Output discipline
39
+ Concise and honest. Confirm only the writes you actually made, in plain language. Resolve ids via \`pm_meta\`.
40
+ Keep titles and descriptions concrete and scoped — a task a teammate can pick up cold.`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aioproductoscom/mcp",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "description": "ProductOS MCP — manage tickets, read your product brain + analytics, and run the support inbox from Claude Code, Cursor, Codex, and any MCP host.",
5
5
  "type": "module",
6
6
  "license": "MIT",