@tacitai/mcp-server 0.1.1 → 0.3.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 +130 -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 +199 -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/skills.d.ts +9 -0
- package/dist/tools/skills.js +98 -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
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tacit_list_skills / tacit_run_skill — MCP skill orchestration (MV-1533 W6)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/internal/skills — list available exec skills
|
|
5
|
+
* POST /api/v1/converse — invoke a skill by hint (SSE stream)
|
|
6
|
+
*/
|
|
7
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
|
+
import { TacitClient } from '../client.js';
|
|
9
|
+
export declare function registerSkillTools(server: McpServer, getClientFn: () => Promise<TacitClient>): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
/**
|
|
3
|
+
* tacit_list_skills / tacit_run_skill — MCP skill orchestration (MV-1533 W6)
|
|
4
|
+
*
|
|
5
|
+
* GET /api/v1/internal/skills — list available exec skills
|
|
6
|
+
* POST /api/v1/converse — invoke a skill by hint (SSE stream)
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
export function registerSkillTools(server, getClientFn) {
|
|
10
|
+
// --- tacit_list_skills ---
|
|
11
|
+
server.tool('tacit_list_skills', 'List available TACIT exec skills with their domains, descriptions, and access levels.', {}, async () => {
|
|
12
|
+
const client = await getClientFn();
|
|
13
|
+
try {
|
|
14
|
+
const data = await client.get('/api/v1/internal/skills');
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
21
|
+
return { content: [{ type: 'text', text: `Error listing skills: ${msg}` }], isError: true };
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
// --- tacit_run_skill ---
|
|
25
|
+
server.tool('tacit_run_skill', 'Run a TACIT exec skill by name. Sends the message through the converse pipeline with skill_hint routing. Returns the skill response, which may include tool calls, structured answers, or approval requests.', {
|
|
26
|
+
skill_id: z.string().min(1).describe('The skill domain ID (e.g., "pyr-exec", "lake-exec", "kb-exec", "sales-exec", "gm-exec")'),
|
|
27
|
+
message: z.string().min(1).describe('The natural-language instruction for the skill'),
|
|
28
|
+
session_id: z.string().optional().describe('Existing converse session UUID to continue; omit for a new session'),
|
|
29
|
+
}, async (args) => {
|
|
30
|
+
const client = await getClientFn();
|
|
31
|
+
// Validate skill_id against the catalog
|
|
32
|
+
try {
|
|
33
|
+
const catalog = await client.get('/api/v1/internal/skills');
|
|
34
|
+
const validDomains = catalog.skills.map((s) => s.domain);
|
|
35
|
+
if (!validDomains.includes(args.skill_id)) {
|
|
36
|
+
return {
|
|
37
|
+
content: [{ type: 'text', text: `Unknown skill_id "${args.skill_id}". Valid skills: ${validDomains.join(', ')}` }],
|
|
38
|
+
isError: true,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Catalog unreachable — proceed anyway, backend will validate
|
|
44
|
+
}
|
|
45
|
+
const body = {
|
|
46
|
+
message: args.message,
|
|
47
|
+
skill_hint: args.skill_id,
|
|
48
|
+
channel: 'mcp',
|
|
49
|
+
};
|
|
50
|
+
if (args.session_id)
|
|
51
|
+
body['session_id'] = args.session_id;
|
|
52
|
+
try {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
let sessionId = '';
|
|
55
|
+
let structuredAnswer = null;
|
|
56
|
+
let pendingApproval = null;
|
|
57
|
+
for await (const chunk of client.postSSE('/api/v1/converse', body)) {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = JSON.parse(chunk);
|
|
60
|
+
if (parsed.type === 'session_start')
|
|
61
|
+
sessionId = parsed.session_id;
|
|
62
|
+
else if (parsed.type === 'token')
|
|
63
|
+
chunks.push(parsed.delta);
|
|
64
|
+
else if (parsed.type === 'structured_answer')
|
|
65
|
+
structuredAnswer = parsed.data;
|
|
66
|
+
else if (parsed.type === 'pending_approval')
|
|
67
|
+
pendingApproval = parsed;
|
|
68
|
+
else if (parsed.type === 'tool_call_end') {
|
|
69
|
+
if (parsed.result)
|
|
70
|
+
chunks.push(typeof parsed.result === 'string' ? parsed.result : JSON.stringify(parsed.result));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
chunks.push(chunk);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const text = chunks.join('');
|
|
78
|
+
const result = {
|
|
79
|
+
skill_id: args.skill_id,
|
|
80
|
+
session_id: sessionId,
|
|
81
|
+
response: text,
|
|
82
|
+
};
|
|
83
|
+
if (structuredAnswer)
|
|
84
|
+
result['structured_answer'] = structuredAnswer;
|
|
85
|
+
if (pendingApproval) {
|
|
86
|
+
result['pending_approval'] = pendingApproval;
|
|
87
|
+
result['instructions'] = 'This operation requires approval. Use tacit_converse with the session_id and confirm_token to approve or reject.';
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
catch (err) {
|
|
94
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
95
|
+
return { content: [{ type: 'text', text: `Error running skill ${args.skill_id}: ${msg}` }], isError: true };
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
@@ -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;
|