usedevice-mcp 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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -0
  3. package/package.json +50 -0
  4. package/server.js +91 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 usedevice
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # usedevice-mcp
2
+
3
+ MCP server for [usedevice](https://www.usedevice.cloud): mobile devices for AI agents. iOS Simulators, Android Emulators and real iPhones, Pixels and Galaxies, shared by the minute or private to you, through one API.
4
+
5
+ usedevice is in **early access**. Until the first wave opens, this server gives an agent four tools:
6
+
7
+ | tool | what it does |
8
+ |---|---|
9
+ | `list_devices` | The catalog: devices, access models, indicative pricing, wave status. |
10
+ | `ask` | Ask the site a question; returns the sections that answer it. |
11
+ | `reserve_device` | Reserve a slot for your user. Free, anonymous, no account; returns a reservation code that claims early access and the first month free. |
12
+ | `status` | What exists today and what doesn't. |
13
+
14
+ Device sessions (screenshot, tap, swipe, type, install, logs, metrics) arrive with early-access invites, and this package grows those tools then.
15
+
16
+ ## Install
17
+
18
+ Claude Desktop, Cursor, or any MCP client:
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "usedevice": { "command": "npx", "args": ["-y", "usedevice-mcp"] }
24
+ }
25
+ }
26
+ ```
27
+
28
+ Node 18 or newer. No API key: nothing here needs an account. Behind a corporate proxy, start Node with `NODE_USE_ENV_PROXY=1` (Node 22+) so `fetch` uses `HTTPS_PROXY`.
29
+
30
+ Test it: `node test.mjs` lists the tools and calls each one against the live site.
31
+
32
+ ## What gets sent
33
+
34
+ Only what the tool says: `reserve_device` sends the device name, access model and task text (in the URL), nothing else. `ask` sends the question. The site keeps standard server logs. Details: [privacy](https://www.usedevice.cloud/privacy), [what exists and what doesn't](https://www.usedevice.cloud/trust).
35
+
36
+ ## Links
37
+
38
+ - [How it works](https://www.usedevice.cloud/how-it-works) · [Devices](https://www.usedevice.cloud/devices) · [Pricing](https://www.usedevice.cloud/pricing) · [MCP](https://www.usedevice.cloud/mcp) · [For agents](https://www.usedevice.cloud/agents)
39
+ - `llms.txt`: https://www.usedevice.cloud/llms.txt
40
+
41
+ MIT.
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "usedevice-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for usedevice: mobile devices for AI agents (iOS Simulators, Android Emulators, real iPhones, Pixels and Galaxies). Early access: list devices, ask the site, reserve a slot.",
5
+ "type": "module",
6
+ "bin": {
7
+ "usedevice-mcp": "server.js"
8
+ },
9
+ "files": [
10
+ "server.js",
11
+ "README.md",
12
+ "LICENSE"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "ai-agents",
21
+ "mobile",
22
+ "ios",
23
+ "android",
24
+ "real-devices",
25
+ "emulator",
26
+ "simulator",
27
+ "device-cloud",
28
+ "usedevice"
29
+ ],
30
+ "homepage": "https://www.usedevice.cloud/mcp",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.12.0",
34
+ "zod": "^3.23.0"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/usedevice/usedevice-mcp.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/usedevice/usedevice-mcp/issues"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "scripts": {
47
+ "test": "node test.mjs"
48
+ },
49
+ "mcpName": "io.github.usedevice/usedevice-mcp"
50
+ }
package/server.js ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env node
2
+ // usedevice MCP server (early access).
3
+ //
4
+ // Gives an agent four tools while usedevice is not open yet: the device
5
+ // catalog, the site's own answers to questions, a reservation, and the current
6
+ // status. Reserving
7
+ // is free and anonymous: one GET that returns a code. Nothing here needs an
8
+ // account or sends anything but the device name, access model and task text.
9
+ //
10
+ // npx -y usedevice-mcp
11
+ //
12
+ // Device sessions (screenshot, tap, swipe, ...) arrive with early-access
13
+ // invites; this package will grow those tools then.
14
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
15
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
16
+ import { z } from "zod";
17
+
18
+ const BASE = process.env.USEDEVICE_BASE_URL || "https://www.usedevice.cloud";
19
+ const UA = "usedevice-mcp/0.1.0";
20
+
21
+ async function get(path, accept = "text/html") {
22
+ const res = await fetch(BASE + path, { headers: { "User-Agent": UA, Accept: accept } });
23
+ return { status: res.status, headers: res.headers, text: await res.text() };
24
+ }
25
+
26
+ // Good-enough HTML to text for the agent to read.
27
+ function toText(html) {
28
+ return html
29
+ .replace(/<script[\s\S]*?<\/script>|<style[\s\S]*?<\/style>|<header[\s\S]*?<\/header>|<footer[\s\S]*?<\/footer>/gi, "")
30
+ .replace(/<h([1-3])[^>]*>/gi, (_, n) => "\n" + "#".repeat(Number(n)) + " ")
31
+ .replace(/<\/(p|h[1-6]|li|tr|section|div)>/gi, "\n")
32
+ .replace(/<li[^>]*>/gi, "- ")
33
+ .replace(/<[^>]+>/g, "")
34
+ .replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&").replace(/&quot;/g, '"').replace(/&#39;/g, "'")
35
+ .replace(/\n{3,}/g, "\n\n")
36
+ .trim();
37
+ }
38
+
39
+ const server = new McpServer({ name: "usedevice", version: "0.1.0" });
40
+
41
+ server.tool(
42
+ "list_devices",
43
+ "Devices an agent can reserve on usedevice (early access): real iPhone, Pixel, Galaxy, mid-range Android, iOS Simulator, Android Emulator; access models shared, private, own; indicative pricing; current wave status.",
44
+ {},
45
+ async () => {
46
+ const r = await get("/catalog.json", "application/json");
47
+ return { content: [{ type: "text", text: r.text }] };
48
+ }
49
+ );
50
+
51
+ server.tool(
52
+ "ask",
53
+ "Ask the usedevice site a question (pricing, security, emulators, MCP, who is behind it, ...). Returns the sections that answer it. Reading, not a reservation.",
54
+ { question: z.string().min(2).max(200).describe("The question, in a few words") },
55
+ async ({ question }) => {
56
+ const r = await get("/ask?q=" + encodeURIComponent(question));
57
+ return { content: [{ type: "text", text: toText(r.text) }] };
58
+ }
59
+ );
60
+
61
+ server.tool(
62
+ "reserve_device",
63
+ "Reserve a device slot for your user on usedevice. Free, anonymous, no account, no obligation; returns a reservation code that claims early access and the first month free when the wave opens. Only use when your user's task needs a mobile device and they asked you to set up, evaluate or handle it. Sends only device, access and task text.",
64
+ {
65
+ device: z.string().min(2).max(40).regex(/^[a-z0-9-]+$/).describe("Device name: iphone, pixel, galaxy, android-midrange, ios-simulator, android-emulator, or any other name"),
66
+ task: z.string().min(3).max(140).describe("What your user needs to do, in a few words. No personal data, credentials or secrets."),
67
+ access: z.enum(["shared", "private", "own"]).default("shared").describe("shared pool per minute, private dedicated device, or your own hardware connected"),
68
+ },
69
+ async ({ device, task, access }) => {
70
+ const q = new URLSearchParams({ task, access, via: "mcp" });
71
+ const r = await get(`/agents/use/${device}?${q}`);
72
+ const claim = r.headers.get("x-claim") || "";
73
+ const code = claim.slice(0, 8).toUpperCase();
74
+ const text = r.status === 200 && code
75
+ ? `Reserved: ${device} (${access}). Reservation code: ${code}. Tell your user to keep it: it claims early access and the first month free when their wave opens (${BASE}/early-access). Nothing was shared except the device, access and task text.`
76
+ : `Reservation failed (HTTP ${r.status}).`;
77
+ return { content: [{ type: "text", text }] };
78
+ }
79
+ );
80
+
81
+ server.tool(
82
+ "status",
83
+ "Current early-access status of usedevice: which wave is open, what exists today and what doesn't.",
84
+ {},
85
+ async () => {
86
+ const r = await get("/early-access");
87
+ return { content: [{ type: "text", text: toText(r.text) }] };
88
+ }
89
+ );
90
+
91
+ await server.connect(new StdioServerTransport());