@tacitai/mcp-server 0.1.0 → 0.2.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 (69) hide show
  1. package/README.md +3 -3
  2. package/dist/auth.d.ts +32 -0
  3. package/dist/auth.js +64 -0
  4. package/dist/client.d.ts +60 -5
  5. package/dist/client.js +234 -53
  6. package/dist/index.js +128 -106
  7. package/dist/oauth.d.ts +115 -0
  8. package/dist/oauth.js +619 -0
  9. package/dist/prompts.d.ts +2 -0
  10. package/dist/prompts.js +12 -0
  11. package/dist/setup.d.ts +28 -5
  12. package/dist/setup.js +267 -60
  13. package/dist/tools/agents.d.ts +277 -0
  14. package/dist/tools/agents.js +335 -0
  15. package/dist/tools/annotations.d.ts +117 -0
  16. package/dist/tools/annotations.js +130 -0
  17. package/dist/tools/artifacts.d.ts +178 -8
  18. package/dist/tools/artifacts.js +372 -81
  19. package/dist/tools/chronicle.d.ts +3 -0
  20. package/dist/tools/chronicle.js +165 -0
  21. package/dist/tools/connector_write_back.d.ts +3 -0
  22. package/dist/tools/connector_write_back.js +27 -0
  23. package/dist/tools/converse.d.ts +10 -0
  24. package/dist/tools/converse.js +88 -0
  25. package/dist/tools/crews.d.ts +3 -0
  26. package/dist/tools/crews.js +115 -0
  27. package/dist/tools/drafts.d.ts +83 -0
  28. package/dist/tools/drafts.js +86 -0
  29. package/dist/tools/errors.d.ts +15 -0
  30. package/dist/tools/errors.js +21 -0
  31. package/dist/tools/export.d.ts +54 -0
  32. package/dist/tools/export.js +55 -0
  33. package/dist/tools/gm.d.ts +214 -0
  34. package/dist/tools/gm.js +198 -0
  35. package/dist/tools/ingest.d.ts +58 -4
  36. package/dist/tools/ingest.js +109 -76
  37. package/dist/tools/kb.d.ts +544 -0
  38. package/dist/tools/kb.js +687 -0
  39. package/dist/tools/lake-structure.d.ts +3 -0
  40. package/dist/tools/lake-structure.js +126 -0
  41. package/dist/tools/lake.d.ts +932 -0
  42. package/dist/tools/lake.js +1017 -0
  43. package/dist/tools/move.d.ts +200 -0
  44. package/dist/tools/move.js +96 -0
  45. package/dist/tools/outbox.d.ts +274 -0
  46. package/dist/tools/outbox.js +223 -0
  47. package/dist/tools/people.d.ts +3 -0
  48. package/dist/tools/people.js +59 -0
  49. package/dist/tools/pyramid.d.ts +554 -0
  50. package/dist/tools/pyramid.js +758 -0
  51. package/dist/tools/recall.d.ts +2 -0
  52. package/dist/tools/recall.js +28 -17
  53. package/dist/tools/registry.d.ts +17 -0
  54. package/dist/tools/registry.js +197 -0
  55. package/dist/tools/research.d.ts +47 -0
  56. package/dist/tools/research.js +575 -0
  57. package/dist/tools/search.d.ts +9 -0
  58. package/dist/tools/search.js +86 -25
  59. package/dist/tools/sharing.d.ts +63 -0
  60. package/dist/tools/sharing.js +157 -0
  61. package/dist/tools/synthesize.d.ts +54 -2
  62. package/dist/tools/synthesize.js +80 -23
  63. package/dist/tools/web-url.d.ts +57 -0
  64. package/dist/tools/web-url.js +74 -0
  65. package/dist/tools/workspaces.d.ts +3 -0
  66. package/dist/tools/workspaces.js +47 -0
  67. package/dist/transport.d.ts +9 -0
  68. package/dist/transport.js +209 -0
  69. package/package.json +5 -4
@@ -1,9 +1,8 @@
1
- "use strict";
2
1
  // SPDX-License-Identifier: MIT
