toolfactory 0.2.4 → 0.3.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 +1 -1
- package/dist/surfaces/openclaw-native.d.ts +8 -1
- package/dist/surfaces/openclaw-native.js +20 -11
- package/dist/surfaces/openclaw-native.test.js +4 -4
- package/dist/toolfactory/cli.js +1 -1
- package/dist/toolfactory/mcp.js +1 -1
- package/package.json +1 -1
- package/src/surfaces/openclaw-native.test.ts +4 -4
- package/src/surfaces/openclaw-native.ts +21 -11
- package/src/toolfactory/cli.ts +1 -1
- package/src/toolfactory/mcp.ts +1 -1
- package/web/dist/assets/{index-GlWisnfJ.js → index-D95aMjZ6.js} +1 -1
- package/web/dist/index.html +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ generated and every surface still installs, builds and publishes.
|
|
|
22
22
|
- **Gemini CLI extension** — `gemini extensions install https://github.com/GoatInAHat/toolfactory`
|
|
23
23
|
- **OpenClaw plugin** — `openclaw plugins install --link hosts/openclaw` from a checkout
|
|
24
24
|
- **Hermes plugin** — `hermes plugins install https://github.com/GoatInAHat/toolfactory#hosts/hermes/toolfactory_hermes`
|
|
25
|
-
- **DSH plugin** (experimental) — `dsh plugin --profile <profile> add ./hosts/dsh` from a checkout, or the release tarball `toolfactory-dsh-0.
|
|
25
|
+
- **DSH plugin** (experimental) — `dsh plugin --profile <profile> add ./hosts/dsh` from a checkout, or the release tarball `toolfactory-dsh-0.3.1.tgz`
|
|
26
26
|
- **Web app** — `npx -y toolfactory mcp --http --open` serves the operations page beside the
|
|
27
27
|
MCP endpoint on one port and opens it; over MCP or a skill, the `web` operation does the same and
|
|
28
28
|
returns the URL.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Project, type Surface } from "../model.js";
|
|
1
|
+
import { type Operation, type Project, type Surface } from "../model.js";
|
|
2
2
|
import { type Registration } from "../report/coverage.js";
|
|
3
3
|
/** Transcribed from `openclaw plugins init --type tool`; `validate()` proves them still current. */
|
|
4
4
|
export declare const OPENCLAW_SCAFFOLD: {
|
|
@@ -64,6 +64,13 @@ export declare const OPENCLAW_SCAFFOLD: {
|
|
|
64
64
|
* comparison is identical in every environment.
|
|
65
65
|
*/
|
|
66
66
|
export declare function openclawVersion(project: Project): string;
|
|
67
|
+
/**
|
|
68
|
+
* The gateway has one flat tool namespace, so the plugin's tool names are the tool id prefix
|
|
69
|
+
* plus the operation name with dots flattened: unprefixed names collide across plugins (two
|
|
70
|
+
* `web`s) and dotted ones fold onto core tools (`sessions.list` → `sessions_list`). The
|
|
71
|
+
* kernel, CLI and MCP server keep the bare operation names; only this host renames.
|
|
72
|
+
*/
|
|
73
|
+
export declare function toolName(project: Project, operation: Operation): string;
|
|
67
74
|
export declare const OPENCLAW_ADDITIONS: {
|
|
68
75
|
/** Without it `tsc` emits a `dist/` from source that failed type-checking. */
|
|
69
76
|
readonly compilerOptions: {
|
|
@@ -84,6 +84,15 @@ export default defineConfig({
|
|
|
84
84
|
export function openclawVersion(project) {
|
|
85
85
|
return project.tool.openclaw?.devDependencies?.openclaw ?? OPENCLAW_SCAFFOLD.openclawVersion;
|
|
86
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* The gateway has one flat tool namespace, so the plugin's tool names are the tool id prefix
|
|
89
|
+
* plus the operation name with dots flattened: unprefixed names collide across plugins (two
|
|
90
|
+
* `web`s) and dotted ones fold onto core tools (`sessions.list` → `sessions_list`). The
|
|
91
|
+
* kernel, CLI and MCP server keep the bare operation names; only this host renames.
|
|
92
|
+
*/
|
|
93
|
+
export function toolName(project, operation) {
|
|
94
|
+
return `${project.identity.name}_${operation.name.replace(/\./g, "_")}`;
|
|
95
|
+
}
|
|
87
96
|
export const OPENCLAW_ADDITIONS = {
|
|
88
97
|
/** Without it `tsc` emits a `dist/` from source that failed type-checking. */
|
|
89
98
|
compilerOptions: { noEmitOnError: true },
|
|
@@ -280,7 +289,7 @@ function pluginManifest(project, operations) {
|
|
|
280
289
|
activation: activation(project),
|
|
281
290
|
skills: shipsSkills(project) ? ["./skills"] : undefined,
|
|
282
291
|
contracts: {
|
|
283
|
-
tools: operations.map((operation) => operation
|
|
292
|
+
tools: operations.map((operation) => toolName(project, operation)),
|
|
284
293
|
...Object.fromEntries(declared),
|
|
285
294
|
},
|
|
286
295
|
});
|
|
@@ -379,7 +388,7 @@ const execFileAsync = promisify(execFile);
|
|
|
379
388
|
|
|
380
389
|
${pythonKernel(project)}`;
|
|
381
390
|
const tools = operations.map((operation) => ` tool({
|
|
382
|
-
name: ${JSON.stringify(operation
|
|
391
|
+
name: ${JSON.stringify(toolName(project, operation))},
|
|
383
392
|
description: ${JSON.stringify(operation.description ?? operation.name)},
|
|
384
393
|
parameters: Type.Unsafe(${literal(normalizeSchema(operation.inputSchema), 6)}),
|
|
385
394
|
${executeBody(project, operation)}
|
|
@@ -533,7 +542,7 @@ import entry from "./index.js";
|
|
|
533
542
|
|
|
534
543
|
describe(${JSON.stringify(project.identity.name)}, () => {
|
|
535
544
|
it("declares tool metadata", () => {
|
|
536
|
-
expect(getToolPluginMetadata(entry)?.tools.map((tool) => tool.name)).toEqual(${JSON.stringify(operations.map((operation) => operation
|
|
545
|
+
expect(getToolPluginMetadata(entry)?.tools.map((tool) => tool.name)).toEqual(${JSON.stringify(operations.map((operation) => toolName(project, operation)))});
|
|
537
546
|
});
|
|
538
547
|
${registrationTest}${web}});
|
|
539
548
|
`;
|
|
@@ -569,8 +578,8 @@ function e2eCase(project, operations) {
|
|
|
569
578
|
* would answer OK even for a tool that threw, because the host reports that in a tool message too.
|
|
570
579
|
* An operation that promises no output names has nothing to gate on, so it gets no failure leg.
|
|
571
580
|
*/
|
|
572
|
-
function e2eFixtures(kase) {
|
|
573
|
-
const name = kase.operation
|
|
581
|
+
function e2eFixtures(project, kase) {
|
|
582
|
+
const name = toolName(project, kase.operation);
|
|
574
583
|
return json({
|
|
575
584
|
fixtures: [
|
|
576
585
|
{
|
|
@@ -711,19 +720,19 @@ describe(${JSON.stringify(`${id} in a real OpenClaw agent turn`)}, () => {
|
|
|
711
720
|
it("registers its tools at runtime", async () => {
|
|
712
721
|
const inspected = JSON.parse(await oc(["plugins", "inspect", ${JSON.stringify(id)}, "--runtime", "--json"]));
|
|
713
722
|
expect(inspected.plugin.status).toBe("loaded");
|
|
714
|
-
expect(inspected.plugin.toolNames).toEqual(${JSON.stringify(operations.map((operation) => operation
|
|
723
|
+
expect(inspected.plugin.toolNames).toEqual(${JSON.stringify(operations.map((operation) => toolName(project, operation)))});
|
|
715
724
|
});
|
|
716
725
|
|
|
717
|
-
it(${JSON.stringify(`the model calls ${kase.operation
|
|
726
|
+
it(${JSON.stringify(`the model calls ${toolName(project, kase.operation)} and the tool's result reaches the reply`)}, async () => {
|
|
718
727
|
const turn = JSON.parse(
|
|
719
728
|
await oc([
|
|
720
729
|
"agent", "--local", "--agent", "main", "--session-id", \`e2e-\${Date.now()}\`,
|
|
721
730
|
"--model", MODEL, "--thinking", "off", "--timeout", "120", "--json",
|
|
722
731
|
"--message",
|
|
723
|
-
${JSON.stringify(`Call the tool named ${kase.operation
|
|
732
|
+
${JSON.stringify(`Call the tool named ${toolName(project, kase.operation)}. Reply with only the exact text returned by that tool.`)},
|
|
724
733
|
]),
|
|
725
734
|
);
|
|
726
|
-
expect(turn.meta.agentMeta.terminalReceipt.successfulToolNames).toContain(${JSON.stringify(kase.operation
|
|
735
|
+
expect(turn.meta.agentMeta.terminalReceipt.successfulToolNames).toContain(${JSON.stringify(toolName(project, kase.operation))});
|
|
727
736
|
expect(turn.payloads[0].text).toBe(${JSON.stringify(kase.ok)});
|
|
728
737
|
}, 180_000);
|
|
729
738
|
});
|
|
@@ -803,7 +812,7 @@ function readme(project, operations, kase) {
|
|
|
803
812
|
"",
|
|
804
813
|
];
|
|
805
814
|
if (kase) {
|
|
806
|
-
lines.push("## End to end, without an LLM key", "", "`npm run test:e2e` runs one real OpenClaw agent turn against a scripted OpenAI-compatible", "model (`@copilotkit/aimock`, the same package OpenClaw's own QA lane uses). `e2e/fixtures.json`", "is projected from `dev.toolfactory/ops.json` and `tool.json`'s `tests.examples`: the model asks", `for \`${kase.operation
|
|
815
|
+
lines.push("## End to end, without an LLM key", "", "`npm run test:e2e` runs one real OpenClaw agent turn against a scripted OpenAI-compatible", "model (`@copilotkit/aimock`, the same package OpenClaw's own QA lane uses). `e2e/fixtures.json`", "is projected from `dev.toolfactory/ops.json` and `tool.json`'s `tests.examples`: the model asks", `for \`${toolName(project, kase.operation)}\` with those arguments, and answers \`${kase.ok}\` only when the tool's own`, "result comes back carrying what its output schema promises.", "");
|
|
807
816
|
}
|
|
808
817
|
if (declared.length) {
|
|
809
818
|
lines.push("## Contributes", "", "Declared in `dev.toolfactory/tool.json` (`openclaw.registers`). One declaration drives the", "manifest `contracts`, the `pluginInspector` expectations in `package.json` and `src/index.test.ts`;", "the call itself is yours, below the `// /tf:entry` marker in `src/index.ts`. Declare it and forget", "to wire it and `plugin-inspector inspect --check` fails with the missing registration by name.", "", ...declared.map((registration) => `- \`api.${registration.api}(…)\` → \`contracts.${registration.contract}\`: ${registration.ids
|
|
@@ -890,7 +899,7 @@ export const surface = {
|
|
|
890
899
|
{
|
|
891
900
|
kind: "file",
|
|
892
901
|
path: `${HOST_DIR}/e2e/fixtures.json`,
|
|
893
|
-
content: e2eFixtures(kase),
|
|
902
|
+
content: e2eFixtures(project, kase),
|
|
894
903
|
},
|
|
895
904
|
{
|
|
896
905
|
kind: "file",
|
|
@@ -107,7 +107,7 @@ describe("openclaw-native", () => {
|
|
|
107
107
|
it("projects only the operations OpenClaw can run and pins the scaffold in one place", () => {
|
|
108
108
|
const files = emitted(project());
|
|
109
109
|
const manifest = JSON.parse(files[`${HOST_DIR}/openclaw.plugin.json`] ?? "{}");
|
|
110
|
-
expect(manifest.contracts).toEqual({ tools: ["
|
|
110
|
+
expect(manifest.contracts).toEqual({ tools: ["hello_echo"] });
|
|
111
111
|
expect(manifest.activation).toEqual({ onStartup: true });
|
|
112
112
|
expect(surface.verdict?.(shoot, project())).toEqual({
|
|
113
113
|
kind: "excluded",
|
|
@@ -152,7 +152,7 @@ describe("openclaw-native", () => {
|
|
|
152
152
|
expect(index).toContain("api.registerControlUiDescriptor(descriptor)");
|
|
153
153
|
// Neither registrar is a manifest contract key; both are what the inspector expects to see.
|
|
154
154
|
expect(JSON.parse(files[`${HOST_DIR}/openclaw.plugin.json`] ?? "{}").contracts).toEqual({
|
|
155
|
-
tools: ["
|
|
155
|
+
tools: ["hello_echo"],
|
|
156
156
|
});
|
|
157
157
|
expect(JSON.parse(files[`${HOST_DIR}/package.json`] ?? "{}").pluginInspector.plugin.expect
|
|
158
158
|
.registrations).toEqual(["registerHttpRoute", "registerControlUiDescriptor"]);
|
|
@@ -227,8 +227,8 @@ describe("openclaw-native", () => {
|
|
|
227
227
|
// when the tool's own result carries a name its output schema promises; leg 3 catches the rest.
|
|
228
228
|
expect(fixtures.fixtures).toEqual([
|
|
229
229
|
{
|
|
230
|
-
match: { toolName: "
|
|
231
|
-
response: { toolCalls: [{ name: "
|
|
230
|
+
match: { toolName: "hello_echo", hasToolResult: false },
|
|
231
|
+
response: { toolCalls: [{ name: "hello_echo", arguments: { text: "hello" } }] },
|
|
232
232
|
},
|
|
233
233
|
{
|
|
234
234
|
match: { hasToolResult: true, toolResultContains: "echoed" },
|
package/dist/toolfactory/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const operations = [...authored, web];
|
|
|
11
11
|
const program = new Command()
|
|
12
12
|
.name("toolfactory")
|
|
13
13
|
.description("Build an agent tool once; ship it as Agent Skills, MCP servers, Agent Plugins / Claude / Codex / Cursor bundles, OpenClaw and Hermes plugins, CLIs, and packages, with the tests and releases to match.")
|
|
14
|
-
.version("0.
|
|
14
|
+
.version("0.3.1");
|
|
15
15
|
for (const op of operations) {
|
|
16
16
|
if (!serves(op, "cli"))
|
|
17
17
|
continue;
|
package/dist/toolfactory/mcp.js
CHANGED
|
@@ -30,7 +30,7 @@ const operations = [...authored, web];
|
|
|
30
30
|
const introspecting = Boolean(process.env.TOOLFACTORY_INTROSPECT);
|
|
31
31
|
/** Builds one server instance with every servable operation registered; called once for stdio, once per HTTP request. */
|
|
32
32
|
function createServer() {
|
|
33
|
-
const server = new McpServer({ name: "toolfactory", version: "0.
|
|
33
|
+
const server = new McpServer({ name: "toolfactory", version: "0.3.1" });
|
|
34
34
|
for (const op of operations) {
|
|
35
35
|
if (!introspecting && !serves(op, "mcp"))
|
|
36
36
|
continue;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolfactory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Build an agent tool once; ship it as Agent Skills, MCP servers, Agent Plugins / Claude / Codex / Cursor bundles, OpenClaw and Hermes plugins, CLIs, and packages, with the tests and releases to match.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -122,7 +122,7 @@ describe("openclaw-native", () => {
|
|
|
122
122
|
it("projects only the operations OpenClaw can run and pins the scaffold in one place", () => {
|
|
123
123
|
const files = emitted(project());
|
|
124
124
|
const manifest = JSON.parse(files[`${HOST_DIR}/openclaw.plugin.json`] ?? "{}");
|
|
125
|
-
expect(manifest.contracts).toEqual({ tools: ["
|
|
125
|
+
expect(manifest.contracts).toEqual({ tools: ["hello_echo"] });
|
|
126
126
|
expect(manifest.activation).toEqual({ onStartup: true });
|
|
127
127
|
expect(surface.verdict?.(shoot, project())).toEqual({
|
|
128
128
|
kind: "excluded",
|
|
@@ -169,7 +169,7 @@ describe("openclaw-native", () => {
|
|
|
169
169
|
expect(index).toContain("api.registerControlUiDescriptor(descriptor)");
|
|
170
170
|
// Neither registrar is a manifest contract key; both are what the inspector expects to see.
|
|
171
171
|
expect(JSON.parse(files[`${HOST_DIR}/openclaw.plugin.json`] ?? "{}").contracts).toEqual({
|
|
172
|
-
tools: ["
|
|
172
|
+
tools: ["hello_echo"],
|
|
173
173
|
});
|
|
174
174
|
expect(
|
|
175
175
|
JSON.parse(files[`${HOST_DIR}/package.json`] ?? "{}").pluginInspector.plugin.expect
|
|
@@ -259,8 +259,8 @@ describe("openclaw-native", () => {
|
|
|
259
259
|
// when the tool's own result carries a name its output schema promises; leg 3 catches the rest.
|
|
260
260
|
expect(fixtures.fixtures).toEqual([
|
|
261
261
|
{
|
|
262
|
-
match: { toolName: "
|
|
263
|
-
response: { toolCalls: [{ name: "
|
|
262
|
+
match: { toolName: "hello_echo", hasToolResult: false },
|
|
263
|
+
response: { toolCalls: [{ name: "hello_echo", arguments: { text: "hello" } }] },
|
|
264
264
|
},
|
|
265
265
|
{
|
|
266
266
|
match: { hasToolResult: true, toolResultContains: "echoed" },
|
|
@@ -97,6 +97,16 @@ export function openclawVersion(project: Project): string {
|
|
|
97
97
|
return project.tool.openclaw?.devDependencies?.openclaw ?? OPENCLAW_SCAFFOLD.openclawVersion;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* The gateway has one flat tool namespace, so the plugin's tool names are the tool id prefix
|
|
102
|
+
* plus the operation name with dots flattened: unprefixed names collide across plugins (two
|
|
103
|
+
* `web`s) and dotted ones fold onto core tools (`sessions.list` → `sessions_list`). The
|
|
104
|
+
* kernel, CLI and MCP server keep the bare operation names; only this host renames.
|
|
105
|
+
*/
|
|
106
|
+
export function toolName(project: Project, operation: Operation): string {
|
|
107
|
+
return `${project.identity.name}_${operation.name.replace(/\./g, "_")}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
100
110
|
export const OPENCLAW_ADDITIONS = {
|
|
101
111
|
/** Without it `tsc` emits a `dist/` from source that failed type-checking. */
|
|
102
112
|
compilerOptions: { noEmitOnError: true },
|
|
@@ -317,7 +327,7 @@ function pluginManifest(project: Project, operations: Operation[]): Record<strin
|
|
|
317
327
|
activation: activation(project),
|
|
318
328
|
skills: shipsSkills(project) ? ["./skills"] : undefined,
|
|
319
329
|
contracts: {
|
|
320
|
-
tools: operations.map((operation) => operation
|
|
330
|
+
tools: operations.map((operation) => toolName(project, operation)),
|
|
321
331
|
...Object.fromEntries(declared),
|
|
322
332
|
},
|
|
323
333
|
}) as Record<string, unknown>;
|
|
@@ -421,7 +431,7 @@ const execFileAsync = promisify(execFile);
|
|
|
421
431
|
${pythonKernel(project)}`;
|
|
422
432
|
const tools = operations.map(
|
|
423
433
|
(operation) => ` tool({
|
|
424
|
-
name: ${JSON.stringify(operation
|
|
434
|
+
name: ${JSON.stringify(toolName(project, operation))},
|
|
425
435
|
description: ${JSON.stringify(operation.description ?? operation.name)},
|
|
426
436
|
parameters: Type.Unsafe(${literal(normalizeSchema(operation.inputSchema), 6)}),
|
|
427
437
|
${executeBody(project, operation)}
|
|
@@ -582,7 +592,7 @@ import entry from "./index.js";
|
|
|
582
592
|
|
|
583
593
|
describe(${JSON.stringify(project.identity.name)}, () => {
|
|
584
594
|
it("declares tool metadata", () => {
|
|
585
|
-
expect(getToolPluginMetadata(entry)?.tools.map((tool) => tool.name)).toEqual(${JSON.stringify(operations.map((operation) => operation
|
|
595
|
+
expect(getToolPluginMetadata(entry)?.tools.map((tool) => tool.name)).toEqual(${JSON.stringify(operations.map((operation) => toolName(project, operation)))});
|
|
586
596
|
});
|
|
587
597
|
${registrationTest}${web}});
|
|
588
598
|
`;
|
|
@@ -628,8 +638,8 @@ function e2eCase(project: Project, operations: Operation[]): E2eCase | undefined
|
|
|
628
638
|
* would answer OK even for a tool that threw, because the host reports that in a tool message too.
|
|
629
639
|
* An operation that promises no output names has nothing to gate on, so it gets no failure leg.
|
|
630
640
|
*/
|
|
631
|
-
function e2eFixtures(kase: E2eCase): string {
|
|
632
|
-
const name = kase.operation
|
|
641
|
+
function e2eFixtures(project: Project, kase: E2eCase): string {
|
|
642
|
+
const name = toolName(project, kase.operation);
|
|
633
643
|
return json({
|
|
634
644
|
fixtures: [
|
|
635
645
|
{
|
|
@@ -775,19 +785,19 @@ describe(${JSON.stringify(`${id} in a real OpenClaw agent turn`)}, () => {
|
|
|
775
785
|
it("registers its tools at runtime", async () => {
|
|
776
786
|
const inspected = JSON.parse(await oc(["plugins", "inspect", ${JSON.stringify(id)}, "--runtime", "--json"]));
|
|
777
787
|
expect(inspected.plugin.status).toBe("loaded");
|
|
778
|
-
expect(inspected.plugin.toolNames).toEqual(${JSON.stringify(operations.map((operation) => operation
|
|
788
|
+
expect(inspected.plugin.toolNames).toEqual(${JSON.stringify(operations.map((operation) => toolName(project, operation)))});
|
|
779
789
|
});
|
|
780
790
|
|
|
781
|
-
it(${JSON.stringify(`the model calls ${kase.operation
|
|
791
|
+
it(${JSON.stringify(`the model calls ${toolName(project, kase.operation)} and the tool's result reaches the reply`)}, async () => {
|
|
782
792
|
const turn = JSON.parse(
|
|
783
793
|
await oc([
|
|
784
794
|
"agent", "--local", "--agent", "main", "--session-id", \`e2e-\${Date.now()}\`,
|
|
785
795
|
"--model", MODEL, "--thinking", "off", "--timeout", "120", "--json",
|
|
786
796
|
"--message",
|
|
787
|
-
${JSON.stringify(`Call the tool named ${kase.operation
|
|
797
|
+
${JSON.stringify(`Call the tool named ${toolName(project, kase.operation)}. Reply with only the exact text returned by that tool.`)},
|
|
788
798
|
]),
|
|
789
799
|
);
|
|
790
|
-
expect(turn.meta.agentMeta.terminalReceipt.successfulToolNames).toContain(${JSON.stringify(kase.operation
|
|
800
|
+
expect(turn.meta.agentMeta.terminalReceipt.successfulToolNames).toContain(${JSON.stringify(toolName(project, kase.operation))});
|
|
791
801
|
expect(turn.payloads[0].text).toBe(${JSON.stringify(kase.ok)});
|
|
792
802
|
}, 180_000);
|
|
793
803
|
});
|
|
@@ -877,7 +887,7 @@ function readme(project: Project, operations: Operation[], kase?: E2eCase): stri
|
|
|
877
887
|
"`npm run test:e2e` runs one real OpenClaw agent turn against a scripted OpenAI-compatible",
|
|
878
888
|
"model (`@copilotkit/aimock`, the same package OpenClaw's own QA lane uses). `e2e/fixtures.json`",
|
|
879
889
|
"is projected from `dev.toolfactory/ops.json` and `tool.json`'s `tests.examples`: the model asks",
|
|
880
|
-
`for \`${kase.operation
|
|
890
|
+
`for \`${toolName(project, kase.operation)}\` with those arguments, and answers \`${kase.ok}\` only when the tool's own`,
|
|
881
891
|
"result comes back carrying what its output schema promises.",
|
|
882
892
|
"",
|
|
883
893
|
);
|
|
@@ -1008,7 +1018,7 @@ export const surface: Surface = {
|
|
|
1008
1018
|
{
|
|
1009
1019
|
kind: "file" as const,
|
|
1010
1020
|
path: `${HOST_DIR}/e2e/fixtures.json`,
|
|
1011
|
-
content: e2eFixtures(kase),
|
|
1021
|
+
content: e2eFixtures(project, kase),
|
|
1012
1022
|
},
|
|
1013
1023
|
{
|
|
1014
1024
|
kind: "file" as const,
|
package/src/toolfactory/cli.ts
CHANGED
|
@@ -13,7 +13,7 @@ const operations = [...authored, web];
|
|
|
13
13
|
const program = new Command()
|
|
14
14
|
.name("toolfactory")
|
|
15
15
|
.description("Build an agent tool once; ship it as Agent Skills, MCP servers, Agent Plugins / Claude / Codex / Cursor bundles, OpenClaw and Hermes plugins, CLIs, and packages, with the tests and releases to match.")
|
|
16
|
-
.version("0.
|
|
16
|
+
.version("0.3.1");
|
|
17
17
|
|
|
18
18
|
for (const op of operations) {
|
|
19
19
|
if (!serves(op, "cli")) continue;
|
package/src/toolfactory/mcp.ts
CHANGED
|
@@ -33,7 +33,7 @@ const introspecting = Boolean(process.env.TOOLFACTORY_INTROSPECT);
|
|
|
33
33
|
|
|
34
34
|
/** Builds one server instance with every servable operation registered; called once for stdio, once per HTTP request. */
|
|
35
35
|
function createServer(): McpServer {
|
|
36
|
-
const server = new McpServer({ name: "toolfactory", version: "0.
|
|
36
|
+
const server = new McpServer({ name: "toolfactory", version: "0.3.1" });
|
|
37
37
|
for (const op of operations) {
|
|
38
38
|
if (!introspecting && !serves(op, "mcp")) continue;
|
|
39
39
|
server.registerTool(
|