blackops-onboard 0.1.0 → 0.4.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 +18 -5
  2. package/bin/onboard.mjs +115 -49
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -2,17 +2,30 @@
2
2
 
3
3
  Agent-first onboarding for [BlackOps Center](https://blackopscenter.com).
4
4
 
5
+ **The whole flow:**
6
+
5
7
  ```bash
6
8
  npx blackops-onboard
7
9
  ```
8
10
 
9
- That command:
11
+ Then open your MCP-capable agent (Claude Code, Claude Desktop, Cursor, Cline, …) and say:
12
+
13
+ > Help me onboard to BlackOps Center and publish my first piece of content.
14
+
15
+ That's it. The agent walks you through account creation, brand voice, social OAuth (X / Threads / LinkedIn), publishing your first four pieces of content, and getting a magic link to your dashboard.
16
+
17
+ ## Supported MCP clients
18
+
19
+ The CLI auto-detects and writes the BlackOps MCP server entry to any of these configs it finds:
10
20
 
11
- 1. Provisions an onboarding token for you.
12
- 2. Adds a `blackops` MCP server entry to your Claude Code settings (`~/.claude/settings.json`).
13
- 3. Prints a prompt to paste into Claude Code.
21
+ | Client | Config path |
22
+ |---|---|
23
+ | Claude Code | `~/.claude/settings.json` |
24
+ | Claude Desktop | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` · Windows: `%APPDATA%\Claude\claude_desktop_config.json` · Linux: `~/.config/Claude/claude_desktop_config.json` |
25
+ | Cursor | `~/.cursor/mcp.json` |
26
+ | Cline (VS Code) | `~/.cline/mcp_settings.json` |
14
27
 
15
- From there, the agent walks you through account creation, brand-voice capture, social-account OAuth, and publishing your first content across X, Threads, LinkedIn, and your new blog — entirely conversationally.
28
+ For any other MCP client, the CLI also prints a universal JSON snippet you can paste into that client's `mcpServers` config.
16
29
 
17
30
  ## Re-running
18
31
 
package/bin/onboard.mjs CHANGED
@@ -4,8 +4,9 @@
4
4
  // What this does:
5
5
  // 1. POSTs to https://blackopscenter.com/api/install to mint an onboarding token.
6
6
  // 2. Writes ~/.blackops/config.json with token + MCP URL.
7
- // 3. Adds a `blackops` MCP server entry to Claude Code's settings.json.
8
- // 4. Prints the onboarding prompt for the user to paste into Claude Code.
7
+ // 3. Detects installed MCP clients (Claude Code, Claude Desktop, Cursor, Cline)
8
+ // and adds a `blackops` MCP server entry to each one's config.
9
+ // 4. Always prints a universal copy-paste snippet as fallback for unknown clients.
9
10
  //
10
11
  // Re-runs are idempotent: an existing ~/.blackops/config.json will prompt the
11
12
  // user before overwriting.
@@ -15,7 +16,7 @@ import path from 'node:path'
15
16
  import os from 'node:os'
16
17
  import readline from 'node:readline/promises'
17
18
 
18
- const VERSION = '0.1.0'
19
+ const VERSION = '0.4.0'
19
20
  const INSTALL_URL = process.env.BLACKOPS_INSTALL_URL || 'https://blackopscenter.com/api/install'
20
21
  const CONFIG_DIR = path.join(os.homedir(), '.blackops')
21
22
  const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json')
@@ -41,18 +42,6 @@ async function confirm(question) {
41
42
  }
42
43
  }
43
44
 
44
- function findClaudeCodeSettingsPath() {
45
- // Claude Code stores user-level settings in ~/.claude/settings.json on macOS/Linux.
46
- // (The same file holds permissions, hooks, and MCP servers.)
47
- const candidates = [
48
- path.join(os.homedir(), '.claude', 'settings.json'),
49
- ]
50
- for (const p of candidates) {
51
- if (fs.existsSync(p)) return p
52
- }
53
- return candidates[0] // first candidate; we'll create it
54
- }
55
-
56
45
  function readJsonSafe(filePath) {
57
46
  try {
58
47
  const raw = fs.readFileSync(filePath, 'utf8')
@@ -69,6 +58,68 @@ function writeJsonAtomic(filePath, value) {
69
58
  fs.renameSync(tmp, filePath)
70
59
  }
71
60
 
61
+ // ── MCP client detection ────────────────────────────────────────────────
62
+ //
63
+ // Each entry is one MCP client we know how to install into. `path` is where
64
+ // the client stores its MCP config; `installed` is a heuristic that checks
65
+ // whether the client appears to be set up. `alwaysInstall` is true for
66
+ // Claude Code (the canonical target — we create the file if missing).
67
+ //
68
+ // All known clients use the same `mcpServers.<name>.{type,url,headers}`
69
+ // JSON shape, so a single writer covers them.
70
+
71
+ function getClientConfigs(homedir, platform) {
72
+ const APPDATA = process.env.APPDATA || ''
73
+ return [
74
+ {
75
+ id: 'claude-code',
76
+ label: 'Claude Code',
77
+ path: path.join(homedir, '.claude', 'settings.json'),
78
+ alwaysInstall: true,
79
+ },
80
+ {
81
+ id: 'claude-desktop',
82
+ label: 'Claude Desktop',
83
+ path:
84
+ platform === 'darwin'
85
+ ? path.join(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
86
+ : platform === 'win32'
87
+ ? path.join(APPDATA, 'Claude', 'claude_desktop_config.json')
88
+ : path.join(homedir, '.config', 'Claude', 'claude_desktop_config.json'),
89
+ },
90
+ {
91
+ id: 'cursor',
92
+ label: 'Cursor',
93
+ path: path.join(homedir, '.cursor', 'mcp.json'),
94
+ },
95
+ {
96
+ id: 'cline',
97
+ label: 'Cline (VS Code)',
98
+ path: path.join(homedir, '.cline', 'mcp_settings.json'),
99
+ },
100
+ ]
101
+ }
102
+
103
+ function clientIsInstalled(cfg) {
104
+ if (cfg.alwaysInstall) return true
105
+ // File exists OR its parent dir exists (covers cases where the user
106
+ // has the app installed but hasn't created an MCP config file yet).
107
+ if (fs.existsSync(cfg.path)) return true
108
+ if (fs.existsSync(path.dirname(cfg.path))) return true
109
+ return false
110
+ }
111
+
112
+ function injectMcpServer(cfg, { mcpServerName, mcpUrl, token }) {
113
+ const existing = readJsonSafe(cfg.path) ?? {}
114
+ existing.mcpServers = existing.mcpServers ?? {}
115
+ existing.mcpServers[mcpServerName] = {
116
+ type: 'http',
117
+ url: mcpUrl,
118
+ headers: { Authorization: `Bearer ${token}` },
119
+ }
120
+ writeJsonAtomic(cfg.path, existing)
121
+ }
122
+
72
123
  async function callInstallEndpoint() {
73
124
  const response = await fetch(INSTALL_URL, {
74
125
  method: 'POST',
@@ -86,24 +137,6 @@ async function callInstallEndpoint() {
86
137
  return data
87
138
  }
88
139
 
89
- function injectClaudeCodeMcp({ mcpUrl, mcpServerName, token }) {
90
- const settingsPath = findClaudeCodeSettingsPath()
91
- const settings = readJsonSafe(settingsPath) ?? {}
92
- settings.mcpServers = settings.mcpServers ?? {}
93
-
94
- // Use the SSE/HTTP transport — Claude Code supports `type: "http"` MCP servers.
95
- settings.mcpServers[mcpServerName] = {
96
- type: 'http',
97
- url: mcpUrl,
98
- headers: {
99
- Authorization: `Bearer ${token}`,
100
- },
101
- }
102
-
103
- writeJsonAtomic(settingsPath, settings)
104
- return settingsPath
105
- }
106
-
107
140
  async function main() {
108
141
  log(`${BOLD}${CYAN}BlackOps Center — agent-first onboarding${RESET}`)
109
142
  log(`${DIM}v${VERSION}${RESET}`)
@@ -146,26 +179,59 @@ async function main() {
146
179
  writeJsonAtomic(CONFIG_PATH, config)
147
180
  log(`${GREEN}✓${RESET} Saved config to ${CONFIG_PATH}`)
148
181
 
149
- // 3. Wire up Claude Code MCP.
150
- let settingsPath
151
- try {
152
- settingsPath = injectClaudeCodeMcp({
153
- mcpUrl: config.mcp_url,
154
- mcpServerName: config.mcp_server_name,
155
- token: config.token,
156
- })
157
- log(`${GREEN}✓${RESET} Added MCP server "${config.mcp_server_name}" to ${settingsPath}`)
158
- } catch (e) {
159
- err(`Could not write Claude Code settings: ${e.message}`)
160
- log('You can configure manually by adding this MCP server to your Claude Code settings:')
161
- log(JSON.stringify({ [config.mcp_server_name]: { type: 'http', url: config.mcp_url, headers: { Authorization: `Bearer ${config.token}` } } }, null, 2))
182
+ // 3. Detect installed MCP clients and write to each one we recognize.
183
+ const clientConfigs = getClientConfigs(os.homedir(), process.platform)
184
+ const installedTo = []
185
+ const failed = []
186
+ for (const c of clientConfigs) {
187
+ if (!clientIsInstalled(c)) continue
188
+ try {
189
+ injectMcpServer(c, {
190
+ mcpServerName: config.mcp_server_name,
191
+ mcpUrl: config.mcp_url,
192
+ token: config.token,
193
+ })
194
+ installedTo.push(c)
195
+ } catch (e) {
196
+ failed.push({ client: c, error: e.message })
197
+ }
198
+ }
199
+
200
+ if (installedTo.length === 0) {
201
+ log(`${YELLOW}No supported MCP client configs detected.${RESET}`)
202
+ } else {
203
+ for (const c of installedTo) {
204
+ log(`${GREEN}✓${RESET} Added MCP server "${config.mcp_server_name}" to ${c.label} (${c.path})`)
205
+ }
206
+ }
207
+ for (const f of failed) {
208
+ err(`Could not write ${f.client.label} config: ${f.error}`)
162
209
  }
163
210
 
164
- // 4. Print onboarding prompt for the user.
211
+ // 4. Always print the universal snippet as fallback for any other MCP client.
212
+ log()
213
+ log(`${DIM}Using a different MCP client? Add this to its config under \`mcpServers\`:${RESET}`)
214
+ log()
215
+ const snippet = JSON.stringify(
216
+ {
217
+ [config.mcp_server_name]: {
218
+ type: 'http',
219
+ url: config.mcp_url,
220
+ headers: { Authorization: `Bearer ${config.token}` },
221
+ },
222
+ },
223
+ null,
224
+ 2
225
+ )
226
+ log(`${DIM}${snippet}${RESET}`)
227
+
228
+ // 5. Tell the user the one thing to say next. Single line, copy-pasteable.
229
+ log()
230
+ log(`${GREEN}✓${RESET} Installed.`)
165
231
  log()
166
- log(`${BOLD}Open Claude Code and paste this prompt to begin:${RESET}`)
232
+ log(`Open your MCP-capable agent (Claude Code, Cursor, etc.) and say:`)
167
233
  log()
168
- log(`${CYAN}${config.prompt}${RESET}`)
234
+ log(` ${CYAN}${config.prompt}${RESET}`)
169
235
  log()
170
236
  log(`${DIM}Token expires: ${config.expires_at}${RESET}`)
171
237
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blackops-onboard",
3
- "version": "0.1.0",
3
+ "version": "0.4.0",
4
4
  "description": "Agent-first onboarding for BlackOps Center. Run via `npx blackops-onboard` to provision your account through Claude Code.",
5
5
  "type": "module",
6
6
  "publishConfig": {