@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/search.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
2
|
/**
|
|
4
3
|
* tacit_search — hybrid BM25+ANN+RRF search
|
|
5
4
|
* Route: GET /api/v1/search?q=<term>
|
|
6
5
|
* JWT-scoped; workspace resolved by TacitClient.
|
|
7
6
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
source_types: zod_1.z.array(zod_1.z.string()).optional().describe('Filter by source types (e.g. ["artifact","chronicle"])'),
|
|
18
|
-
entity_types: zod_1.z.array(zod_1.z.string()).optional().describe('Filter by entity types'),
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { getClient } from '../client.js';
|
|
9
|
+
import { apiErrorResult } from './errors.js';
|
|
10
|
+
export const searchInputSchema = z.object({
|
|
11
|
+
query: z.string().min(1).describe('Search query string'),
|
|
12
|
+
limit: z.number().int().positive().optional().describe('Maximum results to return'),
|
|
13
|
+
source_types: z.array(z.string()).optional().describe('Filter by source types (e.g. ["artifact","chronicle"])'),
|
|
14
|
+
entity_types: z.array(z.string()).optional().describe('Filter by entity types'),
|
|
15
|
+
origin: z.string().optional().describe('Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results'),
|
|
19
16
|
});
|
|
20
|
-
|
|
17
|
+
export const SEARCH_TOOL_DEF = {
|
|
21
18
|
name: 'tacit_search',
|
|
22
19
|
description: 'Search Tacit knowledge using hybrid BM25+ANN+RRF against /api/v1/search. Returns ranked results from the workspace corpus.',
|
|
23
20
|
inputSchema: {
|
|
@@ -27,6 +24,7 @@ exports.SEARCH_TOOL_DEF = {
|
|
|
27
24
|
limit: { type: 'number', description: 'Maximum results to return' },
|
|
28
25
|
source_types: { type: 'array', items: { type: 'string' }, description: 'Filter by source types' },
|
|
29
26
|
entity_types: { type: 'array', items: { type: 'string' }, description: 'Filter by entity types' },
|
|
27
|
+
origin: { type: 'string', description: 'Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results' },
|
|
30
28
|
},
|
|
31
29
|
required: ['query'],
|
|
32
30
|
},
|
|
@@ -35,10 +33,10 @@ exports.SEARCH_TOOL_DEF = {
|
|
|
35
33
|
* Build a search request object. Call .execute() to perform the HTTP call.
|
|
36
34
|
* Exported for testability (allows injecting a mock TacitClient).
|
|
37
35
|
*/
|
|
38
|
-
function buildSearchRequest(client, input) {
|
|
36
|
+
export function buildSearchRequest(client, input) {
|
|
39
37
|
return {
|
|
40
38
|
async execute() {
|
|
41
|
-
const { query, limit, source_types, entity_types } = input;
|
|
39
|
+
const { query, limit, source_types, entity_types, origin } = input;
|
|
42
40
|
try {
|
|
43
41
|
const params = { q: query };
|
|
44
42
|
if (limit !== undefined)
|
|
@@ -47,6 +45,8 @@ function buildSearchRequest(client, input) {
|
|
|
47
45
|
params['source_types'] = source_types.join(',');
|
|
48
46
|
if (entity_types && entity_types.length > 0)
|
|
49
47
|
params['entity_types'] = entity_types.join(',');
|
|
48
|
+
if (origin !== undefined)
|
|
49
|
+
params['origin'] = origin;
|
|
50
50
|
// GET /api/v1/search?q=<term>
|
|
51
51
|
const result = await client.get('/api/v1/search', params);
|
|
52
52
|
return {
|
|
@@ -54,25 +54,86 @@ function buildSearchRequest(client, input) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
catch (err) {
|
|
57
|
-
return apiErrorResult(err);
|
|
57
|
+
return apiErrorResult(err, '/api/v1/search');
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
-
async function handleSearch(args) {
|
|
63
|
-
const parsed =
|
|
62
|
+
export async function handleSearch(args) {
|
|
63
|
+
const parsed = searchInputSchema.safeParse(args);
|
|
64
64
|
if (!parsed.success) {
|
|
65
65
|
return {
|
|
66
66
|
content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }],
|
|
67
67
|
isError: true,
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
return buildSearchRequest(
|
|
70
|
+
return buildSearchRequest(getClient(), parsed.data).execute();
|
|
71
71
|
}
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
export function registerSearchTools(server, getClientFn) {
|
|
73
|
+
server.tool('tacit_search', 'Search Tacit knowledge using hybrid BM25+ANN+RRF against /api/v1/search. Returns ranked results from the workspace corpus.', {
|
|
74
|
+
query: z.string().min(1).describe('Search query string'),
|
|
75
|
+
limit: z.number().int().positive().optional().describe('Maximum results to return'),
|
|
76
|
+
source_types: z.array(z.string()).optional().describe('Filter by source types (e.g. ["artifact","chronicle"])'),
|
|
77
|
+
entity_types: z.array(z.string()).optional().describe('Filter by entity types'),
|
|
78
|
+
origin: z.string().optional().describe('Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results'),
|
|
79
|
+
}, async (args) => {
|
|
80
|
+
const client = await getClientFn();
|
|
81
|
+
return buildSearchRequest(client, args).execute();
|
|
82
|
+
});
|
|
83
|
+
server.tool('tacit_search_events', 'Search across all event types using hybrid BM25+ANN retrieval.', {
|
|
84
|
+
q: z.string().describe('Search query text'),
|
|
85
|
+
limit: z.number().int().optional().describe('Max results'),
|
|
86
|
+
source_type: z.string().optional().describe('Filter: CHRONICLE_EVENT or SPECIFIER_DOC'),
|
|
87
|
+
source_types: z.array(z.string()).optional().describe('Multi-type filter'),
|
|
88
|
+
domain: z.string().optional().describe('Domain context for pre-ANN GIN filtering'),
|
|
89
|
+
hybrid: z.boolean().optional().describe('Use hybrid BM25+ANN search'),
|
|
90
|
+
}, async (args) => {
|
|
91
|
+
try {
|
|
92
|
+
const client = await getClientFn();
|
|
93
|
+
const params = new URLSearchParams();
|
|
94
|
+
params.set('q', args.q);
|
|
95
|
+
if (args.limit !== undefined)
|
|
96
|
+
params.set('limit', String(args.limit));
|
|
97
|
+
if (args.source_type)
|
|
98
|
+
params.set('source_type', args.source_type);
|
|
99
|
+
if (args.source_types?.length)
|
|
100
|
+
params.set('source_types', args.source_types.join(','));
|
|
101
|
+
if (args.domain)
|
|
102
|
+
params.set('domain', args.domain);
|
|
103
|
+
if (args.hybrid !== undefined)
|
|
104
|
+
params.set('hybrid', String(args.hybrid));
|
|
105
|
+
const data = await client.get(`/api/v1/search`, Object.fromEntries(params));
|
|
106
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return apiErrorResult(err, '/api/v1/search');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
server.tool('tacit_entity_search', 'Search for entities by tag.', {
|
|
113
|
+
tag: z.string().describe('Entity tag to search for'),
|
|
114
|
+
limit: z.number().int().optional().describe('Max results (default 20, cap 100)'),
|
|
115
|
+
}, async (args) => {
|
|
116
|
+
try {
|
|
117
|
+
const client = await getClientFn();
|
|
118
|
+
const data = await client.get('/api/v1/search/entity', { tag: args.tag, limit: args.limit });
|
|
119
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
return apiErrorResult(err, '/api/v1/search/entity');
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
server.tool('tacit_search_suggest', 'Get search suggestions for a query prefix.', {
|
|
126
|
+
q: z.string().describe('Search query prefix'),
|
|
127
|
+
limit: z.number().int().optional().describe('Max results (default 10, cap 20)'),
|
|
128
|
+
source_types: z.string().optional().describe('Comma-separated source types'),
|
|
129
|
+
}, async (args) => {
|
|
130
|
+
try {
|
|
131
|
+
const client = await getClientFn();
|
|
132
|
+
const data = await client.get('/api/v1/search/suggest', { q: args.q, limit: args.limit, source_types: args.source_types });
|
|
133
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
return apiErrorResult(err, '/api/v1/search/suggest');
|
|
137
|
+
}
|
|
138
|
+
});
|
|
78
139
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MV-1518b: Guest sharing MCP tools
|
|
3
|
+
* tacit_shared_list — list artifacts shared with the guest
|
|
4
|
+
* tacit_shared_read — read a specific shared artifact
|
|
5
|
+
* tacit_shared_search — search across shared artifacts
|
|
6
|
+
*
|
|
7
|
+
* All three require a GUEST JWT. Non-guest JWTs get a 403 from the backend.
|
|
8
|
+
*/
|
|
9
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
10
|
+
import { TacitClient } from '../client.js';
|
|
11
|
+
type ToolResult = {
|
|
12
|
+
content: Array<{
|
|
13
|
+
type: 'text';
|
|
14
|
+
text: string;
|
|
15
|
+
}>;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
};
|
|
18
|
+
export declare const SHARED_LIST_TOOL_DEF: {
|
|
19
|
+
name: "tacit_shared_list";
|
|
20
|
+
description: string;
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: "object";
|
|
23
|
+
properties: {};
|
|
24
|
+
required: string[];
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export declare const SHARED_READ_TOOL_DEF: {
|
|
28
|
+
name: "tacit_shared_read";
|
|
29
|
+
description: string;
|
|
30
|
+
inputSchema: {
|
|
31
|
+
type: "object";
|
|
32
|
+
properties: {
|
|
33
|
+
artifact_id: {
|
|
34
|
+
type: string;
|
|
35
|
+
description: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
required: string[];
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
export declare const SHARED_SEARCH_TOOL_DEF: {
|
|
42
|
+
name: "tacit_shared_search";
|
|
43
|
+
description: string;
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object";
|
|
46
|
+
properties: {
|
|
47
|
+
query: {
|
|
48
|
+
type: string;
|
|
49
|
+
description: string;
|
|
50
|
+
};
|
|
51
|
+
limit: {
|
|
52
|
+
type: string;
|
|
53
|
+
description: string;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
required: string[];
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
export declare function handleSharedList(_args: unknown): Promise<ToolResult>;
|
|
60
|
+
export declare function handleSharedRead(args: unknown): Promise<ToolResult>;
|
|
61
|
+
export declare function handleSharedSearch(args: unknown): Promise<ToolResult>;
|
|
62
|
+
export declare function registerSharingTools(server: McpServer, getClientFn: () => Promise<TacitClient>): void;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
/**
|
|
3
|
+
* MV-1518b: Guest sharing MCP tools
|
|
4
|
+
* tacit_shared_list — list artifacts shared with the guest
|
|
5
|
+
* tacit_shared_read — read a specific shared artifact
|
|
6
|
+
* tacit_shared_search — search across shared artifacts
|
|
7
|
+
*
|
|
8
|
+
* All three require a GUEST JWT. Non-guest JWTs get a 403 from the backend.
|
|
9
|
+
*/
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { getClient } from '../client.js';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Tool definitions
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
export const SHARED_LIST_TOOL_DEF = {
|
|
16
|
+
name: 'tacit_shared_list',
|
|
17
|
+
description: 'List artifacts shared with you (guest access). Returns items from /api/v1/shared-with-me using guest-scoped grants.',
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: 'object',
|
|
20
|
+
properties: {},
|
|
21
|
+
required: [],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export const SHARED_READ_TOOL_DEF = {
|
|
25
|
+
name: 'tacit_shared_read',
|
|
26
|
+
description: 'Read a specific shared artifact by ID (guest access). Returns the artifact content if the guest has a grant for it.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
artifact_id: { type: 'string', description: 'UUID of the artifact to read' },
|
|
31
|
+
},
|
|
32
|
+
required: ['artifact_id'],
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
export const SHARED_SEARCH_TOOL_DEF = {
|
|
36
|
+
name: 'tacit_shared_search',
|
|
37
|
+
description: 'Search across artifacts shared with you (guest access). Queries /api/v1/search scoped to guest-granted artifacts.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
query: { type: 'string', description: 'Search query string' },
|
|
42
|
+
limit: { type: 'number', description: 'Maximum results to return' },
|
|
43
|
+
},
|
|
44
|
+
required: ['query'],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Handlers
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
export async function handleSharedList(_args) {
|
|
51
|
+
try {
|
|
52
|
+
const client = getClient();
|
|
53
|
+
const resp = await client.get('/api/v1/shared-with-me');
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: 'text', text: `Error listing shared artifacts: ${message}` }],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function handleSharedRead(args) {
|
|
67
|
+
try {
|
|
68
|
+
const client = getClient();
|
|
69
|
+
const { artifact_id } = args;
|
|
70
|
+
if (!artifact_id) {
|
|
71
|
+
return {
|
|
72
|
+
content: [{ type: 'text', text: 'artifact_id is required' }],
|
|
73
|
+
isError: true,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const resp = await client.get(`/api/v1/artifacts/${artifact_id}`);
|
|
77
|
+
return {
|
|
78
|
+
content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }],
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
return {
|
|
84
|
+
content: [{ type: 'text', text: `Error reading shared artifact: ${message}` }],
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export async function handleSharedSearch(args) {
|
|
90
|
+
try {
|
|
91
|
+
const client = getClient();
|
|
92
|
+
const { query, limit } = args;
|
|
93
|
+
if (!query) {
|
|
94
|
+
return {
|
|
95
|
+
content: [{ type: 'text', text: 'query is required' }],
|
|
96
|
+
isError: true,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
const params = { q: query };
|
|
100
|
+
if (limit)
|
|
101
|
+
params.limit = limit;
|
|
102
|
+
const resp = await client.get('/api/v1/search', params);
|
|
103
|
+
return {
|
|
104
|
+
content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
109
|
+
return {
|
|
110
|
+
content: [{ type: 'text', text: `Error searching shared artifacts: ${message}` }],
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export function registerSharingTools(server, getClientFn) {
|
|
116
|
+
server.tool('tacit_shared_list', 'List artifacts shared with you (guest access).', {}, async () => {
|
|
117
|
+
const client = await getClientFn();
|
|
118
|
+
try {
|
|
119
|
+
const resp = await client.get('/api/v1/shared-with-me');
|
|
120
|
+
return { content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }] };
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
124
|
+
return { content: [{ type: 'text', text: `Error listing shared artifacts: ${message}` }], isError: true };
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
server.tool('tacit_shared_read', 'Read a specific shared artifact by ID (guest access).', {
|
|
128
|
+
artifact_id: z.string().min(1).describe('UUID of the artifact to read'),
|
|
129
|
+
}, async (args) => {
|
|
130
|
+
const client = await getClientFn();
|
|
131
|
+
try {
|
|
132
|
+
const resp = await client.get(`/api/v1/artifacts/${args.artifact_id}`);
|
|
133
|
+
return { content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }] };
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
137
|
+
return { content: [{ type: 'text', text: `Error reading shared artifact: ${message}` }], isError: true };
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
server.tool('tacit_shared_search', 'Search across artifacts shared with you (guest access).', {
|
|
141
|
+
query: z.string().min(1).describe('Search query string'),
|
|
142
|
+
limit: z.number().int().positive().optional().describe('Maximum results to return'),
|
|
143
|
+
}, async (args) => {
|
|
144
|
+
const client = await getClientFn();
|
|
145
|
+
try {
|
|
146
|
+
const params = { q: args.query };
|
|
147
|
+
if (args.limit)
|
|
148
|
+
params.limit = args.limit;
|
|
149
|
+
const resp = await client.get('/api/v1/search', params);
|
|
150
|
+
return { content: [{ type: 'text', text: JSON.stringify(resp, null, 2) }] };
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
154
|
+
return { content: [{ type: 'text', text: `Error searching shared artifacts: ${message}` }], isError: true };
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
@@ -8,19 +8,38 @@
|
|
|
8
8
|
* stream:true selects the SSE variant and returns aggregated chunks.
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
11
12
|
import { TacitClient } from '../client.js';
|
|
12
13
|
export declare const synthesizeInputSchema: z.ZodObject<{
|
|
13
14
|
query: z.ZodString;
|
|
14
15
|
scope: z.ZodOptional<z.ZodString>;
|
|
15
16
|
stream: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
workspace_id: z.ZodOptional<z.ZodString>;
|
|
18
|
+
collection_ids: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
19
|
+
program_ids: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
20
|
+
origin: z.ZodOptional<z.ZodString>;
|
|
21
|
+
synthesis_mode: z.ZodOptional<z.ZodEnum<["burst", "orbit", "crew_compose"]>>;
|
|
22
|
+
max_turns: z.ZodOptional<z.ZodNumber>;
|
|
16
23
|
}, "strip", z.ZodTypeAny, {
|
|
17
24
|
query: string;
|
|
18
|
-
|
|
25
|
+
workspace_id?: string | undefined;
|
|
19
26
|
scope?: string | undefined;
|
|
27
|
+
stream?: boolean | undefined;
|
|
28
|
+
origin?: string | undefined;
|
|
29
|
+
collection_ids?: string[] | undefined;
|
|
30
|
+
program_ids?: string[] | undefined;
|
|
31
|
+
synthesis_mode?: "burst" | "orbit" | "crew_compose" | undefined;
|
|
32
|
+
max_turns?: number | undefined;
|
|
20
33
|
}, {
|
|
21
34
|
query: string;
|
|
22
|
-
|
|
35
|
+
workspace_id?: string | undefined;
|
|
23
36
|
scope?: string | undefined;
|
|
37
|
+
stream?: boolean | undefined;
|
|
38
|
+
origin?: string | undefined;
|
|
39
|
+
collection_ids?: string[] | undefined;
|
|
40
|
+
program_ids?: string[] | undefined;
|
|
41
|
+
synthesis_mode?: "burst" | "orbit" | "crew_compose" | undefined;
|
|
42
|
+
max_turns?: number | undefined;
|
|
24
43
|
}>;
|
|
25
44
|
export type SynthesizeInput = z.infer<typeof synthesizeInputSchema>;
|
|
26
45
|
export declare const SYNTHESIZE_TOOL_DEF: {
|
|
@@ -41,6 +60,38 @@ export declare const SYNTHESIZE_TOOL_DEF: {
|
|
|
41
60
|
type: string;
|
|
42
61
|
description: string;
|
|
43
62
|
};
|
|
63
|
+
workspace_id: {
|
|
64
|
+
type: string;
|
|
65
|
+
description: string;
|
|
66
|
+
};
|
|
67
|
+
collection_ids: {
|
|
68
|
+
type: string;
|
|
69
|
+
items: {
|
|
70
|
+
type: string;
|
|
71
|
+
};
|
|
72
|
+
description: string;
|
|
73
|
+
};
|
|
74
|
+
program_ids: {
|
|
75
|
+
type: string;
|
|
76
|
+
items: {
|
|
77
|
+
type: string;
|
|
78
|
+
};
|
|
79
|
+
description: string;
|
|
80
|
+
};
|
|
81
|
+
origin: {
|
|
82
|
+
type: string;
|
|
83
|
+
description: string;
|
|
84
|
+
};
|
|
85
|
+
synthesis_mode: {
|
|
86
|
+
type: string;
|
|
87
|
+
enum: string[];
|
|
88
|
+
description: string;
|
|
89
|
+
};
|
|
90
|
+
max_turns: {
|
|
91
|
+
type: string;
|
|
92
|
+
minimum: number;
|
|
93
|
+
description: string;
|
|
94
|
+
};
|
|
44
95
|
};
|
|
45
96
|
required: string[];
|
|
46
97
|
};
|
|
@@ -66,3 +117,4 @@ export declare function handleSynthesize(args: unknown): Promise<{
|
|
|
66
117
|
}>;
|
|
67
118
|
isError?: boolean;
|
|
68
119
|
}>;
|
|
120
|
+
export declare function registerSynthesizeTools(server: McpServer, getClientFn: () => Promise<TacitClient>): void;
|
package/dist/tools/synthesize.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
2
|
/**
|
|
4
3
|
* tacit_synthesize — canonical synthesis tool
|
|
@@ -9,18 +8,20 @@
|
|
|
9
8
|
* Aliases: tacit_ask / tacit_compose route to this same handler.
|
|
10
9
|
* stream:true selects the SSE variant and returns aggregated chunks.
|
|
11
10
|
*/
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { getClient, TacitApiError, TokenExpiredError } from '../client.js';
|
|
13
|
+
export const synthesizeInputSchema = z.object({
|
|
14
|
+
query: z.string().min(1).describe('Question or synthesis prompt'),
|
|
15
|
+
scope: z.string().optional().describe('Optional scope filter (e.g. portfolio or program id)'),
|
|
16
|
+
stream: z.boolean().optional().describe('If true, use SSE stream variant (/mind/synthesize/stream)'),
|
|
17
|
+
workspace_id: z.string().optional().describe('Optional workspace UUID'),
|
|
18
|
+
collection_ids: z.array(z.string()).optional().describe('Optional collection UUIDs to scope synthesis to'),
|
|
19
|
+
program_ids: z.array(z.string()).optional().describe('Optional program UUIDs to scope synthesis to'),
|
|
20
|
+
origin: z.string().optional().describe('Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results'),
|
|
21
|
+
synthesis_mode: z.enum(['burst', 'orbit', 'crew_compose']).optional().describe('Synthesis mode: burst (fast retrieval), orbit (agentic multi-turn), crew_compose (crew artifact generation). Default: burst.'),
|
|
22
|
+
max_turns: z.number().int().min(1).optional().describe('Max agentic loop iterations for orbit/crew_compose mode (1-12 for orbit, 1-54 for crew_compose).'),
|
|
22
23
|
});
|
|
23
|
-
|
|
24
|
+
export const SYNTHESIZE_TOOL_DEF = {
|
|
24
25
|
name: 'tacit_synthesize',
|
|
25
26
|
description: 'Synthesize knowledge from the Tacit workspace. ' +
|
|
26
27
|
'stream:false (default) → POST /api/v1/mind/synthesize (JSON). ' +
|
|
@@ -31,6 +32,12 @@ exports.SYNTHESIZE_TOOL_DEF = {
|
|
|
31
32
|
query: { type: 'string', description: 'Question or synthesis prompt' },
|
|
32
33
|
scope: { type: 'string', description: 'Optional scope filter' },
|
|
33
34
|
stream: { type: 'boolean', description: 'Use SSE stream variant if true' },
|
|
35
|
+
workspace_id: { type: 'string', description: 'Optional workspace UUID' },
|
|
36
|
+
collection_ids: { type: 'array', items: { type: 'string' }, description: 'Optional collection UUIDs to scope synthesis to' },
|
|
37
|
+
program_ids: { type: 'array', items: { type: 'string' }, description: 'Optional program UUIDs to scope synthesis to' },
|
|
38
|
+
origin: { type: 'string', description: 'Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results' },
|
|
39
|
+
synthesis_mode: { type: 'string', enum: ['burst', 'orbit', 'crew_compose'], description: 'Synthesis mode: burst (fast retrieval), orbit (agentic multi-turn), crew_compose (crew artifact generation). Default: burst.' },
|
|
40
|
+
max_turns: { type: 'integer', minimum: 1, description: 'Max agentic loop iterations for orbit/crew_compose mode (1-12 for orbit, 1-54 for crew_compose).' },
|
|
34
41
|
},
|
|
35
42
|
required: ['query'],
|
|
36
43
|
},
|
|
@@ -39,29 +46,53 @@ exports.SYNTHESIZE_TOOL_DEF = {
|
|
|
39
46
|
* Build a synthesize request object. Call .execute() to perform the HTTP call.
|
|
40
47
|
* Exported for testability (allows injecting a mock TacitClient).
|
|
41
48
|
*/
|
|
42
|
-
function buildSynthesizeRequest(client, input) {
|
|
49
|
+
export function buildSynthesizeRequest(client, input) {
|
|
43
50
|
return {
|
|
44
51
|
async execute() {
|
|
45
|
-
const { query, scope, stream } = input;
|
|
52
|
+
const { query, scope, stream, workspace_id, collection_ids, program_ids, origin, synthesis_mode, max_turns } = input;
|
|
46
53
|
const body = { query };
|
|
47
54
|
if (scope)
|
|
48
55
|
body['scope'] = scope;
|
|
56
|
+
if (workspace_id)
|
|
57
|
+
body['workspace_id'] = workspace_id;
|
|
58
|
+
if (collection_ids)
|
|
59
|
+
body['collection_ids'] = collection_ids;
|
|
60
|
+
if (program_ids)
|
|
61
|
+
body['program_ids'] = program_ids;
|
|
62
|
+
if (origin)
|
|
63
|
+
body['origin'] = origin;
|
|
64
|
+
if (synthesis_mode)
|
|
65
|
+
body['synthesis_mode'] = synthesis_mode;
|
|
66
|
+
if (max_turns)
|
|
67
|
+
body['max_turns'] = max_turns;
|
|
49
68
|
try {
|
|
50
69
|
if (stream) {
|
|
51
70
|
// SSE path: POST /api/v1/mind/synthesize/stream
|
|
52
71
|
const chunks = [];
|
|
72
|
+
let structuredAnswer = null;
|
|
53
73
|
for await (const chunk of client.postSSE('/api/v1/mind/synthesize/stream', body)) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(chunk);
|
|
76
|
+
if (parsed.type === 'structured_answer') {
|
|
77
|
+
structuredAnswer = parsed.data;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// not JSON — raw text chunk
|
|
83
|
+
}
|
|
54
84
|
chunks.push(chunk);
|
|
55
85
|
}
|
|
56
|
-
if (chunks.length === 0) {
|
|
86
|
+
if (chunks.length === 0 && !structuredAnswer) {
|
|
57
87
|
return {
|
|
58
88
|
content: [{ type: 'text', text: JSON.stringify({ chunks: 0, text: '' }) }],
|
|
59
89
|
};
|
|
60
90
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
91
|
+
const contentItems = chunks.map((c) => ({ type: 'text', text: c }));
|
|
92
|
+
if (structuredAnswer) {
|
|
93
|
+
contentItems.push({ type: 'text', text: JSON.stringify({ structured_answer: structuredAnswer }) });
|
|
94
|
+
}
|
|
95
|
+
return { content: contentItems };
|
|
65
96
|
}
|
|
66
97
|
else {
|
|
67
98
|
// JSON path: POST /api/v1/mind/synthesize
|
|
@@ -72,7 +103,13 @@ function buildSynthesizeRequest(client, input) {
|
|
|
72
103
|
}
|
|
73
104
|
}
|
|
74
105
|
catch (err) {
|
|
75
|
-
if (err instanceof
|
|
106
|
+
if (err instanceof TokenExpiredError) {
|
|
107
|
+
return {
|
|
108
|
+
content: [{ type: 'text', text: err.message }],
|
|
109
|
+
isError: true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (err instanceof TacitApiError) {
|
|
76
113
|
return { content: [{ type: 'text', text: `API error ${err.statusCode}: ${err.body}` }], isError: true };
|
|
77
114
|
}
|
|
78
115
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -81,13 +118,33 @@ function buildSynthesizeRequest(client, input) {
|
|
|
81
118
|
},
|
|
82
119
|
};
|
|
83
120
|
}
|
|
84
|
-
async function handleSynthesize(args) {
|
|
85
|
-
const parsed =
|
|
121
|
+
export async function handleSynthesize(args) {
|
|
122
|
+
const parsed = synthesizeInputSchema.safeParse(args);
|
|
86
123
|
if (!parsed.success) {
|
|
87
124
|
return {
|
|
88
125
|
content: [{ type: 'text', text: `Invalid input: ${parsed.error.message}` }],
|
|
89
126
|
isError: true,
|
|
90
127
|
};
|
|
91
128
|
}
|
|
92
|
-
return buildSynthesizeRequest(
|
|
129
|
+
return buildSynthesizeRequest(getClient(), parsed.data).execute();
|
|
130
|
+
}
|
|
131
|
+
const SYNTH_SCHEMA = {
|
|
132
|
+
query: z.string().min(1).describe('Question or synthesis prompt'),
|
|
133
|
+
scope: z.string().optional().describe('Optional scope filter (e.g. portfolio or program id)'),
|
|
134
|
+
stream: z.boolean().optional().describe('If true, use SSE stream variant (/mind/synthesize/stream)'),
|
|
135
|
+
workspace_id: z.string().optional().describe('Optional workspace UUID'),
|
|
136
|
+
collection_ids: z.array(z.string()).optional().describe('Optional collection UUIDs to scope synthesis to'),
|
|
137
|
+
program_ids: z.array(z.string()).optional().describe('Optional program UUIDs to scope synthesis to'),
|
|
138
|
+
origin: z.string().optional().describe('Optional origin filter (e.g., PYRAMID, LAKE_SYNC, MANUAL) to scope results'),
|
|
139
|
+
synthesis_mode: z.enum(['burst', 'orbit', 'crew_compose']).optional().describe('Synthesis mode: burst (fast retrieval), orbit (agentic multi-turn), crew_compose (crew artifact generation). Default: burst.'),
|
|
140
|
+
max_turns: z.number().int().min(1).optional().describe('Max agentic loop iterations for orbit/crew_compose mode (1-12 for orbit, 1-54 for crew_compose).'),
|
|
141
|
+
};
|
|
142
|
+
export function registerSynthesizeTools(server, getClientFn) {
|
|
143
|
+
const handler = async (args) => {
|
|
144
|
+
const client = await getClientFn();
|
|
145
|
+
return buildSynthesizeRequest(client, args).execute();
|
|
146
|
+
};
|
|
147
|
+
server.tool('tacit_synthesize', 'Synthesize knowledge from the Tacit workspace. synthesis_mode: burst (default, fast retrieval), orbit (agentic multi-turn), crew_compose (crew artifact generation). stream:false → JSON, stream:true → SSE.', SYNTH_SCHEMA, handler);
|
|
148
|
+
server.tool('tacit_ask', 'Ask a question against the Tacit workspace. Supports synthesis_mode: burst (default), orbit (agentic), crew_compose.', SYNTH_SCHEMA, handler);
|
|
149
|
+
server.tool('tacit_compose', 'Compose knowledge from the Tacit workspace. Supports synthesis_mode: burst (default), orbit (agentic), crew_compose.', SYNTH_SCHEMA, handler);
|
|
93
150
|
}
|