amalgm 0.1.72 → 0.1.73
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 -2
- package/runtime/scripts/amalgm-mcp/agents/rest.js +29 -22
- package/runtime/scripts/amalgm-mcp/agents/store.js +90 -24
- package/runtime/scripts/amalgm-mcp/agents/talk.js +66 -37
- package/runtime/scripts/amalgm-mcp/agents/tools.js +0 -4
- package/runtime/scripts/amalgm-mcp/automations/runner.js +4 -5
- package/runtime/scripts/amalgm-mcp/automations/tool-actions.js +54 -57
- package/runtime/scripts/amalgm-mcp/automations/tools.js +4 -4
- package/runtime/scripts/amalgm-mcp/email/inbound.js +21 -0
- package/runtime/scripts/amalgm-mcp/events/internal-workflows.js +3 -3
- package/runtime/scripts/amalgm-mcp/index.js +3 -1
- package/runtime/scripts/amalgm-mcp/lib/prefs.js +15 -0
- package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +5 -3
- package/runtime/scripts/amalgm-mcp/runtime-worker.js +47 -0
- package/runtime/scripts/amalgm-mcp/server/combined-service.js +27 -0
- package/runtime/scripts/amalgm-mcp/server/control-plane-service.js +24 -0
- package/runtime/scripts/amalgm-mcp/server/core-tools.js +52 -7
- package/runtime/scripts/amalgm-mcp/server/http-context.js +44 -0
- package/runtime/scripts/amalgm-mcp/server/http-service.js +34 -0
- package/runtime/scripts/amalgm-mcp/server/http.js +31 -465
- package/runtime/scripts/amalgm-mcp/server/local-service-router.js +57 -0
- package/runtime/scripts/amalgm-mcp/server/mcp-adapter.js +22 -0
- package/runtime/scripts/amalgm-mcp/server/mcp-http-service.js +24 -0
- package/runtime/scripts/amalgm-mcp/server/mcp-request-context.js +32 -0
- package/runtime/scripts/amalgm-mcp/server/mcp.js +14 -73
- package/runtime/scripts/amalgm-mcp/server/routes/agents.js +29 -0
- package/runtime/scripts/amalgm-mcp/server/routes/apps.js +59 -0
- package/runtime/scripts/amalgm-mcp/server/routes/automations.js +45 -0
- package/runtime/scripts/amalgm-mcp/server/routes/browser.js +49 -0
- package/runtime/scripts/amalgm-mcp/server/routes/credentials.js +30 -0
- package/runtime/scripts/amalgm-mcp/server/routes/events.js +18 -0
- package/runtime/scripts/amalgm-mcp/server/routes/files.js +33 -0
- package/runtime/scripts/amalgm-mcp/server/routes/health.js +24 -0
- package/runtime/scripts/amalgm-mcp/server/routes/inbound.js +18 -0
- package/runtime/scripts/amalgm-mcp/server/routes/local.js +21 -0
- package/runtime/scripts/amalgm-mcp/server/routes/preferences.js +18 -0
- package/runtime/scripts/amalgm-mcp/server/routes/state.js +23 -0
- package/runtime/scripts/amalgm-mcp/server/routes/toolbox.js +59 -0
- package/runtime/scripts/amalgm-mcp/server/routes/workspace.js +57 -0
- package/runtime/scripts/amalgm-mcp/server/service-lifecycle.js +54 -0
- package/runtime/scripts/amalgm-mcp/services/control-plane.js +19 -0
- package/runtime/scripts/amalgm-mcp/services/mcp-adapter.js +12 -0
- package/runtime/scripts/amalgm-mcp/services/runtime-worker.js +18 -0
- package/runtime/scripts/amalgm-mcp/slack/inbound.js +21 -0
- package/runtime/scripts/amalgm-mcp/state/snapshot.js +3 -3
- package/runtime/scripts/amalgm-mcp/tests/agents-source-of-truth.test.js +82 -0
- package/runtime/scripts/amalgm-mcp/tests/automations-store-runner.test.js +5 -5
- package/runtime/scripts/amalgm-mcp/tests/core-tools.test.js +37 -0
- package/runtime/scripts/amalgm-mcp/tests/mcp-surface.test.js +158 -0
- package/runtime/scripts/amalgm-mcp/tests/server-routing.test.js +81 -0
- package/runtime/scripts/amalgm-mcp/tests/server-services.test.js +156 -0
- package/runtime/scripts/amalgm-mcp/tests/system-catalog.test.js +81 -0
- package/runtime/scripts/amalgm-mcp/tests/toolbox-service.test.js +250 -0
- package/runtime/scripts/amalgm-mcp/toolbox/mcp-surface.js +106 -0
- package/runtime/scripts/amalgm-mcp/toolbox/names.js +41 -0
- package/runtime/scripts/amalgm-mcp/toolbox/rest.js +107 -53
- package/runtime/scripts/amalgm-mcp/toolbox/runner.js +9 -75
- package/runtime/scripts/amalgm-mcp/toolbox/selection.js +22 -0
- package/runtime/scripts/amalgm-mcp/toolbox/service.js +227 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +9 -78
- package/runtime/scripts/amalgm-mcp/toolbox/system-catalog.js +112 -0
- package/runtime/scripts/amalgm-mcp/toolbox/tools.js +11 -74
- package/runtime/scripts/amalgm-mcp/workflows/compiler.js +1 -1
- package/runtime/scripts/amalgm-mcp/workflows/runner.js +4 -5
|
@@ -1,90 +1,24 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { spawn } = require('child_process');
|
|
4
|
-
const path = require('path');
|
|
5
4
|
const { errorResult, textResult } = require('../lib/tool-result');
|
|
6
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
TOOLBOX_TOOL_PREFIX,
|
|
7
|
+
toolboxMcpToolName,
|
|
8
|
+
} = require('./names');
|
|
9
|
+
const {
|
|
10
|
+
defaultToolboxService: toolboxService,
|
|
11
|
+
} = require('./service');
|
|
7
12
|
|
|
8
|
-
const TOOLBOX_TOOL_PREFIX = 'toolbox__';
|
|
9
13
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
10
14
|
const MAX_OUTPUT_BYTES = 512 * 1024;
|
|
11
|
-
const MODEL_UNSUPPORTED_TOP_LEVEL_SCHEMA_KEYS = ['anyOf', 'oneOf', 'allOf', 'not', 'enum'];
|
|
12
|
-
|
|
13
|
-
function isObject(value) {
|
|
14
|
-
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function safeMcpName(value) {
|
|
18
|
-
return String(value || '')
|
|
19
|
-
.trim()
|
|
20
|
-
.replace(/[^A-Za-z0-9_.-]+/g, '_')
|
|
21
|
-
.replace(/[.-]+/g, '_')
|
|
22
|
-
.replace(/_+/g, '_')
|
|
23
|
-
.replace(/^_+|_+$/g, '')
|
|
24
|
-
|| 'tool';
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function toolboxMcpToolName(action) {
|
|
28
|
-
return `${TOOLBOX_TOOL_PREFIX}${safeMcpName(action.id)}`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function selectedToolIdsForContext(context) {
|
|
32
|
-
const tools = context?.sessionMetadata?.tools;
|
|
33
|
-
if (!tools || tools.mode !== 'selected') return null;
|
|
34
|
-
const selected = Array.isArray(tools.selectedToolIds)
|
|
35
|
-
? tools.selectedToolIds
|
|
36
|
-
: Array.isArray(tools.selected)
|
|
37
|
-
? tools.selected
|
|
38
|
-
: [];
|
|
39
|
-
return new Set(selected.map((value) => String(value || '').trim()).filter(Boolean));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function portableInputSchema(inputSchema) {
|
|
43
|
-
if (!isObject(inputSchema) || inputSchema.type !== 'object') {
|
|
44
|
-
return { type: 'object', properties: {}, additionalProperties: true };
|
|
45
|
-
}
|
|
46
|
-
const schema = { ...inputSchema };
|
|
47
|
-
for (const key of MODEL_UNSUPPORTED_TOP_LEVEL_SCHEMA_KEYS) {
|
|
48
|
-
delete schema[key];
|
|
49
|
-
}
|
|
50
|
-
if (!isObject(schema.properties)) schema.properties = {};
|
|
51
|
-
return schema;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function enabledToolboxActions(context) {
|
|
55
|
-
const toolbox = readToolbox();
|
|
56
|
-
const selectedToolIds = selectedToolIdsForContext(context);
|
|
57
|
-
return Object.values(toolbox.toolActions || {})
|
|
58
|
-
.map((action) => {
|
|
59
|
-
const tool = toolbox.tools?.[action.toolId];
|
|
60
|
-
return { action, tool };
|
|
61
|
-
})
|
|
62
|
-
.filter(({ action, tool }) => {
|
|
63
|
-
if (!tool || tool.status !== 'enabled' || action.status !== 'enabled') return false;
|
|
64
|
-
if (tool.origin === 'system') return false;
|
|
65
|
-
// Remote/local MCP tools are injected as MCP providers. The toolbox MCP
|
|
66
|
-
// runner is for tools Amalgm itself must execute, currently CLI/API.
|
|
67
|
-
if (tool.type !== 'cli' && tool.type !== 'api') return false;
|
|
68
|
-
if (!selectedToolIds) return true;
|
|
69
|
-
return selectedToolIds.has(tool.id) || selectedToolIds.has(action.id);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
15
|
|
|
73
16
|
function listToolboxMcpTools(context) {
|
|
74
|
-
return
|
|
75
|
-
name: toolboxMcpToolName(action),
|
|
76
|
-
description: [
|
|
77
|
-
action.description || `Run ${tool.name} ${action.name}.`,
|
|
78
|
-
tool.guide?.kind === 'skill' ? `Guidance skill: ${tool.guide.path}` : null,
|
|
79
|
-
action.guide?.kind === 'skill' ? `Action skill: ${action.guide.path}` : null,
|
|
80
|
-
].filter(Boolean).join('\n'),
|
|
81
|
-
inputSchema: portableInputSchema(action.inputSchema),
|
|
82
|
-
}));
|
|
17
|
+
return toolboxService.listMcpTools(context);
|
|
83
18
|
}
|
|
84
19
|
|
|
85
20
|
function resolveToolboxMcpAction(toolName, context) {
|
|
86
|
-
return
|
|
87
|
-
.find(({ action }) => toolboxMcpToolName(action) === toolName) || null;
|
|
21
|
+
return toolboxService.resolveMcpTool(toolName, context);
|
|
88
22
|
}
|
|
89
23
|
|
|
90
24
|
function collectOutput(child, resolve, reject, timeoutMs) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function selectedToolIdsForContext(context) {
|
|
4
|
+
const tools = context?.sessionMetadata?.tools;
|
|
5
|
+
if (!tools || tools.mode !== 'selected') return null;
|
|
6
|
+
const selected = Array.isArray(tools.selectedToolIds)
|
|
7
|
+
? tools.selectedToolIds
|
|
8
|
+
: Array.isArray(tools.selected)
|
|
9
|
+
? tools.selected
|
|
10
|
+
: [];
|
|
11
|
+
return new Set(selected.map((value) => String(value || '').trim()).filter(Boolean));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function selectedContainsToolOrAction(selectedToolIds, toolId, actionId) {
|
|
15
|
+
if (!selectedToolIds) return true;
|
|
16
|
+
return selectedToolIds.has(toolId) || selectedToolIds.has(actionId);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
selectedContainsToolOrAction,
|
|
21
|
+
selectedToolIdsForContext,
|
|
22
|
+
};
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Toolbox service — registry and action domain boundary.
|
|
5
|
+
*
|
|
6
|
+
* Persistence lives in store.js. HTTP and MCP adapters should depend on this
|
|
7
|
+
* service rather than shaping toolbox records directly.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const store = require('./store');
|
|
11
|
+
const { portableInputSchema, toolboxMcpToolName } = require('./names');
|
|
12
|
+
const {
|
|
13
|
+
selectedContainsToolOrAction,
|
|
14
|
+
selectedToolIdsForContext,
|
|
15
|
+
} = require('./selection');
|
|
16
|
+
const { mergeSystemToolboxCatalog } = require('./system-catalog');
|
|
17
|
+
|
|
18
|
+
function actionRef(toolId, actionName) {
|
|
19
|
+
return `${toolId}.${actionName}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function normalizeId(value) {
|
|
23
|
+
return String(value || '')
|
|
24
|
+
.trim()
|
|
25
|
+
.toLowerCase()
|
|
26
|
+
.replace(/[^a-z0-9_.-]+/g, '.')
|
|
27
|
+
.replace(/\.+/g, '.')
|
|
28
|
+
.replace(/^[._-]+|[._-]+$/g, '');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRunnableToolboxTool(tool) {
|
|
32
|
+
return !!tool
|
|
33
|
+
&& tool.status === 'enabled'
|
|
34
|
+
&& tool.origin !== 'system'
|
|
35
|
+
&& (tool.type === 'cli' || tool.type === 'api');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isExternalMcpTool(tool) {
|
|
39
|
+
return !!tool
|
|
40
|
+
&& tool.status === 'enabled'
|
|
41
|
+
&& tool.type === 'mcp'
|
|
42
|
+
&& tool.origin !== 'system';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function compactAction(action) {
|
|
46
|
+
return {
|
|
47
|
+
id: action.id,
|
|
48
|
+
name: action.name,
|
|
49
|
+
ref: actionRef(action.toolId, action.name),
|
|
50
|
+
status: action.status,
|
|
51
|
+
description: action.description,
|
|
52
|
+
mcpToolName: toolboxMcpToolName(action),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compactTool(tool, actions) {
|
|
57
|
+
return {
|
|
58
|
+
id: tool.id,
|
|
59
|
+
name: tool.name,
|
|
60
|
+
type: tool.type,
|
|
61
|
+
owner: tool.owner,
|
|
62
|
+
origin: tool.origin,
|
|
63
|
+
status: tool.status,
|
|
64
|
+
discovery: tool.discovery,
|
|
65
|
+
actions: actions.map(compactAction),
|
|
66
|
+
actionCount: actions.length,
|
|
67
|
+
source: tool.source,
|
|
68
|
+
guide: tool.guide,
|
|
69
|
+
display: tool.display,
|
|
70
|
+
policy: tool.policy,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function actionsByToolId(toolbox) {
|
|
75
|
+
const byToolId = new Map();
|
|
76
|
+
for (const action of Object.values(toolbox.toolActions || {})) {
|
|
77
|
+
const existing = byToolId.get(action.toolId) || [];
|
|
78
|
+
existing.push(action);
|
|
79
|
+
byToolId.set(action.toolId, existing);
|
|
80
|
+
}
|
|
81
|
+
return byToolId;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function createToolboxService(repository = store) {
|
|
85
|
+
function readToolbox() {
|
|
86
|
+
return repository.readToolbox();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readCatalog() {
|
|
90
|
+
return mergeSystemToolboxCatalog(readToolbox());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function getTool(id, options = {}) {
|
|
94
|
+
const idValue = normalizeId(id);
|
|
95
|
+
if (!idValue) throw new Error('id is required');
|
|
96
|
+
const toolbox = readCatalog();
|
|
97
|
+
const tool = toolbox.tools?.[idValue];
|
|
98
|
+
if (!tool) throw new Error(`Unknown tool: ${idValue}`);
|
|
99
|
+
return {
|
|
100
|
+
tool,
|
|
101
|
+
...(options.includeActions === false
|
|
102
|
+
? {}
|
|
103
|
+
: {
|
|
104
|
+
actions: Object.values(toolbox.toolActions || {})
|
|
105
|
+
.filter((action) => action.toolId === idValue),
|
|
106
|
+
}),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getAction(id) {
|
|
111
|
+
const idValue = normalizeId(id);
|
|
112
|
+
if (!idValue) throw new Error('id is required');
|
|
113
|
+
const toolbox = readCatalog();
|
|
114
|
+
const action = toolbox.toolActions?.[idValue];
|
|
115
|
+
if (!action) throw new Error(`Unknown tool action: ${idValue}`);
|
|
116
|
+
return { action };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function listTools(filters = {}) {
|
|
120
|
+
const toolbox = readCatalog();
|
|
121
|
+
const groupedActions = actionsByToolId(toolbox);
|
|
122
|
+
const tools = Object.values(toolbox.tools || {})
|
|
123
|
+
.filter((tool) => !filters.type || tool.type === filters.type)
|
|
124
|
+
.filter((tool) => !filters.owner || tool.owner === filters.owner)
|
|
125
|
+
.filter((tool) => !filters.origin || tool.origin === filters.origin)
|
|
126
|
+
.map((tool) => compactTool(tool, groupedActions.get(tool.id) || []));
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
version: toolbox.version,
|
|
130
|
+
tools,
|
|
131
|
+
...(filters.include_actions ? { toolActions: toolbox.toolActions } : {}),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function externalMcpToolIdsForMode(mode = 'all', toolIds = []) {
|
|
136
|
+
const selected = new Set(
|
|
137
|
+
(Array.isArray(toolIds) ? toolIds : [])
|
|
138
|
+
.map((value) => String(value || '').trim())
|
|
139
|
+
.filter(Boolean),
|
|
140
|
+
);
|
|
141
|
+
const toolbox = readCatalog();
|
|
142
|
+
return Object.values(toolbox.tools || {})
|
|
143
|
+
.filter((tool) => mode !== 'selected' || selected.has(tool.id))
|
|
144
|
+
.filter(isExternalMcpTool)
|
|
145
|
+
.map((tool) => tool.id);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function externalMcpToolIdsForSelection(toolIds = []) {
|
|
149
|
+
return externalMcpToolIdsForMode('selected', toolIds);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function listRunnableActions(context) {
|
|
153
|
+
const toolbox = readToolbox();
|
|
154
|
+
const selectedToolIds = selectedToolIdsForContext(context);
|
|
155
|
+
return Object.values(toolbox.toolActions || {})
|
|
156
|
+
.map((action) => ({ action, tool: toolbox.tools?.[action.toolId] }))
|
|
157
|
+
.filter(({ action, tool }) => {
|
|
158
|
+
if (!isRunnableToolboxTool(tool) || action.status !== 'enabled') return false;
|
|
159
|
+
return selectedContainsToolOrAction(selectedToolIds, tool.id, action.id);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function resolveRunnableAction(toolId, actionName) {
|
|
164
|
+
const normalizedToolId = String(toolId || '').trim();
|
|
165
|
+
const normalizedActionName = String(actionName || '').trim();
|
|
166
|
+
const toolbox = readToolbox();
|
|
167
|
+
const action = Object.values(toolbox.toolActions || {}).find((candidate) => {
|
|
168
|
+
if (candidate.toolId !== normalizedToolId) return false;
|
|
169
|
+
return candidate.name === normalizedActionName
|
|
170
|
+
|| candidate.id === normalizedActionName
|
|
171
|
+
|| candidate.id === `${normalizedToolId}.${normalizedActionName}`
|
|
172
|
+
|| candidate.id === `${normalizedToolId}:${normalizedActionName}`;
|
|
173
|
+
});
|
|
174
|
+
if (!action || action.status !== 'enabled') return null;
|
|
175
|
+
const tool = toolbox.tools?.[action.toolId];
|
|
176
|
+
return isRunnableToolboxTool(tool) ? { tool, action } : null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function listMcpTools(context) {
|
|
180
|
+
return listRunnableActions(context).map(({ action, tool }) => ({
|
|
181
|
+
name: toolboxMcpToolName(action),
|
|
182
|
+
description: [
|
|
183
|
+
action.description || `Run ${tool.name} ${action.name}.`,
|
|
184
|
+
tool.guide?.kind === 'skill' ? `Guidance skill: ${tool.guide.path}` : null,
|
|
185
|
+
action.guide?.kind === 'skill' ? `Action skill: ${action.guide.path}` : null,
|
|
186
|
+
].filter(Boolean).join('\n'),
|
|
187
|
+
inputSchema: portableInputSchema(action.inputSchema),
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function resolveMcpTool(toolName, context) {
|
|
192
|
+
return listRunnableActions(context)
|
|
193
|
+
.find(({ action }) => toolboxMcpToolName(action) === toolName) || null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
deleteAction: repository.deleteToolAction,
|
|
198
|
+
deleteTool: repository.deleteTool,
|
|
199
|
+
externalMcpToolIdsForMode,
|
|
200
|
+
externalMcpToolIdsForSelection,
|
|
201
|
+
getAction,
|
|
202
|
+
getTool,
|
|
203
|
+
listMcpTools,
|
|
204
|
+
listRunnableActions,
|
|
205
|
+
listTools,
|
|
206
|
+
readCatalog,
|
|
207
|
+
readToolbox,
|
|
208
|
+
registerAction: repository.upsertToolAction,
|
|
209
|
+
registerTool: repository.upsertTool,
|
|
210
|
+
resolveMcpTool,
|
|
211
|
+
resolveRunnableAction,
|
|
212
|
+
updateAction: repository.updateToolAction,
|
|
213
|
+
updateTool: repository.updateTool,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const defaultToolboxService = createToolboxService();
|
|
218
|
+
|
|
219
|
+
module.exports = {
|
|
220
|
+
actionRef,
|
|
221
|
+
compactAction,
|
|
222
|
+
compactTool,
|
|
223
|
+
createToolboxService,
|
|
224
|
+
defaultToolboxService,
|
|
225
|
+
isExternalMcpTool,
|
|
226
|
+
isRunnableToolboxTool,
|
|
227
|
+
};
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const path = require('path');
|
|
15
|
-
const { AMALGM_DIR
|
|
15
|
+
const { AMALGM_DIR } = require('../config');
|
|
16
16
|
const { ensureDir, readJson, writeJsonAtomic } = require('../lib/storage');
|
|
17
17
|
const { openLocalDb } = require('../state/db');
|
|
18
18
|
const { insertStateEvent, publishStateEvent } = require('../state/events');
|
|
@@ -406,87 +406,18 @@ function defaultCliAction(tool) {
|
|
|
406
406
|
}, tool);
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
function
|
|
410
|
-
return normalizeAction({
|
|
411
|
-
id: `${tool.id}.${mcpTool.name}`,
|
|
412
|
-
name: mcpTool.name,
|
|
413
|
-
displayName: mcpTool.name,
|
|
414
|
-
description: mcpTool.description,
|
|
415
|
-
inputSchema: isObject(mcpTool.inputSchema) ? mcpTool.inputSchema : { type: 'object', properties: {} },
|
|
416
|
-
sourceAction: { mcpToolName: mcpTool.name },
|
|
417
|
-
}, tool);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
function systemAmalgmTool() {
|
|
421
|
-
return {
|
|
422
|
-
id: 'amalgm',
|
|
423
|
-
name: 'amalgm',
|
|
424
|
-
type: 'mcp',
|
|
425
|
-
owner: DEFAULT_TOOL_OWNER,
|
|
426
|
-
origin: 'system',
|
|
427
|
-
status: 'enabled',
|
|
428
|
-
source: {
|
|
429
|
-
transport: 'http',
|
|
430
|
-
url: `http://127.0.0.1:${PORT}/mcp`,
|
|
431
|
-
},
|
|
432
|
-
discovery: {
|
|
433
|
-
mode: 'dynamic',
|
|
434
|
-
},
|
|
435
|
-
display: {
|
|
436
|
-
icon: '/assets/images/amalgm_logo.svg',
|
|
437
|
-
description: 'Built-in Amalgm tools for tasks, events, agents, artifacts, browser, and notifications.',
|
|
438
|
-
},
|
|
439
|
-
policy: {
|
|
440
|
-
system: true,
|
|
441
|
-
},
|
|
442
|
-
createdAt: nowIso(),
|
|
443
|
-
updatedAt: nowIso(),
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function seedSystemTools(toolbox) {
|
|
409
|
+
function dropLegacyAggregateAmalgmTool(toolbox) {
|
|
448
410
|
let changed = false;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
...systemAmalgmTool(),
|
|
452
|
-
createdAt: existing?.createdAt || nowIso(),
|
|
453
|
-
updatedAt: existing?.updatedAt || nowIso(),
|
|
454
|
-
};
|
|
455
|
-
|
|
456
|
-
if (!existing || JSON.stringify({ ...existing, updatedAt: undefined }) !== JSON.stringify({ ...systemTool, updatedAt: undefined })) {
|
|
457
|
-
toolbox.tools.amalgm = { ...existing, ...systemTool };
|
|
411
|
+
if (toolbox.tools?.amalgm) {
|
|
412
|
+
delete toolbox.tools.amalgm;
|
|
458
413
|
changed = true;
|
|
459
414
|
}
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
toolsList = require('../server/core-tools').CORE_TOOLS || [];
|
|
464
|
-
} catch {
|
|
465
|
-
toolsList = [];
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
for (const mcpTool of toolsList) {
|
|
469
|
-
if (!mcpTool?.name) continue;
|
|
470
|
-
const action = actionFromMcpTool(toolbox.tools.amalgm, mcpTool);
|
|
471
|
-
const existingAction = toolbox.toolActions[action.id];
|
|
472
|
-
if (!existingAction || JSON.stringify({
|
|
473
|
-
...existingAction,
|
|
474
|
-
createdAt: undefined,
|
|
475
|
-
updatedAt: undefined,
|
|
476
|
-
}) !== JSON.stringify({
|
|
477
|
-
...action,
|
|
478
|
-
createdAt: undefined,
|
|
479
|
-
updatedAt: undefined,
|
|
480
|
-
})) {
|
|
481
|
-
toolbox.toolActions[action.id] = {
|
|
482
|
-
...existingAction,
|
|
483
|
-
...action,
|
|
484
|
-
createdAt: existingAction?.createdAt || action.createdAt,
|
|
485
|
-
};
|
|
415
|
+
for (const [actionId, action] of Object.entries(toolbox.toolActions || {})) {
|
|
416
|
+
if (action?.toolId === 'amalgm') {
|
|
417
|
+
delete toolbox.toolActions[actionId];
|
|
486
418
|
changed = true;
|
|
487
419
|
}
|
|
488
420
|
}
|
|
489
|
-
|
|
490
421
|
return changed;
|
|
491
422
|
}
|
|
492
423
|
|
|
@@ -637,8 +568,8 @@ function insertToolActionEvent(db, op, action, options = {}) {
|
|
|
637
568
|
function readToolbox() {
|
|
638
569
|
migrateLegacyToolboxFileIfNeeded();
|
|
639
570
|
const toolbox = readToolboxFromDb();
|
|
640
|
-
const
|
|
641
|
-
if (
|
|
571
|
+
const removedLegacyAggregate = dropLegacyAggregateAmalgmTool(toolbox);
|
|
572
|
+
if (removedLegacyAggregate) writeToolbox(toolbox);
|
|
642
573
|
return toolbox;
|
|
643
574
|
}
|
|
644
575
|
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Virtual first-party Toolbox catalog entries.
|
|
5
|
+
*
|
|
6
|
+
* These records make Amalgm's default capabilities look like normal Toolbox
|
|
7
|
+
* tools/actions to UI and control-plane consumers without changing the legacy
|
|
8
|
+
* MCP tool names or writing additional system rows to local storage.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { PORT } = require('../config');
|
|
12
|
+
const { toolboxMcpToolName } = require('./names');
|
|
13
|
+
|
|
14
|
+
const SYSTEM_TIMESTAMP = '1970-01-01T00:00:00.000Z';
|
|
15
|
+
|
|
16
|
+
function coreToolGroups() {
|
|
17
|
+
return require('../server/core-tools').CORE_TOOL_GROUPS || [];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function systemToolForGroup(group) {
|
|
21
|
+
return {
|
|
22
|
+
id: group.id,
|
|
23
|
+
name: group.name,
|
|
24
|
+
type: group.type || 'mcp',
|
|
25
|
+
owner: group.owner || 'amalgm',
|
|
26
|
+
origin: group.origin || 'system',
|
|
27
|
+
status: group.status || 'enabled',
|
|
28
|
+
source: {
|
|
29
|
+
transport: 'http',
|
|
30
|
+
url: `http://127.0.0.1:${PORT}/mcp`,
|
|
31
|
+
},
|
|
32
|
+
discovery: { mode: 'static' },
|
|
33
|
+
display: {
|
|
34
|
+
icon: '/assets/images/amalgm_logo.svg',
|
|
35
|
+
description: group.description,
|
|
36
|
+
},
|
|
37
|
+
policy: {
|
|
38
|
+
system: true,
|
|
39
|
+
firstParty: true,
|
|
40
|
+
},
|
|
41
|
+
createdAt: SYSTEM_TIMESTAMP,
|
|
42
|
+
updatedAt: SYSTEM_TIMESTAMP,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function systemActionForTool(tool, group) {
|
|
47
|
+
return {
|
|
48
|
+
id: tool.toolboxActionId,
|
|
49
|
+
toolId: group.id,
|
|
50
|
+
name: tool.toolboxActionName,
|
|
51
|
+
displayName: tool.toolboxActionName,
|
|
52
|
+
description: tool.description || '',
|
|
53
|
+
inputSchema: tool.inputSchema || { type: 'object', properties: {} },
|
|
54
|
+
status: 'enabled',
|
|
55
|
+
sourceAction: {
|
|
56
|
+
handlerName: tool.name,
|
|
57
|
+
mcpToolName: toolboxMcpToolName({ id: tool.toolboxActionId }),
|
|
58
|
+
},
|
|
59
|
+
policy: {
|
|
60
|
+
system: true,
|
|
61
|
+
firstParty: true,
|
|
62
|
+
},
|
|
63
|
+
createdAt: SYSTEM_TIMESTAMP,
|
|
64
|
+
updatedAt: SYSTEM_TIMESTAMP,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function buildSystemToolboxCatalog() {
|
|
69
|
+
const tools = {};
|
|
70
|
+
const toolActions = {};
|
|
71
|
+
|
|
72
|
+
for (const group of coreToolGroups()) {
|
|
73
|
+
tools[group.id] = systemToolForGroup(group);
|
|
74
|
+
for (const tool of group.tools) {
|
|
75
|
+
toolActions[tool.toolboxActionId] = systemActionForTool(tool, group);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { tools, toolActions };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function persistedCatalogWithoutLegacyAggregate(toolbox) {
|
|
83
|
+
const tools = { ...(toolbox?.tools || {}) };
|
|
84
|
+
const toolActions = { ...(toolbox?.toolActions || {}) };
|
|
85
|
+
delete tools.amalgm;
|
|
86
|
+
for (const [actionId, action] of Object.entries(toolActions)) {
|
|
87
|
+
if (action?.toolId === 'amalgm') delete toolActions[actionId];
|
|
88
|
+
}
|
|
89
|
+
return { tools, toolActions };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function mergeSystemToolboxCatalog(toolbox) {
|
|
93
|
+
const catalog = buildSystemToolboxCatalog();
|
|
94
|
+
const persisted = persistedCatalogWithoutLegacyAggregate(toolbox);
|
|
95
|
+
return {
|
|
96
|
+
...toolbox,
|
|
97
|
+
tools: {
|
|
98
|
+
...catalog.tools,
|
|
99
|
+
...persisted.tools,
|
|
100
|
+
},
|
|
101
|
+
toolActions: {
|
|
102
|
+
...catalog.toolActions,
|
|
103
|
+
...persisted.toolActions,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
buildSystemToolboxCatalog,
|
|
110
|
+
mergeSystemToolboxCatalog,
|
|
111
|
+
persistedCatalogWithoutLegacyAggregate,
|
|
112
|
+
};
|