From 0a2df3c861de3e252a0a5ee24372b6a06c52ed34 Mon Sep 17 00:00:00 2001 From: Tobias Herber <22559657+herber@users.noreply.github.com> Date: Thu, 9 Apr 2026 08:46:20 +0200 Subject: [PATCH] Update npm packages --- bun.lock | 12 ++ npm/call-tool/package.json | 21 +++ npm/call-tool/src/bin.ts | 256 +++++++++++++++++++++++++++++++++++ npm/call-tool/tsup.config.ts | 18 +++ package.json | 2 +- scripts/publish-npm.ts | 2 +- 6 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 npm/call-tool/package.json create mode 100644 npm/call-tool/src/bin.ts create mode 100644 npm/call-tool/tsup.config.ts diff --git a/bun.lock b/bun.lock index e805156..e18c645 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,16 @@ "typescript": "^5.9.2", }, }, + "npm/call-tool": { + "name": "call-tool", + "version": "0.0.0-dev", + "bin": { + "tool-call": "./dist/bin.cjs", + }, + "dependencies": { + "@metorial/cli-core": "workspace:*", + }, + }, "npm/cli": { "name": "@metorial/cli", "version": "0.0.0-dev", @@ -200,6 +210,8 @@ "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + "call-tool": ["call-tool@workspace:npm/call-tool"], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], diff --git a/npm/call-tool/package.json b/npm/call-tool/package.json new file mode 100644 index 0000000..045c315 --- /dev/null +++ b/npm/call-tool/package.json @@ -0,0 +1,21 @@ +{ + "name": "call-tool", + "version": "0.0.0-dev", + "description": "tool-call npm entrypoint for Metorial integrations", + "license": "MIT", + "bin": { + "call-tool": "./dist/bin.cjs" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup --config tsup.config.ts" + }, + "dependencies": { + "@metorial/cli-core": "workspace:*" + } +} diff --git a/npm/call-tool/src/bin.ts b/npm/call-tool/src/bin.ts new file mode 100644 index 0000000..b58de5a --- /dev/null +++ b/npm/call-tool/src/bin.ts @@ -0,0 +1,256 @@ +import { npmInstallEnvironment, runCLI } from '@metorial/cli-core'; + +let PACKAGE_NAME = 'call-tool'; +let BINARY_NAME = PACKAGE_NAME; + +let ROOT_VALUE_FLAGS = new Set([ + '--api-key', + '--api-host', + '--instance', + '--profile', + '--format' +]); + +let ROOT_BOOLEAN_FLAGS = new Set(['--help', '-h', '--version', '-v']); + +let OUTPUT_REPLACEMENTS: Array<[string, string]> = [ + ['metorial integrations catalog search', `${BINARY_NAME} search`], + ['metorial integrations catalog list', `${BINARY_NAME} search`], + ['metorial integrations search', `${BINARY_NAME} search`], + ['metorial integrations setup', `${BINARY_NAME} setup`], + ['metorial integrations schema', `${BINARY_NAME} schema`], + ['metorial integrations tools', `${BINARY_NAME} info`], + ['metorial integrations get', `${BINARY_NAME} info`], + ['metorial integrations client', `${BINARY_NAME} client`], + ['metorial integrations install', `${BINARY_NAME} install`], + ['metorial integrations call', BINARY_NAME] +]; + +let ROOT_HELP = `Call Tool by Metorial + +Call MCP tools exposed through your Metorial integrations. + +Usage: + ${BINARY_NAME} [flags] + ${BINARY_NAME} [args] + +Commands: + setup [listing] Create and finish an integration setup session + search [search] Search installable integrations + info Show provider details and tools for an integration + schema + Show the MCP input schema for a tool + client list List supported local MCP clients + install Install an integration into a local client + +Flags: + -d, --data string JSON tool input, or @- to read from stdin + -h, --help Show help + --version Show the underlying Metorial CLI version + +Global Flags: + --api-key string API key to use for authenticated requests + --api-host string API host or base URL (default: api.metorial.com) + --instance string Instance ID to use for organization-scoped tokens + --profile string Profile ID to use for authenticated requests + --format string Output format: yaml, toml, json, or structured + +Examples: + ${BINARY_NAME} my-integration search_docs --data '{"query":"oauth"}' + echo '{"query":"oauth"}' | ${BINARY_NAME} my-integration search_docs --data @- + ${BINARY_NAME} search github + ${BINARY_NAME} setup github + ${BINARY_NAME} info my-integration + ${BINARY_NAME} schema my-integration search_docs + ${BINARY_NAME} install codex my-integration + +The default command is \`call\`, so \`${BINARY_NAME} my-integration my-tool\` +maps to \`metorial integrations call my-integration my-tool\`. + +Use "${BINARY_NAME} --help" for more details. +`; + +void main(); + +async function main() { + let args = process.argv.slice(2); + let parsed = splitRootArgs(args); + let env = npmInstallEnvironment(PACKAGE_NAME); + + if (parsed.incompleteRootFlag) { + process.exitCode = await runCLI(args, { env }); + return; + } + + if (shouldShowRootHelp(parsed)) { + process.stdout.write(ROOT_HELP); + return; + } + + if (shouldShowRootVersion(parsed)) { + process.exitCode = await runCLI(parsed.rootArgs, { env }); + return; + } + + process.exitCode = await runCLI(buildCLIArgs(parsed), { + env, + stdoutTransform: patchToolCallOutput, + stderrTransform: patchToolCallOutput + }); +} + +function splitRootArgs(args: string[]) { + let rootArgs: string[] = []; + let index = 0; + let incompleteRootFlag = false; + + while (index < args.length) { + let current = args[index]; + + if (current === '--') { + break; + } + + if (ROOT_VALUE_FLAGS.has(current)) { + rootArgs.push(current); + if (index + 1 >= args.length) { + incompleteRootFlag = true; + break; + } + rootArgs.push(args[index + 1]); + index += 2; + continue; + } + + if (matchesInlineRootValueFlag(current)) { + rootArgs.push(current); + index += 1; + continue; + } + + if (ROOT_BOOLEAN_FLAGS.has(current)) { + rootArgs.push(current); + index += 1; + continue; + } + + break; + } + + return { + rootArgs, + commandArgs: args.slice(index), + incompleteRootFlag + }; +} + +function matchesInlineRootValueFlag(value: string) { + for (let flag of ROOT_VALUE_FLAGS) { + if (value.startsWith(`${flag}=`)) { + return true; + } + } + + return false; +} + +function shouldShowRootHelp(parsed: { + rootArgs: string[]; + commandArgs: string[]; + incompleteRootFlag: boolean; +}) { + if (parsed.incompleteRootFlag) { + return false; + } + + if (parsed.commandArgs.length === 0) { + return !hasVersionFlag(parsed.rootArgs); + } + + return parsed.commandArgs.length === 1 && parsed.commandArgs[0] === 'help'; +} + +function shouldShowRootVersion(parsed: { + rootArgs: string[]; + commandArgs: string[]; + incompleteRootFlag: boolean; +}) { + return ( + !parsed.incompleteRootFlag && + parsed.commandArgs.length === 0 && + hasVersionFlag(parsed.rootArgs) + ); +} + +function hasVersionFlag(args: string[]) { + return args.includes('--version') || args.includes('-v'); +} + +function buildCLIArgs(parsed: { + rootArgs: string[]; + commandArgs: string[]; + incompleteRootFlag: boolean; +}) { + if (parsed.commandArgs[0] === 'help') { + return [...parsed.rootArgs, ...routeHelpCommand(parsed.commandArgs.slice(1)), '--help']; + } + + return [...parsed.rootArgs, ...routeCommand(parsed.commandArgs)]; +} + +function routeHelpCommand(args: string[]) { + let command = args[0] || 'call'; + + switch (command) { + case 'setup': + return ['integrations', 'setup']; + case 'search': + return ['integrations', 'search']; + case 'schema': + return ['integrations', 'schema']; + case 'info': + case 'tools': + return ['integrations', 'tools']; + case 'client': + return ['integrations', 'client']; + case 'install': + return ['integrations', 'install']; + case 'call': + default: + return ['integrations', 'call']; + } +} + +function routeCommand(args: string[]) { + let [command, ...rest] = args; + + switch (command) { + case 'setup': + return ['integrations', 'setup', ...rest]; + case 'search': + return ['integrations', 'search', ...rest]; + case 'schema': + return ['integrations', 'schema', ...rest]; + case 'info': + case 'tools': + return ['integrations', 'tools', ...rest]; + case 'client': + return ['integrations', 'client', ...rest]; + case 'install': + return ['integrations', 'install', ...rest]; + case 'call': + return ['integrations', 'call', ...rest]; + default: + return ['integrations', 'call', ...args]; + } +} + +function patchToolCallOutput(value: string) { + let next = value; + + for (let [searchValue, replaceValue] of OUTPUT_REPLACEMENTS) { + next = next.replaceAll(searchValue, replaceValue); + } + + return next; +} diff --git a/npm/call-tool/tsup.config.ts b/npm/call-tool/tsup.config.ts new file mode 100644 index 0000000..1edca03 --- /dev/null +++ b/npm/call-tool/tsup.config.ts @@ -0,0 +1,18 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/bin.ts'], + format: ['cjs'], + bundle: true, + clean: true, + target: 'node20', + outDir: 'dist', + banner: { + js: '#!/usr/bin/env node' + }, + outExtension() { + return { + js: '.cjs' + }; + } +}); diff --git a/package.json b/package.json index fb95840..e4da030 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "npm/*" ], "scripts": { - "build:npm": "bun run --cwd npm/cli-core build && bun run --cwd npm/cli build && bun run --cwd npm/create build", + "build:npm": "bun run --cwd npm/cli-core build && bun run --cwd npm/cli build && bun run --cwd npm/create build && bun run --cwd npm/call-tool build", "build:browser-shell": "bun ./scripts/build-browser-shell.ts", "set-npm-version": "bun ./scripts/set-npm-version.ts", "publish:npm": "bun ./scripts/publish-npm.ts" diff --git a/scripts/publish-npm.ts b/scripts/publish-npm.ts index 0c8ce6e..77a1a4c 100644 --- a/scripts/publish-npm.ts +++ b/scripts/publish-npm.ts @@ -8,7 +8,7 @@ let currentFile = fileURLToPath(import.meta.url); let scriptsDir = path.dirname(currentFile); let rootDir = path.resolve(scriptsDir, '..'); -let publishOrder = ['npm/cli-core', 'npm/cli', 'npm/create']; +let publishOrder = ['npm/cli-core', 'npm/cli', 'npm/create', 'npm/call-tool']; for (let packagePath of publishOrder) { execFileSync(