@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.
- package/README.md +3 -3
- 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 +267 -60
- 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/kb.js
ADDED
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
/**
|
|
3
|
+
* KB tools — CLI wrappers for knowledge collections, articles, categories,
|
|
4
|
+
* and browse/promote flows on the Axum REST API.
|
|
5
|
+
*
|
|
6
|
+
* Routes (Axum, all under /api/v1):
|
|
7
|
+
* GET/POST /knowledge/collections
|
|
8
|
+
* PATCH/DELETE /knowledge/collections/:id
|
|
9
|
+
* GET/POST /knowledge/articles
|
|
10
|
+
* GET/PUT /knowledge/articles/:id
|
|
11
|
+
* GET /knowledge/browse
|
|
12
|
+
* POST /knowledge/articles/from-artifact/:id
|
|
13
|
+
* GET/POST /knowledge/categories
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { getClient, TacitApiError, TokenExpiredError } from '../client.js';
|
|
17
|
+
import { apiErrorResult as sharedApiErrorResult } from './errors.js';
|
|
18
|
+
function apiErrorResult(err, toolName) {
|
|
19
|
+
if (err instanceof TokenExpiredError) {
|
|
20
|
+
return {
|
|
21
|
+
content: [{ type: 'text', text: err.message }],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (err instanceof TacitApiError) {
|
|
26
|
+
return { content: [{ type: 'text', text: `API error ${err.statusCode} on ${toolName}: ${err.body}` }], isError: true };
|
|
27
|
+
}
|
|
28
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
29
|
+
return { content: [{ type: 'text', text: `Error: ${msg}` }], isError: true };
|
|
30
|
+
}
|
|
31
|
+
// ─── tacit_kb_list_collections ──────────────────────────────────────────────
|
|
32
|
+
export const listCollectionsInputSchema = z.object({
|
|
33
|
+
kb_category_id: z.string().optional().describe('Optional UUID of a KB category to filter collections by'),
|
|
34
|
+
});
|
|
35
|
+
export const LIST_COLLECTIONS_TOOL_DEF = {
|
|
36
|
+
name: 'tacit_kb_list_collections',
|
|
37
|
+
description: 'List all KB collections in the workspace, optionally filtered by category.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
kb_category_id: { type: 'string', description: 'Optional UUID of a KB category to filter by' },
|
|
42
|
+
},
|
|
43
|
+
required: [],
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
export function buildListCollectionsRequest(client, input) {
|
|
47
|
+
return {
|
|
48
|
+
async execute() {
|
|
49
|
+
try {
|
|
50
|
+
const result = await client.get('/api/v1/knowledge/collections', {
|
|
51
|
+
kb_category_id: input.kb_category_id,
|
|
52
|
+
});
|
|
53
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return apiErrorResult(err, 'tacit_kb_list_collections');
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function handleListCollections(args) {
|
|
62
|
+
const parsed = listCollectionsInputSchema.safeParse(args);
|
|
63
|
+
if (!parsed.success) {
|
|
64
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
65
|
+
}
|
|
66
|
+
return buildListCollectionsRequest(getClient(), parsed.data).execute();
|
|
67
|
+
}
|
|
68
|
+
// ─── tacit_kb_create_collection ─────────────────────────────────────────────
|
|
69
|
+
export const createCollectionInputSchema = z.object({
|
|
70
|
+
slug: z.string().min(1).describe('URL-safe slug for the collection (unique per workspace)'),
|
|
71
|
+
name: z.string().min(1).describe('Collection display name'),
|
|
72
|
+
description: z.string().optional().describe('Optional description'),
|
|
73
|
+
kb_category_id: z.string().optional().describe('Optional UUID of the KB category to place this collection in'),
|
|
74
|
+
});
|
|
75
|
+
export const CREATE_COLLECTION_TOOL_DEF = {
|
|
76
|
+
name: 'tacit_kb_create_collection',
|
|
77
|
+
description: 'Create a new KB collection. Requires a unique slug within the workspace.',
|
|
78
|
+
inputSchema: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
properties: {
|
|
81
|
+
slug: { type: 'string', description: 'URL-safe slug (unique per workspace)' },
|
|
82
|
+
name: { type: 'string', description: 'Collection display name' },
|
|
83
|
+
description: { type: 'string', description: 'Optional description' },
|
|
84
|
+
kb_category_id: { type: 'string', description: 'Optional KB category UUID' },
|
|
85
|
+
},
|
|
86
|
+
required: ['slug', 'name'],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
export function buildCreateCollectionRequest(client, input) {
|
|
90
|
+
return {
|
|
91
|
+
async execute() {
|
|
92
|
+
try {
|
|
93
|
+
const body = { slug: input.slug, name: input.name };
|
|
94
|
+
if (input.description)
|
|
95
|
+
body['description'] = input.description;
|
|
96
|
+
if (input.kb_category_id)
|
|
97
|
+
body['kb_category_id'] = input.kb_category_id;
|
|
98
|
+
const result = await client.post('/api/v1/knowledge/collections', body);
|
|
99
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return apiErrorResult(err, 'tacit_kb_create_collection');
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export async function handleCreateCollection(args) {
|
|
108
|
+
const parsed = createCollectionInputSchema.safeParse(args);
|
|
109
|
+
if (!parsed.success) {
|
|
110
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
111
|
+
}
|
|
112
|
+
return buildCreateCollectionRequest(getClient(), parsed.data).execute();
|
|
113
|
+
}
|
|
114
|
+
// ─── tacit_kb_update_collection ─────────────────────────────────────────────
|
|
115
|
+
export const updateCollectionInputSchema = z.object({
|
|
116
|
+
id: z.string().min(1).describe('UUID of the collection to update'),
|
|
117
|
+
name: z.string().min(1).describe('Collection name (required by the backend on every update)'),
|
|
118
|
+
slug: z.string().optional().describe('Optional new slug (re-derived from name if omitted)'),
|
|
119
|
+
kb_category_id: z.string().nullable().optional().describe('KB category UUID to set; null clears it; omit to leave unchanged'),
|
|
120
|
+
});
|
|
121
|
+
export const UPDATE_COLLECTION_TOOL_DEF = {
|
|
122
|
+
name: 'tacit_kb_update_collection',
|
|
123
|
+
description: 'Update a KB collection\'s name, slug, or category. The backend requires `name` on every update. ' +
|
|
124
|
+
'Omit kb_category_id to leave it unchanged, or pass null to clear it.',
|
|
125
|
+
inputSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
properties: {
|
|
128
|
+
id: { type: 'string', description: 'UUID of the collection' },
|
|
129
|
+
name: { type: 'string', description: 'Collection name (required)' },
|
|
130
|
+
slug: { type: 'string', description: 'Optional new slug' },
|
|
131
|
+
kb_category_id: { type: ['string', 'null'], description: 'KB category UUID, or null to clear' },
|
|
132
|
+
},
|
|
133
|
+
required: ['id', 'name'],
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
export function buildUpdateCollectionRequest(client, input) {
|
|
137
|
+
return {
|
|
138
|
+
async execute() {
|
|
139
|
+
try {
|
|
140
|
+
const body = { name: input.name };
|
|
141
|
+
if (input.slug)
|
|
142
|
+
body['slug'] = input.slug;
|
|
143
|
+
if (input.kb_category_id !== undefined)
|
|
144
|
+
body['kb_category_id'] = input.kb_category_id;
|
|
145
|
+
const result = await client.patch(`/api/v1/knowledge/collections/${input.id}`, body);
|
|
146
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
return apiErrorResult(err, 'tacit_kb_update_collection');
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
export async function handleUpdateCollection(args) {
|
|
155
|
+
const parsed = updateCollectionInputSchema.safeParse(args);
|
|
156
|
+
if (!parsed.success) {
|
|
157
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
158
|
+
}
|
|
159
|
+
return buildUpdateCollectionRequest(getClient(), parsed.data).execute();
|
|
160
|
+
}
|
|
161
|
+
// ─── tacit_kb_delete_collection ──────────────────────────────────────────────
|
|
162
|
+
export const deleteCollectionInputSchema = z.object({
|
|
163
|
+
id: z.string().min(1).describe('UUID of the collection to delete'),
|
|
164
|
+
});
|
|
165
|
+
export const DELETE_COLLECTION_TOOL_DEF = {
|
|
166
|
+
name: 'tacit_kb_delete_collection',
|
|
167
|
+
description: 'Delete a KB collection. Blocked if any article would be orphaned (no other collection membership) — ' +
|
|
168
|
+
'move or demote such articles first.',
|
|
169
|
+
inputSchema: {
|
|
170
|
+
type: 'object',
|
|
171
|
+
properties: {
|
|
172
|
+
id: { type: 'string', description: 'UUID of the collection to delete' },
|
|
173
|
+
},
|
|
174
|
+
required: ['id'],
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
export function buildDeleteCollectionRequest(client, input) {
|
|
178
|
+
return {
|
|
179
|
+
async execute() {
|
|
180
|
+
try {
|
|
181
|
+
await client.delete(`/api/v1/knowledge/collections/${input.id}`);
|
|
182
|
+
return { content: [{ type: 'text', text: JSON.stringify({ deleted: true, collection_id: input.id }) }] };
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
return apiErrorResult(err, 'tacit_kb_delete_collection');
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
export async function handleDeleteCollection(args) {
|
|
191
|
+
const parsed = deleteCollectionInputSchema.safeParse(args);
|
|
192
|
+
if (!parsed.success) {
|
|
193
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
194
|
+
}
|
|
195
|
+
return buildDeleteCollectionRequest(getClient(), parsed.data).execute();
|
|
196
|
+
}
|
|
197
|
+
// ─── tacit_kb_list_articles ──────────────────────────────────────────────────
|
|
198
|
+
export const listArticlesInputSchema = z.object({
|
|
199
|
+
collection_id: z.string().optional().describe('Optional UUID of a collection to filter articles by'),
|
|
200
|
+
});
|
|
201
|
+
export const LIST_ARTICLES_TOOL_DEF = {
|
|
202
|
+
name: 'tacit_kb_list_articles',
|
|
203
|
+
description: 'List KB articles in the workspace (up to 200, most recently updated first), optionally filtered by collection.',
|
|
204
|
+
inputSchema: {
|
|
205
|
+
type: 'object',
|
|
206
|
+
properties: {
|
|
207
|
+
collection_id: { type: 'string', description: 'Optional collection UUID to filter by' },
|
|
208
|
+
},
|
|
209
|
+
required: [],
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
export function buildListArticlesRequest(client, input) {
|
|
213
|
+
return {
|
|
214
|
+
async execute() {
|
|
215
|
+
try {
|
|
216
|
+
const result = await client.get('/api/v1/knowledge/articles', {
|
|
217
|
+
collection_id: input.collection_id,
|
|
218
|
+
});
|
|
219
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
return apiErrorResult(err, 'tacit_kb_list_articles');
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
export async function handleListArticles(args) {
|
|
228
|
+
const parsed = listArticlesInputSchema.safeParse(args);
|
|
229
|
+
if (!parsed.success) {
|
|
230
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
231
|
+
}
|
|
232
|
+
return buildListArticlesRequest(getClient(), parsed.data).execute();
|
|
233
|
+
}
|
|
234
|
+
// ─── tacit_kb_get_article ────────────────────────────────────────────────────
|
|
235
|
+
export const getArticleInputSchema = z.object({
|
|
236
|
+
id: z.string().min(1).describe('UUID of the article'),
|
|
237
|
+
});
|
|
238
|
+
export const GET_ARTICLE_TOOL_DEF = {
|
|
239
|
+
name: 'tacit_kb_get_article',
|
|
240
|
+
description: 'Get a single KB article by ID, including its full content body and etag for optimistic-concurrency updates.',
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: 'object',
|
|
243
|
+
properties: {
|
|
244
|
+
id: { type: 'string', description: 'UUID of the article' },
|
|
245
|
+
},
|
|
246
|
+
required: ['id'],
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
export function buildGetArticleRequest(client, input) {
|
|
250
|
+
return {
|
|
251
|
+
async execute() {
|
|
252
|
+
try {
|
|
253
|
+
const result = await client.get(`/api/v1/knowledge/articles/${input.id}`);
|
|
254
|
+
const collectionId = typeof result['collection_id'] === 'string' ? result['collection_id'] : undefined;
|
|
255
|
+
const url = client.webUrl('kb_article', input.id, { collectionId });
|
|
256
|
+
const enriched = url ? { ...result, web_url: url } : result;
|
|
257
|
+
return { content: [{ type: 'text', text: JSON.stringify(enriched) }] };
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
return apiErrorResult(err, 'tacit_kb_get_article');
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
export async function handleGetArticle(args) {
|
|
266
|
+
const parsed = getArticleInputSchema.safeParse(args);
|
|
267
|
+
if (!parsed.success) {
|
|
268
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
269
|
+
}
|
|
270
|
+
return buildGetArticleRequest(getClient(), parsed.data).execute();
|
|
271
|
+
}
|
|
272
|
+
// ─── tacit_kb_create_article ─────────────────────────────────────────────────
|
|
273
|
+
export const createArticleInputSchema = z.object({
|
|
274
|
+
collection_id: z.string().min(1).describe('UUID of the collection this article belongs to'),
|
|
275
|
+
slug: z.string().min(1).describe('URL-safe slug for the article (unique per collection)'),
|
|
276
|
+
title: z.string().min(1).describe('Article title'),
|
|
277
|
+
content: z.string().optional().describe('Article content body (markdown; defaults to empty)'),
|
|
278
|
+
status: z.enum(['DRAFT', 'PUBLISHED']).optional().describe('Status (defaults to DRAFT)'),
|
|
279
|
+
domain_id: z.string().optional().describe('Optional domain schema ID (user picker overrides collection default)'),
|
|
280
|
+
});
|
|
281
|
+
export const CREATE_ARTICLE_TOOL_DEF = {
|
|
282
|
+
name: 'tacit_kb_create_article',
|
|
283
|
+
description: 'Create a new KB article within a collection. Status defaults to DRAFT.',
|
|
284
|
+
inputSchema: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
properties: {
|
|
287
|
+
collection_id: { type: 'string', description: 'UUID of the parent collection' },
|
|
288
|
+
slug: { type: 'string', description: 'URL-safe slug (unique per collection)' },
|
|
289
|
+
title: { type: 'string', description: 'Article title' },
|
|
290
|
+
content: { type: 'string', description: 'Article content body (markdown)' },
|
|
291
|
+
status: { type: 'string', enum: ['DRAFT', 'PUBLISHED'], description: 'Status (defaults to DRAFT)' },
|
|
292
|
+
domain_id: { type: 'string', description: 'Optional domain schema ID' },
|
|
293
|
+
},
|
|
294
|
+
required: ['collection_id', 'slug', 'title'],
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
export function buildCreateArticleRequest(client, input) {
|
|
298
|
+
return {
|
|
299
|
+
async execute() {
|
|
300
|
+
try {
|
|
301
|
+
const body = {
|
|
302
|
+
collection_id: input.collection_id,
|
|
303
|
+
slug: input.slug,
|
|
304
|
+
title: input.title,
|
|
305
|
+
};
|
|
306
|
+
if (input.content !== undefined)
|
|
307
|
+
body['content'] = input.content;
|
|
308
|
+
if (input.status)
|
|
309
|
+
body['status'] = input.status;
|
|
310
|
+
if (input.domain_id)
|
|
311
|
+
body['domain_id'] = input.domain_id;
|
|
312
|
+
const result = await client.post('/api/v1/knowledge/articles', body);
|
|
313
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
return apiErrorResult(err, 'tacit_kb_create_article');
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
export async function handleCreateArticle(args) {
|
|
322
|
+
const parsed = createArticleInputSchema.safeParse(args);
|
|
323
|
+
if (!parsed.success) {
|
|
324
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
325
|
+
}
|
|
326
|
+
return buildCreateArticleRequest(getClient(), parsed.data).execute();
|
|
327
|
+
}
|
|
328
|
+
// ─── tacit_kb_update_article ─────────────────────────────────────────────────
|
|
329
|
+
export const updateArticleInputSchema = z.object({
|
|
330
|
+
id: z.string().min(1).describe('UUID of the article to update'),
|
|
331
|
+
title: z.string().optional().describe('Updated title (keeps current if omitted)'),
|
|
332
|
+
content: z.string().optional().describe('Updated content body (keeps current if omitted)'),
|
|
333
|
+
status: z.enum(['DRAFT', 'PUBLISHED']).optional().describe('Updated status (keeps current if omitted)'),
|
|
334
|
+
});
|
|
335
|
+
export const UPDATE_ARTICLE_TOOL_DEF = {
|
|
336
|
+
name: 'tacit_kb_update_article',
|
|
337
|
+
description: 'Update an existing KB article. Only supply the fields you want to change — omitted fields keep their current values.',
|
|
338
|
+
inputSchema: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
id: { type: 'string', description: 'UUID of the article' },
|
|
342
|
+
title: { type: 'string', description: 'Updated title' },
|
|
343
|
+
content: { type: 'string', description: 'Updated content body' },
|
|
344
|
+
status: { type: 'string', enum: ['DRAFT', 'PUBLISHED'], description: 'Updated status' },
|
|
345
|
+
},
|
|
346
|
+
required: ['id'],
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
export function buildUpdateArticleRequest(client, input) {
|
|
350
|
+
return {
|
|
351
|
+
async execute() {
|
|
352
|
+
try {
|
|
353
|
+
const body = {};
|
|
354
|
+
if (input.title !== undefined)
|
|
355
|
+
body['title'] = input.title;
|
|
356
|
+
if (input.content !== undefined)
|
|
357
|
+
body['content'] = input.content;
|
|
358
|
+
if (input.status !== undefined)
|
|
359
|
+
body['status'] = input.status;
|
|
360
|
+
const result = await client.put(`/api/v1/knowledge/articles/${input.id}`, body);
|
|
361
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
return apiErrorResult(err, 'tacit_kb_update_article');
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
export async function handleUpdateArticle(args) {
|
|
370
|
+
const parsed = updateArticleInputSchema.safeParse(args);
|
|
371
|
+
if (!parsed.success) {
|
|
372
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
373
|
+
}
|
|
374
|
+
return buildUpdateArticleRequest(getClient(), parsed.data).execute();
|
|
375
|
+
}
|
|
376
|
+
// ─── tacit_kb_browse_tree ────────────────────────────────────────────────────
|
|
377
|
+
export const browseTreeInputSchema = z.object({
|
|
378
|
+
kb_category_id: z.string().optional().describe('Optional UUID of a KB category to narrow the browse tree'),
|
|
379
|
+
});
|
|
380
|
+
export const BROWSE_TREE_TOOL_DEF = {
|
|
381
|
+
name: 'tacit_kb_browse_tree',
|
|
382
|
+
description: 'Browse the KB as a collection/article tree, optionally narrowed to one category.',
|
|
383
|
+
inputSchema: {
|
|
384
|
+
type: 'object',
|
|
385
|
+
properties: {
|
|
386
|
+
kb_category_id: { type: 'string', description: 'Optional KB category UUID to narrow by' },
|
|
387
|
+
},
|
|
388
|
+
required: [],
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
export function buildBrowseTreeRequest(client, input) {
|
|
392
|
+
return {
|
|
393
|
+
async execute() {
|
|
394
|
+
try {
|
|
395
|
+
const result = await client.get('/api/v1/knowledge/browse', {
|
|
396
|
+
kb_category_id: input.kb_category_id,
|
|
397
|
+
});
|
|
398
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
399
|
+
}
|
|
400
|
+
catch (err) {
|
|
401
|
+
return apiErrorResult(err, 'tacit_kb_browse_tree');
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
export async function handleBrowseTree(args) {
|
|
407
|
+
const parsed = browseTreeInputSchema.safeParse(args);
|
|
408
|
+
if (!parsed.success) {
|
|
409
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
410
|
+
}
|
|
411
|
+
return buildBrowseTreeRequest(getClient(), parsed.data).execute();
|
|
412
|
+
}
|
|
413
|
+
// ─── tacit_kb_promote_from_artifact ─────────────────────────────────────────
|
|
414
|
+
export const promoteFromArtifactInputSchema = z.object({
|
|
415
|
+
artifact_id: z.string().min(1).describe('UUID of the artifact to promote into the KB'),
|
|
416
|
+
collection_id: z.string().min(1).describe('UUID of an existing collection to place the promoted article in'),
|
|
417
|
+
});
|
|
418
|
+
export const PROMOTE_FROM_ARTIFACT_TOOL_DEF = {
|
|
419
|
+
name: 'tacit_kb_promote_from_artifact',
|
|
420
|
+
description: 'Promote an existing artifact into a published KB article, placing it in the given collection. ' +
|
|
421
|
+
'Idempotent — re-promoting an already-promoted artifact only applies the new placement.',
|
|
422
|
+
inputSchema: {
|
|
423
|
+
type: 'object',
|
|
424
|
+
properties: {
|
|
425
|
+
artifact_id: { type: 'string', description: 'UUID of the artifact to promote' },
|
|
426
|
+
collection_id: { type: 'string', description: 'UUID of an existing collection' },
|
|
427
|
+
},
|
|
428
|
+
required: ['artifact_id', 'collection_id'],
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
export function buildPromoteFromArtifactRequest(client, input) {
|
|
432
|
+
return {
|
|
433
|
+
async execute() {
|
|
434
|
+
try {
|
|
435
|
+
const body = {
|
|
436
|
+
placement: {
|
|
437
|
+
kb: { collections: [input.collection_id] },
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
const result = await client.post(`/api/v1/knowledge/articles/from-artifact/${input.artifact_id}`, body);
|
|
441
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
442
|
+
}
|
|
443
|
+
catch (err) {
|
|
444
|
+
return apiErrorResult(err, 'tacit_kb_promote_from_artifact');
|
|
445
|
+
}
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
export async function handlePromoteFromArtifact(args) {
|
|
450
|
+
const parsed = promoteFromArtifactInputSchema.safeParse(args);
|
|
451
|
+
if (!parsed.success) {
|
|
452
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
453
|
+
}
|
|
454
|
+
return buildPromoteFromArtifactRequest(getClient(), parsed.data).execute();
|
|
455
|
+
}
|
|
456
|
+
// ─── tacit_kb_promote_from_catalog ─────────────────────────────────────────
|
|
457
|
+
export const promoteFromCatalogInputSchema = z.object({
|
|
458
|
+
catalog_item_id: z.string().min(1).describe('UUID of the org_lake_catalog item to promote into the KB'),
|
|
459
|
+
collection_id: z.string().min(1).describe('UUID of an existing collection to place the promoted article in'),
|
|
460
|
+
});
|
|
461
|
+
export const PROMOTE_FROM_CATALOG_TOOL_DEF = {
|
|
462
|
+
name: 'tacit_kb_promote_from_catalog',
|
|
463
|
+
description: 'Promote a data lake catalog item into a published KB article. ' +
|
|
464
|
+
'Resolves the catalog item\'s artifact_id and delegates to the promote-from-artifact endpoint. ' +
|
|
465
|
+
'The catalog item must have an artifact_id (created via LAKE-17 artifact bridge).',
|
|
466
|
+
inputSchema: {
|
|
467
|
+
type: 'object',
|
|
468
|
+
properties: {
|
|
469
|
+
catalog_item_id: { type: 'string', description: 'UUID of the catalog item' },
|
|
470
|
+
collection_id: { type: 'string', description: 'UUID of an existing collection' },
|
|
471
|
+
},
|
|
472
|
+
required: ['catalog_item_id', 'collection_id'],
|
|
473
|
+
},
|
|
474
|
+
};
|
|
475
|
+
export function buildPromoteFromCatalogRequest(client, input) {
|
|
476
|
+
return {
|
|
477
|
+
async execute() {
|
|
478
|
+
try {
|
|
479
|
+
const detail = await client.get(`/api/v1/org/lake/catalog/${input.catalog_item_id}`);
|
|
480
|
+
const artifactId = detail?.artifact_id;
|
|
481
|
+
if (!artifactId) {
|
|
482
|
+
return {
|
|
483
|
+
content: [{ type: 'text', text: `Catalog item ${input.catalog_item_id} has no artifact_id — run a sync first (LAKE-17 artifact bridge creates artifacts on ingest).` }],
|
|
484
|
+
isError: true,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const body = {
|
|
488
|
+
placement: {
|
|
489
|
+
kb: { collections: [input.collection_id] },
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
const result = await client.post(`/api/v1/knowledge/articles/from-artifact/${artifactId}`, body);
|
|
493
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
494
|
+
}
|
|
495
|
+
catch (err) {
|
|
496
|
+
return apiErrorResult(err, 'tacit_kb_promote_from_catalog');
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
export async function handlePromoteFromCatalog(args) {
|
|
502
|
+
const parsed = promoteFromCatalogInputSchema.safeParse(args);
|
|
503
|
+
if (!parsed.success) {
|
|
504
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
505
|
+
}
|
|
506
|
+
return buildPromoteFromCatalogRequest(getClient(), parsed.data).execute();
|
|
507
|
+
}
|
|
508
|
+
// ─── tacit_kb_list_categories ────────────────────────────────────────────────
|
|
509
|
+
export const listCategoriesInputSchema = z.object({
|
|
510
|
+
kind: z.string().optional().describe('Optional category kind filter (e.g. "domain")'),
|
|
511
|
+
include_archived: z.boolean().optional().describe('Include archived categories (default false)'),
|
|
512
|
+
});
|
|
513
|
+
export const LIST_CATEGORIES_TOOL_DEF = {
|
|
514
|
+
name: 'tacit_kb_list_categories',
|
|
515
|
+
description: 'List KB categories in the workspace, optionally filtered by kind and including archived ones.',
|
|
516
|
+
inputSchema: {
|
|
517
|
+
type: 'object',
|
|
518
|
+
properties: {
|
|
519
|
+
kind: { type: 'string', description: 'Optional category kind filter' },
|
|
520
|
+
include_archived: { type: 'boolean', description: 'Include archived categories (default false)' },
|
|
521
|
+
},
|
|
522
|
+
required: [],
|
|
523
|
+
},
|
|
524
|
+
};
|
|
525
|
+
export function buildListCategoriesRequest(client, input) {
|
|
526
|
+
return {
|
|
527
|
+
async execute() {
|
|
528
|
+
try {
|
|
529
|
+
const result = await client.get('/api/v1/knowledge/categories', {
|
|
530
|
+
kind: input.kind,
|
|
531
|
+
include_archived: input.include_archived,
|
|
532
|
+
});
|
|
533
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
return apiErrorResult(err, 'tacit_kb_list_categories');
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
export async function handleListCategories(args) {
|
|
542
|
+
const parsed = listCategoriesInputSchema.safeParse(args);
|
|
543
|
+
if (!parsed.success) {
|
|
544
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
545
|
+
}
|
|
546
|
+
return buildListCategoriesRequest(getClient(), parsed.data).execute();
|
|
547
|
+
}
|
|
548
|
+
// ─── tacit_kb_create_category ────────────────────────────────────────────────
|
|
549
|
+
export const createCategoryInputSchema = z.object({
|
|
550
|
+
name: z.string().min(1).describe('Category name (unique per workspace)'),
|
|
551
|
+
description: z.string().optional().describe('Optional description'),
|
|
552
|
+
});
|
|
553
|
+
export const CREATE_CATEGORY_TOOL_DEF = {
|
|
554
|
+
name: 'tacit_kb_create_category',
|
|
555
|
+
description: 'Create a new KB category (kind="domain"). Requires domain categories to be enabled for the workspace.',
|
|
556
|
+
inputSchema: {
|
|
557
|
+
type: 'object',
|
|
558
|
+
properties: {
|
|
559
|
+
name: { type: 'string', description: 'Category name (unique per workspace)' },
|
|
560
|
+
description: { type: 'string', description: 'Optional description' },
|
|
561
|
+
},
|
|
562
|
+
required: ['name'],
|
|
563
|
+
},
|
|
564
|
+
};
|
|
565
|
+
export function buildCreateCategoryRequest(client, input) {
|
|
566
|
+
return {
|
|
567
|
+
async execute() {
|
|
568
|
+
try {
|
|
569
|
+
const body = { name: input.name };
|
|
570
|
+
if (input.description)
|
|
571
|
+
body['description'] = input.description;
|
|
572
|
+
const result = await client.post('/api/v1/knowledge/categories', body);
|
|
573
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
574
|
+
}
|
|
575
|
+
catch (err) {
|
|
576
|
+
return apiErrorResult(err, 'tacit_kb_create_category');
|
|
577
|
+
}
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
export async function handleCreateCategory(args) {
|
|
582
|
+
const parsed = createCategoryInputSchema.safeParse(args);
|
|
583
|
+
if (!parsed.success) {
|
|
584
|
+
return { content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }], isError: true };
|
|
585
|
+
}
|
|
586
|
+
return buildCreateCategoryRequest(getClient(), parsed.data).execute();
|
|
587
|
+
}
|
|
588
|
+
export function registerKbTools(server, getClientFn) {
|
|
589
|
+
server.tool('tacit_kb_list_collections', LIST_COLLECTIONS_TOOL_DEF.description, listCollectionsInputSchema.shape, async (args) => {
|
|
590
|
+
const client = await getClientFn();
|
|
591
|
+
return buildListCollectionsRequest(client, args).execute();
|
|
592
|
+
});
|
|
593
|
+
server.tool('tacit_kb_create_collection', CREATE_COLLECTION_TOOL_DEF.description, createCollectionInputSchema.shape, async (args) => {
|
|
594
|
+
const client = await getClientFn();
|
|
595
|
+
return buildCreateCollectionRequest(client, args).execute();
|
|
596
|
+
});
|
|
597
|
+
server.tool('tacit_kb_update_collection', UPDATE_COLLECTION_TOOL_DEF.description, updateCollectionInputSchema.shape, async (args) => {
|
|
598
|
+
const client = await getClientFn();
|
|
599
|
+
return buildUpdateCollectionRequest(client, args).execute();
|
|
600
|
+
});
|
|
601
|
+
server.tool('tacit_kb_delete_collection', DELETE_COLLECTION_TOOL_DEF.description, deleteCollectionInputSchema.shape, async (args) => {
|
|
602
|
+
const client = await getClientFn();
|
|
603
|
+
return buildDeleteCollectionRequest(client, args).execute();
|
|
604
|
+
});
|
|
605
|
+
server.tool('tacit_kb_list_articles', LIST_ARTICLES_TOOL_DEF.description, listArticlesInputSchema.shape, async (args) => {
|
|
606
|
+
const client = await getClientFn();
|
|
607
|
+
return buildListArticlesRequest(client, args).execute();
|
|
608
|
+
});
|
|
609
|
+
server.tool('tacit_kb_get_article', GET_ARTICLE_TOOL_DEF.description, getArticleInputSchema.shape, async (args) => {
|
|
610
|
+
const client = await getClientFn();
|
|
611
|
+
return buildGetArticleRequest(client, args).execute();
|
|
612
|
+
});
|
|
613
|
+
server.tool('tacit_kb_create_article', CREATE_ARTICLE_TOOL_DEF.description, createArticleInputSchema.shape, async (args) => {
|
|
614
|
+
const client = await getClientFn();
|
|
615
|
+
return buildCreateArticleRequest(client, args).execute();
|
|
616
|
+
});
|
|
617
|
+
server.tool('tacit_kb_update_article', UPDATE_ARTICLE_TOOL_DEF.description, updateArticleInputSchema.shape, async (args) => {
|
|
618
|
+
const client = await getClientFn();
|
|
619
|
+
return buildUpdateArticleRequest(client, args).execute();
|
|
620
|
+
});
|
|
621
|
+
server.tool('tacit_kb_browse_tree', BROWSE_TREE_TOOL_DEF.description, browseTreeInputSchema.shape, async (args) => {
|
|
622
|
+
const client = await getClientFn();
|
|
623
|
+
return buildBrowseTreeRequest(client, args).execute();
|
|
624
|
+
});
|
|
625
|
+
server.tool('tacit_kb_promote_from_artifact', PROMOTE_FROM_ARTIFACT_TOOL_DEF.description, promoteFromArtifactInputSchema.shape, async (args) => {
|
|
626
|
+
const client = await getClientFn();
|
|
627
|
+
return buildPromoteFromArtifactRequest(client, args).execute();
|
|
628
|
+
});
|
|
629
|
+
server.tool('tacit_kb_promote_from_catalog', PROMOTE_FROM_CATALOG_TOOL_DEF.description, promoteFromCatalogInputSchema.shape, async (args) => {
|
|
630
|
+
const client = await getClientFn();
|
|
631
|
+
return buildPromoteFromCatalogRequest(client, args).execute();
|
|
632
|
+
});
|
|
633
|
+
server.tool('tacit_kb_list_categories', LIST_CATEGORIES_TOOL_DEF.description, listCategoriesInputSchema.shape, async (args) => {
|
|
634
|
+
const client = await getClientFn();
|
|
635
|
+
return buildListCategoriesRequest(client, args).execute();
|
|
636
|
+
});
|
|
637
|
+
server.tool('tacit_kb_create_category', CREATE_CATEGORY_TOOL_DEF.description, createCategoryInputSchema.shape, async (args) => {
|
|
638
|
+
const client = await getClientFn();
|
|
639
|
+
return buildCreateCategoryRequest(client, args).execute();
|
|
640
|
+
});
|
|
641
|
+
server.tool('tacit_kb_freshness', 'Get a freshness report for knowledge base articles.', {}, async () => {
|
|
642
|
+
try {
|
|
643
|
+
const client = await getClientFn();
|
|
644
|
+
const data = await client.get('/api/v1/knowledge/freshness');
|
|
645
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
646
|
+
}
|
|
647
|
+
catch (err) {
|
|
648
|
+
return sharedApiErrorResult(err, '/api/v1/knowledge/freshness');
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
server.tool('tacit_kb_pull_source', 'Trigger a sync/pull from a registered data lake source to ingest content into the knowledge base.', {
|
|
652
|
+
source_id: z.string().uuid().describe('Source UUID'),
|
|
653
|
+
confirm_deletions: z.boolean().optional().describe('Delete items removed from source (default: false)'),
|
|
654
|
+
}, async (args) => {
|
|
655
|
+
try {
|
|
656
|
+
const client = await getClientFn();
|
|
657
|
+
const data = await client.post('/api/v1/org/lake/sync', {
|
|
658
|
+
source_id: args.source_id,
|
|
659
|
+
confirm_deletions: args.confirm_deletions ?? false,
|
|
660
|
+
});
|
|
661
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
662
|
+
}
|
|
663
|
+
catch (err) {
|
|
664
|
+
return sharedApiErrorResult(err, '/api/v1/org/lake/sync');
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
server.tool('tacit_kb_register_source', 'Register a data source for lake-to-KB ingestion.', {
|
|
668
|
+
uri: z.string().describe('Source URI (e.g. github://owner/repo, file:///path, s3://bucket/prefix)'),
|
|
669
|
+
kind: z.enum(['git', 'local_fs', 's3']).describe('Source connector type'),
|
|
670
|
+
sync_mode: z.enum(['full', 'incremental']).optional().describe('Sync mode (default: full)'),
|
|
671
|
+
sync_interval_secs: z.number().optional().describe('Auto-sync interval in seconds'),
|
|
672
|
+
}, async (args) => {
|
|
673
|
+
try {
|
|
674
|
+
const client = await getClientFn();
|
|
675
|
+
const data = await client.post('/api/v1/org/lake/onboard', {
|
|
676
|
+
uri: args.uri,
|
|
677
|
+
kind: args.kind,
|
|
678
|
+
sync_mode: args.sync_mode ?? 'full',
|
|
679
|
+
sync_interval_secs: args.sync_interval_secs,
|
|
680
|
+
});
|
|
681
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
682
|
+
}
|
|
683
|
+
catch (err) {
|
|
684
|
+
return sharedApiErrorResult(err, '/api/v1/org/lake/onboard');
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
}
|