@lobsai/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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 noholdplease
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,67 @@
1
+ # @lobsai/mcp
2
+
3
+ Stdio MCP wrapper for the [lobs](https://noholdplease.com/api) phone-call service. Plug any stdio-based MCP client (Claude Desktop, Cursor, Cline, Continue, NanoClaw, OpenAI Agent SDK, Zed) into a real human operator that makes the actual phone call.
4
+
5
+ ## Install
6
+
7
+ ```jsonc
8
+ // In your MCP client's config
9
+ {
10
+ "mcpServers": {
11
+ "lobs": {
12
+ "command": "npx",
13
+ "args": ["-y", "@lobsai/mcp"],
14
+ "env": {
15
+ "LOBS_USER_ID": "your-stable-id"
16
+ }
17
+ }
18
+ }
19
+ }
20
+ ```
21
+
22
+ That's it. The wrapper exposes three tools — `create_call_task`, `get_task_status`, `cancel_task` — to your agent.
23
+
24
+ ## What is `LOBS_USER_ID`?
25
+
26
+ A self-asserted identifier that scopes your tasks to a partition. There's no auth on the lobs server today; pick anything stable per user. Common values:
27
+
28
+ - A phone number (digits only): `16263102445`
29
+ - An email or username: `andy`
30
+ - A UUID per session: `550e8400-e29b-41d4-a716-446655440000`
31
+
32
+ If you omit it, the wrapper sends `anon`. Tasks then land in a shared `anon` bucket — fine for one-off testing, not for production.
33
+
34
+ ## What lobs does
35
+
36
+ Your agent calls `create_call_task` with a goal and a target. A human operator picks it up, makes the phone call, and reports back. Your agent learns the result by polling `get_task_status` (or, optionally, by passing `user_phone` for an SMS callback).
37
+
38
+ Three tools, eight args total. [Full surface docs](https://noholdplease.com/api).
39
+
40
+ ## Limits
41
+
42
+ A global daily cap of **50 task creations** across all callers (UTC midnight rollover). Above the cap, `create_call_task` returns 429. Other tools are unlimited.
43
+
44
+ This is real human capacity. Be respectful — each task is a phone call by a real person.
45
+
46
+ ## Configuration
47
+
48
+ | Env var | Default | Description |
49
+ |---|---|---|
50
+ | `LOBS_USER_ID` | `anon` | Self-asserted user partition id. |
51
+ | `LOBS_MCP_URL` | `https://lobs-ai--lobs-mcp-serve.modal.run/mcp` | Override only for testing against a non-prod lobs deployment. |
52
+
53
+ ## Smoke test
54
+
55
+ ```sh
56
+ LOBS_USER_ID=smoke-test npm run smoke
57
+ ```
58
+
59
+ Spawns the wrapper as a subprocess, connects via stdio, lists tools, asserts the 3 expected names appear.
60
+
61
+ ## Versioning
62
+
63
+ Surface changes follow the lobs MCP server, not this wrapper. The wrapper is a near-passthrough — when lobs adds tools they automatically appear; when arg shapes change you'll see it in `tools/list`. The version in `package.json` bumps only when this wrapper itself changes (e.g. error handling, retries, defaults).
64
+
65
+ ## License
66
+
67
+ MIT. See [LICENSE](LICENSE).
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@lobsai/mcp",
3
+ "version": "0.1.0",
4
+ "description": "Stdio MCP wrapper for the lobs phone-call service. Plug any MCP client into a real human operator.",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "lobs-mcp": "src/index.js"
9
+ },
10
+ "files": [
11
+ "src",
12
+ "README.md",
13
+ "LICENSE"
14
+ ],
15
+ "scripts": {
16
+ "smoke": "node tests/smoke.js"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ },
21
+ "keywords": [
22
+ "mcp",
23
+ "model-context-protocol",
24
+ "phone-calls",
25
+ "ai-agent",
26
+ "lobs",
27
+ "noholdplease"
28
+ ],
29
+ "author": "noholdplease",
30
+ "license": "MIT",
31
+ "homepage": "https://noholdplease.com/api",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "git+https://github.com/noholdplease/lobs-ai.git",
35
+ "directory": "clients/mcp-stdio"
36
+ },
37
+ "bugs": {
38
+ "url": "https://github.com/noholdplease/lobs-ai/issues"
39
+ },
40
+ "dependencies": {
41
+ "@modelcontextprotocol/sdk": "^1.0.0"
42
+ }
43
+ }
package/src/index.js ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @lobsai/mcp — stdio MCP wrapper for the lobs phone-call service.
4
+ *
5
+ * Bridges any stdio-based MCP client (Claude Desktop, Cursor, Cline,
6
+ * NanoClaw, etc.) to lobs's HTTP MCP server. Forwards tools/list and
7
+ * tools/call requests to https://lobs-ai--lobs-mcp-serve.modal.run/mcp
8
+ * and pipes responses back over stdio.
9
+ *
10
+ * Set LOBS_USER_ID in env to pick the partition your tasks live under
11
+ * (the lobs MCP has no auth — the id is self-asserted; defaults to
12
+ * "anon"). Phone digits are conventional for human users:
13
+ *
14
+ * LOBS_USER_ID=16263102445 npx -y @lobsai/mcp
15
+ *
16
+ * Public docs: https://noholdplease.com/api
17
+ */
18
+
19
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
20
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
21
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
22
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
23
+ import {
24
+ CallToolRequestSchema,
25
+ ListToolsRequestSchema,
26
+ } from "@modelcontextprotocol/sdk/types.js";
27
+
28
+ const ENDPOINT =
29
+ process.env.LOBS_MCP_URL || "https://lobs-ai--lobs-mcp-serve.modal.run/mcp";
30
+ const USER_ID = (process.env.LOBS_USER_ID || "anon").trim() || "anon";
31
+
32
+ async function main() {
33
+ // Outbound: HTTP client → lobs MCP. The SDK's streamable-HTTP
34
+ // transport handles SSE framing, JSON-RPC envelopes, reconnection.
35
+ const upstream = new Client(
36
+ { name: "lobs-stdio-wrapper", version: "0.1.0" },
37
+ { capabilities: {} },
38
+ );
39
+ const httpTransport = new StreamableHTTPClientTransport(new URL(ENDPOINT), {
40
+ requestInit: {
41
+ headers: { "X-User-Id": USER_ID },
42
+ },
43
+ });
44
+ await upstream.connect(httpTransport);
45
+
46
+ // Inbound: stdio server. Declare the same capabilities the upstream
47
+ // serves (just tools today). Forward each request type by piping
48
+ // params to the upstream client and returning its response.
49
+ const downstream = new Server(
50
+ { name: "lobs", version: "0.1.0" },
51
+ { capabilities: { tools: {} } },
52
+ );
53
+
54
+ downstream.setRequestHandler(ListToolsRequestSchema, async () => {
55
+ return await upstream.listTools();
56
+ });
57
+
58
+ downstream.setRequestHandler(CallToolRequestSchema, async (req) => {
59
+ return await upstream.callTool(req.params);
60
+ });
61
+
62
+ // Graceful shutdown — close upstream when stdio peer disconnects.
63
+ const shutdown = async () => {
64
+ try {
65
+ await upstream.close();
66
+ } catch {
67
+ /* best-effort */
68
+ }
69
+ process.exit(0);
70
+ };
71
+ process.on("SIGINT", shutdown);
72
+ process.on("SIGTERM", shutdown);
73
+
74
+ await downstream.connect(new StdioServerTransport());
75
+ }
76
+
77
+ main().catch((err) => {
78
+ // Stderr only — stdio's stdout is reserved for the MCP protocol.
79
+ process.stderr.write(`[lobs-mcp] fatal: ${err?.stack || err}\n`);
80
+ process.exit(1);
81
+ });