@aioproductoscom/mcp 0.11.0 → 0.14.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 +13 -0
- package/dist/index.js +32 -1
- package/package.json +17 -6
package/dist/client.js
CHANGED
|
@@ -67,6 +67,19 @@ export class PlatformClient {
|
|
|
67
67
|
customer360(query) {
|
|
68
68
|
return this.req(`/api/me/customer?q=${encodeURIComponent(query)}`);
|
|
69
69
|
}
|
|
70
|
+
nps(opts) {
|
|
71
|
+
const p = new URLSearchParams();
|
|
72
|
+
if (opts.productId)
|
|
73
|
+
p.set("product_id", opts.productId);
|
|
74
|
+
if (opts.windowDays)
|
|
75
|
+
p.set("window_days", String(opts.windowDays));
|
|
76
|
+
const qs = p.toString();
|
|
77
|
+
return this.req(`/api/me/nps${qs ? `?${qs}` : ""}`);
|
|
78
|
+
}
|
|
79
|
+
nrr(windowDays) {
|
|
80
|
+
const qs = windowDays ? `?window_days=${windowDays}` : "";
|
|
81
|
+
return this.req(`/api/me/nrr${qs}`);
|
|
82
|
+
}
|
|
70
83
|
funnel(opts) {
|
|
71
84
|
const p = new URLSearchParams();
|
|
72
85
|
if (opts.productId)
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,12 @@ function text(data) {
|
|
|
18
18
|
],
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/** A prompt result — one user-role message the host injects when the slash
|
|
22
|
+
* command runs. Surfaces as a named slash command in the AI host. */
|
|
23
|
+
function promptMsg(body) {
|
|
24
|
+
return { messages: [{ role: "user", content: { type: "text", text: body } }] };
|
|
25
|
+
}
|
|
26
|
+
const server = new McpServer({ name: "AIOProductOS", version: "0.14.0" }, {
|
|
22
27
|
instructions: "You manage product work on ProductOS for the connected member — board, insights, features, and " +
|
|
23
28
|
"analytics all hang off one spine. Call get_pm_playbook first for how to operate: ground in " +
|
|
24
29
|
"get_product_brain, keep work tied to the spine (insight→feature→task→outcome), and prioritize on " +
|
|
@@ -54,6 +59,8 @@ server.tool("update_task", "Update a task's fields (status, priority, title, des
|
|
|
54
59
|
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)));
|
|
55
60
|
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
61
|
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)));
|
|
62
|
+
server.tool("analyze_nps", "NPS for the product — standard score AND revenue-weighted NPS (each respondent weighted by their account MRR), plus the detractor accounts ranked by what they're worth, with verbatims. The revenue weighting is the spine join no standalone survey tool can compute: it surfaces when your biggest customers are the unhappy ones even if the headline score looks fine. Optional product_id (primary by default) and window_days (default 90).", { product_id: z.string().optional(), window_days: z.number().optional() }, async (a) => text(await client.nps({ productId: a.product_id, windowDays: a.window_days })));
|
|
63
|
+
server.tool("analyze_nrr", "Net Revenue Retention — NRR (revenue-weighted) next to logo retention (count-weighted), the expansion/contraction/churn split, and the accounts that lost the most MRR. The divergence is the point: \"you keep 92% of logos but 78% of revenue\" means a big account churned while the headline looks fine. NRR is the number investors ask for and no standalone analytics tool can compute — it needs revenue on the same record. Optional window_days (default 90). Returns status 'building' until enough daily MRR snapshots exist to compare.", { window_days: z.number().optional() }, async (a) => text(await client.nrr(a.window_days)));
|
|
57
64
|
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
65
|
body: z.string(),
|
|
59
66
|
title: z.string().optional(),
|
|
@@ -85,6 +92,30 @@ server.tool("list_channels", "List the team Comms channels you (this token's mem
|
|
|
85
92
|
server.tool("read_channel", "Read recent messages in a Comms channel you belong to (oldest→newest), so you can catch up before replying. Pass the channel_id from list_channels.", { channel_id: z.string(), limit: z.number().optional() }, async (a) => text(await client.readChannel(a.channel_id, a.limit)));
|
|
86
93
|
server.tool("post_to_channel", "Post a message into a team Comms channel you belong to. It goes out as you (this token's member) and appears live for your teammates — use it to tell the team what you did, share a link, or ask a question. Only works for channels you're a member of (see list_channels).", { channel_id: z.string(), body: z.string() }, async (a) => text(await client.postToChannel(a.channel_id, a.body)));
|
|
87
94
|
server.tool("reply_in_channel", "Reply in a thread under a specific message in a Comms channel you belong to. Pass the parent message's id (from read_channel) as parent_id.", { channel_id: z.string(), parent_id: z.string(), body: z.string() }, async (a) => text(await client.replyInChannel(a.channel_id, a.parent_id, a.body)));
|
|
95
|
+
// ── Routines (slash-command prompts) ────────────────────────────────────────
|
|
96
|
+
// Surface in the AI host as named slash commands (e.g. Claude Code:
|
|
97
|
+
// /productos:standup), so the human — or a scheduled headless run — can run a
|
|
98
|
+
// product routine on demand. Each orchestrates the tools above; they stay
|
|
99
|
+
// read-first and only write what the playbook sanctions.
|
|
100
|
+
server.prompt("standup", "Board standup — what moved, what's blocked, what needs you today (read-only).", async () => promptMsg(`Run the product standup for the connected org. Read-only — don't change anything.\n` +
|
|
101
|
+
`1) pm_meta to resolve lists / statuses / members.\n` +
|
|
102
|
+
`2) list_tasks to read the active board.\n` +
|
|
103
|
+
`Report briefly: what's in progress, what's blocked (and why), what's gone stale with no movement, and the 1–3 ` +
|
|
104
|
+
`decisions that need a human today. Pull get_product_brain if revenue/account context sharpens the call. ` +
|
|
105
|
+
`End with the single most important thing to do next.`));
|
|
106
|
+
server.prompt("triage", "Triage — turn fresh customer signal into prioritized, spine-linked work.", async () => promptMsg(`Run intake triage for the connected org. Follow get_pm_playbook.\n` +
|
|
107
|
+
`1) get_product_brain — ground in revenue, top accounts, recent verbatim signal, and open work.\n` +
|
|
108
|
+
`2) Review the board (list_tasks, pm_meta) and any fresh insights.\n` +
|
|
109
|
+
`3) Prioritize on EVIDENCE — affected accounts + MRR + reach — never an invented score.\n` +
|
|
110
|
+
`4) Make the writes you're confident in: create_task / update_task, linking insight→feature→task; ` +
|
|
111
|
+
`assign owners; place the clear ones in the current sprint. SURFACE the judgment calls for a human instead of guessing.\n` +
|
|
112
|
+
`Confirm only the writes you actually made.`));
|
|
113
|
+
server.prompt("daily", "Daily PM briefing — your plate, blockers, and the one thing to do next.", async () => promptMsg(`Give the connected member their daily briefing.\n` +
|
|
114
|
+
`1) get_product_brain for context.\n` +
|
|
115
|
+
`2) list_tasks + pm_meta — what's assigned to them, what's blocked, what's overdue or stale.\n` +
|
|
116
|
+
`3) Scan the support inbox (list_conversations) and upcoming calls (list_bookings) for anything urgent.\n` +
|
|
117
|
+
`Output a tight briefing: top priorities today, blockers needing a decision, and ONE recommended next action. ` +
|
|
118
|
+
`Read-only unless they ask you to act.`));
|
|
88
119
|
async function main() {
|
|
89
120
|
await server.connect(new StdioServerTransport());
|
|
90
121
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aioproductoscom/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.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",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"url": "git+https://github.com/businesstalksnetwork/productos.git",
|
|
12
12
|
"directory": "packages/mcps/productos-pm"
|
|
13
13
|
},
|
|
14
|
-
"bugs": {
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/businesstalksnetwork/productos/issues"
|
|
16
|
+
},
|
|
15
17
|
"keywords": [
|
|
16
18
|
"mcp",
|
|
17
19
|
"model-context-protocol",
|
|
@@ -22,10 +24,19 @@
|
|
|
22
24
|
"project-management",
|
|
23
25
|
"product-management"
|
|
24
26
|
],
|
|
25
|
-
"bin": {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
"
|
|
27
|
+
"bin": {
|
|
28
|
+
"productos": "dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
29
40
|
"scripts": {
|
|
30
41
|
"build": "tsc",
|
|
31
42
|
"prepublishOnly": "npm run build",
|