@chieflab/mcp-server 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/README.md +102 -0
- package/package.json +45 -0
- package/src/index.js +172 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# @chieflab/mcp-server
|
|
2
|
+
|
|
3
|
+
Stdio MCP server for ChiefLab. Drop into Cursor / Claude Desktop / Claude Code / Codex / Windsurf / Cline / any stdio MCP host.
|
|
4
|
+
|
|
5
|
+
> Package status: this bridge is packaged in the repo but not published to npm yet. Use the hosted MCP endpoint directly today: `https://api.chieflab.io/api/mcp`.
|
|
6
|
+
|
|
7
|
+
Current status: packaged in repo, not published to npm yet. Do not tell users to
|
|
8
|
+
run `npx @chieflab/mcp-server` until `npm view @chieflab/mcp-server` resolves.
|
|
9
|
+
Use the hosted MCP URL directly today.
|
|
10
|
+
|
|
11
|
+
After npm publish:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g @chieflab/mcp-server
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then in your MCP host config:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"mcpServers": {
|
|
22
|
+
"chieflab": {
|
|
23
|
+
"command": "chieflab-mcp",
|
|
24
|
+
"env": { "CHIEFLAB_API_KEY": "clp_live_..." }
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Restart the host. ChiefLab's launch tool set is now available — the primary after-build tool is `chieflab_get_users_after_build` and the canonical launch operator is `chiefmo_launch_product`. Trigger phrases the host's agent should pick up: *launch this · get users · find customers · make money from this · market this · announce this · post this*.
|
|
31
|
+
|
|
32
|
+
## What this is
|
|
33
|
+
|
|
34
|
+
A thin stdio→HTTPS bridge. Forwards JSON-RPC frames over stdio to the hosted ChiefLab MCP at <https://api.chieflab.io/api/mcp>. Same JSON-RPC 2.0 / MCP 2024-11-05 contract on both sides.
|
|
35
|
+
|
|
36
|
+
**Why a bridge instead of bundling the full server:**
|
|
37
|
+
|
|
38
|
+
1. **Zero version skew.** Whatever tools the hosted MCP exposes today, this client surfaces today. No `npm update` required to pick up new tools.
|
|
39
|
+
2. **~5KB package.** No need to ship the full ChiefLab core library + Supabase + connector deps just to run a local stdio MCP.
|
|
40
|
+
3. **Auth + rate limiting + per-workspace scoping all happen server-side.** The local bridge holds nothing sensitive beyond your API key in env.
|
|
41
|
+
|
|
42
|
+
## Get an API key
|
|
43
|
+
|
|
44
|
+
One click, no email: <https://chieflab.io/get-key>
|
|
45
|
+
|
|
46
|
+
## What ChiefLab does
|
|
47
|
+
|
|
48
|
+
ChiefLab is the business execution layer for agent-built companies, starting with GTM. ChiefMO plans the launch grounded in repoContext, creates assets or briefs, opens an approval room, returns a paste-ready `agentGuide.userMessage`, publishes via Zernio after human approval, sends launch email via Resend, reads results 24h later via GA4 + Search Console + Zernio, remembers what worked, and recommends the next iteration. The v1 wedge is launch/distribution; see `/trust` for the live-vs-mock connector reality.
|
|
49
|
+
|
|
50
|
+
Before calling ChiefLab, coding agents can gather repo context locally:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
curl -fsSL https://chieflab.io/repo-context.mjs | node - --what-changed="what I just shipped"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Pass the printed JSON as `repoContext` to `chieflab_get_users_after_build`.
|
|
57
|
+
|
|
58
|
+
To install persistent repo instructions for agents:
|
|
59
|
+
|
|
60
|
+
Working installer today:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -fsSL https://chieflab.io/install-agent-hook.mjs | node -
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Windows PowerShell:
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
iwr https://chieflab.io/install-agent-hook.mjs -OutFile .chieflab-install-agent-hook.mjs; node .\.chieflab-install-agent-hook.mjs
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
npm command after publish:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx @chieflab/cli init
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
This writes the after-build hook (`AGENTS.md`, Cursor rule, Claude command, Codex note, MCP config stub) so future "launch this / get users / make money" prompts route to `chieflab_get_users_after_build`, then resume with `chiefmo_continue_launch_loop`.
|
|
79
|
+
|
|
80
|
+
Full docs: <https://chieflab.io/for-agents>
|
|
81
|
+
|
|
82
|
+
## Direct alternatives
|
|
83
|
+
|
|
84
|
+
- **Without install** — call the hosted MCP directly: `POST https://api.chieflab.io/api/mcp` with `Authorization: Bearer clp_live_...`. Any HTTP client works.
|
|
85
|
+
- **CLI** — packaged in-repo, npm publish pending. Use hosted MCP until it resolves publicly.
|
|
86
|
+
- **One-click Cursor** — <https://chieflab.io/install> has a `cursor://` deeplink.
|
|
87
|
+
|
|
88
|
+
## Env
|
|
89
|
+
|
|
90
|
+
| Var | Default | What |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `CHIEFLAB_API_KEY` | _(required)_ | Bearer key. `clp_live_...` or `clp_dev_...`. |
|
|
93
|
+
| `CHIEFLAB_ENDPOINT` | `https://api.chieflab.io/api/mcp` | Override (staging / self-hosted). |
|
|
94
|
+
| `CHIEFLAB_TIMEOUT_MS` | `120000` | Per-request timeout. |
|
|
95
|
+
|
|
96
|
+
## Self-host the full server
|
|
97
|
+
|
|
98
|
+
If you want a stdio MCP that runs the full ChiefLab tool set locally (Supabase persistence, connector OAuth, your own Zernio/Resend keys), clone the source repo and run `node apps/mcp/src/server.js` from there. This npm package is the **client-side bridge** to the hosted runtime; `apps/mcp` is the **server-side runtime itself**.
|
|
99
|
+
|
|
100
|
+
## Source
|
|
101
|
+
|
|
102
|
+
<https://github.com/bdentech/chieflab/tree/main/packages/mcp-server> · Apache 2.0
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chieflab/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"mcpName": "io.github.bdentech/chieflab",
|
|
5
|
+
"description": "ChiefLab stdio MCP server — after-build GTM for agents. Bridges Cursor / Claude Desktop / Claude Code / Codex / Windsurf / Cline to the hosted ChiefLab MCP so agents can launch products, get users, approval-gate execution, and measure the next move.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"chieflab-mcp": "src/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src/",
|
|
12
|
+
"README.md"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=20"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"chieflab",
|
|
19
|
+
"chiefmo",
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"ai-agent",
|
|
23
|
+
"agent-built",
|
|
24
|
+
"get-users",
|
|
25
|
+
"product-launch",
|
|
26
|
+
"marketing",
|
|
27
|
+
"launch",
|
|
28
|
+
"gtm",
|
|
29
|
+
"startup",
|
|
30
|
+
"cursor",
|
|
31
|
+
"claude",
|
|
32
|
+
"codex",
|
|
33
|
+
"stdio"
|
|
34
|
+
],
|
|
35
|
+
"homepage": "https://chieflab.io",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "git+https://github.com/bdentech/chieflab.git",
|
|
39
|
+
"directory": "packages/mcp-server"
|
|
40
|
+
},
|
|
41
|
+
"license": "Apache-2.0",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @chieflab/mcp-server — stdio MCP server.
|
|
3
|
+
//
|
|
4
|
+
// Thin bridge from stdio JSON-RPC (what Cursor / Claude Desktop / Claude
|
|
5
|
+
// Code / Codex / Windsurf / Cline speak) to the hosted ChiefLab MCP at
|
|
6
|
+
// chieflab.io/api/mcp via HTTPS. Streams every JSON-RPC frame through
|
|
7
|
+
// untouched — same JSON-RPC 2.0 / MCP 2024-11-05 contract on both sides.
|
|
8
|
+
//
|
|
9
|
+
// Why a bridge instead of bundling the full server? Three reasons:
|
|
10
|
+
// 1. Zero version skew. Whatever tools the hosted MCP exposes today,
|
|
11
|
+
// this client surfaces today. No `npm update` required.
|
|
12
|
+
// 2. ~5KB package. No need to ship packages/core or its transitive
|
|
13
|
+
// Supabase / connector deps just to run a stdio MCP locally.
|
|
14
|
+
// 3. Auth + rate limiting + per-workspace scoping all happen server-side.
|
|
15
|
+
// The local bridge holds nothing sensitive beyond the API key in env.
|
|
16
|
+
//
|
|
17
|
+
// USAGE
|
|
18
|
+
// after npm publish: npm install -g @chieflab/mcp-server
|
|
19
|
+
// CHIEFLAB_API_KEY=clp_live_... chieflab-mcp
|
|
20
|
+
//
|
|
21
|
+
// Or in an MCP host config (Cursor / Claude Desktop):
|
|
22
|
+
// {
|
|
23
|
+
// "mcpServers": {
|
|
24
|
+
// "chieflab": {
|
|
25
|
+
// "command": "chieflab-mcp",
|
|
26
|
+
// "env": { "CHIEFLAB_API_KEY": "clp_live_..." }
|
|
27
|
+
// }
|
|
28
|
+
// }
|
|
29
|
+
// }
|
|
30
|
+
//
|
|
31
|
+
// ENV
|
|
32
|
+
// CHIEFLAB_API_KEY Bearer key. Required for everything except the
|
|
33
|
+
// agent-first signup tool. Get one at
|
|
34
|
+
// https://chieflab.io/get-key (one click, no email).
|
|
35
|
+
// CHIEFLAB_ENDPOINT Override hosted endpoint (default
|
|
36
|
+
// https://api.chieflab.io/api/mcp). Useful for staging
|
|
37
|
+
// or self-hosted runners.
|
|
38
|
+
// CHIEFLAB_TIMEOUT_MS Per-request timeout (default 120000).
|
|
39
|
+
|
|
40
|
+
import readline from "node:readline";
|
|
41
|
+
|
|
42
|
+
const ENDPOINT = process.env.CHIEFLAB_ENDPOINT || "https://api.chieflab.io/api/mcp";
|
|
43
|
+
const API_KEY = process.env.CHIEFLAB_API_KEY || process.env.CHIEFMO_API_KEY || "";
|
|
44
|
+
const TIMEOUT_MS = Number(process.env.CHIEFLAB_TIMEOUT_MS) || 120_000;
|
|
45
|
+
const VERSION = "0.1.0";
|
|
46
|
+
|
|
47
|
+
function writeMessage(message) {
|
|
48
|
+
process.stdout.write(`${JSON.stringify(message)}\n`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function logErr(...args) {
|
|
52
|
+
// MCP stdio: stderr is fine for diagnostics; stdout is reserved for
|
|
53
|
+
// JSON-RPC frames the host will parse.
|
|
54
|
+
process.stderr.write(`[chieflab-mcp] ${args.join(" ")}\n`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async function forwardToHosted(request) {
|
|
58
|
+
const headers = { "Content-Type": "application/json", "Accept": "application/json" };
|
|
59
|
+
if (API_KEY) headers["Authorization"] = `Bearer ${API_KEY}`;
|
|
60
|
+
|
|
61
|
+
const ctrl = new AbortController();
|
|
62
|
+
const t = setTimeout(() => ctrl.abort(), TIMEOUT_MS);
|
|
63
|
+
|
|
64
|
+
let res;
|
|
65
|
+
try {
|
|
66
|
+
res = await fetch(ENDPOINT, {
|
|
67
|
+
method: "POST",
|
|
68
|
+
headers,
|
|
69
|
+
body: JSON.stringify(request),
|
|
70
|
+
signal: ctrl.signal
|
|
71
|
+
});
|
|
72
|
+
} catch (err) {
|
|
73
|
+
clearTimeout(t);
|
|
74
|
+
if (err.name === "AbortError") {
|
|
75
|
+
return {
|
|
76
|
+
jsonrpc: "2.0",
|
|
77
|
+
id: request.id ?? null,
|
|
78
|
+
error: { code: -32000, message: `Hosted ChiefLab MCP timed out after ${TIMEOUT_MS}ms` }
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
jsonrpc: "2.0",
|
|
83
|
+
id: request.id ?? null,
|
|
84
|
+
error: { code: -32000, message: `Network error reaching ${ENDPOINT}: ${err.message}` }
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
clearTimeout(t);
|
|
88
|
+
|
|
89
|
+
const text = await res.text();
|
|
90
|
+
if (!res.ok && res.status === 401) {
|
|
91
|
+
return {
|
|
92
|
+
jsonrpc: "2.0",
|
|
93
|
+
id: request.id ?? null,
|
|
94
|
+
error: {
|
|
95
|
+
code: -32001,
|
|
96
|
+
message: "Unauthorized. Set CHIEFLAB_API_KEY env var (get one at https://chieflab.io/get-key) and restart your MCP host."
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
return {
|
|
102
|
+
jsonrpc: "2.0",
|
|
103
|
+
id: request.id ?? null,
|
|
104
|
+
error: { code: -32000, message: `Hosted MCP returned ${res.status}: ${text.slice(0, 300)}` }
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Pass through verbatim. The hosted endpoint already returns a valid
|
|
109
|
+
// JSON-RPC envelope.
|
|
110
|
+
try {
|
|
111
|
+
return JSON.parse(text);
|
|
112
|
+
} catch {
|
|
113
|
+
return {
|
|
114
|
+
jsonrpc: "2.0",
|
|
115
|
+
id: request.id ?? null,
|
|
116
|
+
error: { code: -32700, message: `Hosted MCP returned non-JSON: ${text.slice(0, 200)}` }
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// notifications/initialized is a notification (no id, no response per spec).
|
|
122
|
+
// Don't forward — short-circuit locally to match hosted behavior.
|
|
123
|
+
async function handleRequest(request) {
|
|
124
|
+
if (!request || typeof request !== "object") {
|
|
125
|
+
return {
|
|
126
|
+
jsonrpc: "2.0",
|
|
127
|
+
id: null,
|
|
128
|
+
error: { code: -32600, message: "Invalid request — not a JSON object" }
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
if (request.method === "notifications/initialized") return null;
|
|
132
|
+
return forwardToHosted(request);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Surface a readable startup line on stderr so host logs show what's running.
|
|
136
|
+
logErr(`@chieflab/mcp-server v${VERSION} → ${ENDPOINT}`);
|
|
137
|
+
if (!API_KEY) {
|
|
138
|
+
logErr("CHIEFLAB_API_KEY not set. Most tools will return 401. Get a key: https://chieflab.io/get-key");
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
142
|
+
|
|
143
|
+
// Track in-flight handler promises so we don't exit before responses are
|
|
144
|
+
// written. MCP hosts pipe many requests in succession; each must complete.
|
|
145
|
+
const inflight = new Set();
|
|
146
|
+
|
|
147
|
+
rl.on("line", (line) => {
|
|
148
|
+
if (!line.trim()) return;
|
|
149
|
+
const work = (async () => {
|
|
150
|
+
let request;
|
|
151
|
+
try {
|
|
152
|
+
request = JSON.parse(line);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
writeMessage({
|
|
155
|
+
jsonrpc: "2.0",
|
|
156
|
+
id: null,
|
|
157
|
+
error: { code: -32700, message: `Parse error: ${err.message}` }
|
|
158
|
+
});
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const response = await handleRequest(request);
|
|
162
|
+
if (response) writeMessage(response);
|
|
163
|
+
})();
|
|
164
|
+
inflight.add(work);
|
|
165
|
+
work.finally(() => inflight.delete(work));
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Clean exit on stdin close — but only after all in-flight responses are written.
|
|
169
|
+
rl.on("close", async () => {
|
|
170
|
+
if (inflight.size > 0) await Promise.allSettled(inflight);
|
|
171
|
+
process.exit(0);
|
|
172
|
+
});
|