@amalgm/tools 0.1.7-preview.34436591947 → 0.1.7-preview.34547098528
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/README.md +6 -0
- package/dist/cli-driver.d.ts +4 -3
- package/dist/cli-driver.js +42 -40
- package/dist/cli.d.ts +7 -2
- package/dist/cli.js +51 -49
- package/dist/commands.d.ts +27 -0
- package/dist/commands.js +15 -0
- package/dist/definition.js +1 -0
- package/dist/host-mcp-tool.d.ts +18 -1
- package/dist/ids.d.ts +3 -1
- package/dist/ids.js +7 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +26 -7
- package/dist/toolbox.d.ts +5 -2
- package/dist/toolbox.js +8 -3
- package/dist/types.d.ts +7 -18
- package/dist/updates.d.ts +7 -2
- package/dist/updates.js +15 -1
- package/package.json +4 -4
- package/skills/tools/SKILL.md +6 -0
package/README.md
CHANGED
|
@@ -66,9 +66,15 @@ amalgm-tools apply ./tool.mjs
|
|
|
66
66
|
amalgm-tools list
|
|
67
67
|
amalgm-tools run hello.say --input '{"name":"Ada"}'
|
|
68
68
|
amalgm-tools disable hello.say
|
|
69
|
+
amalgm-tools link canvas app-3f1c
|
|
69
70
|
amalgm-tools connections
|
|
70
71
|
```
|
|
71
72
|
|
|
73
|
+
A tool may belong to an app: `appId` on the tool is the reverse edge of the
|
|
74
|
+
app manifest's `toolIds`, and sharing either side carries the other. The host
|
|
75
|
+
that composes Tools and Apps may pass `environment(tool)` so a linked tool
|
|
76
|
+
runs knowing where its app listens.
|
|
77
|
+
|
|
72
78
|
`amalgm-tools-mcp` serves Toolbox management and enabled action tools over
|
|
73
79
|
newline-delimited MCP stdio.
|
|
74
80
|
|
package/dist/cli-driver.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { ToolDriver } from './types.js';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type { ToolDriver, ToolRecord } from './types.js';
|
|
2
|
+
type Environment = (tool: ToolRecord) => Record<string, string> | Promise<Record<string, string>>;
|
|
3
|
+
declare function createCliDriver(environment?: Environment): ToolDriver;
|
|
4
|
+
export { createCliDriver };
|
package/dist/cli-driver.js
CHANGED
|
@@ -8,45 +8,47 @@ function inputArguments(input) {
|
|
|
8
8
|
}
|
|
9
9
|
return args;
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let result;
|
|
39
|
-
if (source.outputMode === 'json' && stdout) {
|
|
40
|
-
try {
|
|
41
|
-
result = structuredResult(JSON.parse(stdout));
|
|
11
|
+
function createCliDriver(environment = () => ({})) {
|
|
12
|
+
return {
|
|
13
|
+
type: 'cli',
|
|
14
|
+
async call({ tool, action, input, options }) {
|
|
15
|
+
const source = tool.source;
|
|
16
|
+
const target = action.target;
|
|
17
|
+
const secrets = await resolveReferences(source.secretEnv, options.resolveSecret);
|
|
18
|
+
const args = [...(source.args || []), ...(target.args || [])];
|
|
19
|
+
if (source.inputMode === 'argv')
|
|
20
|
+
args.push(...inputArguments(input));
|
|
21
|
+
const output = await runCommand({
|
|
22
|
+
command: source.command, args, cwd: source.cwd,
|
|
23
|
+
env: { ...process.env, ...(await environment(tool)), ...secrets },
|
|
24
|
+
stdin: source.inputMode === 'json-stdin' ? `${JSON.stringify(input)}\n` : undefined,
|
|
25
|
+
timeoutMs: source.timeoutMs, maximumBytes: source.maxOutputBytes, signal: options.signal,
|
|
26
|
+
});
|
|
27
|
+
const stdout = output.stdout.trim();
|
|
28
|
+
const stderr = output.stderr.trim();
|
|
29
|
+
if (output.timedOut)
|
|
30
|
+
return errorResult(`Tool ${action.id} timed out after ${source.timeoutMs}ms`);
|
|
31
|
+
if (output.aborted)
|
|
32
|
+
return errorResult(`Tool ${action.id} was cancelled`);
|
|
33
|
+
if (output.code !== 0) {
|
|
34
|
+
return errorResult([
|
|
35
|
+
`Tool ${action.id} exited with ${output.code ?? output.signal ?? 'unknown'}.`,
|
|
36
|
+
stdout && `stdout:\n${stdout}`, stderr && `stderr:\n${stderr}`,
|
|
37
|
+
].filter(Boolean).join('\n\n'));
|
|
42
38
|
}
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
let result;
|
|
40
|
+
if (source.outputMode === 'json' && stdout) {
|
|
41
|
+
try {
|
|
42
|
+
result = structuredResult(JSON.parse(stdout));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return errorResult(`Tool ${action.id} returned invalid JSON`);
|
|
46
|
+
}
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
result
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
export {
|
|
48
|
+
else
|
|
49
|
+
result = textResult(stdout || `Tool ${action.id} completed.`);
|
|
50
|
+
return { ...result, ...(output.truncated ? { truncated: true } : {}) };
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export { createCliDriver };
|
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CliIo, LoadoutInput } from './types.js';
|
|
2
|
-
declare const HELP = "Usage: amalgm-tools [--state-dir DIR] [--loadout IDS] <command>\n\nCommands:\n apply <definition.js|json> Apply one tool or an array of tools\n list List tools and enabled actions\n catalog Print the complete canonical catalog\n show <tool-or-action-id> Show one record\n enable <tool-or-action-id> Enable one record\n disable <tool-or-action-id> Disable one record\n remove <tool-or-action-id> Remove one record\n run <action-id> [--input JSON]\n Run an action\n connections List MCP connections for a composing host";
|
|
2
|
+
declare const HELP = "Usage: amalgm-tools [--state-dir DIR] [--loadout IDS] <command>\n\nCommands:\n apply <definition.js|json> Apply one tool or an array of tools\n list List tools and enabled actions\n catalog Print the complete canonical catalog\n show <tool-or-action-id> Show one record\n enable <tool-or-action-id> Enable one record\n disable <tool-or-action-id> Disable one record\n remove <tool-or-action-id> Remove one record\n link <tool-id> <app-id> Link one tool to the app it belongs to\n unlink <tool-id> Detach one tool from its app\n run <action-id> [--input JSON]\n Run an action\n connections List MCP connections for a composing host";
|
|
3
3
|
interface Parsed {
|
|
4
4
|
command?: string;
|
|
5
5
|
args: string[];
|
|
@@ -7,7 +7,12 @@ interface Parsed {
|
|
|
7
7
|
loadout?: LoadoutInput;
|
|
8
8
|
input: Record<string, unknown>;
|
|
9
9
|
}
|
|
10
|
+
/** One call on the command surface: an MCP tool name and its arguments. */
|
|
11
|
+
interface Call {
|
|
12
|
+
name: string;
|
|
13
|
+
args: Record<string, unknown>;
|
|
14
|
+
}
|
|
10
15
|
declare function parse(argv: string[]): Parsed;
|
|
11
16
|
declare function runCli(argv: string[], io?: CliIo): Promise<number>;
|
|
12
17
|
export { HELP, parse, runCli };
|
|
13
|
-
export type { Parsed };
|
|
18
|
+
export type { Call, Parsed };
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createToolboxCommands } from './commands.js';
|
|
2
|
+
import { id, mcpName } from './ids.js';
|
|
1
3
|
import { loadDefinitions } from './module.js';
|
|
2
4
|
import { Toolbox } from './toolbox.js';
|
|
3
5
|
const HELP = `Usage: amalgm-tools [--state-dir DIR] [--loadout IDS] <command>
|
|
@@ -10,6 +12,8 @@ Commands:
|
|
|
10
12
|
enable <tool-or-action-id> Enable one record
|
|
11
13
|
disable <tool-or-action-id> Disable one record
|
|
12
14
|
remove <tool-or-action-id> Remove one record
|
|
15
|
+
link <tool-id> <app-id> Link one tool to the app it belongs to
|
|
16
|
+
unlink <tool-id> Detach one tool from its app
|
|
13
17
|
run <action-id> [--input JSON]
|
|
14
18
|
Run an action
|
|
15
19
|
connections List MCP connections for a composing host`;
|
|
@@ -48,9 +52,44 @@ function required(value, label) {
|
|
|
48
52
|
throw new Error(`${label} is required`);
|
|
49
53
|
return value;
|
|
50
54
|
}
|
|
55
|
+
/** Map one parsed command line to its calls; `apply` makes one call per definition. */
|
|
56
|
+
async function calls(parsed) {
|
|
57
|
+
const [first, second] = parsed.args;
|
|
58
|
+
const record = (name, label) => ({ name, args: { id: required(first, label) } });
|
|
59
|
+
switch (parsed.command) {
|
|
60
|
+
case 'apply': {
|
|
61
|
+
const definitions = await loadDefinitions(required(first, 'definition file'));
|
|
62
|
+
return definitions.map((tool) => ({ name: 'toolbox_tool_apply', args: { tool } }));
|
|
63
|
+
}
|
|
64
|
+
case 'list': return [{ name: 'toolbox_tools_selected', args: {} }];
|
|
65
|
+
case 'catalog': return [{ name: 'toolbox_tools_list', args: {} }];
|
|
66
|
+
case 'show': return [record('toolbox_tool_get', 'tool or action id')];
|
|
67
|
+
case 'enable': return [record('toolbox_record_enable', 'record id')];
|
|
68
|
+
case 'disable': return [record('toolbox_record_disable', 'record id')];
|
|
69
|
+
case 'remove': return [record('toolbox_record_remove', 'record id')];
|
|
70
|
+
case 'link': return [{
|
|
71
|
+
name: 'toolbox_tool_link',
|
|
72
|
+
args: { id: required(first, 'tool id'), app_id: required(second, 'app id') },
|
|
73
|
+
}];
|
|
74
|
+
case 'unlink': return [record('toolbox_tool_unlink', 'tool id')];
|
|
75
|
+
case 'run': return [{ name: mcpName({ id: id(first, 'action id') }), args: parsed.input }];
|
|
76
|
+
case 'connections': return [{ name: 'toolbox_mcp_connections', args: {} }];
|
|
77
|
+
default: throw new Error(`Unknown command: ${parsed.command}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function open(io, parsed) {
|
|
81
|
+
if (io.commands)
|
|
82
|
+
return { commands: io.commands, close() { } };
|
|
83
|
+
const toolbox = new Toolbox({ stateDir: parsed.stateDir });
|
|
84
|
+
return { commands: createToolboxCommands(toolbox, parsed.loadout), close: () => toolbox.close() };
|
|
85
|
+
}
|
|
51
86
|
function write(output, value) {
|
|
52
87
|
output.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
53
88
|
}
|
|
89
|
+
function fail(output, error) {
|
|
90
|
+
output.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
54
93
|
async function runCli(argv, io = {}) {
|
|
55
94
|
const stdout = io.stdout || process.stdout;
|
|
56
95
|
const stderr = io.stderr || process.stderr;
|
|
@@ -59,65 +98,28 @@ async function runCli(argv, io = {}) {
|
|
|
59
98
|
parsed = parse(argv);
|
|
60
99
|
}
|
|
61
100
|
catch (error) {
|
|
62
|
-
stderr
|
|
63
|
-
return 1;
|
|
101
|
+
return fail(stderr, error);
|
|
64
102
|
}
|
|
65
103
|
if (!parsed.command || ['help', '-h', '--help'].includes(parsed.command)) {
|
|
66
104
|
stdout.write(`${HELP}\n`);
|
|
67
105
|
return 0;
|
|
68
106
|
}
|
|
69
|
-
const toolbox = io.toolbox || new Toolbox({ stateDir: parsed.stateDir });
|
|
70
107
|
try {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
case 'list':
|
|
82
|
-
write(stdout, toolbox.list(parsed.loadout));
|
|
83
|
-
break;
|
|
84
|
-
case 'catalog':
|
|
85
|
-
write(stdout, toolbox.catalog());
|
|
86
|
-
break;
|
|
87
|
-
case 'show': {
|
|
88
|
-
const value = required(recordId, 'tool or action id');
|
|
89
|
-
const result = toolbox.get(value) || toolbox.action(value);
|
|
90
|
-
if (!result)
|
|
91
|
-
throw new Error(`Unknown tool or action: ${value}`);
|
|
92
|
-
write(stdout, result);
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
case 'enable':
|
|
96
|
-
write(stdout, await toolbox.setStatus(required(recordId, 'record id'), 'enabled'));
|
|
97
|
-
break;
|
|
98
|
-
case 'disable':
|
|
99
|
-
write(stdout, await toolbox.setStatus(required(recordId, 'record id'), 'disabled'));
|
|
100
|
-
break;
|
|
101
|
-
case 'remove':
|
|
102
|
-
write(stdout, await toolbox.remove(required(recordId, 'record id')));
|
|
103
|
-
break;
|
|
104
|
-
case 'run':
|
|
105
|
-
write(stdout, await toolbox.call(required(recordId, 'action id'), parsed.input, { loadout: parsed.loadout }));
|
|
106
|
-
break;
|
|
107
|
-
case 'connections':
|
|
108
|
-
write(stdout, toolbox.connections(parsed.loadout));
|
|
109
|
-
break;
|
|
110
|
-
default: throw new Error(`Unknown command: ${parsed.command}`);
|
|
108
|
+
const planned = await calls(parsed);
|
|
109
|
+
const session = open(io, parsed);
|
|
110
|
+
try {
|
|
111
|
+
const results = [];
|
|
112
|
+
for (const call of planned)
|
|
113
|
+
results.push(await session.commands.execute(call.name, call.args));
|
|
114
|
+
write(stdout, results.length === 1 ? results[0] : results);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
session.close();
|
|
111
118
|
}
|
|
112
119
|
return 0;
|
|
113
120
|
}
|
|
114
121
|
catch (error) {
|
|
115
|
-
stderr
|
|
116
|
-
return 1;
|
|
117
|
-
}
|
|
118
|
-
finally {
|
|
119
|
-
if (!io.toolbox)
|
|
120
|
-
toolbox.close();
|
|
122
|
+
return fail(stderr, error);
|
|
121
123
|
}
|
|
122
124
|
}
|
|
123
125
|
export { HELP, parse, runCli };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Toolbox } from './toolbox.js';
|
|
2
|
+
import type { LoadoutInput } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The one command surface. The CLI speaks only this; a host forwards it to a
|
|
5
|
+
* running runtime's MCP over HTTP, and `createToolboxCommands` runs it against
|
|
6
|
+
* a local Toolbox through that Toolbox's own MCP tools.
|
|
7
|
+
*
|
|
8
|
+
* `name` is an MCP tool name from this package's MCP surface: a management
|
|
9
|
+
* tool (`toolbox_*`, single underscore) or an action tool (`toolbox__<id>`,
|
|
10
|
+
* from `mcpName`). `args` is the MCP `arguments` object for that tool.
|
|
11
|
+
*
|
|
12
|
+
* `execute` resolves to the tool's value, unwrapped from the MCP call result:
|
|
13
|
+
* - any result with `isError: true` rejects with an Error whose message is
|
|
14
|
+
* the result's text content (every `content[].text`, joined by newlines);
|
|
15
|
+
* - a management tool's value is `result.structuredContent` (its text
|
|
16
|
+
* content is the same value as JSON and is never consulted);
|
|
17
|
+
* - an action tool's value is the whole MCP result (`content`,
|
|
18
|
+
* `structuredContent`, `truncated`) exactly as the driver returned it.
|
|
19
|
+
* A remote implementation sends `name`/`args` as an MCP `tools/call` and
|
|
20
|
+
* applies these three rules to the response.
|
|
21
|
+
*/
|
|
22
|
+
interface ToolCommands {
|
|
23
|
+
execute(name: string, args: Record<string, unknown>): Promise<unknown>;
|
|
24
|
+
}
|
|
25
|
+
declare function createToolboxCommands(toolbox: Toolbox, loadout?: LoadoutInput): ToolCommands;
|
|
26
|
+
export { createToolboxCommands };
|
|
27
|
+
export type { ToolCommands };
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { isActionMcpName } from './ids.js';
|
|
2
|
+
import { callMcpTool } from './mcp.js';
|
|
3
|
+
function unwrap(name, result) {
|
|
4
|
+
if (result.isError)
|
|
5
|
+
throw new Error(result.content.map((item) => item.text).join('\n'));
|
|
6
|
+
return isActionMcpName(name) ? result : result.structuredContent;
|
|
7
|
+
}
|
|
8
|
+
function createToolboxCommands(toolbox, loadout) {
|
|
9
|
+
return {
|
|
10
|
+
async execute(name, args) {
|
|
11
|
+
return unwrap(name, await callMcpTool(toolbox, name, args, { loadout }));
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export { createToolboxCommands };
|
package/dist/definition.js
CHANGED
|
@@ -140,6 +140,7 @@ function normalizeDefinition(definition, existing, now = new Date().toISOString(
|
|
|
140
140
|
owner: definition.owner?.trim() || 'user', origin,
|
|
141
141
|
status: definition.status || 'enabled',
|
|
142
142
|
...(definition.description?.trim() ? { description: definition.description.trim() } : {}),
|
|
143
|
+
...(definition.appId?.trim() ? { appId: definition.appId.trim() } : {}),
|
|
143
144
|
...(definition.display ? { display: structuredClone(definition.display) } : {}),
|
|
144
145
|
...(definition.policy ? { policy: structuredClone(definition.policy) } : {}),
|
|
145
146
|
...(definition.metadata ? { metadata: structuredClone(definition.metadata) } : {}),
|
package/dist/host-mcp-tool.d.ts
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { JsonSchema, ToolDefinition } from './types.js';
|
|
2
|
+
interface HostMcpToolDescriptor {
|
|
3
|
+
name: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
inputSchema?: JsonSchema;
|
|
6
|
+
}
|
|
7
|
+
interface HostMcpToolDefinitionInput {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
description?: string;
|
|
11
|
+
serverName: string;
|
|
12
|
+
route: string;
|
|
13
|
+
tools: readonly HostMcpToolDescriptor[];
|
|
14
|
+
owner?: string;
|
|
15
|
+
display?: Record<string, unknown>;
|
|
16
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
}
|
|
2
18
|
/**
|
|
3
19
|
* Project an owning SDK's MCP descriptors into one immutable first-party
|
|
4
20
|
* Toolbox tool. The descriptors remain authoritative; this adapter does not
|
|
@@ -6,3 +22,4 @@ import type { HostMcpToolDefinitionInput, ToolDefinition } from './types.js';
|
|
|
6
22
|
*/
|
|
7
23
|
declare function defineHostMcpTool(input: HostMcpToolDefinitionInput): ToolDefinition;
|
|
8
24
|
export { defineHostMcpTool };
|
|
25
|
+
export type { HostMcpToolDefinitionInput, HostMcpToolDescriptor };
|
package/dist/ids.d.ts
CHANGED
|
@@ -3,5 +3,7 @@ declare function id(value: unknown, label?: string, maximumLength?: number): str
|
|
|
3
3
|
declare function actionName(value: unknown): string;
|
|
4
4
|
declare function actionId(toolId: string, name: string): string;
|
|
5
5
|
declare function mcpName(action: Pick<ActionRecord, 'id'>): string;
|
|
6
|
+
/** Action tools carry the double-underscore prefix; management tools never do. */
|
|
7
|
+
declare function isActionMcpName(name: string): boolean;
|
|
6
8
|
declare function assertMcpNamesUnique(actions: Pick<ActionRecord, 'id'>[]): void;
|
|
7
|
-
export { actionId, actionName, assertMcpNamesUnique, id, mcpName };
|
|
9
|
+
export { actionId, actionName, assertMcpNamesUnique, id, isActionMcpName, mcpName };
|
package/dist/ids.js
CHANGED
|
@@ -21,8 +21,13 @@ function actionId(toolId, name) {
|
|
|
21
21
|
function safeMcpName(value) {
|
|
22
22
|
return value.replace(/[^A-Za-z0-9_-]+/g, '_').replace(/_+/g, '_');
|
|
23
23
|
}
|
|
24
|
+
const MCP_ACTION_PREFIX = 'toolbox__';
|
|
24
25
|
function mcpName(action) {
|
|
25
|
-
return
|
|
26
|
+
return `${MCP_ACTION_PREFIX}${safeMcpName(action.id)}`;
|
|
27
|
+
}
|
|
28
|
+
/** Action tools carry the double-underscore prefix; management tools never do. */
|
|
29
|
+
function isActionMcpName(name) {
|
|
30
|
+
return name.startsWith(MCP_ACTION_PREFIX);
|
|
26
31
|
}
|
|
27
32
|
function assertMcpNamesUnique(actions) {
|
|
28
33
|
const names = new Map();
|
|
@@ -35,4 +40,4 @@ function assertMcpNamesUnique(actions) {
|
|
|
35
40
|
names.set(name, action.id);
|
|
36
41
|
}
|
|
37
42
|
}
|
|
38
|
-
export { actionId, actionName, assertMcpNamesUnique, id, mcpName };
|
|
43
|
+
export { actionId, actionName, assertMcpNamesUnique, id, isActionMcpName, mcpName };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { ArtifactFiles } from './artifact-files.js';
|
|
2
2
|
export { LEGACY_TOOLBOX_BACKUP_FILE_NAME, LEGACY_TOOLBOX_FILE_NAME, TOOL_ARTIFACT_KIND, TOOL_ARTIFACT_SCHEMA_VERSION, TOOLBOX_INDEX_FILE_NAME, artifactDocument, catalogIndexDocument, isToolArtifactDocument, legacyMigrationPlan, safeToolId, toolboxIndexDocument, userArtifactFileName, } from './artifacts.js';
|
|
3
3
|
export type { ArtifactFileWrite, LegacyMigrationPlan, ToolArtifactDocument, ToolboxIndexDocument, } from './artifacts.js';
|
|
4
|
+
export { createToolboxCommands } from './commands.js';
|
|
5
|
+
export type { ToolCommands } from './commands.js';
|
|
4
6
|
export { defineTool, normalizeDefinition } from './definition.js';
|
|
5
7
|
export { defineHostMcpTool } from './host-mcp-tool.js';
|
|
6
8
|
export { TOOL_DEPLOYMENT_CONTRACT, TOOL_DEPLOYMENT_KIND, TOOL_DEPLOYMENT_SCHEMA_VERSION, assertToolDeploymentActivation, createToolDeployment, deploymentRevisionId, emptyToolDeploymentSnapshot, } from './deployments.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { ArtifactFiles } from './artifact-files.js';
|
|
2
2
|
export { LEGACY_TOOLBOX_BACKUP_FILE_NAME, LEGACY_TOOLBOX_FILE_NAME, TOOL_ARTIFACT_KIND, TOOL_ARTIFACT_SCHEMA_VERSION, TOOLBOX_INDEX_FILE_NAME, artifactDocument, catalogIndexDocument, isToolArtifactDocument, legacyMigrationPlan, safeToolId, toolboxIndexDocument, userArtifactFileName, } from './artifacts.js';
|
|
3
|
+
export { createToolboxCommands } from './commands.js';
|
|
3
4
|
export { defineTool, normalizeDefinition } from './definition.js';
|
|
4
5
|
export { defineHostMcpTool } from './host-mcp-tool.js';
|
|
5
6
|
export { TOOL_DEPLOYMENT_CONTRACT, TOOL_DEPLOYMENT_KIND, TOOL_DEPLOYMENT_SCHEMA_VERSION, assertToolDeploymentActivation, createToolDeployment, deploymentRevisionId, emptyToolDeploymentSnapshot, } from './deployments.js';
|
package/dist/mcp.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Toolbox } from './toolbox.js';
|
|
|
2
2
|
import type { JsonSchema, McpOptions, McpTool } from './types.js';
|
|
3
3
|
declare function portable(schema: JsonSchema): JsonSchema;
|
|
4
4
|
declare function managementToolDescriptors(): Array<Omit<McpTool, 'handler'>>;
|
|
5
|
-
declare function managementTools(toolbox: Toolbox): McpTool[];
|
|
5
|
+
declare function managementTools(toolbox: Toolbox, options?: McpOptions): McpTool[];
|
|
6
6
|
declare function actionTools(toolbox: Toolbox, options?: McpOptions): McpTool[];
|
|
7
7
|
declare function createMcpTools(toolbox: Toolbox, options?: McpOptions): McpTool[];
|
|
8
8
|
declare function findMcpTool(toolbox: Toolbox, name: string, options?: McpOptions): McpTool | null;
|
package/dist/mcp.js
CHANGED
|
@@ -29,7 +29,12 @@ function managementToolDescriptors() {
|
|
|
29
29
|
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
30
30
|
},
|
|
31
31
|
{
|
|
32
|
-
name: '
|
|
32
|
+
name: 'toolbox_tools_selected',
|
|
33
|
+
description: 'List tools with their selected actions: the loadout, or every enabled action.',
|
|
34
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'toolbox_tool_get', description: 'Get one tool and its actions, or one action.',
|
|
33
38
|
inputSchema: { type: 'object', properties: idProperty, required: ['id'], additionalProperties: false },
|
|
34
39
|
},
|
|
35
40
|
{
|
|
@@ -51,27 +56,41 @@ function managementToolDescriptors() {
|
|
|
51
56
|
name: 'toolbox_record_remove', description: 'Remove one user-owned tool or action.',
|
|
52
57
|
inputSchema: { type: 'object', properties: idProperty, required: ['id'], additionalProperties: false },
|
|
53
58
|
},
|
|
59
|
+
{
|
|
60
|
+
name: 'toolbox_tool_link', description: 'Link one user-owned tool to the app it belongs to.',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: 'object', required: ['id', 'app_id'], additionalProperties: false,
|
|
63
|
+
properties: { ...idProperty, app_id: { type: 'string', description: 'App id' } },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'toolbox_tool_unlink', description: 'Detach one user-owned tool from its app.',
|
|
68
|
+
inputSchema: { type: 'object', properties: idProperty, required: ['id'], additionalProperties: false },
|
|
69
|
+
},
|
|
54
70
|
{
|
|
55
71
|
name: 'toolbox_mcp_connections', description: 'List external MCP connections for the composing host.',
|
|
56
72
|
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
57
73
|
},
|
|
58
74
|
];
|
|
59
75
|
}
|
|
60
|
-
function managementTools(toolbox) {
|
|
61
|
-
const [list, get, apply, enable, disable, remove, connections] = managementToolDescriptors();
|
|
76
|
+
function managementTools(toolbox, options = {}) {
|
|
77
|
+
const [list, selected, get, apply, enable, disable, remove, link, unlink, connections,] = managementToolDescriptors();
|
|
62
78
|
return [
|
|
63
79
|
managed(list, () => toolbox.catalog()),
|
|
80
|
+
managed(selected, () => toolbox.list(options.loadout)),
|
|
64
81
|
managed(get, ({ id }) => {
|
|
65
|
-
const result = toolbox.get(String(id));
|
|
82
|
+
const result = toolbox.get(String(id)) || toolbox.action(String(id));
|
|
66
83
|
if (!result)
|
|
67
|
-
throw new Error(`Unknown tool: ${id}`);
|
|
84
|
+
throw new Error(`Unknown tool or action: ${id}`);
|
|
68
85
|
return result;
|
|
69
86
|
}),
|
|
70
87
|
managed(apply, ({ tool }) => toolbox.apply(tool)),
|
|
71
88
|
managed(enable, ({ id }) => toolbox.setStatus(String(id), 'enabled')),
|
|
72
89
|
managed(disable, ({ id }) => toolbox.setStatus(String(id), 'disabled')),
|
|
73
90
|
managed(remove, ({ id }) => toolbox.remove(String(id))),
|
|
74
|
-
managed(
|
|
91
|
+
managed(link, ({ id, app_id }) => toolbox.link(String(id), String(app_id))),
|
|
92
|
+
managed(unlink, ({ id }) => toolbox.link(String(id), null)),
|
|
93
|
+
managed(connections, () => toolbox.connections(options.loadout)),
|
|
75
94
|
];
|
|
76
95
|
}
|
|
77
96
|
function actionTools(toolbox, options = {}) {
|
|
@@ -91,7 +110,7 @@ function actionTools(toolbox, options = {}) {
|
|
|
91
110
|
}
|
|
92
111
|
function createMcpTools(toolbox, options = {}) {
|
|
93
112
|
return [
|
|
94
|
-
...(options.includeManagement === false ? [] : managementTools(toolbox)),
|
|
113
|
+
...(options.includeManagement === false ? [] : managementTools(toolbox, options)),
|
|
95
114
|
...actionTools(toolbox, options),
|
|
96
115
|
];
|
|
97
116
|
}
|
package/dist/toolbox.d.ts
CHANGED
|
@@ -34,8 +34,11 @@ declare class Toolbox {
|
|
|
34
34
|
update(toolId: string, patch: ToolPatch): Promise<ApplyResult>;
|
|
35
35
|
upsertAction(toolId: string, definition: ActionDefinition): Promise<ActionRecord>;
|
|
36
36
|
updateAction(actionId: string, patch: ActionPatch): Promise<ActionRecord>;
|
|
37
|
-
setStatus(recordId: string, status: 'enabled' | 'disabled'): Promise<
|
|
38
|
-
|
|
37
|
+
setStatus(recordId: string, status: 'enabled' | 'disabled'): Promise<ActionRecord | ToolRecord>;
|
|
38
|
+
/** Link one user tool to the app it belongs to; `null` unlinks it. */
|
|
39
|
+
link(toolId: string, appId: string | null): Promise<ToolRecord>;
|
|
40
|
+
isSystemTool(toolId: string): boolean;
|
|
41
|
+
remove(recordId: string): Promise<ActionRecord | ToolRecord>;
|
|
39
42
|
currentDeployment(toolId: string): ToolDeployment | null;
|
|
40
43
|
deployment(deploymentId: string): ToolDeployment | null;
|
|
41
44
|
deploymentHistory(toolId: string): ToolDeployment[];
|
package/dist/toolbox.js
CHANGED
|
@@ -5,7 +5,7 @@ import { apiDriver } from './api-driver.js';
|
|
|
5
5
|
import { applyDefinition } from './apply-definition.js';
|
|
6
6
|
import { ArtifactFiles } from './artifact-files.js';
|
|
7
7
|
import { preserveProjectionTime } from './artifacts.js';
|
|
8
|
-
import {
|
|
8
|
+
import { createCliDriver } from './cli-driver.js';
|
|
9
9
|
import { DeploymentFiles } from './deployment-files.js';
|
|
10
10
|
import { deploymentRevisionId } from './deployments.js';
|
|
11
11
|
import { normalizeDefinition } from './definition.js';
|
|
@@ -14,7 +14,7 @@ import { Store } from './store.js';
|
|
|
14
14
|
import { ToolboxDeployments } from './toolbox-deployments.js';
|
|
15
15
|
import { ToolboxProjection } from './toolbox-projection.js';
|
|
16
16
|
import { ToolboxView } from './toolbox-view.js';
|
|
17
|
-
import { removeAction, updateAction, updateTool, upsertAction } from './updates.js';
|
|
17
|
+
import { linkTool, removeAction, updateAction, updateTool, upsertAction } from './updates.js';
|
|
18
18
|
function defaultDatabase(options) {
|
|
19
19
|
if (options.databaseFile)
|
|
20
20
|
return path.resolve(options.databaseFile);
|
|
@@ -43,7 +43,7 @@ class Toolbox {
|
|
|
43
43
|
const artifacts = new ArtifactFiles(path.dirname(databaseFile));
|
|
44
44
|
const deploymentFiles = new DeploymentFiles(path.resolve(options.deploymentDir || options.stateDir || path.dirname(databaseFile)));
|
|
45
45
|
const priorIndex = artifacts.readIndex();
|
|
46
|
-
for (const driver of [
|
|
46
|
+
for (const driver of [createCliDriver(options.environment), apiDriver, ...(options.drivers || [])])
|
|
47
47
|
this.drivers.set(driver.type, driver);
|
|
48
48
|
const tools = [];
|
|
49
49
|
const actions = [];
|
|
@@ -138,6 +138,11 @@ class Toolbox {
|
|
|
138
138
|
throw new Error(`Unknown tool or action: ${value}`);
|
|
139
139
|
return action.status === status ? action : this.updateAction(value, { status });
|
|
140
140
|
}
|
|
141
|
+
/** Link one user tool to the app it belongs to; `null` unlinks it. */
|
|
142
|
+
async link(toolId, appId) {
|
|
143
|
+
return linkTool(this, toolId, appId);
|
|
144
|
+
}
|
|
145
|
+
isSystemTool(toolId) { return this.system.tools.some((tool) => tool.id === toolId); }
|
|
141
146
|
async remove(recordId) {
|
|
142
147
|
const value = id(recordId, 'record id');
|
|
143
148
|
if (this.system.tools.some((tool) => tool.id === value)
|
package/dist/types.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ interface ToolDefinition {
|
|
|
75
75
|
id: string;
|
|
76
76
|
name: string;
|
|
77
77
|
description?: string;
|
|
78
|
+
/** The app this tool belongs to: the reverse edge of the app manifest's `toolIds`. */
|
|
79
|
+
appId?: string;
|
|
78
80
|
owner?: string;
|
|
79
81
|
origin?: Origin;
|
|
80
82
|
status?: Status;
|
|
@@ -163,6 +165,8 @@ interface ToolboxOptions {
|
|
|
163
165
|
deploymentDir?: string;
|
|
164
166
|
systemTools?: ToolDefinition[];
|
|
165
167
|
drivers?: ToolDriver[];
|
|
168
|
+
/** Host-supplied process environment for one CLI tool call, such as where its linked app listens. */
|
|
169
|
+
environment?: (tool: ToolRecord) => Record<string, string> | Promise<Record<string, string>>;
|
|
166
170
|
onChange?: (catalog: Catalog) => void | Promise<void>;
|
|
167
171
|
/** Outbound local Live operation. Remote application never calls it. */
|
|
168
172
|
onDeployment?: (activation: ToolDeploymentActivation) => void | Promise<void>;
|
|
@@ -176,26 +180,11 @@ interface McpTool {
|
|
|
176
180
|
interface McpOptions extends CallOptions {
|
|
177
181
|
includeManagement?: boolean;
|
|
178
182
|
}
|
|
179
|
-
interface HostMcpToolDescriptor {
|
|
180
|
-
name: string;
|
|
181
|
-
description?: string;
|
|
182
|
-
inputSchema?: JsonSchema;
|
|
183
|
-
}
|
|
184
|
-
interface HostMcpToolDefinitionInput {
|
|
185
|
-
id: string;
|
|
186
|
-
name: string;
|
|
187
|
-
description?: string;
|
|
188
|
-
serverName: string;
|
|
189
|
-
route: string;
|
|
190
|
-
tools: readonly HostMcpToolDescriptor[];
|
|
191
|
-
owner?: string;
|
|
192
|
-
display?: Record<string, unknown>;
|
|
193
|
-
metadata?: Record<string, unknown>;
|
|
194
|
-
}
|
|
195
183
|
interface CliIo {
|
|
196
184
|
stdout?: Writable;
|
|
197
185
|
stderr?: Writable;
|
|
198
|
-
|
|
186
|
+
commands?: import('./commands.js').ToolCommands;
|
|
199
187
|
}
|
|
200
|
-
export type { ActionDefinition, ActionRecord, ActionTarget, ApiSource, ApiTarget, ApplyResult, CallOptions, Catalog, CliIo, CliSource, CliTarget, DriverCall,
|
|
188
|
+
export type { ActionDefinition, ActionRecord, ActionTarget, ApiSource, ApiTarget, ApplyResult, CallOptions, Catalog, CliIo, CliSource, CliTarget, DriverCall, JsonSchema, Loadout, LoadoutInput, McpOptions, McpSource, McpTarget, McpTool, Origin, SecretResolver, Status, ToolDefinition, ToolDriver, ToolboxOptions, ToolRecord, ToolResult, ToolPatch, ActionPatch, ToolQuery, ToolSource, ToolType, };
|
|
201
189
|
export type { ToolDeployment, ToolDeploymentActivation, ToolDeploymentSnapshot, ToolDeploymentSurface, } from './deployment-types.js';
|
|
190
|
+
export type { HostMcpToolDefinitionInput, HostMcpToolDescriptor } from './host-mcp-tool.js';
|
package/dist/updates.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { Toolbox } from './toolbox.js';
|
|
2
|
-
import type { ActionDefinition, ActionPatch, ActionRecord, ApplyResult, ToolPatch } from './types.js';
|
|
2
|
+
import type { ActionDefinition, ActionPatch, ActionRecord, ApplyResult, ToolPatch, ToolRecord } from './types.js';
|
|
3
3
|
type UpdatePort = Pick<Toolbox, 'action' | 'apply' | 'get'>;
|
|
4
|
+
type LinkPort = UpdatePort & Pick<Toolbox, 'update'> & {
|
|
5
|
+
isSystemTool(toolId: string): boolean;
|
|
6
|
+
};
|
|
4
7
|
declare function updateTool(toolbox: UpdatePort, toolId: string, patch: ToolPatch): Promise<ApplyResult>;
|
|
8
|
+
/** Link one user tool to its app; `null` unlinks. The record is unchanged when the link already holds. */
|
|
9
|
+
declare function linkTool(toolbox: LinkPort, toolId: string, appId: string | null): Promise<ToolRecord>;
|
|
5
10
|
declare function upsertAction(toolbox: UpdatePort, toolId: string, definition: ActionDefinition): Promise<ActionRecord>;
|
|
6
11
|
declare function updateAction(toolbox: UpdatePort, actionId: string, patch: ActionPatch): Promise<ActionRecord>;
|
|
7
12
|
declare function removeAction(toolbox: UpdatePort, actionId: string): Promise<ActionRecord>;
|
|
8
|
-
export { removeAction, updateAction, updateTool, upsertAction };
|
|
13
|
+
export { linkTool, removeAction, updateAction, updateTool, upsertAction };
|
package/dist/updates.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { id } from './ids.js';
|
|
1
2
|
function actionDefinition(action) {
|
|
2
3
|
const { id, toolId: _toolId, createdAt: _createdAt, updatedAt: _updatedAt, ...definition } = action;
|
|
3
4
|
return { ...structuredClone(definition), id };
|
|
@@ -25,6 +26,19 @@ async function updateTool(toolbox, toolId, patch) {
|
|
|
25
26
|
actions: patch.actions === undefined ? definition.actions : patch.actions,
|
|
26
27
|
});
|
|
27
28
|
}
|
|
29
|
+
/** Link one user tool to its app; `null` unlinks. The record is unchanged when the link already holds. */
|
|
30
|
+
async function linkTool(toolbox, toolId, appId) {
|
|
31
|
+
const value = id(toolId, 'tool id');
|
|
32
|
+
if (toolbox.isSystemTool(value))
|
|
33
|
+
throw new Error(`System record is immutable: ${value}`);
|
|
34
|
+
const current = toolbox.get(value);
|
|
35
|
+
if (!current)
|
|
36
|
+
throw new Error(`Unknown tool: ${value}`);
|
|
37
|
+
const trimmed = appId?.trim() || null;
|
|
38
|
+
if ((current.tool.appId ?? null) === trimmed)
|
|
39
|
+
return current.tool;
|
|
40
|
+
return (await toolbox.update(value, { appId: trimmed ?? undefined })).tool;
|
|
41
|
+
}
|
|
28
42
|
async function upsertAction(toolbox, toolId, definition) {
|
|
29
43
|
const current = toolbox.get(toolId);
|
|
30
44
|
if (!current)
|
|
@@ -65,4 +79,4 @@ async function removeAction(toolbox, actionId) {
|
|
|
65
79
|
});
|
|
66
80
|
return action;
|
|
67
81
|
}
|
|
68
|
-
export { removeAction, updateAction, updateTool, upsertAction };
|
|
82
|
+
export { linkTool, removeAction, updateAction, updateTool, upsertAction };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/tools",
|
|
3
|
-
"version": "0.1.7-preview.
|
|
3
|
+
"version": "0.1.7-preview.34547098528",
|
|
4
4
|
"description": "Local-first tool definitions, Toolbox registry, and agent execution surfaces.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -87,11 +87,11 @@
|
|
|
87
87
|
"package.json"
|
|
88
88
|
]
|
|
89
89
|
},
|
|
90
|
-
"gitHead": "
|
|
90
|
+
"gitHead": "16bda6108cbbd03c67fa4d6892a7b24b58e343e3",
|
|
91
91
|
"amalgmSource": {
|
|
92
92
|
"repository": "amalgm-inc/amalgm-tools",
|
|
93
93
|
"branch": "preview",
|
|
94
|
-
"commit": "
|
|
95
|
-
"occurrence": "
|
|
94
|
+
"commit": "16bda6108cbbd03c67fa4d6892a7b24b58e343e3",
|
|
95
|
+
"occurrence": "34547098528"
|
|
96
96
|
}
|
|
97
97
|
}
|
package/skills/tools/SKILL.md
CHANGED
|
@@ -144,10 +144,16 @@ amalgm-tools show hello.say
|
|
|
144
144
|
amalgm-tools enable hello.say
|
|
145
145
|
amalgm-tools disable hello.say
|
|
146
146
|
amalgm-tools remove hello
|
|
147
|
+
amalgm-tools link canvas app-3f1c
|
|
148
|
+
amalgm-tools unlink canvas
|
|
147
149
|
amalgm-tools run hello.say --input '{"name":"Ada"}'
|
|
148
150
|
amalgm-tools connections
|
|
149
151
|
```
|
|
150
152
|
|
|
153
|
+
`link` records the app a tool belongs to (`appId`, the reverse edge of the app
|
|
154
|
+
manifest's `toolIds`); sharing the app carries the tool and sharing the tool
|
|
155
|
+
carries the app. The MCP twins are `toolbox_tool_link` and `toolbox_tool_unlink`.
|
|
156
|
+
|
|
151
157
|
Use `--state-dir DIR` to select a Toolbox and `--loadout one,two` to bound
|
|
152
158
|
`list`, `run`, or connection projection.
|
|
153
159
|
|