@tacitai/mcp-server 0.3.1 → 0.3.2
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/dist/setup.d.ts +5 -2
- package/dist/setup.js +33 -15
- package/package.json +1 -1
package/dist/setup.d.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Contract (§2.1 of sparc_specification.md, Phase-2 QE resolutions + W5 device-auth):
|
|
8
8
|
* F1 smoke — calls tacit_synthesize via getClient()/postSSE; exits non-zero on empty/error.
|
|
9
|
-
* F2
|
|
9
|
+
* F2 token — when token is obtained (device-auth or --token), writes it directly to mcp.json
|
|
10
|
+
* (file is 0600). Falls back to env-var placeholder "${TACIT_TOKEN_*}" when token
|
|
11
|
+
* came from the environment.
|
|
10
12
|
* Merge — deep-merges ONLY mcpServers[profile]; all other keys preserved.
|
|
11
13
|
* Idempotent — same --api-url + --profile → byte-identical mcp.json.
|
|
12
|
-
* Secrets — token
|
|
14
|
+
* Secrets — token written to mcp.json (0600) when obtained via device-auth or --token;
|
|
15
|
+
* NEVER printed to stdout/stderr (dry-run output is redacted).
|
|
13
16
|
*
|
|
14
17
|
* W5 additions:
|
|
15
18
|
* --profile <name> — profile name (default: 'tacit'); drives env-var name + mcp.json key.
|
package/dist/setup.js
CHANGED
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Contract (§2.1 of sparc_specification.md, Phase-2 QE resolutions + W5 device-auth):
|
|
9
9
|
* F1 smoke — calls tacit_synthesize via getClient()/postSSE; exits non-zero on empty/error.
|
|
10
|
-
* F2
|
|
10
|
+
* F2 token — when token is obtained (device-auth or --token), writes it directly to mcp.json
|
|
11
|
+
* (file is 0600). Falls back to env-var placeholder "${TACIT_TOKEN_*}" when token
|
|
12
|
+
* came from the environment.
|
|
11
13
|
* Merge — deep-merges ONLY mcpServers[profile]; all other keys preserved.
|
|
12
14
|
* Idempotent — same --api-url + --profile → byte-identical mcp.json.
|
|
13
|
-
* Secrets — token
|
|
15
|
+
* Secrets — token written to mcp.json (0600) when obtained via device-auth or --token;
|
|
16
|
+
* NEVER printed to stdout/stderr (dry-run output is redacted).
|
|
14
17
|
*
|
|
15
18
|
* W5 additions:
|
|
16
19
|
* --profile <name> — profile name (default: 'tacit'); drives env-var name + mcp.json key.
|
|
@@ -128,16 +131,19 @@ function readExistingConfig() {
|
|
|
128
131
|
}
|
|
129
132
|
/**
|
|
130
133
|
* Build the MCP server entry for a given profile.
|
|
131
|
-
*
|
|
134
|
+
*
|
|
135
|
+
* When `token` is provided (device-auth or --token), the actual token is written
|
|
136
|
+
* to mcp.json so the user doesn't need to set an env var. mcp.json is written
|
|
137
|
+
* with mode 0600 (owner-only), consistent with how CLI tools store auth tokens
|
|
138
|
+
* (e.g. ~/.npmrc). When `token` is null, the env-var placeholder is written
|
|
139
|
+
* so Claude Code expands ${VAR} at launch.
|
|
132
140
|
*/
|
|
133
|
-
function buildProfileEntry(apiUrl, envVarName) {
|
|
141
|
+
function buildProfileEntry(apiUrl, envVarName, token) {
|
|
134
142
|
return {
|
|
135
143
|
command: 'npx',
|
|
136
144
|
args: ['-y', '@tacitai/mcp-server'],
|
|
137
145
|
env: {
|
|
138
|
-
|
|
139
|
-
[envVarName]: `\${${envVarName}}`,
|
|
140
|
-
// TACIT_API_URL is a non-secret URL; written as-is.
|
|
146
|
+
TACIT_TOKEN: token ?? `\${${envVarName}}`,
|
|
141
147
|
TACIT_API_URL: apiUrl,
|
|
142
148
|
},
|
|
143
149
|
};
|
|
@@ -380,15 +386,22 @@ export async function setup(argv) {
|
|
|
380
386
|
}
|
|
381
387
|
// Final env-var name derived from (possibly updated) profile.
|
|
382
388
|
const envVarName = profileToEnvVar(profile);
|
|
383
|
-
|
|
389
|
+
// Pass the actual token when we have it (device-auth or --token);
|
|
390
|
+
// fall back to env-var placeholder when the token came from the environment.
|
|
391
|
+
const resolvedToken = token ?? null;
|
|
392
|
+
const profileEntry = buildProfileEntry(apiUrl, envVarName, resolvedToken);
|
|
384
393
|
const existing = readExistingConfig();
|
|
385
394
|
const merged = mergeConfig(existing, profile, profileEntry);
|
|
386
395
|
const output = configToBytes(merged);
|
|
387
396
|
// D10: emit confirmation string BEFORE writing (also printed in dry-run).
|
|
388
397
|
process.stdout.write(`Writing tacit entry to ~/.claude/mcp.json (profile: ${profile})...\n`);
|
|
389
398
|
if (opts.dryRun) {
|
|
399
|
+
// Redact any literal token in dry-run preview to avoid leaking secrets to stdout.
|
|
400
|
+
const redacted = resolvedToken
|
|
401
|
+
? configToBytes(mergeConfig(existing, profile, buildProfileEntry(apiUrl, envVarName, null)))
|
|
402
|
+
: output;
|
|
390
403
|
process.stdout.write('(dry-run) Would write:\n');
|
|
391
|
-
process.stdout.write(
|
|
404
|
+
process.stdout.write(redacted);
|
|
392
405
|
process.exit(0);
|
|
393
406
|
return;
|
|
394
407
|
}
|
|
@@ -399,16 +412,21 @@ export async function setup(argv) {
|
|
|
399
412
|
}
|
|
400
413
|
const dest = mcpJsonPath();
|
|
401
414
|
fs.writeFileSync(dest, output, { encoding: 'utf-8', mode: 0o600 });
|
|
415
|
+
// Ensure 0600 even when the file pre-existed with looser permissions.
|
|
416
|
+
fs.chmodSync(dest, 0o600);
|
|
402
417
|
// AC-12: write ~/.tacit/profiles/<profile>.json metadata (token NOT included).
|
|
403
418
|
if (expiresAt) {
|
|
404
419
|
writeProfileMetadata(profile, expiresAt, apiUrl);
|
|
405
420
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
421
|
+
if (resolvedToken) {
|
|
422
|
+
process.stdout.write(`\nSetup complete. Your token has been saved to ~/.claude/mcp.json.\n` +
|
|
423
|
+
`Start a new Claude Code session to connect.\n`);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
process.stdout.write(`\nSetup complete. Add this to your shell profile:\n` +
|
|
427
|
+
` export ${envVarName}=<your-token>\n` +
|
|
428
|
+
`\nThen start a new Claude Code session to connect.\n`);
|
|
429
|
+
}
|
|
412
430
|
if (opts.smoke) {
|
|
413
431
|
await runSmoke(apiUrl, token);
|
|
414
432
|
// runSmoke calls process.exit internally; return here for type safety.
|