@tacitai/mcp-server 0.5.0 → 0.6.1

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/auth.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * HTTPS transport auth + rate limiting.
4
3
  *
package/dist/client.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * TacitClient — REST/SSE client over TACIT_API_URL.
4
3
  *
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // SPDX-License-Identifier: MIT
3
2
  /**
4
3
  * @tacit/mcp-server — stdio MCP server
5
4
  *
package/dist/oauth.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * OAuth 2.0 Authorization Server for MCP (FEAT-176).
4
3
  *
package/dist/prompts.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  import { z } from 'zod';
3
2
  const ASSISTANT_SYSTEM_PROMPT_STATIC = "You are TACIT's workspace assistant. Answer from the user's workspace through TACIT tools. " +
4
3
  'Prefer a single read tool for direct lookups, synthesis for open questions, and ask before any write or dispatch.';
package/dist/setup.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * setup — wizard subcommand for @tacit/mcp-server
4
3
  *
@@ -33,6 +32,7 @@ import * as path from 'path';
33
32
  import * as os from 'os';
34
33
  import * as http from 'http';
35
34
  import * as crypto from 'crypto';
35
+ import * as readline from 'readline';
36
36
  import { execFile } from 'child_process';
37
37
  import { TacitClient, TacitApiError } from './client.js';
38
38
  function parseArgs(argv) {
@@ -188,11 +188,24 @@ const DEVICE_AUTH_TIMEOUT_MS = 120_000;
188
188
  * - Token plaintext is NEVER logged.
189
189
  *
190
190
  * Error paths:
191
- * - Port 9123 occupied → print error + --token fallback instructions → exit 1 (AC-15).
192
- * - 120s timeout → exit 1 (AC-15).
191
+ * - /auth/device returns 404 → reject immediately (pre-flight probe).
192
+ * - Port 9123 occupied → reject (caller falls back to loginFlow).
193
+ * - 120s timeout → reject (caller falls back to loginFlow).
193
194
  * - Browser open fails → print URL to stdout for manual copy-paste (AC-16 fallback).
194
195
  */
195
196
  async function deviceAuthFlow(apiUrl) {
197
+ // Pre-flight: check if /auth/device exists before opening a browser to it.
198
+ try {
199
+ const probe = await fetch(`${apiUrl}/auth/device`, { method: 'HEAD', redirect: 'manual' });
200
+ if (probe.status === 404) {
201
+ throw new Error('device-auth endpoint not available');
202
+ }
203
+ }
204
+ catch (err) {
205
+ if (err instanceof Error && err.message === 'device-auth endpoint not available')
206
+ throw err;
207
+ // Network error (e.g., no connectivity) — let device auth attempt anyway.
208
+ }
196
209
  // 1. Generate a cryptographically random state nonce (32 bytes, hex).
197
210
  const stateNonce = crypto.randomBytes(32).toString('hex');
198
211
  return new Promise((resolve, reject) => {
@@ -264,14 +277,6 @@ async function deviceAuthFlow(apiUrl) {
264
277
  }
265
278
  });
266
279
  server.on('error', (err) => {
267
- if (err.code === 'EADDRINUSE') {
268
- process.stderr.write(`Error: port ${DEVICE_AUTH_CALLBACK_PORT} is already in use.\n` +
269
- `Cannot start device-auth callback server.\n` +
270
- `Use --token <your-token> instead:\n` +
271
- ` npx @tacitai/mcp-server setup --api-url ${apiUrl} --token <your-token>\n` +
272
- `Get your token from: ${apiUrl}/settings\n`);
273
- process.exit(1);
274
- }
275
280
  cleanup();
276
281
  reject(err);
277
282
  });
@@ -302,18 +307,89 @@ async function deviceAuthFlow(apiUrl) {
302
307
  ` ${deviceUrl}\n`);
303
308
  }
304
309
  });
