@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 +0 -1
- package/dist/client.js +0 -1
- package/dist/index.js +0 -1
- package/dist/oauth.js +0 -1
- package/dist/prompts.js +0 -1
- package/dist/setup.js +113 -40
- package/dist/tools/agents.js +0 -1
- package/dist/tools/annotations.js +0 -1
- package/dist/tools/artifacts.js +0 -1
- package/dist/tools/connector_write_back.js +0 -1
- package/dist/tools/converse.js +0 -1
- package/dist/tools/drafts.js +0 -1
- package/dist/tools/errors.js +0 -1
- package/dist/tools/export.js +0 -1
- package/dist/tools/github.js +0 -1
- package/dist/tools/gm.js +0 -1
- package/dist/tools/ingest.js +0 -1
- package/dist/tools/kb.js +0 -1
- package/dist/tools/lake.js +0 -1
- package/dist/tools/move.js +0 -1
- package/dist/tools/outbox.js +0 -1
- package/dist/tools/pyramid.js +0 -1
- package/dist/tools/recall.js +0 -1
- package/dist/tools/registry.js +0 -1
- package/dist/tools/research.js +0 -1
- package/dist/tools/search.js +0 -1
- package/dist/tools/sharing.js +0 -1
- package/dist/tools/skills.js +0 -1
- package/dist/tools/synthesize.js +0 -1
- package/dist/tools/voice.js +0 -1
- package/dist/tools/web-url.js +0 -1
- package/dist/tools/wiki.js +0 -1
- package/dist/transport.js +0 -1
- package/package.json +3 -5
- package/LICENSE +0 -21
package/dist/auth.js
CHANGED
package/dist/client.js
CHANGED
package/dist/index.js
CHANGED
package/dist/oauth.js
CHANGED
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
|
-
* -
|
|
192
|
-
* -
|
|
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
|
-
|
|
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'] ??
|
|
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 →
|
|
369
|
-
|
|
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
|
|
374
|
-
|
|
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
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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.
|
package/dist/tools/agents.js
CHANGED
package/dist/tools/artifacts.js
CHANGED
package/dist/tools/converse.js
CHANGED
package/dist/tools/drafts.js
CHANGED
package/dist/tools/errors.js
CHANGED
package/dist/tools/export.js
CHANGED
package/dist/tools/github.js
CHANGED
package/dist/tools/gm.js
CHANGED
package/dist/tools/ingest.js
CHANGED
package/dist/tools/kb.js
CHANGED
package/dist/tools/lake.js
CHANGED
package/dist/tools/move.js
CHANGED
package/dist/tools/outbox.js
CHANGED
package/dist/tools/pyramid.js
CHANGED
package/dist/tools/recall.js
CHANGED
package/dist/tools/registry.js
CHANGED
package/dist/tools/research.js
CHANGED
package/dist/tools/search.js
CHANGED
package/dist/tools/sharing.js
CHANGED
package/dist/tools/skills.js
CHANGED
package/dist/tools/synthesize.js
CHANGED
package/dist/tools/voice.js
CHANGED
package/dist/tools/web-url.js
CHANGED
package/dist/tools/wiki.js
CHANGED
package/dist/transport.js
CHANGED
package/package.json
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacitai/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "stdio MCP server wrapping Tacit /api/v1 REST/SSE API",
|
|
5
|
-
"license": "
|
|
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.
|