blackops-onboard 0.4.0 → 0.6.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/bin/onboard.mjs +62 -19
- package/package.json +1 -1
package/bin/onboard.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import path from 'node:path'
|
|
|
16
16
|
import os from 'node:os'
|
|
17
17
|
import readline from 'node:readline/promises'
|
|
18
18
|
|
|
19
|
-
const VERSION = '0.
|
|
19
|
+
const VERSION = '0.6.0'
|
|
20
20
|
const INSTALL_URL = process.env.BLACKOPS_INSTALL_URL || 'https://blackopscenter.com/api/install'
|
|
21
21
|
const CONFIG_DIR = path.join(os.homedir(), '.blackops')
|
|
22
22
|
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json')
|
|
@@ -68,6 +68,14 @@ function writeJsonAtomic(filePath, value) {
|
|
|
68
68
|
// All known clients use the same `mcpServers.<name>.{type,url,headers}`
|
|
69
69
|
// JSON shape, so a single writer covers them.
|
|
70
70
|
|
|
71
|
+
// `transport: 'http'` writes the native streamable HTTP entry that
|
|
72
|
+
// Claude Code and Cursor both understand:
|
|
73
|
+
// { type: 'http', url, headers: { Authorization: 'Bearer ...' } }
|
|
74
|
+
//
|
|
75
|
+
// `transport: 'stdio-bridge'` writes an `mcp-remote` stdio shim, used for
|
|
76
|
+
// Claude Desktop and Cline which only speak stdio MCP today:
|
|
77
|
+
// { command: 'npx', args: ['-y', 'mcp-remote', url, '--header', 'Authorization: Bearer ...'] }
|
|
78
|
+
|
|
71
79
|
function getClientConfigs(homedir, platform) {
|
|
72
80
|
const APPDATA = process.env.APPDATA || ''
|
|
73
81
|
return [
|
|
@@ -75,6 +83,7 @@ function getClientConfigs(homedir, platform) {
|
|
|
75
83
|
id: 'claude-code',
|
|
76
84
|
label: 'Claude Code',
|
|
77
85
|
path: path.join(homedir, '.claude', 'settings.json'),
|
|
86
|
+
transport: 'http',
|
|
78
87
|
alwaysInstall: true,
|
|
79
88
|
},
|
|
80
89
|
{
|
|
@@ -86,20 +95,52 @@ function getClientConfigs(homedir, platform) {
|
|
|
86
95
|
: platform === 'win32'
|
|
87
96
|
? path.join(APPDATA, 'Claude', 'claude_desktop_config.json')
|
|
88
97
|
: path.join(homedir, '.config', 'Claude', 'claude_desktop_config.json'),
|
|
98
|
+
transport: 'stdio-bridge',
|
|
89
99
|
},
|
|
90
100
|
{
|
|
91
101
|
id: 'cursor',
|
|
92
102
|
label: 'Cursor',
|
|
93
103
|
path: path.join(homedir, '.cursor', 'mcp.json'),
|
|
104
|
+
transport: 'http',
|
|
94
105
|
},
|
|
95
106
|
{
|
|
96
107
|
id: 'cline',
|
|
97
108
|
label: 'Cline (VS Code)',
|
|
98
109
|
path: path.join(homedir, '.cline', 'mcp_settings.json'),
|
|
110
|
+
transport: 'stdio-bridge',
|
|
99
111
|
},
|
|
100
112
|
]
|
|
101
113
|
}
|
|
102
114
|
|
|
115
|
+
function buildServerEntry(transport, { mcpUrl, token }) {
|
|
116
|
+
if (transport === 'stdio-bridge') {
|
|
117
|
+
// --transport http-only avoids mcp-remote's SSE-fallback path (which has
|
|
118
|
+
// historically hung for several minutes on slow tool calls).
|
|
119
|
+
// --timeout 30000 caps any single request at 30s instead of the 4-minute
|
|
120
|
+
// default, so a stuck call surfaces as an error the agent can recover
|
|
121
|
+
// from instead of a silent hang.
|
|
122
|
+
return {
|
|
123
|
+
command: 'npx',
|
|
124
|
+
args: [
|
|
125
|
+
'-y',
|
|
126
|
+
'mcp-remote',
|
|
127
|
+
mcpUrl,
|
|
128
|
+
'--header',
|
|
129
|
+
`Authorization: Bearer ${token}`,
|
|
130
|
+
'--transport',
|
|
131
|
+
'http-only',
|
|
132
|
+
'--timeout',
|
|
133
|
+
'30000',
|
|
134
|
+
],
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
type: 'http',
|
|
139
|
+
url: mcpUrl,
|
|
140
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
function clientIsInstalled(cfg) {
|
|
104
145
|
if (cfg.alwaysInstall) return true
|
|
105
146
|
// File exists OR its parent dir exists (covers cases where the user
|
|
@@ -112,11 +153,7 @@ function clientIsInstalled(cfg) {
|
|
|
112
153
|
function injectMcpServer(cfg, { mcpServerName, mcpUrl, token }) {
|
|
113
154
|
const existing = readJsonSafe(cfg.path) ?? {}
|
|
114
155
|
existing.mcpServers = existing.mcpServers ?? {}
|
|
115
|
-
existing.mcpServers[mcpServerName] = {
|
|
116
|
-
type: 'http',
|
|
117
|
-
url: mcpUrl,
|
|
118
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
119
|
-
}
|
|
156
|
+
existing.mcpServers[mcpServerName] = buildServerEntry(cfg.transport, { mcpUrl, token })
|
|
120
157
|
writeJsonAtomic(cfg.path, existing)
|
|
121
158
|
}
|
|
122
159
|
|
|
@@ -208,22 +245,28 @@ async function main() {
|
|
|
208
245
|
err(`Could not write ${f.client.label} config: ${f.error}`)
|
|
209
246
|
}
|
|
210
247
|
|
|
211
|
-
// 4. Always print
|
|
248
|
+
// 4. Always print universal snippets as fallback for any other MCP client.
|
|
249
|
+
// Two formats — pick whichever your client supports.
|
|
212
250
|
log()
|
|
213
|
-
log(`${DIM}Using a different MCP client? Add
|
|
251
|
+
log(`${DIM}Using a different MCP client? Add ONE of these to its config under \`mcpServers\`:${RESET}`)
|
|
214
252
|
log()
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
253
|
+
log(`${DIM}// HTTP transport (Claude Code, Cursor, etc.):${RESET}`)
|
|
254
|
+
log(
|
|
255
|
+
`${DIM}${JSON.stringify(
|
|
256
|
+
{ [config.mcp_server_name]: buildServerEntry('http', { mcpUrl: config.mcp_url, token: config.token }) },
|
|
257
|
+
null,
|
|
258
|
+
2
|
|
259
|
+
)}${RESET}`
|
|
260
|
+
)
|
|
261
|
+
log()
|
|
262
|
+
log(`${DIM}// stdio bridge (Claude Desktop, Cline, anything stdio-only):${RESET}`)
|
|
263
|
+
log(
|
|
264
|
+
`${DIM}${JSON.stringify(
|
|
265
|
+
{ [config.mcp_server_name]: buildServerEntry('stdio-bridge', { mcpUrl: config.mcp_url, token: config.token }) },
|
|
266
|
+
null,
|
|
267
|
+
2
|
|
268
|
+
)}${RESET}`
|
|
225
269
|
)
|
|
226
|
-
log(`${DIM}${snippet}${RESET}`)
|
|
227
270
|
|
|
228
271
|
// 5. Tell the user the one thing to say next. Single line, copy-pasteable.
|
|
229
272
|
log()
|
package/package.json
CHANGED