3
2
  /**
4
3
  * Artifact tools:
5
4
  * tacit_artifact_list — GET /api/v1/artifacts
6
- * tacit_artifact_create — POST /api/v1/artifacts (career-scoped handler)
5
+ * tacit_artifact_create — POST /api/v1/artifacts
7
6
  * tacit_artifact_update — PATCH /api/v1/artifacts/:id
8
7
  * tacit_artifact_publish — POST /api/v1/artifacts/:id/publish
9
8
  *
@@ -16,54 +15,64 @@
16
15
  * - artifact_kind is hardcoded server-side (GENERIC) — the client must NOT send it.
17
16
  * - The artifact body is populated server-side from structured_metadata
18
17
  * (e.g. full_posting_text); the client does NOT send a content field.
19
- * - This tool always sends "domain":"CAREER" in the request body.
18
+ * - domain defaults to GENERAL if omitted; caller may pass any valid domain.
20
19
  */
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.ARTIFACT_PUBLISH_TOOL_DEF = exports.artifactPublishInputSchema = exports.ARTIFACT_UPDATE_TOOL_DEF = exports.artifactUpdateInputSchema = exports.ARTIFACT_CREATE_TOOL_DEF = exports.artifactCreateInputSchema = exports.ARTIFACT_LIST_TOOL_DEF = exports.artifactListInputSchema = void 0;
23
- exports.buildArtifactListRequest = buildArtifactListRequest;
24
- exports.handleArtifactList = handleArtifactList;
25
- exports.buildArtifactCreateRequest = buildArtifactCreateRequest;
26
- exports.handleArtifactCreate = handleArtifactCreate;
27
- exports.buildArtifactUpdateRequest = buildArtifactUpdateRequest;
28
- exports.handleArtifactUpdate = handleArtifactUpdate;
29
- exports.buildArtifactPublishRequest = buildArtifactPublishRequest;
30
- exports.handleArtifactPublish = handleArtifactPublish;
31
- const zod_1 = require("zod");
32
- const client_js_1 = require("../client.js");
20
+ import { z } from 'zod';
21
+ import { getClient } from '../client.js';
22
+ import { apiErrorResult } from './errors.js';
33
23
  // ─── List ────────────────────────────────────────────────────────────────────
