@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.
Files changed (3) hide show
  1. package/README.md +102 -0
  2. package/package.json +45 -0
  3. 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
+ });