@amalgm/tools 0.1.0 → 0.1.1
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 +16 -0
- package/dist/http.d.ts +31 -0
- package/dist/http.js +115 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -49,6 +49,22 @@ amalgm-tools connections
|
|
|
49
49
|
`amalgm-tools-mcp` serves Toolbox management and enabled action tools over
|
|
50
50
|
newline-delimited MCP stdio.
|
|
51
51
|
|
|
52
|
+
## HTTP
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
import { Toolbox } from '@amalgm/tools';
|
|
56
|
+
import { createToolboxHttpServer } from '@amalgm/tools/http';
|
|
57
|
+
|
|
58
|
+
const toolbox = new Toolbox({ stateDir: './.tools' });
|
|
59
|
+
const server = createToolboxHttpServer({ toolbox });
|
|
60
|
+
await server.listen(8083);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
The read adapter serves the Engine-compatible `GET /toolbox`,
|
|
64
|
+
`GET /toolbox/tools?id=...`, and `GET /toolbox/actions?id=...` routes over
|
|
65
|
+
the same live Toolbox used by CLI and MCP consumers. Mutation routes return
|
|
66
|
+
405 until their legacy record fields can be represented without data loss.
|
|
67
|
+
|
|
52
68
|
## Tool types
|
|
53
69
|
|
|
54
70
|
- `cli`: executed directly with `spawn`; a shell is never involved.
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Toolbox } from './toolbox.js';
|
|
2
|
+
import type { Catalog, ToolboxOptions } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Toolbox read API.
|
|
5
|
+
*
|
|
6
|
+
* Route contract ported from Engine
|
|
7
|
+
* `runtime/scripts/amalgm-mcp/server/routes/toolbox.js` and
|
|
8
|
+
* `runtime/scripts/amalgm-mcp/toolbox/rest.js`:
|
|
9
|
+
*
|
|
10
|
+
* - `GET /toolbox` returns the whole catalog;
|
|
11
|
+
* - `GET /toolbox/tools?id=...` returns one tool and, by default, its actions;
|
|
12
|
+
* - `GET /toolbox/actions?id=...` returns one action;
|
|
13
|
+
* - domain errors are JSON with status 400; unknown routes are 404.
|
|
14
|
+
*
|
|
15
|
+
* Mutation routes remain deliberately absent until their engine record fields
|
|
16
|
+
* are represented losslessly by the SDK. Returning 405 is honest; translating
|
|
17
|
+
* them approximately would turn a visible parity gap into data loss.
|
|
18
|
+
*/
|
|
19
|
+
type Json = Record<string, unknown>;
|
|
20
|
+
interface ToolboxHttpServer {
|
|
21
|
+
listen(port?: number, host?: string): Promise<{
|
|
22
|
+
port: number;
|
|
23
|
+
}>;
|
|
24
|
+
close(): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
declare function catalogDocument(catalog: Catalog): Json;
|
|
27
|
+
declare function createToolboxHttpServer(options?: ToolboxOptions & {
|
|
28
|
+
toolbox?: Toolbox;
|
|
29
|
+
}): ToolboxHttpServer;
|
|
30
|
+
export { catalogDocument, createToolboxHttpServer };
|
|
31
|
+
export type { ToolboxHttpServer };
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { toolboxIndexDocument } from './artifacts.js';
|
|
3
|
+
import { Toolbox } from './toolbox.js';
|
|
4
|
+
function sourceWithoutType(source) {
|
|
5
|
+
const { type: _type, ...value } = source;
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
function toolDocument(tool) {
|
|
9
|
+
return {
|
|
10
|
+
...tool,
|
|
11
|
+
type: tool.source.type,
|
|
12
|
+
source: sourceWithoutType(tool.source),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function actionDocument(action) {
|
|
16
|
+
return { ...action };
|
|
17
|
+
}
|
|
18
|
+
function catalogDocument(catalog) {
|
|
19
|
+
return toolboxIndexDocument(Object.fromEntries(catalog.tools.map((tool) => [tool.id, toolDocument(tool)])), Object.fromEntries(catalog.actions.map((action) => [action.id, actionDocument(action)])));
|
|
20
|
+
}
|
|
21
|
+
async function readBody(req) {
|
|
22
|
+
const chunks = [];
|
|
23
|
+
for await (const chunk of req)
|
|
24
|
+
chunks.push(Buffer.from(chunk));
|
|
25
|
+
if (chunks.length === 0)
|
|
26
|
+
return {};
|
|
27
|
+
return JSON.parse(Buffer.concat(chunks).toString('utf8'));
|
|
28
|
+
}
|
|
29
|
+
function createToolboxHttpServer(options = {}) {
|
|
30
|
+
const toolbox = options.toolbox ?? new Toolbox(options);
|
|
31
|
+
const ownsToolbox = options.toolbox === undefined;
|
|
32
|
+
const server = createServer((req, res) => {
|
|
33
|
+
void (async () => {
|
|
34
|
+
const method = req.method ?? 'GET';
|
|
35
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
36
|
+
const send = (status, body) => {
|
|
37
|
+
res.writeHead(status, { 'content-type': 'application/json' });
|
|
38
|
+
res.end(JSON.stringify(body));
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
if (method !== 'GET') {
|
|
42
|
+
if (url.pathname.startsWith('/toolbox')) {
|
|
43
|
+
await readBody(req);
|
|
44
|
+
send(405, { error: 'method not allowed' });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
send(404, { error: 'not found' });
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (url.pathname === '/toolbox') {
|
|
51
|
+
send(200, catalogDocument(toolbox.catalog()));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (url.pathname === '/toolbox/tools') {
|
|
55
|
+
const id = url.searchParams.get('id') || url.searchParams.get('toolId') || '';
|
|
56
|
+
if (!id)
|
|
57
|
+
throw new Error('id is required');
|
|
58
|
+
const found = toolbox.get(id);
|
|
59
|
+
if (!found)
|
|
60
|
+
throw new Error(`Unknown tool: ${id}`);
|
|
61
|
+
const includeActions = url.searchParams.get('include_actions') !== 'false'
|
|
62
|
+
&& url.searchParams.get('includeActions') !== 'false';
|
|
63
|
+
send(200, {
|
|
64
|
+
tool: toolDocument(found.tool),
|
|
65
|
+
...(includeActions
|
|
66
|
+
? { actions: found.actions.map(actionDocument) }
|
|
67
|
+
: {}),
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (url.pathname === '/toolbox/actions') {
|
|
72
|
+
const id = url.searchParams.get('id') || url.searchParams.get('actionId') || '';
|
|
73
|
+
if (!id)
|
|
74
|
+
throw new Error('id is required');
|
|
75
|
+
const action = toolbox.action(id);
|
|
76
|
+
if (!action)
|
|
77
|
+
throw new Error(`Unknown tool action: ${id}`);
|
|
78
|
+
send(200, { action: actionDocument(action) });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
send(404, { error: 'not found' });
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
send(400, { error: error instanceof Error ? error.message : 'Failed to read Toolbox' });
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
});
|
|
88
|
+
return {
|
|
89
|
+
listen(port = 0, host = '127.0.0.1') {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
server.once('error', reject);
|
|
92
|
+
server.listen(port, host, () => {
|
|
93
|
+
server.off('error', reject);
|
|
94
|
+
const address = server.address();
|
|
95
|
+
if (!address || typeof address === 'string')
|
|
96
|
+
throw new Error('Toolbox HTTP server has no port');
|
|
97
|
+
resolve({ port: address.port });
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
close() {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
server.close((error) => {
|
|
104
|
+
if (ownsToolbox)
|
|
105
|
+
toolbox.close();
|
|
106
|
+
if (error)
|
|
107
|
+
reject(error);
|
|
108
|
+
else
|
|
109
|
+
resolve();
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export { catalogDocument, createToolboxHttpServer };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ 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
4
|
export { defineTool, normalizeDefinition } from './definition.js';
|
|
5
|
+
export { catalogDocument, createToolboxHttpServer } from './http.js';
|
|
6
|
+
export type { ToolboxHttpServer } from './http.js';
|
|
5
7
|
export { actionId, actionName, id, mcpName } from './ids.js';
|
|
6
8
|
export { actionTools, callMcpTool, createMcpTools, findMcpTool, managementTools, } from './mcp.js';
|
|
7
9
|
export { createMcpServer } from './mcp-server.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
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 { defineTool, normalizeDefinition } from './definition.js';
|
|
4
|
+
export { catalogDocument, createToolboxHttpServer } from './http.js';
|
|
4
5
|
export { actionId, actionName, id, mcpName } from './ids.js';
|
|
5
6
|
export { actionTools, callMcpTool, createMcpTools, findMcpTool, managementTools, } from './mcp.js';
|
|
6
7
|
export { createMcpServer } from './mcp-server.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amalgm/tools",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Local-first tool definitions, Toolbox registry, and agent execution surfaces.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"private": false,
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"./mcp": {
|
|
23
23
|
"types": "./dist/mcp.d.ts",
|
|
24
24
|
"default": "./dist/mcp.js"
|
|
25
|
+
},
|
|
26
|
+
"./http": {
|
|
27
|
+
"types": "./dist/http.d.ts",
|
|
28
|
+
"default": "./dist/http.js"
|
|
25
29
|
}
|
|
26
30
|
},
|
|
27
31
|
"bin": {
|