toolcraft 0.0.3 → 0.0.5

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 CHANGED
@@ -285,8 +285,11 @@ defineGroup({
285
285
 
286
286
  - `tools` filters by upstream tool name.
287
287
  - `rename` remaps to dotted toolcraft paths; missing intermediate groups are created.
288
- - Discovery is cached at `<projectRoot>/.toolcraft/mcp/<group>.json` (project root = nearest ancestor with `package.json`).
288
+ - Proxy discovery is eager for `runCLI` and `runMCP`: they resolve every `defineGroup({ mcp })` proxy in the root tree before routing, command execution, or CLI help rendering. SDK proxies resolve when the deferred SDK is awaited or first used.
289
+ - On a first run without cached schemas, even `my-cli --help` or `my-cli some-group --help` may connect to every configured upstream MCP server.
290
+ - Discovery is cached at `<projectRoot>/.toolcraft/mcp/<group>.json` (project root = nearest ancestor with `package.json`), so successful discovery avoids repeated upstream connects unless refreshed.
289
291
  - `TOOLCRAFT_MCP_REFRESH=1` refreshes all proxies; `TOOLCRAFT_MCP_REFRESH=github,linear` refreshes specific ones.
292
+ - Selective or lazy discovery for only the requested command path is not currently supported. CLIs that wrap many MCP servers should expect first-run help to touch all of them.
290
293
  - Discovery output goes to stderr only.
291
294
 
292
295
  ## Human-in-loop approvals
package/dist/cli.js CHANGED
@@ -7,6 +7,7 @@ import { mergeApprovalsGroup } from "./human-in-loop/approvals-commands.js";
7
7
  import { invokeWithHumanInLoop } from "./human-in-loop/index.js";
8
8
  import { resolveMcpProxies } from "./mcp-proxy.js";
9
9
  import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
10
+ import { findEntrypointPackageMetadata } from "./package-metadata.js";
10
11
  import { renderResult } from "./renderer.js";
11
12
  const RESERVED_SERVICE_NAMES = new Set(["params", "secrets", "fetch", "fs", "env", "progress"]);
12
13
  const NULL_OPTION_VALUE = Symbol("toolcraft.cli.null");
@@ -1990,6 +1991,7 @@ export async function runCLI(roots, options = {}) {
1990
1991
  const casing = options.casing ?? "kebab";
1991
1992
  const services = (options.services ?? {});
1992
1993
  const runtimeOptions = options.humanInLoop ?? {};
1994
+ const version = options.version ?? findEntrypointPackageMetadata(process.argv[1])?.version;
1993
1995
  const servicesWithBuiltIns = {
1994
1996
  ...services,
1995
1997
  runtimeOptions,
@@ -2000,7 +2002,7 @@ export async function runCLI(roots, options = {}) {
2000
2002
  };
2001
2003
  validateServices(services);
2002
2004
  if (hasHelpFlag(process.argv)) {
2003
- await renderGeneratedHelp(root, process.argv, options);
2005
+ await renderGeneratedHelp(root, process.argv, { ...options, version });
2004
2006
  return;
2005
2007
  }
2006
2008
  const program = new CommanderCommand();
@@ -2009,8 +2011,8 @@ export async function runCLI(roots, options = {}) {
2009
2011
  program.showHelpAfterError();
2010
2012
  program.addHelpCommand(false);
2011
2013
  addGlobalOptions(program);
2012
- if (options.version !== undefined) {
2013
- program.version(options.version, "--version");
2014
+ if (version !== undefined) {
2015
+ program.version(version, "--version");
2014
2016
  }
2015
2017
  let lastActionCommand;
2016
2018
  const execute = async (state) => {
package/dist/index.d.ts CHANGED
@@ -179,5 +179,7 @@ export declare function defineGroup<TServices extends object = EmptyServices, TN
179
179
  export declare function getCommandSourcePath(command: Command<any, any, any, any>): string | undefined;
180
180
  export { S, toJsonSchema } from "toolcraft-schema";
181
181
  export { ApprovalDeclinedError, UserError };
182
+ export { findPackageMetadata, packageMetadata } from "./package-metadata.js";
183
+ export type { PackageMetadata } from "./package-metadata.js";
182
184
  export type { AnySchema, ArraySchema, BooleanSchema, EnumSchema, JsonSchema, NumberSchema, ObjectSchema, OptionalSchema, Static, StringSchema } from "toolcraft-schema";
183
185
  export type { HumanInLoopConfig, HumanInLoopPending, HumanInLoopRuntimeOptions };
package/dist/index.js CHANGED
@@ -429,3 +429,4 @@ export function getCommandSourcePath(command) {
429
429
  }
430
430
  export { S, toJsonSchema } from "toolcraft-schema";
431
431
  export { ApprovalDeclinedError, UserError };
432
+ export { findPackageMetadata, packageMetadata } from "./package-metadata.js";
package/dist/mcp.d.ts CHANGED
@@ -7,7 +7,7 @@ type CmdkitServer = Omit<TinyServer, "connect"> & {
7
7
  };
8
8
  export interface RunMCPOptions<TServices extends object = Record<string, unknown>> {
9
9
  name: string;
10
- version: string;
10
+ version?: string;
11
11
  humanInLoop?: HumanInLoopRuntimeOptions;
12
12
  /**
13
13
  * Optional allowlist of MCP tool names or group prefixes.
package/dist/mcp.js CHANGED
@@ -6,6 +6,7 @@ import { mergeApprovalsGroup } from "./human-in-loop/approvals-commands.js";
6
6
  import { ApprovalDeclinedError, invokeWithHumanInLoop, } from "./human-in-loop/index.js";
7
7
  import { hasMcpProxyGroups, resolveMcpProxies } from "./mcp-proxy.js";
8
8
  import { getExpectedNumberDescription, isValidNumberSchemaValue } from "./number-schema.js";
9
+ import { findEntrypointPackageMetadata } from "./package-metadata.js";
9
10
  import { filterSchemaForScope } from "./schema-scope.js";
10
11
  const RESERVED_SERVICE_NAMES = new Set(["params", "secrets", "fetch", "fs", "env", "progress"]);
11
12
  function normalizeRoots(roots) {
@@ -365,7 +366,8 @@ function createResolvedMCPServer(root, options) {
365
366
  };
366
367
  validateServices(services);
367
368
  const tools = enumerateTools(root, casing, options.tools);
368
- const server = createServer({ name: options.name, version: options.version });
369
+ const version = resolveMCPVersion(options.version);
370
+ const server = createServer({ name: options.name, version });
369
371
  for (const tool of tools) {
370
372
  server.tool(tool.name, tool.description, tool.inputSchema, async (argumentsValue) => {
371
373
  try {
@@ -406,6 +408,13 @@ function createResolvedMCPServer(root, options) {
406
408
  },
407
409
  };
408
410
  }
411
+ function resolveMCPVersion(version) {
412
+ const resolvedVersion = version ?? findEntrypointPackageMetadata(process.argv[1])?.version;
413
+ if (resolvedVersion === undefined) {
414
+ throw new Error("MCP version is required when no package.json version can be inferred from the entrypoint.");
415
+ }
416
+ return resolvedVersion;
417
+ }
409
418
  function createDeferredMCPServer(root, options) {
410
419
  let serverPromise;
411
420
  const resolveServer = () => {
@@ -0,0 +1,10 @@
1
+ export interface PackageMetadata {
2
+ name?: string;
3
+ path: string;
4
+ version?: string;
5
+ }
6
+ type PackageMetadataInput = string | URL;
7
+ export declare function findPackageMetadata(from: PackageMetadataInput): PackageMetadata | undefined;
8
+ export declare function packageMetadata(from?: PackageMetadataInput): PackageMetadata;
9
+ export declare function findEntrypointPackageMetadata(entrypoint: string | undefined): PackageMetadata | undefined;
10
+ export {};
@@ -0,0 +1,62 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ function pathFromInput(from) {
5
+ if (from instanceof URL) {
6
+ return fileURLToPath(from);
7
+ }
8
+ if (from.startsWith("file:")) {
9
+ return fileURLToPath(from);
10
+ }
11
+ return path.resolve(from);
12
+ }
13
+ function getSearchDirectory(from) {
14
+ const resolved = pathFromInput(from);
15
+ try {
16
+ return statSync(resolved).isDirectory() ? resolved : path.dirname(resolved);
17
+ }
18
+ catch {
19
+ return path.dirname(resolved);
20
+ }
21
+ }
22
+ function readPackageMetadata(packageJsonPath) {
23
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8"));
24
+ const metadata = { path: packageJsonPath };
25
+ if (typeof parsed.name === "string") {
26
+ metadata.name = parsed.name;
27
+ }
28
+ if (typeof parsed.version === "string") {
29
+ metadata.version = parsed.version;
30
+ }
31
+ return metadata;
32
+ }
33
+ export function findPackageMetadata(from) {
34
+ let current = getSearchDirectory(from);
35
+ while (true) {
36
+ const packageJsonPath = path.join(current, "package.json");
37
+ if (existsSync(packageJsonPath)) {
38
+ return readPackageMetadata(packageJsonPath);
39
+ }
40
+ const parent = path.dirname(current);
41
+ if (parent === current) {
42
+ return undefined;
43
+ }
44
+ current = parent;
45
+ }
46
+ }
47
+ export function packageMetadata(from = process.cwd()) {
48
+ const metadata = findPackageMetadata(from);
49
+ if (metadata === undefined) {
50
+ throw new Error(`No package.json found from ${pathFromInput(from)}.`);
51
+ }
52
+ return metadata;
53
+ }
54
+ export function findEntrypointPackageMetadata(entrypoint) {
55
+ if (entrypoint === undefined || entrypoint.length === 0) {
56
+ return undefined;
57
+ }
58
+ if (!path.isAbsolute(entrypoint) && !entrypoint.startsWith("file:")) {
59
+ return undefined;
60
+ }
61
+ return findPackageMetadata(entrypoint);
62
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toolcraft",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -50,7 +50,7 @@
50
50
  "console-table-printer": "^2.15.0",
51
51
  "tiny-mcp-client": "*",
52
52
  "tiny-stdio-mcp-server": "^0.1.0",
53
- "toolcraft-schema": "^0.0.3"
53
+ "toolcraft-schema": "^0.0.5"
54
54
  },
55
55
  "files": [
56
56
  "dist"