@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.
Files changed (68) hide show
  1. package/dist/auth.d.ts +32 -0
  2. package/dist/auth.js +64 -0
  3. package/dist/client.d.ts +60 -5
  4. package/dist/client.js +234 -53
  5. package/dist/index.js +128 -106
  6. package/dist/oauth.d.ts +115 -0
  7. package/dist/oauth.js +619 -0
  8. package/dist/prompts.d.ts +2 -0
  9. package/dist/prompts.js +12 -0
  10. package/dist/setup.d.ts +28 -5
  11. package/dist/setup.js +266 -59
  12. package/dist/tools/agents.d.ts +277 -0
  13. package/dist/tools/agents.js +335 -0
  14. package/dist/tools/annotations.d.ts +117 -0
  15. package/dist/tools/annotations.js +130 -0
  16. package/dist/tools/artifacts.d.ts +178 -8
  17. package/dist/tools/artifacts.js +372 -81
  18. package/dist/tools/chronicle.d.ts +3 -0
  19. package/dist/tools/chronicle.js +165 -0
  20. package/dist/tools/connector_write_back.d.ts +3 -0
  21. package/dist/tools/connector_write_back.js +27 -0
  22. package/dist/tools/converse.d.ts +10 -0
  23. package/dist/tools/converse.js +88 -0
  24. package/dist/tools/crews.d.ts +3 -0
  25. package/dist/tools/crews.js +115 -0
  26. package/dist/tools/drafts.d.ts +83 -0
  27. package/dist/tools/drafts.js +86 -0
  28. package/dist/tools/errors.d.ts +15 -0
  29. package/dist/tools/errors.js +21 -0
  30. package/dist/tools/export.d.ts +54 -0
  31. package/dist/tools/export.js +55 -0
  32. package/dist/tools/gm.d.ts +214 -0
  33. package/dist/tools/gm.js +198 -0
  34. package/dist/tools/ingest.d.ts +58 -4
  35. package/dist/tools/ingest.js +109 -76
  36. package/dist/tools/kb.d.ts +544 -0
  37. package/dist/tools/kb.js +687 -0
  38. package/dist/tools/lake-structure.d.ts +3 -0
  39. package/dist/tools/lake-structure.js +126 -0
  40. package/dist/tools/lake.d.ts +932 -0
  41. package/dist/tools/lake.js +1017 -0
  42. package/dist/tools/move.d.ts +200 -0
  43. package/dist/tools/move.js +96 -0
  44. package/dist/tools/outbox.d.ts +274 -0
  45. package/dist/tools/outbox.js +223 -0
  46. package/dist/tools/people.d.ts +3 -0
  47. package/dist/tools/people.js +59 -0
  48. package/dist/tools/pyramid.d.ts +554 -0
  49. package/dist/tools/pyramid.js +758 -0
  50. package/dist/tools/recall.d.ts +2 -0
  51. package/dist/tools/recall.js +28 -17
  52. package/dist/tools/registry.d.ts +17 -0
  53. package/dist/tools/registry.js +197 -0
  54. package/dist/tools/research.d.ts +47 -0
  55. package/dist/tools/research.js +575 -0
  56. package/dist/tools/search.d.ts +9 -0
  57. package/dist/tools/search.js +86 -25
  58. package/dist/tools/sharing.d.ts +63 -0
  59. package/dist/tools/sharing.js +157 -0
  60. package/dist/tools/synthesize.d.ts +54 -2
  61. package/dist/tools/synthesize.js +80 -23
  62. package/dist/tools/web-url.d.ts +57 -0
  63. package/dist/tools/web-url.js +74 -0
  64. package/dist/tools/workspaces.d.ts +3 -0
  65. package/dist/tools/workspaces.js +47 -0
  66. package/dist/transport.d.ts +9 -0
  67. package/dist/transport.js +209 -0
  68. package/package.json +5 -4
@@ -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
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.SEARCH_TOOL_DEF = exports.searchInputSchema = void 0;
10
- exports.buildSearchRequest = buildSearchRequest;
11
- exports.handleSearch = handleSearch;
12
- const zod_1 = require("zod");
13
- const client_js_1 = require("../client.js");
14
- exports.searchInputSchema = zod_1.z.object({
15
- query: zod_1.z.string().min(1).describe('Search query string'),
16
- limit: zod_1.z.number().int().positive().optional().describe('Maximum results to return'),
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
- exports.SEARCH_TOOL_DEF = {
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 = exports.searchInputSchema.safeParse(args);
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((0, client_js_1.getClient)(), parsed.data).execute();
70
+ return buildSearchRequest(getClient(), parsed.data).execute();
71
71
  }
72
- function apiErrorResult(err) {
73
- if (err instanceof client_js_1.TacitApiError) {
74
- return { content: [{ type: 'text', text: `API error ${err.statusCode}: ${err.body}` }], isError: true };
75
- }
76
- const msg = err instanceof Error ? err.message : String(err);
77
- return { content: [{ type: 'text', text: `Error: ${msg}` }], isError: true };
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
- stream?: boolean | undefined;
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
- stream?: boolean | undefined;
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;
@@ -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
- Object.defineProperty(exports, "__esModule", { value: true });
13
- exports.SYNTHESIZE_TOOL_DEF = exports.synthesizeInputSchema = void 0;
14
- exports.buildSynthesizeRequest = buildSynthesizeRequest;
15
- exports.handleSynthesize = handleSynthesize;
16
- const zod_1 = require("zod");
17
- const client_js_1 = require("../client.js");
18
- exports.synthesizeInputSchema = zod_1.z.object({
19
- query: zod_1.z.string().min(1).describe('Question or synthesis prompt'),
20
- scope: zod_1.z.string().optional().describe('Optional scope filter (e.g. portfolio or program id)'),
21
- stream: zod_1.z.boolean().optional().describe('If true, use SSE stream variant (/mind/synthesize/stream)'),
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
- exports.SYNTHESIZE_TOOL_DEF = {
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
- // Aggregate: return all chunks as content items for D7 chunk counting.
62
- return {
63
- content: chunks.map((c) => ({ type: 'text', text: c })),
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 client_js_1.TacitApiError) {
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 = exports.synthesizeInputSchema.safeParse(args);
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((0, client_js_1.getClient)(), parsed.data).execute();
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
  }