@aioproductoscom/mcp 0.15.9 → 0.16.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 +57 -28
- package/dist/client.d.ts +16 -0
- package/dist/client.js +30 -198
- package/dist/index.d.ts +2 -0
- package/dist/index.js +19 -269
- package/dist/registry.d.ts +63 -0
- package/dist/registry.js +1074 -0
- package/dist/server.d.ts +20 -0
- package/dist/server.js +128 -0
- package/dist/ui-views.d.ts +15 -0
- package/dist/ui-views.js +71 -0
- package/dist/update-notifier.d.ts +6 -0
- package/dist/update-notifier.js +1 -1
- package/package.json +15 -2
- package/dist/pm-skill.js +0 -40
package/README.md
CHANGED
|
@@ -41,39 +41,68 @@ at a self-hosted platform.
|
|
|
41
41
|
### Prefer no local install? Use the hosted remote
|
|
42
42
|
|
|
43
43
|
AIOProductOS is also a **hosted remote MCP** (OAuth 2.1 sign-in, no token
|
|
44
|
-
pasting,
|
|
44
|
+
pasting, the same 71 tools) — add the custom connector URL
|
|
45
45
|
`https://platform.aioproductos.com/api/mcp` in Claude.ai, Claude Code, or any
|
|
46
46
|
remote-capable MCP host. Listed on the
|
|
47
47
|
[official MCP Registry](https://registry.modelcontextprotocol.io) as
|
|
48
48
|
`com.aioproductos/mcp` and [verified on Glama](https://glama.ai/mcp/connectors/com.aioproductos/mcp).
|
|
49
49
|
|
|
50
|
-
## Tools
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
50
|
+
## Tools — 71
|
|
51
|
+
|
|
52
|
+
It reads **and** writes across the whole strategy-to-delivery ladder, so an
|
|
53
|
+
assistant can run product work end to end instead of reporting on it. Identical
|
|
54
|
+
tool set to the hosted remote connector.
|
|
55
|
+
|
|
56
|
+
**Orientation** — `whoami` (org + member), `get_pm_playbook` (how to operate),
|
|
57
|
+
`pm_meta` (lists / statuses / members / features — resolve names → ids)
|
|
58
|
+
|
|
59
|
+
**Grounding** — `get_product_brain` (revenue, accounts, analytics, features,
|
|
60
|
+
signals, open work in one read), `get_weekly_signal_memo`, `get_customer_360`
|
|
61
|
+
(money + people + voice for one account), `get_codebase_map`
|
|
62
|
+
|
|
63
|
+
**Discovery — ideas** — `list_ideas`, `create_idea`, `update_idea`, `vote_idea`,
|
|
64
|
+
`promote_idea` (idea → feature)
|
|
65
|
+
|
|
66
|
+
**Strategy — initiatives & OKRs** — `list_initiatives`, `create_initiative`,
|
|
67
|
+
`update_initiative`, `list_objectives`, `create_objective`, `update_objective`,
|
|
68
|
+
`update_key_result`
|
|
69
|
+
|
|
70
|
+
**Roadmap** — `list_features`, `create_feature`, `update_feature`,
|
|
71
|
+
`list_releases`, `create_release`, `update_release`, `get_roadmap_drift`
|
|
72
|
+
(planned vs shipped, scored)
|
|
73
|
+
|
|
74
|
+
**Delivery — board & sprints** — `list_tasks`, `get_task`, `create_task`,
|
|
75
|
+
`update_task`, `delete_task`, `comment_on_task`, `list_sprints`, `create_sprint`,
|
|
76
|
+
`update_sprint`
|
|
77
|
+
|
|
78
|
+
**Docs & decisions** — `list_pages`, `get_page`, `create_page`, `update_page`,
|
|
79
|
+
`list_decisions`, `create_decision`, `update_decision`
|
|
80
|
+
|
|
81
|
+
**Experiments** — `list_experiments`, `create_experiment`, `update_experiment`
|
|
82
|
+
|
|
83
|
+
**Voice of customer** — `list_insights`, `capture_insight`
|
|
84
|
+
|
|
85
|
+
**Analytics** — `analyze_nps`, `analyze_nrr` (both revenue-weighted),
|
|
86
|
+
`analyze_funnel`, `get_retention`, `analyze_paths`
|
|
87
|
+
|
|
88
|
+
**Support inbox** — `list_conversations`, `get_conversation`,
|
|
89
|
+
`reply_to_conversation` (**visible in the visitor's chat widget**), `add_note`
|
|
90
|
+
(internal only), `resolve_conversation`
|
|
91
|
+
|
|
92
|
+
**Scheduling** — `list_bookings`, `cancel_booking`, `reschedule_booking`
|
|
93
|
+
|
|
94
|
+
**Team comms** — `list_channels`, `read_channel`, `post_to_channel`,
|
|
95
|
+
`reply_in_channel`
|
|
96
|
+
|
|
97
|
+
**Identity graph** — `get_device_candidates`, `merge_end_users`,
|
|
98
|
+
`unmerge_end_users` (reversible 30 days), `list_identity_merges`
|
|
99
|
+
|
|
100
|
+
**AI artifacts** — `review_artifact` (critic vs your PM bar),
|
|
101
|
+
`list_artifact_versions`, `revert_to_version`
|
|
102
|
+
|
|
103
|
+
Every tool declares read-only / write / destructive hints, so a host can gate
|
|
104
|
+
them. `delete_task` is the only destructive one. Writes are scoped to your org
|
|
105
|
+
and respect your role's permissions — the token can never reach another tenant.
|
|
77
106
|
|
|
78
107
|
> For an assignable **coding teammate** (Backend / Frontend / Mobile / QA that
|
|
79
108
|
> opens PRs in your repo), install the separate
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RestCall } from "./registry.js";
|
|
2
|
+
export interface RestResult {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
status: number;
|
|
5
|
+
/** Raw response body. Passed through verbatim — same contract as the hosted
|
|
6
|
+
* dispatcher, and it sidesteps the old failure mode where a non-JSON error
|
|
7
|
+
* page (a 502 from the edge) crashed JSON.parse with "Unexpected token '<'"
|
|
8
|
+
* instead of reporting what actually happened. */
|
|
9
|
+
text: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class PlatformClient {
|
|
12
|
+
private readonly baseUrl;
|
|
13
|
+
private readonly token;
|
|
14
|
+
constructor(baseUrl: string, token: string);
|
|
15
|
+
request(call: RestCall): Promise<RestResult>;
|
|
16
|
+
}
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
1
|
+
// One hardened HTTP executor over the platform's token-authed REST endpoints.
|
|
2
|
+
// The registry's RestCall describes WHAT to call; this describes HOW — the PAT
|
|
3
|
+
// header, a timeout, and honest failure text. The PAT is the only credential;
|
|
4
|
+
// nothing is stored.
|
|
5
|
+
//
|
|
6
|
+
// This file used to be 70 near-identical methods (one per tool). The shared
|
|
7
|
+
// registry made them redundant: every tool already declares its own
|
|
8
|
+
// method/path/body, so the client collapses to a single request function.
|
|
9
|
+
/** A hung platform request must fail the TOOL CALL, not hang the agent's turn
|
|
10
|
+
* forever — 30s is far beyond any healthy endpoint here. */
|
|
11
|
+
const TIMEOUT_MS = 30_000;
|
|
3
12
|
export class PlatformClient {
|
|
4
13
|
baseUrl;
|
|
5
14
|
token;
|
|
@@ -7,202 +16,25 @@ export class PlatformClient {
|
|
|
7
16
|
this.baseUrl = baseUrl;
|
|
8
17
|
this.token = token;
|
|
9
18
|
}
|
|
10
|
-
async
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
async request(call) {
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(`${this.baseUrl}${call.path}`, {
|
|
22
|
+
method: call.method,
|
|
23
|
+
headers: {
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
Authorization: `Bearer ${this.token}`,
|
|
26
|
+
},
|
|
27
|
+
body: call.body !== undefined ? JSON.stringify(call.body) : undefined,
|
|
28
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
29
|
+
});
|
|
30
|
+
return { ok: res.ok, status: res.status, text: await res.text() };
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
// Transport-level failure (offline, DNS, timeout) — return it as an
|
|
34
|
+
// honest error result rather than throwing, so the tool result tells the
|
|
35
|
+
// model what happened instead of surfacing a stack trace.
|
|
36
|
+
const why = e instanceof Error && e.name === "TimeoutError" ? `platform did not respond within ${TIMEOUT_MS / 1000}s` : String(e);
|
|
37
|
+
return { ok: false, status: 0, text: `Platform unreachable: ${why}` };
|
|
27
38
|
}
|
|
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
|
-
createFeature(body) {
|
|
52
|
-
return this.req("/api/me/features", { method: "POST", body: JSON.stringify(body) });
|
|
53
|
-
}
|
|
54
|
-
createObjective(body) {
|
|
55
|
-
return this.req("/api/me/objectives", { method: "POST", body: JSON.stringify(body) });
|
|
56
|
-
}
|
|
57
|
-
createSprint(body) {
|
|
58
|
-
return this.req("/api/me/sprints", { method: "POST", body: JSON.stringify(body) });
|
|
59
|
-
}
|
|
60
|
-
createPage(body) {
|
|
61
|
-
return this.req("/api/me/pages", { method: "POST", body: JSON.stringify(body) });
|
|
62
|
-
}
|
|
63
|
-
updateFeature(body) {
|
|
64
|
-
return this.req("/api/me/features", { method: "PATCH", body: JSON.stringify(body) });
|
|
65
|
-
}
|
|
66
|
-
updateObjective(body) {
|
|
67
|
-
return this.req("/api/me/objectives", { method: "PATCH", body: JSON.stringify(body) });
|
|
68
|
-
}
|
|
69
|
-
updateKeyResult(body) {
|
|
70
|
-
return this.req("/api/me/key-results", { method: "PATCH", body: JSON.stringify(body) });
|
|
71
|
-
}
|
|
72
|
-
updateSprint(body) {
|
|
73
|
-
return this.req("/api/me/sprints", { method: "PATCH", body: JSON.stringify(body) });
|
|
74
|
-
}
|
|
75
|
-
updatePage(body) {
|
|
76
|
-
return this.req("/api/me/pages", { method: "PATCH", body: JSON.stringify(body) });
|
|
77
|
-
}
|
|
78
|
-
createRelease(body) {
|
|
79
|
-
return this.req("/api/me/releases", { method: "POST", body: JSON.stringify(body) });
|
|
80
|
-
}
|
|
81
|
-
updateRelease(body) {
|
|
82
|
-
return this.req("/api/me/releases", { method: "PATCH", body: JSON.stringify(body) });
|
|
83
|
-
}
|
|
84
|
-
createExperiment(body) {
|
|
85
|
-
return this.req("/api/me/experiments", { method: "POST", body: JSON.stringify(body) });
|
|
86
|
-
}
|
|
87
|
-
updateExperiment(body) {
|
|
88
|
-
return this.req("/api/me/experiments", { method: "PATCH", body: JSON.stringify(body) });
|
|
89
|
-
}
|
|
90
|
-
listDecisions(status) {
|
|
91
|
-
return this.req(`/api/me/decisions${status ? `?status=${encodeURIComponent(status)}` : ""}`);
|
|
92
|
-
}
|
|
93
|
-
createDecision(body) {
|
|
94
|
-
return this.req("/api/me/decisions", { method: "POST", body: JSON.stringify(body) });
|
|
95
|
-
}
|
|
96
|
-
updateDecision(body) {
|
|
97
|
-
return this.req("/api/me/decisions", { method: "PATCH", body: JSON.stringify(body) });
|
|
98
|
-
}
|
|
99
|
-
updateTask(id, body) {
|
|
100
|
-
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}`, { method: "PATCH", body: JSON.stringify(body) });
|
|
101
|
-
}
|
|
102
|
-
deleteTask(id) {
|
|
103
|
-
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}`, { method: "DELETE" });
|
|
104
|
-
}
|
|
105
|
-
comment(id, body) {
|
|
106
|
-
return this.req(`/api/pm/tasks/${encodeURIComponent(id)}/comments`, {
|
|
107
|
-
method: "POST",
|
|
108
|
-
body: JSON.stringify({ body }),
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
brain(productId) {
|
|
112
|
-
const qs = productId ? `?product_id=${encodeURIComponent(productId)}` : "";
|
|
113
|
-
return this.req(`/api/me/brain${qs}`);
|
|
114
|
-
}
|
|
115
|
-
captureInsight(body) {
|
|
116
|
-
return this.req("/api/me/insight", { method: "POST", body: JSON.stringify(body) });
|
|
117
|
-
}
|
|
118
|
-
customer360(query) {
|
|
119
|
-
return this.req(`/api/me/customer?q=${encodeURIComponent(query)}`);
|
|
120
|
-
}
|
|
121
|
-
nps(opts) {
|
|
122
|
-
const p = new URLSearchParams();
|
|
123
|
-
if (opts.productId)
|
|
124
|
-
p.set("product_id", opts.productId);
|
|
125
|
-
if (opts.windowDays)
|
|
126
|
-
p.set("window_days", String(opts.windowDays));
|
|
127
|
-
const qs = p.toString();
|
|
128
|
-
return this.req(`/api/me/nps${qs ? `?${qs}` : ""}`);
|
|
129
|
-
}
|
|
130
|
-
nrr(windowDays) {
|
|
131
|
-
const qs = windowDays ? `?window_days=${windowDays}` : "";
|
|
132
|
-
return this.req(`/api/me/nrr${qs}`);
|
|
133
|
-
}
|
|
134
|
-
funnel(opts) {
|
|
135
|
-
const p = new URLSearchParams();
|
|
136
|
-
if (opts.productId)
|
|
137
|
-
p.set("product_id", opts.productId);
|
|
138
|
-
if (opts.window)
|
|
139
|
-
p.set("window", String(opts.window));
|
|
140
|
-
for (const s of opts.steps ?? [])
|
|
141
|
-
p.append("step", s);
|
|
142
|
-
const qs = p.toString();
|
|
143
|
-
return this.req(`/api/me/funnel${qs ? `?${qs}` : ""}`);
|
|
144
|
-
}
|
|
145
|
-
retention(opts) {
|
|
146
|
-
const p = new URLSearchParams();
|
|
147
|
-
if (opts.productId)
|
|
148
|
-
p.set("product_id", opts.productId);
|
|
149
|
-
if (opts.window)
|
|
150
|
-
p.set("window", String(opts.window));
|
|
151
|
-
const qs = p.toString();
|
|
152
|
-
return this.req(`/api/me/retention${qs ? `?${qs}` : ""}`);
|
|
153
|
-
}
|
|
154
|
-
paths(opts) {
|
|
155
|
-
const p = new URLSearchParams();
|
|
156
|
-
if (opts.productId)
|
|
157
|
-
p.set("product_id", opts.productId);
|
|
158
|
-
if (opts.window)
|
|
159
|
-
p.set("window", String(opts.window));
|
|
160
|
-
if (opts.start)
|
|
161
|
-
p.set("start", opts.start);
|
|
162
|
-
const qs = p.toString();
|
|
163
|
-
return this.req(`/api/me/paths${qs ? `?${qs}` : ""}`);
|
|
164
|
-
}
|
|
165
|
-
listConversations(opts) {
|
|
166
|
-
const p = new URLSearchParams();
|
|
167
|
-
if (opts.productId)
|
|
168
|
-
p.set("product_id", opts.productId);
|
|
169
|
-
if (opts.status)
|
|
170
|
-
p.set("status", opts.status);
|
|
171
|
-
const qs = p.toString();
|
|
172
|
-
return this.req(`/api/me/inbox${qs ? `?${qs}` : ""}`);
|
|
173
|
-
}
|
|
174
|
-
getConversation(id) {
|
|
175
|
-
return this.req(`/api/me/inbox?conversation_id=${encodeURIComponent(id)}`);
|
|
176
|
-
}
|
|
177
|
-
inboxAction(body) {
|
|
178
|
-
return this.req("/api/me/inbox", { method: "POST", body: JSON.stringify(body) });
|
|
179
|
-
}
|
|
180
|
-
listBookings(opts) {
|
|
181
|
-
const qs = opts.include === "all" ? "?include=all" : "";
|
|
182
|
-
return this.req(`/api/me/scheduling${qs}`);
|
|
183
|
-
}
|
|
184
|
-
schedulingAction(body) {
|
|
185
|
-
return this.req("/api/me/scheduling", { method: "POST", body: JSON.stringify(body) });
|
|
186
|
-
}
|
|
187
|
-
listChannels() {
|
|
188
|
-
return this.req("/api/me/comms");
|
|
189
|
-
}
|
|
190
|
-
readChannel(channelId, limit) {
|
|
191
|
-
const p = new URLSearchParams({ channel_id: channelId });
|
|
192
|
-
if (limit)
|
|
193
|
-
p.set("limit", String(limit));
|
|
194
|
-
return this.req(`/api/me/comms?${p.toString()}`);
|
|
195
|
-
}
|
|
196
|
-
postToChannel(channelId, body) {
|
|
197
|
-
return this.req("/api/me/comms", {
|
|
198
|
-
method: "POST",
|
|
199
|
-
body: JSON.stringify({ action: "post", channel_id: channelId, body }),
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
replyInChannel(channelId, parentId, body) {
|
|
203
|
-
return this.req("/api/me/comms", {
|
|
204
|
-
method: "POST",
|
|
205
|
-
body: JSON.stringify({ action: "reply", channel_id: channelId, parent_id: parentId, body }),
|
|
206
|
-
});
|
|
207
39
|
}
|
|
208
40
|
}
|
package/dist/index.d.ts
ADDED