34
- exports.artifactListInputSchema = zod_1.z.object({
35
- limit: zod_1.z.number().int().positive().optional().describe('Maximum artifacts to return'),
36
- offset: zod_1.z.number().int().nonnegative().optional().describe('Pagination offset'),
37
- status: zod_1.z.string().optional().describe('Filter by status (e.g. PUBLISHED, DRAFT)'),
24
+ export const artifactListInputSchema = z.object({
25
+ q: z.string().optional().describe('Full-text search over title and body (websearch syntax); results include a snippet'),
26
+ origin: z.enum(['IMPORTED', 'SYNTHESIZED', 'WIKI_CONVERGENCE']).optional().describe('Filter by artifact origin'),
27
+ status: z.string().optional().describe('Filter by status — comma-separated (e.g. "DRAFT,UNDER_REVIEW,PUBLISHED,REJECTED")'),
28
+ portfolio_id: z.string().optional().describe('Filter by portfolio UUID'),
29
+ limit: z.number().int().positive().optional().describe('Maximum artifacts to return'),
30
+ offset: z.number().int().nonnegative().optional().describe('Pagination offset (retained for compatibility; ignored by the backend)'),
38
31
  });
39
- exports.ARTIFACT_LIST_TOOL_DEF = {
32
+ export const ARTIFACT_LIST_TOOL_DEF = {
40
33
  name: 'tacit_artifact_list',
41
- description: 'List artifacts from the Tacit workspace. Route: GET /api/v1/artifacts.',
34
+ description: 'List/search artifacts from the Tacit workspace. `q` runs a full-text search over title and body ' +
35
+ '(websearch syntax) and results include a snippet. Route: GET /api/v1/artifacts.',
42
36
  inputSchema: {
43
37
  type: 'object',
44
38
  properties: {
39
+ q: { type: 'string', description: 'Full-text search over title and body (websearch syntax); results include a snippet' },
40
+ origin: { type: 'string', enum: ['IMPORTED', 'SYNTHESIZED', 'WIKI_CONVERGENCE'] },
41
+ status: { type: 'string', description: 'Comma-separated: DRAFT,UNDER_REVIEW,PUBLISHED,REJECTED' },
42
+ portfolio_id: { type: 'string' },
45
43
  limit: { type: 'number', description: 'Maximum artifacts to return' },
46
- offset: { type: 'number', description: 'Pagination offset' },
47
- status: { type: 'string', description: 'Filter by status' },
44
+ offset: { type: 'number', description: 'Pagination offset (retained for compatibility; ignored by the backend)' },
48
45
  },
49
46
  required: [],
50
47
  },
51
48
  };
52
- function buildArtifactListRequest(client, input) {
49
+ export function buildArtifactListRequest(client, input) {
53
50
  return {
54
51
  async execute() {
55
- const { limit, offset, status } = input;
52
+ const { q, origin, status, portfolio_id, limit, offset } = input;
56
53
  try {
57
54
  const params = {};
55
+ if (q)
56
+ params['q'] = q;
57
+ if (origin)
58
+ params['origin'] = origin;
59
+ if (status)
60
+ params['status'] = status;
61
+ if (portfolio_id)
62
+ params['portfolio_id'] = portfolio_id;
58
63
  if (limit !== undefined)
59
64
  params['limit'] = limit;
60
65
  if (offset !== undefined)
61
66
  params['offset'] = offset;
62
- if (status)
63
- params['status'] = status;
64
- // GET /api/v1/artifacts
65
- const result = await client.get('/api/v1/artifacts', params);
66
- return { content: [{ type: 'text', text: JSON.stringify(result) }] };
67
+ // GET /api/v1/artifacts — response is {artifacts: [...]} or bare array
68
+ const raw = await client.get('/api/v1/artifacts', params);
69
+ const result = Array.isArray(raw) ? raw : raw['artifacts'] ?? [];
70
+ const enriched = result.map((item) => {
71
+ const rec = item;
72
+ const url = client.webUrl('artifact', String(rec['id'] ?? ''));
73
+ return url ? { ...rec, web_url: url } : rec;
74
+ });
75
+ return { content: [{ type: 'text', text: JSON.stringify(enriched) }] };
67
76
  }
68
77
  catch (err) {
69
78
  return apiErrorResult(err, '/api/v1/artifacts');
@@ -71,35 +80,36 @@ function buildArtifactListRequest(client, input) {
71
80
  },
72
81
  };
73
82
  }
74
- async function handleArtifactList(args) {
75
- const parsed = exports.artifactListInputSchema.safeParse(args);
83
+ export async function handleArtifactList(args) {
84
+ const parsed = artifactListInputSchema.safeParse(args);
76
85
  if (!parsed.success) {
77
86
  return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
78
87
  }
79
- return buildArtifactListRequest((0, client_js_1.getClient)(), parsed.data).execute();
88
+ return buildArtifactListRequest(getClient(), parsed.data).execute();
80
89
  }
81
90
  // ─── Create ──────────────────────────────────────────────────────────────────
82
- exports.artifactCreateInputSchema = zod_1.z.object({
83
- title: zod_1.z.string().min(1).describe('Artifact title'),
84
- domain: zod_1.z.string().optional().describe('Domain (always overridden to CAREER by this tool)'),
85
- portfolio_id: zod_1.z.string().min(1).describe('Portfolio to associate with (REQUIRED by the backend; 403 without it)'),
86
- structured_metadata: zod_1.z
87
- .record(zod_1.z.unknown())
91
+ export const artifactCreateInputSchema = z.object({
92
+ title: z.string().min(1).describe('Artifact title'),
93
+ domain: z.string().optional().describe('Domain tag (e.g. CAREER, GENERAL, ENGINEERING, PRODUCT). Defaults to GENERAL if omitted.'),
94
+ portfolio_id: z.string().min(1).describe('Portfolio to associate with (REQUIRED by the backend; 403 without it)'),
95
+ structured_metadata: z
96
+ .record(z.unknown())
88
97
  .optional()
89
98
  .describe('Structured metadata; the artifact body is populated server-side from this (e.g. full_posting_text)'),
90
99
  });
91
- exports.ARTIFACT_CREATE_TOOL_DEF = {
100
+ export const ARTIFACT_CREATE_TOOL_DEF = {
92
101
  name: 'tacit_artifact_create',
93
- description: 'Create a career-scoped artifact in Tacit (always domain CAREER). ' +
94
- 'Accepts a title plus an optional structured_metadata object; portfolio_id is required. ' +
102
+ description: 'Create an artifact in Tacit. ' +
103
+ 'Accepts a title, optional domain (CAREER, GENERAL, ENGINEERING, PRODUCT — defaults to GENERAL), ' +
104
+ 'and an optional structured_metadata object; portfolio_id is required. ' +
95
105
  'The artifact body is populated server-side from structured_metadata (e.g. full_posting_text), ' +
96
106
  'and artifact_kind is hardcoded server-side — neither is sent by this tool. ' +
97
- 'Route: POST /api/v1/artifacts (handlers::artifacts_career::create_artifact).',
107
+ 'Route: POST /api/v1/artifacts.',
98
108
  inputSchema: {
99
109
  type: 'object',
100
110
  properties: {
101
111
  title: { type: 'string', description: 'Artifact title' },
102
- domain: { type: 'string', description: 'Domain (always CAREER for this tool)' },
112
+ domain: { type: 'string', description: 'Domain tag (e.g. CAREER, GENERAL, ENGINEERING, PRODUCT). Defaults to GENERAL.' },
103
113
  portfolio_id: { type: 'string', description: 'Portfolio to associate with (REQUIRED; 403 without it)' },
104
114
  structured_metadata: {
105
115
  type: 'object',
@@ -109,16 +119,14 @@ exports.ARTIFACT_CREATE_TOOL_DEF = {
109
119
  required: ['title', 'portfolio_id'],
110
120
  },
111
121
  };
112
- function buildArtifactCreateRequest(client, input) {
122
+ export function buildArtifactCreateRequest(client, input) {
113
123
  return {
114
124
  async execute() {
115
- const { title, portfolio_id, structured_metadata } = input;
125
+ const { title, domain, portfolio_id, structured_metadata } = input;
116
126
  try {
117
- // Backend CreateArtifactRequest accepts only title/domain/output_format/portfolio_id/structured_metadata.
118
- // artifact_kind is hardcoded server-side; the body comes from structured_metadata.full_posting_text.
119
127
  const body = {
120
128
  title,
121
- domain: 'CAREER',
129
+ domain: (domain ?? 'general').toLowerCase(),
122
130
  portfolio_id,
123
131
  };
124
132
  if (structured_metadata !== undefined)
@@ -133,35 +141,36 @@ function buildArtifactCreateRequest(client, input) {
133
141
  },
134
142
  };
135
143
  }
136
- async function handleArtifactCreate(args) {
137
- const parsed = exports.artifactCreateInputSchema.safeParse(args);
144
+ export async function handleArtifactCreate(args) {
145
+ const parsed = artifactCreateInputSchema.safeParse(args);
138
146
  if (!parsed.success) {
139
147
  return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
140
148
  }
141
- return buildArtifactCreateRequest((0, client_js_1.getClient)(), parsed.data).execute();
149
+ return buildArtifactCreateRequest(getClient(), parsed.data).execute();
142
150
  }
143
151
  // ─── Update ──────────────────────────────────────────────────────────────────
144
- exports.artifactUpdateInputSchema = zod_1.z.object({
145
- id: zod_1.z.string().min(1).describe('Artifact UUID to update'),
146
- title: zod_1.z.string().optional().describe('New title'),
147
- content: zod_1.z.string().optional().describe('New content body'),
148
- tags: zod_1.z.array(zod_1.z.string()).optional().describe('Updated tags'),
152
+ export const artifactUpdateInputSchema = z.object({
153
+ id: z.string().min(1).describe('Artifact UUID to update'),
154
+ title: z.string().optional().describe('New title (omit to preserve the current title)'),
155
+ content: z.string().optional().describe('New content body'),
156
+ tags: z.array(z.string()).optional().describe('Updated tags'),
149
157
  });
150
- exports.ARTIFACT_UPDATE_TOOL_DEF = {
158
+ export const ARTIFACT_UPDATE_TOOL_DEF = {
151
159
  name: 'tacit_artifact_update',
152
- description: 'Update a career artifact by id. Route: PATCH /api/v1/artifacts/:id.',
160
+ description: 'Update a career artifact by id. Route: PATCH /api/v1/artifacts/:id. ' +
161
+ 'Publishing is not possible through this tool; use tacit_artifact_publish (PATCH status=published returns 422).',
153
162
  inputSchema: {
154
163
  type: 'object',
155
164
  properties: {
156
165
  id: { type: 'string', description: 'Artifact UUID to update' },
157
- title: { type: 'string', description: 'New title' },
166
+ title: { type: 'string', description: 'New title (omit to preserve the current title)' },
158
167
  content: { type: 'string', description: 'New content body' },
159
168
  tags: { type: 'array', items: { type: 'string' }, description: 'Updated tags' },
160
169
  },
161
170
  required: ['id'],
162
171
  },
163
172
  };
164
- function buildArtifactUpdateRequest(client, input) {
173
+ export function buildArtifactUpdateRequest(client, input) {
165
174
  return {
166
175
  async execute() {
167
176
  const { id, title, content, tags } = input;
@@ -184,18 +193,18 @@ function buildArtifactUpdateRequest(client, input) {
184
193
  },
185
194
  };
186
195
  }
187
- async function handleArtifactUpdate(args) {
188
- const parsed = exports.artifactUpdateInputSchema.safeParse(args);
196
+ export async function handleArtifactUpdate(args) {
197
+ const parsed = artifactUpdateInputSchema.safeParse(args);
189
198
  if (!parsed.success) {
190
199
  return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
191
200
  }
192
- return buildArtifactUpdateRequest((0, client_js_1.getClient)(), parsed.data).execute();
201
+ return buildArtifactUpdateRequest(getClient(), parsed.data).execute();
193
202
  }
194
203
  // ─── Publish ─────────────────────────────────────────────────────────────────
195
- exports.artifactPublishInputSchema = zod_1.z.object({
196
- id: zod_1.z.string().min(1).describe('Artifact UUID to publish'),
204
+ export const artifactPublishInputSchema = z.object({
205
+ id: z.string().min(1).describe('Artifact UUID to publish'),
197
206
  });
198
- exports.ARTIFACT_PUBLISH_TOOL_DEF = {
207
+ export const ARTIFACT_PUBLISH_TOOL_DEF = {
199
208
  name: 'tacit_artifact_publish',
200
209
  description: 'Publish an artifact by id. Route: POST /api/v1/artifacts/:id/publish.',
201
210
  inputSchema: {
@@ -206,7 +215,7 @@ exports.ARTIFACT_PUBLISH_TOOL_DEF = {
206
215
  required: ['id'],
207
216
  },
208
217
  };
209
- function buildArtifactPublishRequest(client, input) {
218
+ export function buildArtifactPublishRequest(client, input) {
210
219
  return {
211
220
  async execute() {
212
221
  const { id } = input;
@@ -221,21 +230,303 @@ function buildArtifactPublishRequest(client, input) {
221
230
  },
222
231
  };
223
232
  }
224
- async function handleArtifactPublish(args) {
225
- const parsed = exports.artifactPublishInputSchema.safeParse(args);
233
+ export async function handleArtifactPublish(args) {
234
+ const parsed = artifactPublishInputSchema.safeParse(args);
226
235
  if (!parsed.success) {
227
236
  return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
228
237
  }
229
- return buildArtifactPublishRequest((0, client_js_1.getClient)(), parsed.data).execute();
230
- }
231
- // ─── Shared error helper ──────────────────────────────────────────────────────
232
- function apiErrorResult(err, endpoint) {
233
- if (err instanceof client_js_1.TacitApiError) {
234
- return {
235
- content: [{ type: 'text', text: `API error ${err.statusCode} on ${endpoint}: ${err.body}` }],
236
- isError: true,
237
- };
238
+ return buildArtifactPublishRequest(getClient(), parsed.data).execute();
239
+ }
240
+ // ─── Get (editor payload) ──────────────────────────────────────────────────────
241
+ export const artifactGetInputSchema = z.object({
242
+ id: z.string().min(1).describe('Artifact UUID'),
243
+ });
244
+ export const ARTIFACT_GET_TOOL_DEF = {
245
+ name: 'tacit_artifact_get',
246
+ description: 'Fetch the editor payload for a single artifact by id (content = COALESCE(NULLIF(raw_content,\'\'), content_body), ' +
247
+ 'title, status, version, …). Workspace-scoped; 404 across tenants. Route: GET /api/v1/artifacts/editor/:id.',
248
+ inputSchema: {
249
+ type: 'object',
250
+ properties: {
251
+ id: { type: 'string', description: 'Artifact UUID' },
252
+ },
253
+ required: ['id'],
254
+ },
255
+ };
256
+ export function buildArtifactGetRequest(client, input) {
257
+ return {
258
+ async execute() {
259
+ const { id } = input;
260
+ try {
261
+ // GET /api/v1/artifacts/editor/:id
262
+ const result = await client.get(`/api/v1/artifacts/editor/${id}`);
263
+ const url = client.webUrl('artifact', id);
264
+ const enriched = url ? { ...result, web_url: url } : result;
265
+ return { content: [{ type: 'text', text: JSON.stringify(enriched) }] };
266
+ }
267
+ catch (err) {
268
+ return apiErrorResult(err, '/api/v1/artifacts/editor/:id');
269
+ }
270
+ },
271
+ };
272
+ }
273
+ export async function handleArtifactGet(args) {
274
+ const parsed = artifactGetInputSchema.safeParse(args);
275
+ if (!parsed.success) {
276
+ return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
238
277
  }
239
- const msg = err instanceof Error ? err.message : String(err);
240
- return { content: [{ type: 'text', text: `Error: ${msg}` }], isError: true };
278
+ return buildArtifactGetRequest(getClient(), parsed.data).execute();
279
+ }
280
+ // ─── Revise ──────────────────────────────────────────────────────────────────
281
+ export const artifactReviseInputSchema = z.object({
282
+ id: z.string().min(1).describe('UUID of the PUBLISHED artifact to revise'),
283
+ title: z.string().optional().describe('New title (omit to preserve the original title; the version sidebar tracks origin)'),
284
+ if_match: z.string().optional().describe('RFC3339 updated_at of the parent (optional concurrency guard)'),
285
+ });
286
+ export const ARTIFACT_REVISE_TOOL_DEF = {
287
+ name: 'tacit_artifact_revise',
288
+ description: 'Create a new revision of a PUBLISHED artifact (must be the chain tail). ' +
289
+ '409 not_chain_tail if a newer revision already exists; 412 stale_artifact if if_match does not match. ' +
290
+ 'Route: POST /api/v1/artifacts/:id/revise.',
291
+ inputSchema: {
292
+ type: 'object',
293
+ properties: {
294
+ id: { type: 'string', description: 'UUID of the PUBLISHED artifact to revise' },
295
+ title: { type: 'string', description: 'New title (omit to preserve the original)' },
296
+ if_match: { type: 'string', description: 'RFC3339 updated_at of the parent (optional concurrency guard)' },
297
+ },
298
+ required: ['id'],
299
+ },
300
+ };
301
+ export function buildArtifactReviseRequest(client, input) {
302
+ return {
303
+ async execute() {
304
+ const { id, title, if_match } = input;
305
+ try {
306
+ const body = {};
307
+ if (title !== undefined)
308
+ body['title'] = title;
309
+ // POST /api/v1/artifacts/:id/revise — if_match forwarded as the If-Match header, not the body.
310
+ const result = await client.post(`/api/v1/artifacts/${id}/revise`, body, if_match !== undefined ? { 'If-Match': if_match } : undefined);
311
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
312
+ }
313
+ catch (err) {
314
+ return apiErrorResult(err, '/api/v1/artifacts/:id/revise');
315
+ }
316
+ },
317
+ };
318
+ }
319
+ export async function handleArtifactRevise(args) {
320
+ const parsed = artifactReviseInputSchema.safeParse(args);
321
+ if (!parsed.success) {
322
+ return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
323
+ }
324
+ return buildArtifactReviseRequest(getClient(), parsed.data).execute();
325
+ }
326
+ // ─── Versions ────────────────────────────────────────────────────────────────
327
+ export const artifactVersionsInputSchema = z.object({
328
+ id: z.string().min(1).describe('Artifact UUID (any member of the revision chain)'),
329
+ });
330
+ export const ARTIFACT_VERSIONS_TOOL_DEF = {
331
+ name: 'tacit_artifact_versions',
332
+ description: 'List the revision chain for an artifact. Route: GET /api/v1/artifacts/:id/versions.',
333
+ inputSchema: {
334
+ type: 'object',
335
+ properties: {
336
+ id: { type: 'string', description: 'Artifact UUID' },
337
+ },
338
+ required: ['id'],
339
+ },
340
+ };
341
+ export function buildArtifactVersionsRequest(client, input) {
342
+ return {
343
+ async execute() {
344
+ const { id } = input;
345
+ try {
346
+ // GET /api/v1/artifacts/:id/versions
347
+ const result = await client.get(`/api/v1/artifacts/${id}/versions`);
348
+ return { content: [{ type: 'text', text: JSON.stringify(result) }] };
349
+ }
350
+ catch (err) {
351
+ return apiErrorResult(err, '/api/v1/artifacts/:id/versions');
352
+ }
353
+ },
354
+ };
355
+ }
356
+ export async function handleArtifactVersions(args) {
357
+ const parsed = artifactVersionsInputSchema.safeParse(args);
358
+ if (!parsed.success) {
359
+ return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
360
+ }
361
+ return buildArtifactVersionsRequest(getClient(), parsed.data).execute();
362
+ }
363
+ // ─── Delete ─────────────────────────────────────────────────────────────────
364
+ export const artifactDeleteInputSchema = z.object({
365
+ id: z.string().uuid().describe('Artifact UUID to delete'),
366
+ confirm: z.boolean().describe('Must be true to execute the delete — safety guard against accidental deletion'),
367
+ force: z.boolean().optional().describe('Set true to delete a PUBLISHED artifact. Requires MENTAT or TWIN autonomy.'),
368
+ });
369
+ export const ARTIFACT_DELETE_TOOL_DEF = {
370
+ name: 'tacit_artifact_delete',
371
+ description: 'Soft-delete an artifact by id (sets deleted_at, cascades search_projections cleanup). ' +
372
+ 'DRAFT / UNDER_REVIEW artifacts delete normally. PUBLISHED artifacts require force=true ' +
373
+ 'and MENTAT or TWIN autonomy. confirm must be true. Route: DELETE /api/v1/artifacts/editor/:id.',
374
+ inputSchema: {
375
+ type: 'object',
376
+ properties: {
377
+ id: { type: 'string', description: 'Artifact UUID to delete' },
378
+ confirm: { type: 'boolean', description: 'Must be true to execute — safety guard' },
379
+ force: { type: 'boolean', description: 'Set true to delete a PUBLISHED artifact (requires MENTAT/TWIN autonomy)' },
380
+ },
381
+ required: ['id', 'confirm'],
382
+ },
383
+ };
384
+ export function buildArtifactDeleteRequest(client, input) {
385
+ return {
386
+ async execute() {
387
+ const { id, confirm, force } = input;
388
+ if (!confirm) {
389
+ return {
390
+ content: [{ type: 'text', text: 'Delete aborted: confirm must be true.' }],
391
+ isError: true,
392
+ };
393
+ }
394
+ try {
395
+ const qs = force ? '?force=true' : '';
396
+ const result = await client.delete(`/api/v1/artifacts/editor/${id}${qs}`);
397
+ return { content: [{ type: 'text', text: JSON.stringify(result ?? { artifact_id: id, deleted: true }) }] };
398
+ }
399
+ catch (err) {
400
+ return apiErrorResult(err, '/api/v1/artifacts/editor/:id');
401
+ }
402
+ },
403
+ };
404
+ }
405
+ export async function handleArtifactDelete(args) {
406
+ const parsed = artifactDeleteInputSchema.safeParse(args);
407
+ if (!parsed.success) {
408
+ return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
409
+ }
410
+ return buildArtifactDeleteRequest(getClient(), parsed.data).execute();
411
+ }
412
+ export function registerArtifactTools(server, getClientFn) {
413
+ server.tool('tacit_artifact_list', 'List/search artifacts from the Tacit workspace. Route: GET /api/v1/artifacts.', {
414
+ q: z.string().optional().describe('Full-text search over title and body'),
415
+ origin: z.enum(['IMPORTED', 'SYNTHESIZED', 'WIKI_CONVERGENCE']).optional().describe('Filter by artifact origin'),
416
+ status: z.string().optional().describe('Comma-separated status filter'),
417
+ portfolio_id: z.string().optional().describe('Filter by portfolio UUID'),
418
+ limit: z.number().int().positive().optional().describe('Maximum artifacts to return'),
419
+ offset: z.number().int().nonnegative().optional().describe('Pagination offset'),
420
+ }, async (args) => {
421
+ const client = await getClientFn();
422
+ return buildArtifactListRequest(client, args).execute();
423
+ });
424
+ server.tool('tacit_artifact_create', 'Create an artifact. Route: POST /api/v1/artifacts.', {
425
+ title: z.string().min(1).describe('Artifact title'),
426
+ domain: z.string().optional().describe('Domain tag (e.g. CAREER, GENERAL, ENGINEERING, PRODUCT). Defaults to GENERAL.'),
427
+ portfolio_id: z.string().min(1).describe('Portfolio UUID (REQUIRED)'),
428
+ structured_metadata: z.record(z.unknown()).optional().describe('Structured metadata'),
429
+ }, async (args) => {
430
+ const client = await getClientFn();
431
+ return buildArtifactCreateRequest(client, args).execute();
432
+ });
433
+ server.tool('tacit_artifact_update', 'Update an artifact by id. Route: PATCH /api/v1/artifacts/:id.', {
434
+ id: z.string().min(1).describe('Artifact UUID to update'),
435
+ title: z.string().optional().describe('New title'),
436
+ content: z.string().optional().describe('New content body'),
437
+ tags: z.array(z.string()).optional().describe('Updated tags'),
438
+ }, async (args) => {
439
+ const client = await getClientFn();
440
+ return buildArtifactUpdateRequest(client, args).execute();
441
+ });
442
+ server.tool('tacit_artifact_publish', 'Publish an artifact by id. Route: POST /api/v1/artifacts/:id/publish.', {
443
+ id: z.string().min(1).describe('Artifact UUID to publish'),
444
+ }, async (args) => {
445
+ const client = await getClientFn();
446
+ return buildArtifactPublishRequest(client, args).execute();
447
+ });
448
+ server.tool('tacit_artifact_get', 'Fetch editor payload for a single artifact. Route: GET /api/v1/artifacts/editor/:id.', {
449
+ id: z.string().min(1).describe('Artifact UUID'),
450
+ }, async (args) => {
451
+ const client = await getClientFn();
452
+ return buildArtifactGetRequest(client, args).execute();
453
+ });
454
+ server.tool('tacit_artifact_revise', 'Create a new revision of a PUBLISHED artifact. Route: POST /api/v1/artifacts/:id/revise.', {
455
+ id: z.string().min(1).describe('UUID of the PUBLISHED artifact to revise'),
456
+ title: z.string().optional().describe('New title'),
457
+ if_match: z.string().optional().describe('RFC3339 updated_at concurrency guard'),
458
+ }, async (args) => {
459
+ const client = await getClientFn();
460
+ return buildArtifactReviseRequest(client, args).execute();
461
+ });
462
+ server.tool('tacit_artifact_versions', 'List the revision chain for an artifact. Route: GET /api/v1/artifacts/:id/versions.', {
463
+ id: z.string().min(1).describe('Artifact UUID'),
464
+ }, async (args) => {
465
+ const client = await getClientFn();
466
+ return buildArtifactVersionsRequest(client, args).execute();
467
+ });
468
+ server.tool('tacit_artifact_delete', 'Soft-delete an artifact. DRAFT/UNDER_REVIEW delete normally. PUBLISHED requires force=true and MENTAT/TWIN autonomy. Route: DELETE /api/v1/artifacts/editor/:id.', {
469
+ id: z.string().uuid().describe('Artifact UUID to delete'),
470
+ confirm: z.boolean().describe('Must be true to execute — safety guard'),
471
+ force: z.boolean().optional().describe('Set true to delete a PUBLISHED artifact (requires MENTAT/TWIN autonomy)'),
472
+ }, async (args) => {
473
+ const client = await getClientFn();
474
+ return buildArtifactDeleteRequest(client, args).execute();
475
+ });
476
+ server.tool('tacit_artifact_pdf', 'Export an artifact as PDF.', {
477
+ artifact_id: z.string().uuid().describe('Artifact UUID'),
478
+ }, async (args) => {
479
+ try {
480
+ const client = await getClientFn();
481
+ const data = await client.post(`/api/v1/artifacts/${args.artifact_id}/pdf`, {});
482
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
483
+ }
484
+ catch (err) {
485
+ return apiErrorResult(err, `/api/v1/artifacts/${args.artifact_id}/pdf`);
486
+ }
487
+ });
488
+ server.tool('tacit_artifact_html_token', 'Generate a token for authenticated HTML viewing of an artifact.', {
489
+ artifact_id: z.string().uuid().describe('Artifact UUID'),
490
+ }, async (args) => {
491
+ try {
492
+ const client = await getClientFn();
493
+ const data = await client.post(`/api/v1/artifacts/${args.artifact_id}/html-token`, {});
494
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
495
+ }
496
+ catch (err) {
497
+ return apiErrorResult(err, `/api/v1/artifacts/${args.artifact_id}/html-token`);
498
+ }
499
+ });
500
+ server.tool('tacit_import_artifact', 'Import an artifact from external content into a portfolio/program.', {
501
+ portfolio_id: z.string().uuid().describe('Target portfolio UUID'),
502
+ program_id: z.string().uuid().describe('Target program UUID'),
503
+ title: z.string().describe('Artifact title'),
504
+ content: z.string().describe('Artifact content body'),
505
+ output_format: z.string().describe('Output format'),
506
+ domain: z.string().describe('Domain: research, engineering, design, etc.'),
507
+ source_event_id: z.string().uuid().optional().describe('Originating event UUID'),
508
+ source_type: z.string().optional().describe('Source type (OBSIDIAN_GRAPH triggers vault pre-processing)'),
509
+ filename: z.string().optional().describe('Original filename (required when source_type=OBSIDIAN_GRAPH)'),
510
+ }, async (args) => {
511
+ try {
512
+ const client = await getClientFn();
513
+ const data = await client.post('/api/v1/artifacts/import', args);
514
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
515
+ }
516
+ catch (err) {
517
+ return apiErrorResult(err, '/api/v1/artifacts/import');
518
+ }
519
+ });
520
+ server.tool('tacit_share_artifact', 'Generate a share token URL for external access to an artifact.', {
521
+ artifact_id: z.string().uuid().describe('Artifact UUID'),
522
+ }, async (args) => {
523
+ try {
524
+ const client = await getClientFn();
525
+ const data = await client.post(`/api/v1/artifacts/${args.artifact_id}/share-token`, {});
526
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
527
+ }
528
+ catch (err) {
529
+ return apiErrorResult(err, `/api/v1/artifacts/${args.artifact_id}/share-token`);
530
+ }
531
+ });
241
532
  }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { TacitClient } from '../client.js';
3
+ export declare function registerChronicleTools(server: McpServer, getClient: () => Promise<TacitClient>): void;