jujugrowth-mcp 1.0.8 → 1.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/package.json +2 -2
- package/server.mjs +63 -16
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jujugrowth-mcp",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "MCP server connecting your AI coding assistant (Claude, Cursor, Codex) to jujugrowth — pull recommendations and apply them in your repo.",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "MCP server connecting your AI coding assistant (Claude, Cursor, Codex) to jujugrowth — pull your living marketing plan's dev tasks + recommendations and apply them in your repo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"jujugrowth-mcp": "server.mjs"
|
package/server.mjs
CHANGED
|
@@ -58,12 +58,35 @@ const TOOLS = [
|
|
|
58
58
|
inputSchema: {
|
|
59
59
|
type: "object",
|
|
60
60
|
properties: {
|
|
61
|
-
tenantId: {
|
|
61
|
+
tenantId: {
|
|
62
|
+
type: "string",
|
|
63
|
+
description: "the site to list (optional; required when more than one site is connected)",
|
|
64
|
+
},
|
|
62
65
|
},
|
|
63
66
|
additionalProperties: false,
|
|
64
67
|
},
|
|
65
68
|
run: async (a) =>
|
|
66
|
-
call(
|
|
69
|
+
call(
|
|
70
|
+
"GET",
|
|
71
|
+
`/recommendations${a.tenantId ? `?tenantId=${encodeURIComponent(a.tenantId)}` : ""}`,
|
|
72
|
+
),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "list_plan_tasks",
|
|
76
|
+
description:
|
|
77
|
+
"List the dev-AI tasks from this site's LIVING MARKETING PLAN — the roadmap steps the plan assigned to the developer AI (changes to the business's own site/app/code; jujugrowth executes the 'autonomous' marketing steps itself). Each task has a ready-to-run `prompt`, plus why it matters, its priority, and the plan's north-star + strategy for context. This is your growth to-do list for the business: pull it, implement the highest-priority tasks in the repo (verify current state first; show a diff/PR), and the next plan regeneration reflects what you shipped via its evidence-grounded status. Pass `tenantId` to target a site; if more than one is connected and you omit it you get {needsSite:true,...} — ASK the user which site, then call again. Never pick a site for them.",
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: "object",
|
|
80
|
+
properties: {
|
|
81
|
+
tenantId: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: "the site (optional; required when more than one site is connected)",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
additionalProperties: false,
|
|
87
|
+
},
|
|
88
|
+
run: async (a) =>
|
|
89
|
+
call("GET", `/plan-tasks${a.tenantId ? `?tenantId=${encodeURIComponent(a.tenantId)}` : ""}`),
|
|
67
90
|
},
|
|
68
91
|
{
|
|
69
92
|
name: "get_recommendation",
|
|
@@ -92,7 +115,11 @@ const TOOLS = [
|
|
|
92
115
|
additionalProperties: false,
|
|
93
116
|
},
|
|
94
117
|
run: async (a) =>
|
|
95
|
-
call(
|
|
118
|
+
call(
|
|
119
|
+
"POST",
|
|
120
|
+
`/recommendations/${a.tenantId}/${a.recId}/handled`,
|
|
121
|
+
a.note ? { note: a.note } : undefined,
|
|
122
|
+
),
|
|
96
123
|
},
|
|
97
124
|
{
|
|
98
125
|
name: "request_site_advice",
|
|
@@ -106,6 +133,13 @@ const TOOLS = [
|
|
|
106
133
|
},
|
|
107
134
|
run: async (a) => call("POST", `/site-advice/${a.tenantId}`),
|
|
108
135
|
},
|
|
136
|
+
{
|
|
137
|
+
name: "list_capability_gaps",
|
|
138
|
+
description:
|
|
139
|
+
"ADMIN/OPERATOR TOKEN ONLY. The jujugrowth system's OWN dev backlog: platform rules it has LEARNED (from docs + real platform responses) but cannot yet check because no collector observes the property the rule needs. Each item names the generic tool to build — `subject.attribute` per platform — with an example rule. Build that collector in the jujugrowth repo (make it write the property as a flat dotted attribute key on the synced entity); the gap auto-resolves once the fact is observable. The list is generated from real learned rules, never a hardcoded checklist. Returns 403 for non-admin tokens.",
|
|
140
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
141
|
+
run: async () => call("GET", "/capability-gaps"),
|
|
142
|
+
},
|
|
109
143
|
];
|
|
110
144
|
|
|
111
145
|
function send(msg) {
|
|
@@ -129,13 +163,16 @@ async function handle(msg) {
|
|
|
129
163
|
// Workflow the AI should follow unprompted (so the human never has to
|
|
130
164
|
// say "now mark it handled"). Surfaced to the model by MCP clients.
|
|
131
165
|
instructions:
|
|
132
|
-
`This server
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
166
|
+
`This server connects ${SITE_LABEL ? `the site "${SITE_LABEL}"` : "the user's site(s)"} growth work to your coding. Two task sources:\n` +
|
|
167
|
+
"• list_plan_tasks — the dev-AI steps of the business's LIVING MARKETING PLAN (the strategic roadmap). Start here: these are the prioritized site/app/code changes the growth plan needs (each has a ready-to-run prompt + the north-star for context).\n" +
|
|
168
|
+
"• list_recommendations — individual one-off growth recommendations.\n" +
|
|
169
|
+
"Standard loop:\n" +
|
|
170
|
+
"1) list_plan_tasks (and/or list_recommendations).\n" +
|
|
171
|
+
"2) get_recommendation for any rec you'll act on.\n" +
|
|
172
|
+
"3) Before changing anything, VERIFY current-state claims against the actual code — they can be stale; skip what's already done.\n" +
|
|
173
|
+
"4) Implement in the user's repo, scoped to wording/structure/SEO/tracking the plan asks for. Show a diff / open a PR for the user to review.\n" +
|
|
174
|
+
"5) ALWAYS call mark_recommendation_handled with a short note once a recommendation is shipped — don't wait to be told. (Plan-task progress is reflected automatically by the next plan regeneration's evidence-grounded status.)\n" +
|
|
175
|
+
"Campaigns and ad budgets are NOT here — those are owner decisions the jujugrowth system runs itself. This server is bound to ONE site; never act on another.",
|
|
139
176
|
},
|
|
140
177
|
});
|
|
141
178
|
}
|
|
@@ -144,14 +181,22 @@ async function handle(msg) {
|
|
|
144
181
|
return send({
|
|
145
182
|
jsonrpc: "2.0",
|
|
146
183
|
id,
|
|
147
|
-
result: {
|
|
184
|
+
result: {
|
|
185
|
+
tools: TOOLS.map(({ name, description, inputSchema }) => ({
|
|
186
|
+
name,
|
|
187
|
+
description,
|
|
188
|
+
inputSchema,
|
|
189
|
+
})),
|
|
190
|
+
},
|
|
148
191
|
});
|
|
149
192
|
}
|
|
150
193
|
if (method === "tools/call") {
|
|
151
194
|
const tool = TOOLS.find((t) => t.name === params?.name);
|
|
152
|
-
if (!tool)
|
|
195
|
+
if (!tool)
|
|
196
|
+
return send({ jsonrpc: "2.0", id, error: { code: -32601, message: "unknown tool" } });
|
|
153
197
|
try {
|
|
154
|
-
if (!TOKEN)
|
|
198
|
+
if (!TOKEN)
|
|
199
|
+
throw new Error("JUJUGROWTH_TOKEN not set — generate one in Settings → Developer");
|
|
155
200
|
const result = await tool.run(params.arguments ?? {});
|
|
156
201
|
return send({
|
|
157
202
|
jsonrpc: "2.0",
|
|
@@ -168,17 +213,19 @@ async function handle(msg) {
|
|
|
168
213
|
});
|
|
169
214
|
}
|
|
170
215
|
}
|
|
171
|
-
if (id !== undefined)
|
|
216
|
+
if (id !== undefined)
|
|
217
|
+
send({ jsonrpc: "2.0", id, error: { code: -32601, message: "method not found" } });
|
|
172
218
|
}
|
|
173
219
|
|
|
174
220
|
let buf = "";
|
|
175
221
|
process.stdin.setEncoding("utf8");
|
|
176
222
|
process.stdin.on("data", (chunk) => {
|
|
177
223
|
buf += chunk;
|
|
178
|
-
let nl;
|
|
179
|
-
while (
|
|
224
|
+
let nl = buf.indexOf("\n");
|
|
225
|
+
while (nl >= 0) {
|
|
180
226
|
const line = buf.slice(0, nl).trim();
|
|
181
227
|
buf = buf.slice(nl + 1);
|
|
182
228
|
if (line) handle(JSON.parse(line)).catch((e) => process.stderr.write(`${e}\n`));
|
|
229
|
+
nl = buf.indexOf("\n");
|
|
183
230
|
}
|
|
184
231
|
});
|