305
- // 5. Timeout guard (AC-15).
310
+ // 5. Timeout guard (AC-15). Reject so main() falls back to loginFlow.
306
311
  timeoutHandle = setTimeout(() => {
307
312
  cleanup();
308
- process.stderr.write(`Error: device-auth timed out after ${DEVICE_AUTH_TIMEOUT_MS / 1000}s.\n` +
309
- `Use --token <your-token> instead:\n` +
310
- ` npx @tacitai/mcp-server setup --api-url ${apiUrl} --token <your-token>\n` +
311
- `Get your token from: ${apiUrl}/settings\n`);
312
- process.exit(1);
313
+ reject(new Error(`device-auth timed out after ${DEVICE_AUTH_TIMEOUT_MS / 1000}s`));
313
314
  }, DEVICE_AUTH_TIMEOUT_MS);
314
315
  });
315
316
  });
316
317
  }
318
+ function prompt(question, hidden = false) {
319
+ return new Promise((resolve) => {
320
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
321
+ if (hidden && process.stdin.isTTY) {
322
+ process.stderr.write(question);
323
+ const stdin = process.stdin;
324
+ stdin.setRawMode(true);
325
+ stdin.resume();
326
+ stdin.setEncoding('utf-8');
327
+ let input = '';
328
+ const onData = (ch) => {
329
+ if (ch === '\n' || ch === '\r' || ch === '\u0004') {
330
+ stdin.setRawMode(false);
331
+ stdin.pause();
332
+ stdin.removeListener('data', onData);
333
+ rl.close();
334
+ process.stderr.write('\n');
335
+ resolve(input);
336
+ }
337
+ else if (ch === '\u007F' || ch === '\b') {
338
+ if (input.length > 0)
339
+ input = input.slice(0, -1);
340
+ }
341
+ else if (ch === '\u0003') {
342
+ process.exit(1);
343
+ }
344
+ else {
345
+ input += ch;
346
+ }
347
+ };
348
+ stdin.on('data', onData);
349
+ }
350
+ else {
351
+ rl.question(question, (answer) => {
352
+ rl.close();
353
+ resolve(answer.trim());
354
+ });
355
+ }
356
+ });
357
+ }
358
+ async function loginFlow(apiUrl) {
359
+ process.stderr.write('\n Log in with your TACIT credentials.\n\n');
360
+ const email = await prompt(' Email: ');
361
+ const pin = await prompt(' PIN: ', true);
362
+ if (!email || !pin) {
363
+ process.stderr.write('Error: email and PIN are required.\n');
364
+ process.exit(1);
365
+ }
366
+ const loginUrl = apiUrl.endsWith('/api/v1')
367
+ ? `${apiUrl}/auth/login`
368
+ : `${apiUrl}/api/v1/auth/login`;
369
+ const res = await fetch(loginUrl, {
370
+ method: 'POST',
371
+ headers: { 'Content-Type': 'application/json' },
372
+ body: JSON.stringify({ email, pin }),
373
+ });
374
+ if (!res.ok) {
375
+ const body = await res.text().catch(() => '');
376
+ if (res.status === 401) {
377
+ process.stderr.write('\n Login failed — check your email and PIN.\n');
378
+ }
379
+ else {
380
+ process.stderr.write(`\n Login failed (${res.status}): ${body}\n`);
381
+ }
382
+ process.exit(1);
383
+ }
384
+ const data = await res.json();
385
+ const token = typeof data['session_token'] === 'string' ? data['session_token'] : '';
386
+ const workspaceName = typeof data['workspace_name'] === 'string' ? data['workspace_name'] : '';
387
+ if (!token) {
388
+ process.stderr.write('Error: login returned no session token.\n');
389
+ process.exit(1);
390
+ }
391
+ return { token, workspaceName };
392
+ }
317
393
  // ─── Smoke synthesis ──────────────────────────────────────────────────────────
