effing-use 0.1.0 → 0.1.1
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 +30 -13
- package/package.json +7 -3
- package/skills/effing-use/SKILL.md +2 -2
- package/skills/effing-use/references/setup.md +6 -6
- package/skills/effing-use/references/troubleshooting.md +1 -1
- package/src/browser/session.ts +12 -4
- package/src/config.ts +7 -0
- package/src/http.ts +9 -1
- package/src/server.ts +2 -1
package/README.md
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
# effing-use — stop paying 19.5 KB every session for browser control
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/effing-use) [](LICENSE) [](https://bun.sh)
|
|
4
|
+
|
|
3
5
|
Full Chromium automation in **3 tools, 3.9 KB**. Same pages, same clicks, same scrapes — without the 24-tool handshake eating your context window before you load a page.
|
|
4
6
|
|
|
5
7
|
**Measured, not marketed:** `tools/list` is **3,913 bytes** here vs **19,517 bytes** for `@playwright/mcp@latest` (~5x smaller, ~15.6 KB saved every session). Local ops stay in milliseconds — snapshot ~15 ms, extract ~50 ms, batch ~65 ms, screenshot ~60–190 ms. Page loads still cost seconds (network, not us). Full numbers in [`docs/COMPARISON.md`](docs/COMPARISON.md).
|
|
6
8
|
|
|
7
9
|
## Install (Bun-only)
|
|
8
10
|
|
|
9
|
-
Requires [Bun](https://bun.sh) 1.4+.
|
|
11
|
+
Requires [Bun](https://bun.sh) 1.4+. Installs from npm in seconds — the tarball is ~16 kB ([`effing-use` v0.1.1](https://www.npmjs.com/package/effing-use)):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# No install needed — bunx fetches from npm on first run
|
|
15
|
+
bunx effing-use # STDIO (single editor) — runs src/index.ts via bin
|
|
16
|
+
bunx effing-use-http # HTTP on :3123 (/mcp) — shared across editors
|
|
17
|
+
bunx playwright install chromium --only-shell # one-time Chromium download (~150 MB)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Optional — install globally so `effing-use` is on your PATH:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bun install -g effing-use
|
|
24
|
+
effing-use # STDIO
|
|
25
|
+
effing-use-http # HTTP on :3123 (/mcp)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The `playwright` npm package ships the driver, **not** the browser — every user needs Playwright's version-pinned Chromium once. From source instead:
|
|
10
29
|
|
|
11
30
|
```bash
|
|
12
|
-
bunx effing-use --help # after: bun publish (runs src/index.ts via bin)
|
|
13
|
-
# or from source:
|
|
14
31
|
bun install
|
|
15
|
-
bunx playwright install chromium --only-shell
|
|
16
|
-
bun src/index.ts # STDIO
|
|
17
|
-
bun src/http.ts # HTTP on :
|
|
32
|
+
bunx playwright install chromium --only-shell
|
|
33
|
+
bun src/index.ts # STDIO
|
|
34
|
+
bun src/http.ts # HTTP on :3123 (/mcp)
|
|
18
35
|
```
|
|
19
36
|
|
|
20
37
|
Re-run `bunx playwright install chromium --only-shell` whenever you bump the `playwright` dependency (each Playwright version pins its own browser build). Verify with `bunx playwright install --dry-run chromium` or check `~/.cache/ms-playwright/`.
|
|
@@ -29,25 +46,25 @@ Re-run `bunx playwright install chromium --only-shell` whenever you bump the `pl
|
|
|
29
46
|
bun install
|
|
30
47
|
bunx playwright install chromium --only-shell
|
|
31
48
|
docker compose up --build -d
|
|
32
|
-
curl http://localhost:
|
|
49
|
+
curl http://localhost:3123/healthz
|
|
33
50
|
```
|
|
34
51
|
|
|
35
|
-
Point any VS Code instance at `http://localhost:
|
|
52
|
+
Point any VS Code instance at `http://localhost:3123/mcp` (see `.vscode/mcp.json` → `effing-use (http)`). Plain HTTP on loopback is intentional — add TLS at the edge for remote use.
|
|
36
53
|
|
|
37
54
|
**Step 2/3 — Connect.** STDIO for one editor, HTTP for all of them:
|
|
38
55
|
|
|
39
56
|
```json
|
|
40
57
|
{
|
|
41
58
|
"mcpServers": {
|
|
42
|
-
"
|
|
43
|
-
"command": "
|
|
44
|
-
"args": ["
|
|
59
|
+
"effing-use": {
|
|
60
|
+
"command": "bunx",
|
|
61
|
+
"args": ["effing-use"]
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
64
|
}
|
|
48
65
|
```
|
|
49
66
|
|
|
50
|
-
Or HTTP: `http://localhost:
|
|
67
|
+
Or HTTP: `http://localhost:3123/mcp` (via `bunx effing-use-http` or Docker). From source instead: `command: "bun"`, `args: ["/path/to/effing-use/src/index.ts"]`.
|
|
51
68
|
|
|
52
69
|
**Step 3/3 — Drive.** Search Wikipedia in one call instead of two round-trips:
|
|
53
70
|
|
|
@@ -62,7 +79,7 @@ Or HTTP: `http://localhost:3000/mcp`.
|
|
|
62
79
|
}
|
|
63
80
|
```
|
|
64
81
|
|
|
65
|
-
No Docker? `bun src/index.ts` (STDIO) or `bun src/http.ts` (`:
|
|
82
|
+
No Docker? `bun src/index.ts` (STDIO) or `bun src/http.ts` (`:3123` `/mcp`) works directly.
|
|
66
83
|
|
|
67
84
|
## Why agents prefer 3 tools
|
|
68
85
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effing-use",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Token-efficient browser control: 3 tools (act, observe, extract) built with tmcp + Bun + Playwright",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Michael Obubelebra Amachree",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/Michael-Obele/
|
|
9
|
+
"url": "git+https://github.com/Michael-Obele/effing-use.git"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://github.com/Michael-Obele/
|
|
11
|
+
"homepage": "https://github.com/Michael-Obele/effing-use#readme",
|
|
12
12
|
"type": "module",
|
|
13
13
|
"main": "src/index.ts",
|
|
14
14
|
"bin": {
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"README.md",
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public",
|
|
26
|
+
"provenance": true
|
|
27
|
+
},
|
|
24
28
|
"scripts": {
|
|
25
29
|
"start": "bun src/index.ts",
|
|
26
30
|
"start:http": "bun src/http.ts",
|
|
@@ -4,7 +4,7 @@ description: Drive a headless Chromium browser through 3 token-efficient MCP too
|
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: Requires the effing-use MCP server (Bun + Playwright Chromium). Works over stdio or Streamable HTTP at /mcp.
|
|
6
6
|
metadata:
|
|
7
|
-
repo: Michael-Obele/
|
|
7
|
+
repo: Michael-Obele/effing-use
|
|
8
8
|
tools: browser_act,browser_observe,browser_extract
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -53,6 +53,6 @@ Errors always come back as `{ ok: false, code, message, hint }` with codes
|
|
|
53
53
|
## Setup
|
|
54
54
|
|
|
55
55
|
See [references/setup.md](references/setup.md) for install (local Bun,
|
|
56
|
-
Docker, VS Code `mcp.json` entries), port config (`
|
|
56
|
+
Docker, VS Code `mcp.json` entries), port config (`EFFING_PORT`), and the
|
|
57
57
|
`.browser-use/` gitignore contract. See [references/troubleshooting.md](references/troubleshooting.md)
|
|
58
58
|
for port conflicts, Chromium sandbox notes, and the `doQuery` arity lesson.
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
bun install
|
|
15
15
|
bunx playwright install chromium --only-shell
|
|
16
16
|
bun src/index.ts # STDIO transport
|
|
17
|
-
bun src/http.ts # Streamable HTTP on $PORT (default
|
|
17
|
+
bun src/http.ts # Streamable HTTP on $PORT (default 3123), MCP at /mcp
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
VS Code `.vscode/mcp.json`:
|
|
@@ -40,12 +40,12 @@ VS Code `.vscode/mcp.json`:
|
|
|
40
40
|
## Option B — Docker (shared across VS Code instances)
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
|
|
43
|
+
EFFING_PORT=3123 docker compose up --build -d
|
|
44
44
|
curl http://localhost:3123/healthz # {"ok":true,"name":"effing-use"}
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
Then point any local client at `http://localhost:<
|
|
48
|
-
The container listens on
|
|
47
|
+
Then point any local client at `http://localhost:<EFFING_PORT>/mcp`.
|
|
48
|
+
The container listens on 3123 internally; only the host-side port moves.
|
|
49
49
|
|
|
50
50
|
- `restart: unless-stopped` — survives daemon restarts and host reboots
|
|
51
51
|
once started with `up -d`.
|
|
@@ -62,8 +62,8 @@ All optional; defaults shown (see `.env.example`):
|
|
|
62
62
|
|
|
63
63
|
| Var | Default | Notes |
|
|
64
64
|
| ---------------------- | -------------- | ---------------------------------------------- |
|
|
65
|
-
| `PORT` | `
|
|
66
|
-
| `
|
|
65
|
+
| `PORT` | `3123` | `src/http.ts` listen port (container-internal) |
|
|
66
|
+
| `EFFING_PORT` | `3123` | compose host-side port override |
|
|
67
67
|
| `BROWSER_HEADLESS` | `true` | Docker supports headless Chromium only |
|
|
68
68
|
| `BROWSER_VIEWPORT_W/H` | `1280/800` | |
|
|
69
69
|
| `BROWSER_TIMEOUT_MS` | `15000` | per-action Playwright timeout |
|
|
@@ -12,7 +12,7 @@ docker ps --format "{{.Names}} {{.Ports}}"
|
|
|
12
12
|
Fix — move our host port, never the container's:
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
|
|
15
|
+
EFFING_PORT=3123 docker compose up -d # client → http://localhost:3123/mcp
|
|
16
16
|
PORT=3123 bun src/http.ts # local Bun route
|
|
17
17
|
```
|
|
18
18
|
|
package/src/browser/session.ts
CHANGED
|
@@ -18,8 +18,15 @@ interface SessionEntry {
|
|
|
18
18
|
const sessions = new Map<string, SessionEntry>();
|
|
19
19
|
|
|
20
20
|
async function getBrowser(config: Config): Promise<Browser> {
|
|
21
|
+
if (browser && !browser.isConnected()) {
|
|
22
|
+
sessions.clear();
|
|
23
|
+
browser = null;
|
|
24
|
+
}
|
|
21
25
|
if (!browser) {
|
|
22
|
-
browser = await chromium.launch({
|
|
26
|
+
browser = await chromium.launch({
|
|
27
|
+
headless: config.headless,
|
|
28
|
+
args: ["--no-sandbox", "--disable-dev-shm-usage"],
|
|
29
|
+
});
|
|
23
30
|
}
|
|
24
31
|
return browser;
|
|
25
32
|
}
|
|
@@ -29,7 +36,8 @@ export async function getPage(
|
|
|
29
36
|
sessionId = "default",
|
|
30
37
|
): Promise<Page> {
|
|
31
38
|
const existing = sessions.get(sessionId);
|
|
32
|
-
if (existing) return existing.page;
|
|
39
|
+
if (existing && !existing.page.isClosed()) return existing.page;
|
|
40
|
+
if (existing) sessions.delete(sessionId);
|
|
33
41
|
const b = await getBrowser(config);
|
|
34
42
|
const context = await b.newContext({
|
|
35
43
|
viewport: { width: config.viewportW, height: config.viewportH },
|
|
@@ -66,7 +74,7 @@ export async function getContext(
|
|
|
66
74
|
sessionId = "default",
|
|
67
75
|
): Promise<BrowserContext> {
|
|
68
76
|
const existing = sessions.get(sessionId);
|
|
69
|
-
if (existing) return existing.context;
|
|
77
|
+
if (existing && !existing.page.isClosed()) return existing.context;
|
|
70
78
|
await getPage(config, sessionId);
|
|
71
79
|
return sessions.get(sessionId)!.context;
|
|
72
80
|
}
|
|
@@ -86,7 +94,7 @@ export function getNetworkLogs(
|
|
|
86
94
|
export async function closeSession(sessionId = "default"): Promise<void> {
|
|
87
95
|
const s = sessions.get(sessionId);
|
|
88
96
|
if (s) {
|
|
89
|
-
await s.context.close();
|
|
97
|
+
await s.context.close().catch(() => undefined);
|
|
90
98
|
sessions.delete(sessionId);
|
|
91
99
|
}
|
|
92
100
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
2
|
|
|
3
|
+
// The npm package version — single source of truth is package.json, so the MCP
|
|
4
|
+
// server's reported version (serverInfo.version) never drifts from the
|
|
5
|
+
// published release.
|
|
6
|
+
export const VERSION = (
|
|
7
|
+
await Bun.file(new URL("../package.json", import.meta.url)).json()
|
|
8
|
+
).version as string;
|
|
9
|
+
|
|
3
10
|
const ConfigSchema = v.object({
|
|
4
11
|
headless: v.optional(v.boolean(), true),
|
|
5
12
|
viewportW: v.optional(v.number(), 1280),
|
package/src/http.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { HttpTransport } from "@tmcp/transport-http";
|
|
3
3
|
import { server } from "./server.js";
|
|
4
4
|
|
|
5
|
-
const port = Number(process.env.PORT ??
|
|
5
|
+
const port = Number(process.env.PORT ?? 3123);
|
|
6
6
|
|
|
7
7
|
// Streamable HTTP transport (MCP spec). Serves the MCP endpoint at /mcp.
|
|
8
8
|
// No auth on local loopback; put a reverse proxy / tunnel in front for remote use.
|
|
@@ -11,6 +11,14 @@ const transport = new HttpTransport(server, { path: "/mcp" });
|
|
|
11
11
|
|
|
12
12
|
Bun.serve({
|
|
13
13
|
port,
|
|
14
|
+
// Bun closes idle connections after 10s by default (idleTimeout), and the
|
|
15
|
+
// timer applies even while a response is being streamed. The MCP
|
|
16
|
+
// Streamable-HTTP SSE notification stream sits idle between server->client
|
|
17
|
+
// messages, so Bun was killing it every ~10s and clients logged
|
|
18
|
+
// "Error reading from async stream: terminated" on a loop until the
|
|
19
|
+
// connection hard-failed. idleTimeout: 0 disables the idle close entirely
|
|
20
|
+
// (safe: this server is meant for local loopback / private networks).
|
|
21
|
+
idleTimeout: 0,
|
|
14
22
|
async fetch(req) {
|
|
15
23
|
const url = new URL(req.url);
|
|
16
24
|
if (url.pathname === "/healthz") {
|
package/src/server.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { McpServer } from "tmcp";
|
|
2
2
|
import { ValibotJsonSchemaAdapter } from "@tmcp/adapter-valibot";
|
|
3
|
+
import { VERSION } from "./config.js";
|
|
3
4
|
import { actTool } from "./tools/act.js";
|
|
4
5
|
import { observeTool } from "./tools/observe.js";
|
|
5
6
|
import { extractTool } from "./tools/extract.js";
|
|
@@ -9,7 +10,7 @@ const adapter = new ValibotJsonSchemaAdapter();
|
|
|
9
10
|
export const server = new McpServer(
|
|
10
11
|
{
|
|
11
12
|
name: "effing-use",
|
|
12
|
-
version:
|
|
13
|
+
version: VERSION,
|
|
13
14
|
description:
|
|
14
15
|
"Token-efficient browser control: 3 tools (act, observe, extract).",
|
|
15
16
|
},
|