@scaleberryai/claude 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 +49 -0
- package/bin/cli.js +208 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @scaleberryai/claude
|
|
2
|
+
|
|
3
|
+
One-command installer that connects [ScaleBerry AI](https://www.scaleberryai.com) to [Claude Code](https://docs.claude.com/claude-code) as an MCP server.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @scaleberryai/claude
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
That's it. The CLI will:
|
|
12
|
+
|
|
13
|
+
1. Open a ScaleBerry authorization page in your browser.
|
|
14
|
+
2. Wait for you to sign in and click **Authorize**.
|
|
15
|
+
3. Securely receive a personal MCP token.
|
|
16
|
+
4. Run `claude mcp add` automatically to register the **scaleberry** MCP server in Claude Code.
|
|
17
|
+
5. Print a success message.
|
|
18
|
+
|
|
19
|
+
Restart Claude Code, then ask it to use ScaleBerry — for example:
|
|
20
|
+
|
|
21
|
+
> "List my ScaleBerry products and draft an ad for the best one."
|
|
22
|
+
|
|
23
|
+
## What Claude Code can do once connected
|
|
24
|
+
|
|
25
|
+
Available MCP tools:
|
|
26
|
+
|
|
27
|
+
- `ping`
|
|
28
|
+
- `get_brand_profile`
|
|
29
|
+
- `list_products`
|
|
30
|
+
- `list_creative_templates`
|
|
31
|
+
- `list_my_content`
|
|
32
|
+
- `create_ad_copy_draft`
|
|
33
|
+
- `create_creative_brief_draft`
|
|
34
|
+
|
|
35
|
+
It **cannot** publish ads, spend credits, or modify billing. Revoke access any time from the [Claude Integration page](https://www.scaleberryai.com/dashboard/claude).
|
|
36
|
+
|
|
37
|
+
## Requirements
|
|
38
|
+
|
|
39
|
+
- Node.js 18+
|
|
40
|
+
- [Claude Code](https://docs.claude.com/claude-code) installed and `claude` on your `PATH`
|
|
41
|
+
- A ScaleBerry account
|
|
42
|
+
|
|
43
|
+
## Manual setup
|
|
44
|
+
|
|
45
|
+
If the automatic `claude mcp add` step fails (e.g. Claude Code is not on `PATH`), the CLI prints the exact command to run manually.
|
|
46
|
+
|
|
47
|
+
## License
|
|
48
|
+
|
|
49
|
+
MIT © ScaleBerry AI
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @scaleberryai/claude — installs the ScaleBerry AI MCP server into Claude Code.
|
|
3
|
+
//
|
|
4
|
+
// Flow:
|
|
5
|
+
// 1) Ask the ScaleBerry backend to start a device-code session.
|
|
6
|
+
// 2) Open the browser to the verification URL.
|
|
7
|
+
// 3) Poll until the user authorizes (or denies / expires).
|
|
8
|
+
// 4) Run `claude mcp add` to register the MCP server with the issued token.
|
|
9
|
+
// 5) Print a clean success message.
|
|
10
|
+
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
13
|
+
import { platform } from "node:os";
|
|
14
|
+
|
|
15
|
+
const SUPABASE_URL =
|
|
16
|
+
process.env.SCALEBERRY_SUPABASE_URL ||
|
|
17
|
+
"https://cgffwfztxlyiwzgyysvo.supabase.co";
|
|
18
|
+
const SUPABASE_ANON_KEY =
|
|
19
|
+
process.env.SCALEBERRY_SUPABASE_ANON_KEY ||
|
|
20
|
+
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImNnZmZ3Znp0eGx5aXd6Z3l5c3ZvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzIwOTA0MTUsImV4cCI6MjA4NzY2NjQxNX0.4dV-ZIPRGoErBTXv3BgpAlyCr37dVvygXh8SjJgrwr4";
|
|
21
|
+
|
|
22
|
+
const MCP_SERVER_URL = `${SUPABASE_URL}/functions/v1/scaleberry-mcp`;
|
|
23
|
+
|
|
24
|
+
const c = {
|
|
25
|
+
reset: "\x1b[0m",
|
|
26
|
+
bold: "\x1b[1m",
|
|
27
|
+
dim: "\x1b[2m",
|
|
28
|
+
green: "\x1b[32m",
|
|
29
|
+
red: "\x1b[31m",
|
|
30
|
+
yellow: "\x1b[33m",
|
|
31
|
+
blue: "\x1b[34m",
|
|
32
|
+
cyan: "\x1b[36m",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function log(msg) {
|
|
36
|
+
process.stdout.write(msg + "\n");
|
|
37
|
+
}
|
|
38
|
+
function info(msg) {
|
|
39
|
+
log(`${c.cyan}›${c.reset} ${msg}`);
|
|
40
|
+
}
|
|
41
|
+
function ok(msg) {
|
|
42
|
+
log(`${c.green}✓${c.reset} ${msg}`);
|
|
43
|
+
}
|
|
44
|
+
function warn(msg) {
|
|
45
|
+
log(`${c.yellow}!${c.reset} ${msg}`);
|
|
46
|
+
}
|
|
47
|
+
function fail(msg) {
|
|
48
|
+
log(`${c.red}✗${c.reset} ${msg}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function call(path, body) {
|
|
52
|
+
const res = await fetch(`${SUPABASE_URL}/functions/v1/${path}`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: {
|
|
55
|
+
"Content-Type": "application/json",
|
|
56
|
+
apikey: SUPABASE_ANON_KEY,
|
|
57
|
+
Authorization: `Bearer ${SUPABASE_ANON_KEY}`,
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body ?? {}),
|
|
60
|
+
});
|
|
61
|
+
let payload = null;
|
|
62
|
+
try {
|
|
63
|
+
payload = await res.json();
|
|
64
|
+
} catch {
|
|
65
|
+
/* non-json */
|
|
66
|
+
}
|
|
67
|
+
return { ok: res.ok, status: res.status, payload };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function openBrowser(url) {
|
|
71
|
+
const cmds = {
|
|
72
|
+
darwin: ["open", [url]],
|
|
73
|
+
win32: ["cmd", ["/c", "start", "", url]],
|
|
74
|
+
linux: ["xdg-open", [url]],
|
|
75
|
+
};
|
|
76
|
+
const entry = cmds[platform()] ?? cmds.linux;
|
|
77
|
+
try {
|
|
78
|
+
const child = spawn(entry[0], entry[1], {
|
|
79
|
+
stdio: "ignore",
|
|
80
|
+
detached: true,
|
|
81
|
+
});
|
|
82
|
+
child.unref();
|
|
83
|
+
return true;
|
|
84
|
+
} catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function runClaudeMcpAdd(token) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
const args = [
|
|
92
|
+
"mcp",
|
|
93
|
+
"add",
|
|
94
|
+
"--transport",
|
|
95
|
+
"http",
|
|
96
|
+
"scaleberry",
|
|
97
|
+
MCP_SERVER_URL,
|
|
98
|
+
"--header",
|
|
99
|
+
`Authorization: Bearer ${token}`,
|
|
100
|
+
];
|
|
101
|
+
const child = spawn("claude", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
102
|
+
let stderr = "";
|
|
103
|
+
child.stdout.on("data", () => {});
|
|
104
|
+
child.stderr.on("data", (d) => (stderr += d.toString()));
|
|
105
|
+
child.on("error", (err) => resolve({ ok: false, error: err.message }));
|
|
106
|
+
child.on("close", (code) => {
|
|
107
|
+
if (code === 0) resolve({ ok: true });
|
|
108
|
+
else resolve({ ok: false, error: stderr.trim() || `exit code ${code}` });
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function main() {
|
|
114
|
+
log("");
|
|
115
|
+
log(`${c.bold}ScaleBerry → Claude Code${c.reset}`);
|
|
116
|
+
log(`${c.dim}One-command installer for the ScaleBerry MCP server.${c.reset}`);
|
|
117
|
+
log("");
|
|
118
|
+
|
|
119
|
+
// 1) Start session
|
|
120
|
+
info("Starting authorization session…");
|
|
121
|
+
const start = await call("mcp-auth-start", { cli_name: "Claude Code" });
|
|
122
|
+
if (!start.ok || !start.payload?.device_code) {
|
|
123
|
+
fail(`Could not start session: ${start.payload?.error ?? start.status}`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
const { device_code, verification_url, expires_in, interval } = start.payload;
|
|
127
|
+
|
|
128
|
+
// 2) Open browser
|
|
129
|
+
log("");
|
|
130
|
+
log(`${c.bold}Open this URL in your browser to authorize:${c.reset}`);
|
|
131
|
+
log(` ${c.blue}${verification_url}${c.reset}`);
|
|
132
|
+
log("");
|
|
133
|
+
const opened = openBrowser(verification_url);
|
|
134
|
+
if (opened) info("Opened your default browser…");
|
|
135
|
+
else warn("Could not auto-open the browser — please open the URL above.");
|
|
136
|
+
|
|
137
|
+
// 3) Poll
|
|
138
|
+
const pollMs = Math.max((interval ?? 2) * 1000, 1000);
|
|
139
|
+
const deadline = Date.now() + (expires_in ?? 600) * 1000;
|
|
140
|
+
log("");
|
|
141
|
+
info("Waiting for authorization…");
|
|
142
|
+
|
|
143
|
+
let token = null;
|
|
144
|
+
while (Date.now() < deadline) {
|
|
145
|
+
await sleep(pollMs);
|
|
146
|
+
const poll = await call("mcp-auth-poll", { device_code });
|
|
147
|
+
const status = poll.payload?.status;
|
|
148
|
+
if (status === "approved" && poll.payload?.token) {
|
|
149
|
+
token = poll.payload.token;
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
if (status === "denied") {
|
|
153
|
+
fail("Authorization denied.");
|
|
154
|
+
process.exit(1);
|
|
155
|
+
}
|
|
156
|
+
if (status === "expired" || status === "invalid") {
|
|
157
|
+
fail(`Authorization ${status}. Run \`npx @scaleberryai/claude\` again.`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
// else pending — keep polling
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!token) {
|
|
164
|
+
fail("Timed out waiting for authorization. Please try again.");
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
ok("Authorized.");
|
|
169
|
+
|
|
170
|
+
// 4) Register with Claude Code
|
|
171
|
+
log("");
|
|
172
|
+
info("Registering ScaleBerry MCP server in Claude Code…");
|
|
173
|
+
const reg = await runClaudeMcpAdd(token);
|
|
174
|
+
if (!reg.ok) {
|
|
175
|
+
fail("Could not run `claude mcp add` automatically.");
|
|
176
|
+
log("");
|
|
177
|
+
log(
|
|
178
|
+
`${c.dim}Either Claude Code is not installed, or the \`claude\` CLI is not on PATH.${c.reset}`,
|
|
179
|
+
);
|
|
180
|
+
log("Run this command manually to finish setup:");
|
|
181
|
+
log("");
|
|
182
|
+
log(
|
|
183
|
+
` ${c.bold}claude mcp add --transport http scaleberry ${MCP_SERVER_URL} --header "Authorization: Bearer ${token}"${c.reset}`,
|
|
184
|
+
);
|
|
185
|
+
log("");
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ok("ScaleBerry registered as an MCP server.");
|
|
190
|
+
log("");
|
|
191
|
+
log(`${c.green}${c.bold}ScaleBerry connected successfully.${c.reset}`);
|
|
192
|
+
log("");
|
|
193
|
+
log("Next steps:");
|
|
194
|
+
log(" 1. Restart Claude Code.");
|
|
195
|
+
log(
|
|
196
|
+
` 2. Ask it: ${c.dim}"List my ScaleBerry products and draft an ad for the best one."${c.reset}`,
|
|
197
|
+
);
|
|
198
|
+
log("");
|
|
199
|
+
log(
|
|
200
|
+
`${c.dim}You can revoke access any time at https://www.scaleberryai.com/dashboard/claude${c.reset}`,
|
|
201
|
+
);
|
|
202
|
+
log("");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
main().catch((err) => {
|
|
206
|
+
fail(`Unexpected error: ${err?.message ?? err}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@scaleberryai/claude",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One-command installer that connects ScaleBerry AI to Claude Code as an MCP server.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"scaleberry-claude": "./bin/cli.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin"
|
|
10
|
+
],
|
|
11
|
+
"type": "module",
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=18"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test:local": "node ./bin/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"scaleberry",
|
|
20
|
+
"claude",
|
|
21
|
+
"claude-code",
|
|
22
|
+
"mcp",
|
|
23
|
+
"ai",
|
|
24
|
+
"ads",
|
|
25
|
+
"marketing"
|
|
26
|
+
],
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"homepage": "https://www.scaleberryai.com",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/scaleberryai/claude"
|
|
32
|
+
},
|
|
33
|
+
"author": "ScaleBerry AI"
|
|
34
|
+
}
|