@lightcone-ai/daemon 0.12.0 → 0.13.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/package.json +1 -1
- package/src/chat-bridge.js +614 -1
package/package.json
CHANGED
package/src/chat-bridge.js
CHANGED
|
@@ -14,9 +14,19 @@ function getArg(name) {
|
|
|
14
14
|
return idx !== -1 && cliArgs[idx + 1] ? cliArgs[idx + 1] : '';
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function parseBooleanFlag(value, fallback = false) {
|
|
18
|
+
if (value == null) return fallback;
|
|
19
|
+
if (typeof value === 'boolean') return value;
|
|
20
|
+
const normalized = String(value).trim().toLowerCase();
|
|
21
|
+
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;
|
|
22
|
+
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
const SERVER_URL = process.env.SERVER_URL || getArg('--server-url') || 'http://localhost:9779';
|
|
18
27
|
const MACHINE_API_KEY = process.env.MACHINE_API_KEY || getArg('--auth-token') || '';
|
|
19
28
|
const AGENT_ID = process.env.AGENT_ID || getArg('--agent-id') || '';
|
|
29
|
+
const HOST_AGENT_HINT = process.env.IS_HOST_AGENT || getArg('--is-host-agent') || '';
|
|
20
30
|
const WORKSPACE_ID = process.env.WORKSPACE_ID || getArg('--workspace-id') || ''; // injected per-workspace at spawn time
|
|
21
31
|
const WORKSPACE_DIR = path.resolve(process.env.WORKSPACE_DIR || getArg('--workspace-dir') || process.cwd());
|
|
22
32
|
const WORKSPACE_ROOT_DIR = path.dirname(WORKSPACE_DIR);
|
|
@@ -102,6 +112,18 @@ const DEFAULT_TOOL_CLASSIFICATION = {
|
|
|
102
112
|
request_approval: 'mandatory',
|
|
103
113
|
execute_approved_action: 'mandatory',
|
|
104
114
|
promote_context: 'mandatory',
|
|
115
|
+
update_goal_field: 'mandatory',
|
|
116
|
+
supersede_goal_field: 'mandatory',
|
|
117
|
+
request_credential_auth: 'mandatory',
|
|
118
|
+
bind_workspace_scenario: 'mandatory',
|
|
119
|
+
create_workspace: 'mandatory',
|
|
120
|
+
rename_workspace: 'mandatory',
|
|
121
|
+
delete_workspace: 'mandatory',
|
|
122
|
+
revoke_credential: 'mandatory',
|
|
123
|
+
submit_feedback: 'mandatory',
|
|
124
|
+
escalate_to_human: 'mandatory',
|
|
125
|
+
navigate_to_workspace: 'mandatory',
|
|
126
|
+
navigate_to_settings: 'mandatory',
|
|
105
127
|
|
|
106
128
|
search_messages: 'cacheable',
|
|
107
129
|
list_server: 'cacheable',
|
|
@@ -111,6 +133,19 @@ const DEFAULT_TOOL_CLASSIFICATION = {
|
|
|
111
133
|
skill_list: 'cacheable',
|
|
112
134
|
skill_read: 'cacheable',
|
|
113
135
|
skill_search: 'cacheable',
|
|
136
|
+
search_scenarios: 'cacheable',
|
|
137
|
+
get_scenario: 'cacheable',
|
|
138
|
+
list_scenarios: 'cacheable',
|
|
139
|
+
recommend_scenario: 'cacheable',
|
|
140
|
+
list_workspaces: 'cacheable',
|
|
141
|
+
get_workspace_summary: 'cacheable',
|
|
142
|
+
query_user_history: 'cacheable',
|
|
143
|
+
list_accounts: 'cacheable',
|
|
144
|
+
list_credentials: 'cacheable',
|
|
145
|
+
get_data_locations: 'cacheable',
|
|
146
|
+
explain_concept: 'cacheable',
|
|
147
|
+
get_release_notes: 'cacheable',
|
|
148
|
+
get_known_issues: 'cacheable',
|
|
114
149
|
};
|
|
115
150
|
|
|
116
151
|
const TOOL_CLASSIFICATION = Object.freeze({
|
|
@@ -132,6 +167,18 @@ const CACHE_INVALIDATION_TOOLS = new Set([
|
|
|
132
167
|
'request_approval',
|
|
133
168
|
'execute_approved_action',
|
|
134
169
|
'promote_context',
|
|
170
|
+
'update_goal_field',
|
|
171
|
+
'supersede_goal_field',
|
|
172
|
+
'request_credential_auth',
|
|
173
|
+
'bind_workspace_scenario',
|
|
174
|
+
'create_workspace',
|
|
175
|
+
'rename_workspace',
|
|
176
|
+
'delete_workspace',
|
|
177
|
+
'revoke_credential',
|
|
178
|
+
'submit_feedback',
|
|
179
|
+
'escalate_to_human',
|
|
180
|
+
'navigate_to_workspace',
|
|
181
|
+
'navigate_to_settings',
|
|
135
182
|
]);
|
|
136
183
|
|
|
137
184
|
const governanceContext = {
|
|
@@ -196,11 +243,37 @@ function inferToolForApi(method, apiPath, body) {
|
|
|
196
243
|
if (method === 'PATCH' && cleanPath.startsWith('/skills/')) return 'skill_update';
|
|
197
244
|
if (method === 'POST' && cleanPath === '/actions/request') return 'request_approval';
|
|
198
245
|
if (method === 'POST' && /^\/actions\/[^/]+\/execute$/.test(cleanPath)) return 'execute_approved_action';
|
|
246
|
+
if (method === 'POST' && cleanPath === '/goal-fields/update') return 'update_goal_field';
|
|
247
|
+
if (method === 'POST' && cleanPath === '/goal-fields/supersede') return 'supersede_goal_field';
|
|
248
|
+
if (method === 'POST' && cleanPath === '/credential-auth/request') return 'request_credential_auth';
|
|
199
249
|
if (method === 'POST' && cleanPath === '/orchestrate/decision') return 'write_governance_decision';
|
|
200
250
|
if (method === 'POST' && cleanPath === '/orchestrate/correction') return 'write_governance_correction';
|
|
201
251
|
if (method === 'GET' && cleanPath === '/orchestrate/context') return 'get_orchestrate_context';
|
|
202
252
|
if (method === 'POST' && cleanPath === '/orchestrate/complete') return 'complete_orchestrate_trigger';
|
|
203
253
|
if (method === 'POST' && cleanPath === '/context-proposals') return 'promote_context';
|
|
254
|
+
|
|
255
|
+
if (method === 'GET' && cleanPath === '/host/scenarios/search') return 'search_scenarios';
|
|
256
|
+
if (method === 'POST' && cleanPath === '/host/scenarios/recommend') return 'recommend_scenario';
|
|
257
|
+
if (method === 'GET' && /^\/host\/scenarios\/[^/]+$/.test(cleanPath)) return 'get_scenario';
|
|
258
|
+
if (method === 'GET' && cleanPath === '/host/scenarios') return 'list_scenarios';
|
|
259
|
+
if (method === 'POST' && cleanPath === '/host/workspaces/bind-scenario') return 'bind_workspace_scenario';
|
|
260
|
+
if (method === 'GET' && cleanPath === '/host/workspaces') return 'list_workspaces';
|
|
261
|
+
if (method === 'POST' && cleanPath === '/host/workspaces') return 'create_workspace';
|
|
262
|
+
if (method === 'PATCH' && /^\/host\/workspaces\/[^/]+$/.test(cleanPath)) return 'rename_workspace';
|
|
263
|
+
if (method === 'DELETE' && /^\/host\/workspaces\/[^/]+$/.test(cleanPath)) return 'delete_workspace';
|
|
264
|
+
if (method === 'GET' && /^\/host\/workspaces\/[^/]+\/summary$/.test(cleanPath)) return 'get_workspace_summary';
|
|
265
|
+
if (method === 'GET' && cleanPath === '/host/history') return 'query_user_history';
|
|
266
|
+
if (method === 'GET' && cleanPath === '/host/accounts') return 'list_accounts';
|
|
267
|
+
if (method === 'GET' && cleanPath === '/host/credentials') return 'list_credentials';
|
|
268
|
+
if (method === 'POST' && /^\/host\/credentials\/[^/]+\/revoke$/.test(cleanPath)) return 'revoke_credential';
|
|
269
|
+
if (method === 'GET' && cleanPath === '/host/data-locations') return 'get_data_locations';
|
|
270
|
+
if (method === 'GET' && cleanPath === '/host/docs/explain') return 'explain_concept';
|
|
271
|
+
if (method === 'GET' && cleanPath === '/host/docs/release-notes') return 'get_release_notes';
|
|
272
|
+
if (method === 'GET' && cleanPath === '/host/docs/known-issues') return 'get_known_issues';
|
|
273
|
+
if (method === 'POST' && cleanPath === '/host/feedback') return 'submit_feedback';
|
|
274
|
+
if (method === 'POST' && cleanPath === '/host/escalate') return 'escalate_to_human';
|
|
275
|
+
if (method === 'POST' && cleanPath === '/host/navigation/workspace') return 'navigate_to_workspace';
|
|
276
|
+
if (method === 'POST' && cleanPath === '/host/navigation/settings') return 'navigate_to_settings';
|
|
204
277
|
return null;
|
|
205
278
|
}
|
|
206
279
|
|
|
@@ -584,6 +657,29 @@ async function api(method, apiPath, body) {
|
|
|
584
657
|
}
|
|
585
658
|
}
|
|
586
659
|
|
|
660
|
+
async function resolveHostAgentFlag() {
|
|
661
|
+
const hinted = parseBooleanFlag(HOST_AGENT_HINT, false);
|
|
662
|
+
if (!AGENT_ID || !SERVER_URL || !MACHINE_API_KEY) return hinted;
|
|
663
|
+
|
|
664
|
+
try {
|
|
665
|
+
const res = await fetch(`${SERVER_URL}/internal/agent/${AGENT_ID}/profile`, {
|
|
666
|
+
method: 'GET',
|
|
667
|
+
headers: {
|
|
668
|
+
'Content-Type': 'application/json',
|
|
669
|
+
'Authorization': `Bearer ${MACHINE_API_KEY}`,
|
|
670
|
+
},
|
|
671
|
+
});
|
|
672
|
+
if (!res.ok) return hinted;
|
|
673
|
+
const data = await res.json();
|
|
674
|
+
if (typeof data?.isHostAgent === 'boolean') return data.isHostAgent;
|
|
675
|
+
return hinted;
|
|
676
|
+
} catch {
|
|
677
|
+
return hinted;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const IS_HOST_AGENT = await resolveHostAgentFlag();
|
|
682
|
+
|
|
587
683
|
const server = new McpServer({ name: 'chat', version: '0.1.0' });
|
|
588
684
|
|
|
589
685
|
// ── check_messages ────────────────────────────────────────────────────────────
|
|
@@ -921,6 +1017,106 @@ server.tool('upload_image',
|
|
|
921
1017
|
}
|
|
922
1018
|
);
|
|
923
1019
|
|
|
1020
|
+
// ── update_goal_field ─────────────────────────────────────────────────────────
|
|
1021
|
+
server.tool('update_goal_field',
|
|
1022
|
+
'Incrementally update one goal field in workspace context. Use this during IM dialogue to capture user intent continuously, instead of waiting for a full goal form.',
|
|
1023
|
+
{
|
|
1024
|
+
field_path: z.string().describe('Dot path under goal context, e.g. "brand_positioning.voice" or "audience.primary".'),
|
|
1025
|
+
value: z.any().describe('Field value (string/number/object/array).'),
|
|
1026
|
+
confidence: z.number().min(0).max(1).optional().describe('Confidence in [0,1].'),
|
|
1027
|
+
status: z.enum(['candidate', 'user_confirmed']).optional().describe('candidate=tentative, user_confirmed=explicitly confirmed by user.'),
|
|
1028
|
+
source_turn_id: z.string().optional().describe('Optional conversation turn id for traceability.'),
|
|
1029
|
+
},
|
|
1030
|
+
async ({ field_path, value, confidence, status, source_turn_id }) => {
|
|
1031
|
+
if (!currentWorkspaceId) {
|
|
1032
|
+
return { isError: true, content: [{ type: 'text', text: 'No workspace context. Call check_messages first or specify workspace context in the current conversation.' }] };
|
|
1033
|
+
}
|
|
1034
|
+
const data = await api('POST', '/goal-fields/update', {
|
|
1035
|
+
workspace_id: currentWorkspaceId,
|
|
1036
|
+
field_path,
|
|
1037
|
+
value,
|
|
1038
|
+
confidence,
|
|
1039
|
+
status,
|
|
1040
|
+
source_turn_id,
|
|
1041
|
+
});
|
|
1042
|
+
return {
|
|
1043
|
+
content: [{
|
|
1044
|
+
type: 'text',
|
|
1045
|
+
text:
|
|
1046
|
+
`Goal field updated.\n` +
|
|
1047
|
+
`workspace=${data.workspaceId ?? currentWorkspaceId}\n` +
|
|
1048
|
+
`field_path=${data.fieldPath ?? field_path}\n` +
|
|
1049
|
+
`status=${data.status ?? status ?? 'candidate'}\n` +
|
|
1050
|
+
`context_item_id=${data.contextItemId ?? 'unknown'} version=${data.contextItemVersion ?? 'unknown'}`,
|
|
1051
|
+
}],
|
|
1052
|
+
};
|
|
1053
|
+
}
|
|
1054
|
+
);
|
|
1055
|
+
|
|
1056
|
+
// ── supersede_goal_field ──────────────────────────────────────────────────────
|
|
1057
|
+
server.tool('supersede_goal_field',
|
|
1058
|
+
'Mark an existing goal field value as superseded when the user explicitly changes their mind.',
|
|
1059
|
+
{
|
|
1060
|
+
field_path: z.string().describe('Dot path to supersede, e.g. "audience.primary".'),
|
|
1061
|
+
reason: z.string().optional().describe('Why this field is superseded (optional).'),
|
|
1062
|
+
source_turn_id: z.string().optional().describe('Optional conversation turn id for traceability.'),
|
|
1063
|
+
},
|
|
1064
|
+
async ({ field_path, reason, source_turn_id }) => {
|
|
1065
|
+
if (!currentWorkspaceId) {
|
|
1066
|
+
return { isError: true, content: [{ type: 'text', text: 'No workspace context. Call check_messages first or specify workspace context in the current conversation.' }] };
|
|
1067
|
+
}
|
|
1068
|
+
const data = await api('POST', '/goal-fields/supersede', {
|
|
1069
|
+
workspace_id: currentWorkspaceId,
|
|
1070
|
+
field_path,
|
|
1071
|
+
reason,
|
|
1072
|
+
source_turn_id,
|
|
1073
|
+
});
|
|
1074
|
+
return {
|
|
1075
|
+
content: [{
|
|
1076
|
+
type: 'text',
|
|
1077
|
+
text:
|
|
1078
|
+
`Goal field superseded.\n` +
|
|
1079
|
+
`workspace=${data.workspaceId ?? currentWorkspaceId}\n` +
|
|
1080
|
+
`field_path=${data.fieldPath ?? field_path}\n` +
|
|
1081
|
+
`context_item_id=${data.contextItemId ?? 'unknown'} version=${data.contextItemVersion ?? 'unknown'}`,
|
|
1082
|
+
}],
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
// ── request_credential_auth ───────────────────────────────────────────────────
|
|
1088
|
+
server.tool('request_credential_auth',
|
|
1089
|
+
'Request just-in-time credential authorization for a platform. Returns a one-time OAuth URL that should be sent to the user as a clickable IM message.',
|
|
1090
|
+
{
|
|
1091
|
+
platform: z.string().describe('Target platform, e.g. "x".'),
|
|
1092
|
+
reason: z.string().describe('Human-readable reason why this credential is needed right now.'),
|
|
1093
|
+
source_turn_id: z.string().optional().describe('Optional conversation turn id for traceability.'),
|
|
1094
|
+
},
|
|
1095
|
+
async ({ platform, reason, source_turn_id }) => {
|
|
1096
|
+
if (!currentWorkspaceId) {
|
|
1097
|
+
return { isError: true, content: [{ type: 'text', text: 'No workspace context. Call check_messages first or specify workspace context in the current conversation.' }] };
|
|
1098
|
+
}
|
|
1099
|
+
const data = await api('POST', '/credential-auth/request', {
|
|
1100
|
+
workspace_id: currentWorkspaceId,
|
|
1101
|
+
platform,
|
|
1102
|
+
reason,
|
|
1103
|
+
source_turn_id,
|
|
1104
|
+
});
|
|
1105
|
+
return {
|
|
1106
|
+
content: [{
|
|
1107
|
+
type: 'text',
|
|
1108
|
+
text:
|
|
1109
|
+
`Credential auth requested.\n` +
|
|
1110
|
+
`platform=${data.platform ?? platform}\n` +
|
|
1111
|
+
`expires_at=${data.expiresAt ?? 'unknown'}\n` +
|
|
1112
|
+
`auth_url=${data.authUrl}\n` +
|
|
1113
|
+
`im_message_id=${data.messageId ?? 'unknown'}\n\n` +
|
|
1114
|
+
`A structured credential auth message has been posted to IM. Ask the user to click the authorization button there.`,
|
|
1115
|
+
}],
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
);
|
|
1119
|
+
|
|
924
1120
|
// ── request_approval ──────────────────────────────────────────────────────────
|
|
925
1121
|
server.tool('request_approval',
|
|
926
1122
|
'Request human approval before executing a sensitive platform action (posting, sending, publishing). Returns an action_id. After the human approves, call execute_approved_action with that ID.',
|
|
@@ -1122,7 +1318,424 @@ server.tool('complete_orchestrate_trigger',
|
|
|
1122
1318
|
}
|
|
1123
1319
|
);
|
|
1124
1320
|
|
|
1321
|
+
function hostJsonResult(data) {
|
|
1322
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
function hostErrorResult(err) {
|
|
1326
|
+
return { isError: true, content: [{ type: 'text', text: `Error: ${err.message}` }] };
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
function buildQuery(params = {}) {
|
|
1330
|
+
const query = new URLSearchParams();
|
|
1331
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1332
|
+
if (value == null) continue;
|
|
1333
|
+
if (typeof value === 'string' && !value.trim()) continue;
|
|
1334
|
+
query.set(key, String(value));
|
|
1335
|
+
}
|
|
1336
|
+
const text = query.toString();
|
|
1337
|
+
return text ? `?${text}` : '';
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
if (IS_HOST_AGENT) {
|
|
1341
|
+
server.tool('search_scenarios',
|
|
1342
|
+
'Search scenario candidates by user intent for host routing decisions.',
|
|
1343
|
+
{
|
|
1344
|
+
intent_text: z.string().describe('User intent text to match against scenario catalog.'),
|
|
1345
|
+
category: z.string().optional().describe('Optional category filter.'),
|
|
1346
|
+
visibility: z.string().optional().describe('Optional visibility filter (public/system/user_private/org_shared).'),
|
|
1347
|
+
status: z.string().optional().describe('Optional status filter (draft/published/active/deprecated/archived).'),
|
|
1348
|
+
limit: z.number().int().min(1).max(20).optional().describe('Maximum number of returned scenarios.'),
|
|
1349
|
+
},
|
|
1350
|
+
async ({ intent_text, category, visibility, status, limit }) => {
|
|
1351
|
+
try {
|
|
1352
|
+
const query = buildQuery({ intent_text, category, visibility, status, limit });
|
|
1353
|
+
const data = await api('GET', `/host/scenarios/search${query}`);
|
|
1354
|
+
return hostJsonResult(data);
|
|
1355
|
+
} catch (err) {
|
|
1356
|
+
return hostErrorResult(err);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
);
|
|
1360
|
+
|
|
1361
|
+
server.tool('get_scenario',
|
|
1362
|
+
'Get full scenario details (manifest + roles) for host explanation.',
|
|
1363
|
+
{
|
|
1364
|
+
scenario_id: z.string().describe('Scenario ID to inspect.'),
|
|
1365
|
+
},
|
|
1366
|
+
async ({ scenario_id }) => {
|
|
1367
|
+
try {
|
|
1368
|
+
const data = await api('GET', `/host/scenarios/${encodeURIComponent(scenario_id)}`);
|
|
1369
|
+
return hostJsonResult(data);
|
|
1370
|
+
} catch (err) {
|
|
1371
|
+
return hostErrorResult(err);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
);
|
|
1375
|
+
|
|
1376
|
+
server.tool('list_scenarios',
|
|
1377
|
+
'List scenarios visible to host agent (public/system + owner-accessible).',
|
|
1378
|
+
{
|
|
1379
|
+
visibility: z.string().optional().describe('Optional visibility filter.'),
|
|
1380
|
+
category: z.string().optional().describe('Optional category filter.'),
|
|
1381
|
+
status: z.string().optional().describe('Optional status filter.'),
|
|
1382
|
+
include_archived: z.boolean().optional().describe('Include archived scenarios when true.'),
|
|
1383
|
+
limit: z.number().int().min(1).max(200).optional().describe('Maximum rows to return.'),
|
|
1384
|
+
},
|
|
1385
|
+
async ({ visibility, category, status, include_archived, limit }) => {
|
|
1386
|
+
try {
|
|
1387
|
+
const query = buildQuery({
|
|
1388
|
+
visibility,
|
|
1389
|
+
category,
|
|
1390
|
+
status,
|
|
1391
|
+
includeArchived: include_archived,
|
|
1392
|
+
limit,
|
|
1393
|
+
});
|
|
1394
|
+
const data = await api('GET', `/host/scenarios${query}`);
|
|
1395
|
+
return hostJsonResult(data);
|
|
1396
|
+
} catch (err) {
|
|
1397
|
+
return hostErrorResult(err);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
);
|
|
1401
|
+
|
|
1402
|
+
server.tool('bind_workspace_scenario',
|
|
1403
|
+
'Bind a scenario to an existing workspace and ensure a primary agent.',
|
|
1404
|
+
{
|
|
1405
|
+
workspace_id: z.string().describe('Workspace ID to bind.'),
|
|
1406
|
+
scenario_id: z.string().describe('Scenario ID to bind.'),
|
|
1407
|
+
role_id: z.string().optional().describe('Optional preferred role id/name for primary agent binding.'),
|
|
1408
|
+
repair_role_binding: z.boolean().optional().describe('When true, ignore existing workspace role binding once and re-resolve primary agent for cleanup.'),
|
|
1409
|
+
},
|
|
1410
|
+
async ({ workspace_id, scenario_id, role_id, repair_role_binding }) => {
|
|
1411
|
+
try {
|
|
1412
|
+
const data = await api('POST', '/host/workspaces/bind-scenario', {
|
|
1413
|
+
workspace_id,
|
|
1414
|
+
scenario_id,
|
|
1415
|
+
role_id,
|
|
1416
|
+
repair_role_binding,
|
|
1417
|
+
});
|
|
1418
|
+
return hostJsonResult(data);
|
|
1419
|
+
} catch (err) {
|
|
1420
|
+
return hostErrorResult(err);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
);
|
|
1424
|
+
|
|
1425
|
+
server.tool('recommend_scenario',
|
|
1426
|
+
'Recommend best-fit scenarios for a given user intent.',
|
|
1427
|
+
{
|
|
1428
|
+
user_intent: z.string().describe('Natural-language user intent.'),
|
|
1429
|
+
},
|
|
1430
|
+
async ({ user_intent }) => {
|
|
1431
|
+
try {
|
|
1432
|
+
const data = await api('POST', '/host/scenarios/recommend', { user_intent });
|
|
1433
|
+
return hostJsonResult(data);
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
return hostErrorResult(err);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
);
|
|
1439
|
+
|
|
1440
|
+
server.tool('list_workspaces',
|
|
1441
|
+
'List all owner workspaces with scenario/mode/last activity for host overview.',
|
|
1442
|
+
{
|
|
1443
|
+
owner_id: z.string().optional().describe('Optional owner id (must match current host owner).'),
|
|
1444
|
+
limit: z.number().int().min(1).max(300).optional().describe('Maximum rows to return.'),
|
|
1445
|
+
},
|
|
1446
|
+
async ({ owner_id, limit }) => {
|
|
1447
|
+
try {
|
|
1448
|
+
const query = buildQuery({ owner_id, limit });
|
|
1449
|
+
const data = await api('GET', `/host/workspaces${query}`);
|
|
1450
|
+
return hostJsonResult(data);
|
|
1451
|
+
} catch (err) {
|
|
1452
|
+
return hostErrorResult(err);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
);
|
|
1456
|
+
|
|
1457
|
+
server.tool('create_workspace',
|
|
1458
|
+
'Create a workspace and optionally bind scenario + primary role.',
|
|
1459
|
+
{
|
|
1460
|
+
name: z.string().optional().describe('Optional workspace name.'),
|
|
1461
|
+
description: z.string().optional().describe('Optional workspace description.'),
|
|
1462
|
+
scenario_id: z.string().optional().describe('Optional scenario id to bind immediately.'),
|
|
1463
|
+
role_id: z.string().optional().describe('Optional preferred role id/name when scenario is bound.'),
|
|
1464
|
+
},
|
|
1465
|
+
async ({ name, description, scenario_id, role_id }) => {
|
|
1466
|
+
try {
|
|
1467
|
+
const data = await api('POST', '/host/workspaces', {
|
|
1468
|
+
name,
|
|
1469
|
+
description,
|
|
1470
|
+
scenario_id,
|
|
1471
|
+
role_id,
|
|
1472
|
+
});
|
|
1473
|
+
return hostJsonResult(data);
|
|
1474
|
+
} catch (err) {
|
|
1475
|
+
return hostErrorResult(err);
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
);
|
|
1479
|
+
|
|
1480
|
+
server.tool('rename_workspace',
|
|
1481
|
+
'Rename an existing workspace.',
|
|
1482
|
+
{
|
|
1483
|
+
workspace_id: z.string().describe('Workspace ID to rename.'),
|
|
1484
|
+
new_name: z.string().describe('New workspace name.'),
|
|
1485
|
+
},
|
|
1486
|
+
async ({ workspace_id, new_name }) => {
|
|
1487
|
+
try {
|
|
1488
|
+
const data = await api('PATCH', `/host/workspaces/${encodeURIComponent(workspace_id)}`, {
|
|
1489
|
+
new_name,
|
|
1490
|
+
});
|
|
1491
|
+
return hostJsonResult(data);
|
|
1492
|
+
} catch (err) {
|
|
1493
|
+
return hostErrorResult(err);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
);
|
|
1497
|
+
|
|
1498
|
+
server.tool('delete_workspace',
|
|
1499
|
+
'Delete (soft-delete) a workspace owned by the current user.',
|
|
1500
|
+
{
|
|
1501
|
+
workspace_id: z.string().describe('Workspace ID to delete.'),
|
|
1502
|
+
confirm: z.boolean().describe('Must be true to confirm deletion.'),
|
|
1503
|
+
},
|
|
1504
|
+
async ({ workspace_id, confirm }) => {
|
|
1505
|
+
try {
|
|
1506
|
+
const query = buildQuery({ confirm });
|
|
1507
|
+
const data = await api('DELETE', `/host/workspaces/${encodeURIComponent(workspace_id)}${query}`);
|
|
1508
|
+
return hostJsonResult(data);
|
|
1509
|
+
} catch (err) {
|
|
1510
|
+
return hostErrorResult(err);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
);
|
|
1514
|
+
|
|
1515
|
+
server.tool('get_workspace_summary',
|
|
1516
|
+
'Get summary of workspace scenario, active agents, recent messages and publish stats.',
|
|
1517
|
+
{
|
|
1518
|
+
workspace_id: z.string().describe('Workspace ID to summarize.'),
|
|
1519
|
+
},
|
|
1520
|
+
async ({ workspace_id }) => {
|
|
1521
|
+
try {
|
|
1522
|
+
const data = await api('GET', `/host/workspaces/${encodeURIComponent(workspace_id)}/summary`);
|
|
1523
|
+
return hostJsonResult(data);
|
|
1524
|
+
} catch (err) {
|
|
1525
|
+
return hostErrorResult(err);
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
);
|
|
1529
|
+
|
|
1530
|
+
server.tool('query_user_history',
|
|
1531
|
+
'Search user history across owner workspaces for prior activities/messages.',
|
|
1532
|
+
{
|
|
1533
|
+
topic: z.string().describe('Topic keyword to search across workspaces.'),
|
|
1534
|
+
limit: z.number().int().min(1).max(50).optional().describe('Maximum number of results.'),
|
|
1535
|
+
},
|
|
1536
|
+
async ({ topic, limit }) => {
|
|
1537
|
+
try {
|
|
1538
|
+
const query = buildQuery({ topic, limit });
|
|
1539
|
+
const data = await api('GET', `/host/history${query}`);
|
|
1540
|
+
return hostJsonResult(data);
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
return hostErrorResult(err);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
);
|
|
1546
|
+
|
|
1547
|
+
server.tool('list_accounts',
|
|
1548
|
+
'List platform accounts owned by the current user.',
|
|
1549
|
+
{
|
|
1550
|
+
owner_id: z.string().optional().describe('Optional owner id (must match current owner).'),
|
|
1551
|
+
workspace_id: z.string().optional().describe('Optional workspace filter; pass "null" for unbound accounts.'),
|
|
1552
|
+
platform: z.string().optional().describe('Optional platform filter.'),
|
|
1553
|
+
status: z.string().optional().describe('Optional status filter.'),
|
|
1554
|
+
include_archived: z.boolean().optional().describe('Include archived accounts.'),
|
|
1555
|
+
},
|
|
1556
|
+
async ({ owner_id, workspace_id, platform, status, include_archived }) => {
|
|
1557
|
+
try {
|
|
1558
|
+
const query = buildQuery({
|
|
1559
|
+
owner_id,
|
|
1560
|
+
workspace_id,
|
|
1561
|
+
platform,
|
|
1562
|
+
status,
|
|
1563
|
+
include_archived,
|
|
1564
|
+
});
|
|
1565
|
+
const data = await api('GET', `/host/accounts${query}`);
|
|
1566
|
+
return hostJsonResult(data);
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
return hostErrorResult(err);
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
);
|
|
1572
|
+
|
|
1573
|
+
server.tool('list_credentials',
|
|
1574
|
+
'List credentials as masked metadata (no plaintext).',
|
|
1575
|
+
{
|
|
1576
|
+
owner_id: z.string().optional().describe('Optional owner id (must match current owner).'),
|
|
1577
|
+
},
|
|
1578
|
+
async ({ owner_id }) => {
|
|
1579
|
+
try {
|
|
1580
|
+
const query = buildQuery({ owner_id });
|
|
1581
|
+
const data = await api('GET', `/host/credentials${query}`);
|
|
1582
|
+
return hostJsonResult(data);
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
return hostErrorResult(err);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
|
|
1589
|
+
server.tool('revoke_credential',
|
|
1590
|
+
'Revoke a credential with explicit confirmation.',
|
|
1591
|
+
{
|
|
1592
|
+
credential_id: z.string().describe('Credential ID to revoke.'),
|
|
1593
|
+
confirm: z.boolean().describe('Must be true to confirm revocation.'),
|
|
1594
|
+
},
|
|
1595
|
+
async ({ credential_id, confirm }) => {
|
|
1596
|
+
try {
|
|
1597
|
+
const data = await api('POST', `/host/credentials/${encodeURIComponent(credential_id)}/revoke`, {
|
|
1598
|
+
confirm,
|
|
1599
|
+
});
|
|
1600
|
+
return hostJsonResult(data);
|
|
1601
|
+
} catch (err) {
|
|
1602
|
+
return hostErrorResult(err);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
);
|
|
1606
|
+
|
|
1607
|
+
server.tool('get_data_locations',
|
|
1608
|
+
'Describe where user/platform data is stored.',
|
|
1609
|
+
{},
|
|
1610
|
+
async () => {
|
|
1611
|
+
try {
|
|
1612
|
+
const data = await api('GET', '/host/data-locations');
|
|
1613
|
+
return hostJsonResult(data);
|
|
1614
|
+
} catch (err) {
|
|
1615
|
+
return hostErrorResult(err);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
);
|
|
1619
|
+
|
|
1620
|
+
server.tool('explain_concept',
|
|
1621
|
+
'Explain a product concept from docs corpus.',
|
|
1622
|
+
{
|
|
1623
|
+
topic: z.string().describe('Concept/topic text to explain.'),
|
|
1624
|
+
},
|
|
1625
|
+
async ({ topic }) => {
|
|
1626
|
+
try {
|
|
1627
|
+
const query = buildQuery({ topic });
|
|
1628
|
+
const data = await api('GET', `/host/docs/explain${query}`);
|
|
1629
|
+
return hostJsonResult(data);
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
return hostErrorResult(err);
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
);
|
|
1635
|
+
|
|
1636
|
+
server.tool('get_release_notes',
|
|
1637
|
+
'Get release notes/events since a specified timestamp.',
|
|
1638
|
+
{
|
|
1639
|
+
since: z.string().optional().describe('Optional ISO timestamp (default: last 30 days).'),
|
|
1640
|
+
},
|
|
1641
|
+
async ({ since }) => {
|
|
1642
|
+
try {
|
|
1643
|
+
const query = buildQuery({ since });
|
|
1644
|
+
const data = await api('GET', `/host/docs/release-notes${query}`);
|
|
1645
|
+
return hostJsonResult(data);
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
return hostErrorResult(err);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
);
|
|
1651
|
+
|
|
1652
|
+
server.tool('get_known_issues',
|
|
1653
|
+
'Get current known issues and limitations.',
|
|
1654
|
+
{},
|
|
1655
|
+
async () => {
|
|
1656
|
+
try {
|
|
1657
|
+
const data = await api('GET', '/host/docs/known-issues');
|
|
1658
|
+
return hostJsonResult(data);
|
|
1659
|
+
} catch (err) {
|
|
1660
|
+
return hostErrorResult(err);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
);
|
|
1664
|
+
|
|
1665
|
+
server.tool('submit_feedback',
|
|
1666
|
+
'Submit user feedback into internal backlog workflow.',
|
|
1667
|
+
{
|
|
1668
|
+
category: z.enum(['bug', 'feature', 'ux', 'performance', 'security', 'other']).describe('Feedback category.'),
|
|
1669
|
+
content: z.string().describe('Feedback content.'),
|
|
1670
|
+
workspace_id: z.string().optional().describe('Optional workspace context for this feedback.'),
|
|
1671
|
+
},
|
|
1672
|
+
async ({ category, content, workspace_id }) => {
|
|
1673
|
+
try {
|
|
1674
|
+
const data = await api('POST', '/host/feedback', {
|
|
1675
|
+
category,
|
|
1676
|
+
content,
|
|
1677
|
+
workspace_id,
|
|
1678
|
+
});
|
|
1679
|
+
return hostJsonResult(data);
|
|
1680
|
+
} catch (err) {
|
|
1681
|
+
return hostErrorResult(err);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
);
|
|
1685
|
+
|
|
1686
|
+
server.tool('escalate_to_human',
|
|
1687
|
+
'Escalate severe issue to human operators.',
|
|
1688
|
+
{
|
|
1689
|
+
issue: z.string().describe('Issue details to escalate.'),
|
|
1690
|
+
priority: z.enum(['low', 'medium', 'high', 'critical']).optional().describe('Escalation priority.'),
|
|
1691
|
+
workspace_id: z.string().optional().describe('Optional workspace context for escalation.'),
|
|
1692
|
+
},
|
|
1693
|
+
async ({ issue, priority, workspace_id }) => {
|
|
1694
|
+
try {
|
|
1695
|
+
const data = await api('POST', '/host/escalate', {
|
|
1696
|
+
issue,
|
|
1697
|
+
priority,
|
|
1698
|
+
workspace_id,
|
|
1699
|
+
});
|
|
1700
|
+
return hostJsonResult(data);
|
|
1701
|
+
} catch (err) {
|
|
1702
|
+
return hostErrorResult(err);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
);
|
|
1706
|
+
|
|
1707
|
+
server.tool('navigate_to_workspace',
|
|
1708
|
+
'Emit navigation intent for UI to open a target workspace.',
|
|
1709
|
+
{
|
|
1710
|
+
workspace_id: z.string().describe('Target workspace ID.'),
|
|
1711
|
+
},
|
|
1712
|
+
async ({ workspace_id }) => {
|
|
1713
|
+
try {
|
|
1714
|
+
const data = await api('POST', '/host/navigation/workspace', { workspace_id });
|
|
1715
|
+
return hostJsonResult(data);
|
|
1716
|
+
} catch (err) {
|
|
1717
|
+
return hostErrorResult(err);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
);
|
|
1721
|
+
|
|
1722
|
+
server.tool('navigate_to_settings',
|
|
1723
|
+
'Emit navigation intent for UI to open a settings section.',
|
|
1724
|
+
{
|
|
1725
|
+
section: z.string().describe('Settings section key, e.g. "credentials", "profile", "workspace".'),
|
|
1726
|
+
},
|
|
1727
|
+
async ({ section }) => {
|
|
1728
|
+
try {
|
|
1729
|
+
const data = await api('POST', '/host/navigation/settings', { section });
|
|
1730
|
+
return hostJsonResult(data);
|
|
1731
|
+
} catch (err) {
|
|
1732
|
+
return hostErrorResult(err);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1125
1738
|
// ── start ─────────────────────────────────────────────────────────────────────
|
|
1126
1739
|
const transport = new StdioServerTransport();
|
|
1127
1740
|
await server.connect(transport);
|
|
1128
|
-
console.error(`[chat-bridge] MCP Server started (agentId=${AGENT_ID})`);
|
|
1741
|
+
console.error(`[chat-bridge] MCP Server started (agentId=${AGENT_ID}, host=${IS_HOST_AGENT ? 'true' : 'false'})`);
|