@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 +21 -0
- package/README.md +67 -0
- package/package.json +43 -0
- package/src/index.js +81 -0
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
|
+
});
|