318
394
  const SMOKE_QUERY = 'What is in this knowledge base?';
319
395
  async function runSmoke(apiUrl, token) {
@@ -348,15 +424,8 @@ async function runSmoke(apiUrl, token) {
348
424
  // ─── Main export ──────────────────────────────────────────────────────────────
349
425
  export async function setup(argv) {
350
426
  const opts = parseArgs(argv);
351
- // Resolve api-url: flag > env.
352
- const apiUrl = opts.apiUrl ?? process.env['TACIT_API_URL'] ?? null;
353
- if (!apiUrl) {
354
- process.stderr.write('Error: no API URL configured.\n' +
355
- 'Provide --api-url <url> or set TACIT_API_URL environment variable.\n' +
356
- 'Example: npx @tacitai/mcp-server setup --api-url https://tacit-beta-prod.fly.dev --token <your-token>\n' +
357
- '\nGet your token from: https://beta.tacitai.app → Settings → Bearer token\n');
358
- process.exit(1);
359
- }
427
+ // Resolve api-url: flag > env > default.
428
+ const apiUrl = opts.apiUrl ?? process.env['TACIT_API_URL'] ?? 'https://beta.tacitai.app';
360
429
  // Determine profile and token.
361
430
  let profile = opts.profile;
362
431
  let token = opts.token;
@@ -365,23 +434,27 @@ export async function setup(argv) {
365
434
  const envVarNameForProfile = profileToEnvVar(profile);
366
435
  const envToken = process.env[envVarNameForProfile] ?? process.env['TACIT_TOKEN'] ?? null;
367
436
  if (!token && !envToken && !opts.dryRun) {
368
- // Neither --token nor env set and not a dry-run → run automated device-auth flow.
369
- let deviceResult;
437
+ // Neither --token nor env set and not a dry-run → authenticate interactively.
438
+ // Try device-auth (browser flow) first; fall back to login prompt.
439
+ let authenticated = false;
370
440
  try {
371
- deviceResult = await deviceAuthFlow(apiUrl);
441
+ const deviceResult = await deviceAuthFlow(apiUrl);
442
+ token = deviceResult.token;
443
+ expiresAt = deviceResult.expiresAt;
444
+ if (!opts.profileExplicit && deviceResult.workspaceName) {
445
+ profile = workspaceNameToProfile(deviceResult.workspaceName);
446
+ }
447
+ authenticated = true;
372
448
  }
373
- catch (err) {
374
- const msg = err instanceof Error ? err.message : String(err);
375
- process.stderr.write(`Error during device authentication: ${msg}\n`);
376
- process.exit(1);
377
- return; // unreachable; satisfies TypeScript
449
+ catch {
450
+ // Device-auth not available — fall back to login prompt.
378
451
  }
379
- // Token from device flow — use in memory only (NEVER log/write).
380
- token = deviceResult.token;
381
- expiresAt = deviceResult.expiresAt;
382
- // AC-12: derive profile from workspace_name unless --profile was explicit.
383
- if (!opts.profileExplicit && deviceResult.workspaceName) {
384
- profile = workspaceNameToProfile(deviceResult.workspaceName);
452
+ if (!authenticated) {
453
+ const loginResult = await loginFlow(apiUrl);
454
+ token = loginResult.token;
455
+ if (!opts.profileExplicit && loginResult.workspaceName) {
456
+ profile = workspaceNameToProfile(loginResult.workspaceName);
457
+ }
385
458
  }
386
459
  }
387
460
  // Final env-var name derived from (possibly updated) profile.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Agent tools — Claude CLI wrappers around the Agent Factory MCP surface.
4
3
  *
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Annotation tools:
4
3
  * tacit_annotate — POST /api/v1/artifacts/:id/annotations
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Artifact tools:
4
3
  * tacit_artifact_list — GET /api/v1/artifacts
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  // MV-1531b W3: Connector write-back MCP tool
3
2
  import { z } from 'zod';
4
3
  export function registerConnectorWriteBackTools(server, getClientFn) {
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * tacit_converse — conversational AI tool (MV-1523 W5)
4
3
  *
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Draft tools (MV-1515n W5 / FEAT-142):
4
3
  * tacit_draft_search — GET /api/v1/artifacts/drafts/search
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  import { TacitApiError, TokenExpiredError } from '../client.js';
3
2
  export function success(data) {
4
3
  return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Export tool:
4
3
  * tacit_export_to_git — POST /api/v1/export/git
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * GitHub connection tools — list, disconnect and reconnect GitHub sync
4
3
  * connections (BUG-305).
package/dist/tools/gm.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * GM (Gateway Manager) tools — unified content ingestion with classification
4
3
  * and human-approved placement across lake, KB, and pyramid surfaces.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Ingest tools:
4
3
  * tacit_ingest_file — POST /api/v1/portfolios/:id/ingest
package/dist/tools/kb.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * KB tools — CLI wrappers for knowledge collections, articles, categories,
4
3
  * and browse/promote flows on the Axum REST API.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Lake tools — CLI wrappers for data lake source, sync, proposal,
4
3
  * classification, catalog, taxonomy, and subject-classification operations
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Move tool:
4
3
  * tacit_move — PATCH /api/v1/objects/:id/placement
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * FEAT-111 Wave 2 — Outbox MCP tools:
4
3
  * tacit_outbox_write — Write an envelope to the local outbox queue
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Pyramid tools — CLI wrappers for portfolio, program, dossier, and
4
3
  * value-stream CRUD on the Axum REST API.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * tacit_recall — Chronicle event recall (hybrid vector)
4
3
  * Route: POST /api/v1/mind/recall
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Compile-time tool classification. Governs which tools may be called over HTTPS.
4
3
  * Partition: 6 stdio_only + 17 admin_only + 2 gate_only + 121 https = 146.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Research tools (MV-1525) — scope discovery + grounded prompt assembly,
4
3
  * pyramid/KB placement with T3 confirmation, and async Gemini launch.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * tacit_search — hybrid BM25+ANN+RRF search
4
3
  * Route: GET /api/v1/search?q=<term>
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * MV-1518b: Guest sharing MCP tools
4
3
  * tacit_shared_list — list artifacts shared with the guest
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * tacit_list_skills / tacit_run_skill — MCP skill orchestration (MV-1533 W6)
4
3
  *
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * tacit_synthesize — canonical synthesis tool
4
3
  *
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Voice resonance tools — profile management, stats, sample upload,
4
3
  * and voice-applied composition via crew_compose + voice_mode.
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  import { z } from 'zod';
3
2
  import { getClient } from '../client.js';
4
3
  const ENTITY_TYPES = [
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * Wiki governance tools — validate articles against governance rules,
4
3
  * apply overrides, write to wiki repos, and author content via LLM.
package/dist/transport.js CHANGED
@@ -1,4 +1,3 @@
1
- // SPDX-License-Identifier: MIT
2
1
  /**
3
2
  * HTTPS (Streamable HTTP) transport for external MCP clients.
4
3
  *
package/package.json CHANGED
@@ -1,16 +1,14 @@
1
1
  {
2
2
  "name": "@tacitai/mcp-server",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "stdio MCP server wrapping Tacit /api/v1 REST/SSE API",
5
- "license": "MIT",
5
+ "license": "UNLICENSED",
6
+ "private": false,
6
7
  "repository": {
7
8
  "type": "git",
8
9
  "url": "git+https://github.com/scottkmcmillan/tacit.git",
9
10
  "directory": "packages/tacit-mcp-server"
10
11
  },
11
- "publishConfig": {
12
- "access": "public"
13
- },
14
12
  "type": "module",
15
13
  "main": "dist/index.js",
16
14
  "bin": {
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Quarterback AI
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.