cdpfleet-mcp 0.1.1 → 0.1.2
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 +1 -1
- package/src/tools.js +16 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cdpfleet-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "MCP server for cdpfleet: let Claude, Cursor or any MCP client drive real remote browsers — Chrome, Edge, Firefox, Camoufox and more — with residential proxies and live-view handoff.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/tools.js
CHANGED
|
@@ -14,20 +14,20 @@ const MAX_TEXT = 25000;
|
|
|
14
14
|
// The free Community plan: a short note at launch and after a session, and with the errors its
|
|
15
15
|
// limits cause, saying what a paid plan adds (same wording as main's public/shared/upgrade.js).
|
|
16
16
|
const UPGRADE_URL = 'https://cdpfleet.com/app#subscriptions';
|
|
17
|
-
const UPGRADE_LINE = 'You\'re on the free Community plan. Ready for more? Paid plans unlock more threads, longer sessions, priority capacity, recordings and persistent profiles — from $
|
|
17
|
+
const UPGRADE_LINE = 'You\'re on the free Community plan. Ready for more? Paid plans unlock more threads, longer sessions, priority capacity, recordings and persistent profiles — from $5 a month';
|
|
18
18
|
const NUDGE_EVERY_MS = 30 * 60000;
|
|
19
19
|
const HINTS = {
|
|
20
20
|
community_capacity_full: 'The free Community capacity is busy right now — paid plans get priority capacity and aren\'t affected.',
|
|
21
21
|
daily_quota_exhausted: 'That\'s today\'s free minutes used — paid plans give you 60 to 480 minutes per thread a day, or unlimited on dedicated threads.',
|
|
22
22
|
hourly_quota_exhausted: 'That\'s this hour\'s share of your free minutes — paid plans come with many more.',
|
|
23
|
-
threads_exceeded: 'The free plan runs 2 threads at a time — paid plans start at
|
|
23
|
+
threads_exceeded: 'The free plan runs 2 threads at a time — paid plans start at 5, and scale to thousands.',
|
|
24
24
|
profiles_disabled: 'Persistent profiles (stay logged in across sessions) come with every paid plan.',
|
|
25
25
|
keep_alive_requires_dedicated: 'keep_alive (reconnect to a running browser) comes with dedicated threads.',
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// The browser a client works with: { fleet, current: { session, browser, page, name } | null }.
|
|
29
29
|
export function createState({ apiKey, defaultProxy } = {}) {
|
|
30
|
-
const state = { fleet: new Cdpfleet(apiKey ? { apiKey } : {}), defaultProxy, current: null,
|
|
30
|
+
const state = { fleet: new Cdpfleet(apiKey ? { apiKey } : {}), defaultProxy, current: null, plan: undefined, nudgedAt: 0 };
|
|
31
31
|
state.close = async () => {
|
|
32
32
|
const c = state.current;
|
|
33
33
|
state.current = null;
|
|
@@ -44,11 +44,14 @@ export function createServer({ apiKey, defaultProxy, state = createState({ apiKe
|
|
|
44
44
|
const fail = (t) => ({ content: [{ type: 'text', text: t }], isError: true });
|
|
45
45
|
const clip = (s) => (s.length > MAX_TEXT ? `${s.slice(0, MAX_TEXT)}\n… (${s.length - MAX_TEXT} more characters; snapshot a narrower selector)` : s);
|
|
46
46
|
|
|
47
|
-
//
|
|
48
|
-
async function
|
|
49
|
-
if (state.
|
|
50
|
-
|
|
47
|
+
// The key's plan, asked once per browser state: Community or not, and the longest idle time it allows.
|
|
48
|
+
async function plan() {
|
|
49
|
+
if (state.plan === undefined) {
|
|
50
|
+
state.plan = await fleet.me().then((m) => ({ community: Boolean(m.subscription?.features?.community), maxIdle: m.subscription?.max_inactivity_seconds || null }), () => null);
|
|
51
|
+
}
|
|
52
|
+
return state.plan;
|
|
51
53
|
}
|
|
54
|
+
const community = async () => Boolean((await plan())?.community);
|
|
52
55
|
// The upgrade note, at most every 30 minutes per key, for Community only.
|
|
53
56
|
async function nudge() {
|
|
54
57
|
if (!(await community()) || Date.now() - state.nudgedAt < NUDGE_EVERY_MS) return '';
|
|
@@ -74,7 +77,7 @@ export function createServer({ apiKey, defaultProxy, state = createState({ apiKe
|
|
|
74
77
|
throw new Error('Give a selector, or a role (and name).');
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
const server = new McpServer({ name: 'cdpfleet', version: '0.1.
|
|
80
|
+
const server = new McpServer({ name: 'cdpfleet', version: '0.1.2' });
|
|
78
81
|
|
|
79
82
|
server.registerTool('launch_browser', {
|
|
80
83
|
description: 'Start a real remote browser on cdpfleet and open a page. Pick the browser the site should see. Headful by default (2 threads; less detectable). Proxy: your own proxy URL, or cdpfleet residential (default; billed per GB from your balance), optionally by country.',
|
|
@@ -98,11 +101,15 @@ export function createServer({ apiKey, defaultProxy, state = createState({ apiKe
|
|
|
98
101
|
let p = proxy || state.defaultProxy || 'cdpfleet-resi';
|
|
99
102
|
if (country && p.startsWith('cdpfleet-resi')) p = `${p}-country-${country.toLowerCase()}`;
|
|
100
103
|
const live = headless && !['camoufox', 'firefox', 'webkit'].includes(browser) ? { live_view: true } : {};
|
|
104
|
+
// A conversation pauses between tool calls: keep the browser for the plan's longest idle time
|
|
105
|
+
// (shared 10 min, Community 5 min; dedicated capped at 30 min here, like the server's own idle).
|
|
106
|
+
const maxIdle = (await plan())?.maxIdle;
|
|
107
|
+
const idle = maxIdle ? { inactivity_timeout: Math.min(maxIdle, 1800) } : {};
|
|
101
108
|
// A key created moments ago reaches the starters with the next accounts sync (about a
|
|
102
109
|
// second): retry a refused key briefly instead of failing the first launch.
|
|
103
110
|
let session;
|
|
104
111
|
for (let i = 0; ; i++) {
|
|
105
|
-
try { session = await fleet.launch(browser, { proxy: p, headless, record, ...live, ...(options || {}) }); break; } catch (err) {
|
|
112
|
+
try { session = await fleet.launch(browser, { proxy: p, headless, record, ...live, ...idle, ...(options || {}) }); break; } catch (err) {
|
|
106
113
|
if (i >= 15 || !/invalid_api_key/.test(String(err.message || err))) throw err;
|
|
107
114
|
await new Promise((r) => setTimeout(r, 1000));
|
|
108
115
|
}
|