@tacitai/mcp-server 0.1.1 → 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.
- package/dist/auth.d.ts +32 -0
- package/dist/auth.js +64 -0
- package/dist/client.d.ts +60 -5
- package/dist/client.js +234 -53
- package/dist/index.js +128 -106
- package/dist/oauth.d.ts +115 -0
- package/dist/oauth.js +619 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +12 -0
- package/dist/setup.d.ts +28 -5
- package/dist/setup.js +266 -59
- package/dist/tools/agents.d.ts +277 -0
- package/dist/tools/agents.js +335 -0
- package/dist/tools/annotations.d.ts +117 -0
- package/dist/tools/annotations.js +130 -0
- package/dist/tools/artifacts.d.ts +178 -8
- package/dist/tools/artifacts.js +372 -81
- package/dist/tools/chronicle.d.ts +3 -0
- package/dist/tools/chronicle.js +165 -0
- package/dist/tools/connector_write_back.d.ts +3 -0
- package/dist/tools/connector_write_back.js +27 -0
- package/dist/tools/converse.d.ts +10 -0
- package/dist/tools/converse.js +88 -0
- package/dist/tools/crews.d.ts +3 -0
- package/dist/tools/crews.js +115 -0
- package/dist/tools/drafts.d.ts +83 -0
- package/dist/tools/drafts.js +86 -0
- package/dist/tools/errors.d.ts +15 -0
- package/dist/tools/errors.js +21 -0
- package/dist/tools/export.d.ts +54 -0
- package/dist/tools/export.js +55 -0
- package/dist/tools/gm.d.ts +214 -0
- package/dist/tools/gm.js +198 -0
- package/dist/tools/ingest.d.ts +58 -4
- package/dist/tools/ingest.js +109 -76
- package/dist/tools/kb.d.ts +544 -0
- package/dist/tools/kb.js +687 -0
- package/dist/tools/lake-structure.d.ts +3 -0
- package/dist/tools/lake-structure.js +126 -0
- package/dist/tools/lake.d.ts +932 -0
- package/dist/tools/lake.js +1017 -0
- package/dist/tools/move.d.ts +200 -0
- package/dist/tools/move.js +96 -0
- package/dist/tools/outbox.d.ts +274 -0
- package/dist/tools/outbox.js +223 -0
- package/dist/tools/people.d.ts +3 -0
- package/dist/tools/people.js +59 -0
- package/dist/tools/pyramid.d.ts +554 -0
- package/dist/tools/pyramid.js +758 -0
- package/dist/tools/recall.d.ts +2 -0
- package/dist/tools/recall.js +28 -17
- package/dist/tools/registry.d.ts +17 -0
- package/dist/tools/registry.js +197 -0
- package/dist/tools/research.d.ts +47 -0
- package/dist/tools/research.js +575 -0
- package/dist/tools/search.d.ts +9 -0
- package/dist/tools/search.js +86 -25
- package/dist/tools/sharing.d.ts +63 -0
- package/dist/tools/sharing.js +157 -0
- package/dist/tools/synthesize.d.ts +54 -2
- package/dist/tools/synthesize.js +80 -23
- package/dist/tools/web-url.d.ts +57 -0
- package/dist/tools/web-url.js +74 -0
- package/dist/tools/workspaces.d.ts +3 -0
- package/dist/tools/workspaces.js +47 -0
- package/dist/transport.d.ts +9 -0
- package/dist/transport.js +209 -0
- package/package.json +5 -4
package/dist/tools/artifacts.js
CHANGED
|
@@ -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
|
|
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
|
-
* -
|
|
18
|
+
* - domain defaults to GENERAL if omitted; caller may pass any valid domain.
|
|
20
19
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
status:
|
|
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
|
-
|
|
32
|
+
export const ARTIFACT_LIST_TOOL_DEF = {
|
|
40
33
|
name: 'tacit_artifact_list',
|
|
41
|
-
description: 'List artifacts from the Tacit workspace.
|
|
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 {
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
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 =
|
|
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(
|
|
88
|
+
return buildArtifactListRequest(getClient(), parsed.data).execute();
|
|
80
89
|
}
|
|
81
90
|
// ─── Create ──────────────────────────────────────────────────────────────────
|
|
82
|
-
|
|
83
|
-
title:
|
|
84
|
-
domain:
|
|
85
|
-
portfolio_id:
|
|
86
|
-
structured_metadata:
|
|
87
|
-
.record(
|
|
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
|
-
|
|
100
|
+
export const ARTIFACT_CREATE_TOOL_DEF = {
|
|
92
101
|
name: 'tacit_artifact_create',
|
|
93
|
-
description: 'Create
|
|
94
|
-
'Accepts a title
|
|
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
|
|
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 (
|
|
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: '
|
|
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 =
|
|
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(
|
|
149
|
+
return buildArtifactCreateRequest(getClient(), parsed.data).execute();
|
|
142
150
|
}
|
|
143
151
|
// ─── Update ──────────────────────────────────────────────────────────────────
|
|
144
|
-
|
|
145
|
-
id:
|
|
146
|
-
title:
|
|
147
|
-
content:
|
|
148
|
-
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
|
-
|
|
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 =
|
|
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(
|
|
201
|
+
return buildArtifactUpdateRequest(getClient(), parsed.data).execute();
|
|
193
202
|
}
|
|
194
203
|
// ─── Publish ─────────────────────────────────────────────────────────────────
|
|
195
|
-
|
|
196
|
-
id:
|
|
204
|
+
export const artifactPublishInputSchema = z.object({
|
|
205
|
+
id: z.string().min(1).describe('Artifact UUID to publish'),
|
|
197
206
|
});
|
|
198
|
-
|
|
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 =
|
|
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(
|
|
230
|
-
}
|
|
231
|
-
// ───
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
240
|
-
|
|
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
|
}
|