From 61ef178cf16f3a7e778028c9baa850ba2b08cefb Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 13 May 2025 19:21:38 +0200 Subject: [PATCH 01/15] =?UTF-8?q?some=20LLM=20scaffolding=20+=20some=20hum?= =?UTF-8?q?an=20edit=20=F0=9F=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- packages/tiny-agents/README.md | 57 ++++++++++++++++++++++++++++++ packages/tiny-agents/package.json | 57 ++++++++++++++++++++++++++++++ packages/tiny-agents/src/cli.ts | 57 ++++++++++++++++++++++++++++++ packages/tiny-agents/src/index.ts | 1 + packages/tiny-agents/tsconfig.json | 21 +++++++++++ 6 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 packages/tiny-agents/README.md create mode 100644 packages/tiny-agents/package.json create mode 100644 packages/tiny-agents/src/cli.ts create mode 100644 packages/tiny-agents/src/index.ts create mode 100644 packages/tiny-agents/tsconfig.json diff --git a/README.md b/README.md index eaf8f71d34..e09f738b67 100644 --- a/README.md +++ b/README.md @@ -241,8 +241,8 @@ const agent = new Agent({ ], }); - await agent.loadTools(); + for await (const chunk of agent.run("What are the top 5 trending models on Hugging Face?")) { if ("choices" in chunk) { const delta = chunk.choices[0]?.delta; diff --git a/packages/tiny-agents/README.md b/packages/tiny-agents/README.md new file mode 100644 index 0000000000..6820985160 --- /dev/null +++ b/packages/tiny-agents/README.md @@ -0,0 +1,57 @@ +# @huggingface/tiny-agents + +A lightweight, composable agent framework for AI applications built on Hugging Face's JS stack. + +## Installation + +```bash +npm install @huggingface/tiny-agents +# or +yarn add @huggingface/tiny-agents +# or +pnpm add @huggingface/tiny-agents +``` + +## Usage + +```typescript +import { Agent } from '@huggingface/tiny-agents'; + +const HF_TOKEN = "hf_..."; + +// Create an Agent +const agent = new Agent({ + provider: "auto", + model: "Qwen/Qwen2.5-72B-Instruct", + apiKey: HF_TOKEN, + servers: [ + { + // Playwright MCP + command: "npx", + args: ["@playwright/mcp@latest"], + }, + ], +}); + +await agent.loadTools(); + +// Use the Agent +for await (const chunk of agent.run("What are the top 5 trending models on Hugging Face?")) { + if ("choices" in chunk) { + const delta = chunk.choices[0]?.delta; + if (delta.content) { + console.log(delta.content); + } + } +} +``` + +## CLI Usage + +```bash +npx @huggingface/tiny-agents +``` + +## License + +MIT diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json new file mode 100644 index 0000000000..ce78160be2 --- /dev/null +++ b/packages/tiny-agents/package.json @@ -0,0 +1,57 @@ +{ + "name": "@huggingface/tiny-agents", + "packageManager": "pnpm@10.10.0", + "version": "0.1.0", + "description": "Lightweight, composable agents for AI applications", + "repository": "https://github.com/huggingface/huggingface.js.git", + "publishConfig": { + "access": "public" + }, + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "bin": { + "tiny-agents": "./dist/cli.js" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "require": "./dist/index.js", + "import": "./dist/index.mjs" + } + }, + "engines": { + "node": ">=18" + }, + "source": "index.ts", + "scripts": { + "lint": "eslint --quiet --fix --ext .cjs,.ts .", + "lint:check": "eslint --ext .cjs,.ts .", + "format": "prettier --write .", + "format:check": "prettier --check .", + "prepublishOnly": "pnpm run build", + "build": "tsup src/index.ts --format cjs,esm --clean && tsc --emitDeclarationOnly --declaration", + "prepare": "pnpm run build", + "test": "vitest run", + "check": "tsc", + "cli": "tsx cli.ts" + }, + "files": [ + "src", + "dist", + "tsconfig.json" + ], + "keywords": [ + "huggingface", + "agent", + "ai", + "llm", + "tiny-agent" + ], + "author": "Hugging Face", + "license": "MIT", + "dependencies": { + "@huggingface/inference": "workspace:^", + "@huggingface/mcp-client": "workspace:^" + } +} diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts new file mode 100644 index 0000000000..5bbe151aa6 --- /dev/null +++ b/packages/tiny-agents/src/cli.ts @@ -0,0 +1,57 @@ +#!/usr/bin/env node +import * as readline from "node:readline/promises"; +import { stdin, stdout } from "node:process"; +import { join } from "node:path"; +import { homedir } from "node:os"; +import { parseArgs } from "node:util"; +import type { InferenceProviderOrPolicy } from "@huggingface/inference"; +import { version as packageVersion } from "../package.json"; + +const SERVERS: ServerConfig[] = [ + { + // Filesystem "official" mcp-server with access to your Desktop + command: "npx", + args: [ + "-y", + "@modelcontextprotocol/server-filesystem", + process.platform === "darwin" ? join(homedir(), "Desktop") : homedir(), + ], + }, + { + // Playwright MCP + command: "npx", + args: ["@playwright/mcp@latest"], + }, +]; + +async function main() { + const { + values: { url: urls }, + } = parseArgs({ + options: { + url: { + type: "string", + multiple: true, + }, + }, + }); + if (urls?.length) { + while (SERVERS.length) { + SERVERS.pop(); + } + for (const url of urls) { + try { + SERVERS.push(urlToServerConfig(url, process.env.HF_TOKEN)); + } catch (error) { + console.error(`Error adding server from URL "${url}": ${error.message}`); + } + } + } + + if (process.argv.includes("--version")) { + console.log(packageVersion); + process.exit(0); + } +} + +main(); diff --git a/packages/tiny-agents/src/index.ts b/packages/tiny-agents/src/index.ts new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/packages/tiny-agents/src/index.ts @@ -0,0 +1 @@ + diff --git a/packages/tiny-agents/tsconfig.json b/packages/tiny-agents/tsconfig.json new file mode 100644 index 0000000000..8274efe5ca --- /dev/null +++ b/packages/tiny-agents/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "lib": ["ES2022", "DOM"], + "module": "CommonJS", + "moduleResolution": "node", + "target": "ES2022", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "skipLibCheck": true, + "noImplicitOverride": true, + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "resolveJsonModule": true + }, + "include": ["src", "test"], + "exclude": ["dist"] +} From 2c4bb34cbdbd29718fbeedaf55e81c421aba0338 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 13 May 2025 20:23:54 +0200 Subject: [PATCH 02/15] A lot of vibe coding.... --- packages/mcp-client/src/index.ts | 1 + packages/tiny-agents/README.md | 26 +++- packages/tiny-agents/package.json | 6 +- packages/tiny-agents/pnpm-lock.yaml | 28 ++++ .../flux-schnell-generator/agent.json | 12 ++ .../src/agents/julien-c/local-coder/PROMPT.md | 5 + .../agents/julien-c/local-coder/agent.json | 13 ++ packages/tiny-agents/src/cli.ts | 124 +++++++++++++----- 8 files changed, 171 insertions(+), 44 deletions(-) create mode 100644 packages/tiny-agents/pnpm-lock.yaml create mode 100644 packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json create mode 100644 packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md create mode 100644 packages/tiny-agents/src/agents/julien-c/local-coder/agent.json diff --git a/packages/mcp-client/src/index.ts b/packages/mcp-client/src/index.ts index 1803f7bec8..c582e121d1 100644 --- a/packages/mcp-client/src/index.ts +++ b/packages/mcp-client/src/index.ts @@ -1,3 +1,4 @@ export * from "./McpClient"; export * from "./Agent"; export type { ChatCompletionInputMessageTool } from "./McpClient"; +export type { ServerConfig } from "./types"; diff --git a/packages/tiny-agents/README.md b/packages/tiny-agents/README.md index 6820985160..231972999c 100644 --- a/packages/tiny-agents/README.md +++ b/packages/tiny-agents/README.md @@ -12,7 +12,26 @@ yarn add @huggingface/tiny-agents pnpm add @huggingface/tiny-agents ``` -## Usage +## CLI Usage + +```bash +npx @huggingface/tiny-agents [command] "agent/id" + +``` + +``` +Usage: + tiny-agents [flags] + tiny-agents run "agent/id" + tiny-agents serve "agent/id" + +Available Commands: + run Run the Agent in command-line + serve Run the Agent as an OpenAI-compatible HTTP server +``` + + +## Programmatic Usage ```typescript import { Agent } from '@huggingface/tiny-agents'; @@ -46,11 +65,6 @@ for await (const chunk of agent.run("What are the top 5 trending models on Huggi } ``` -## CLI Usage - -```bash -npx @huggingface/tiny-agents -``` ## License diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json index ce78160be2..5833bf4f3f 100644 --- a/packages/tiny-agents/package.json +++ b/packages/tiny-agents/package.json @@ -24,6 +24,7 @@ "node": ">=18" }, "source": "index.ts", + "type": "module", "scripts": { "lint": "eslint --quiet --fix --ext .cjs,.ts .", "lint:check": "eslint --ext .cjs,.ts .", @@ -34,7 +35,7 @@ "prepare": "pnpm run build", "test": "vitest run", "check": "tsc", - "cli": "tsx cli.ts" + "cli": "tsx src/cli.ts" }, "files": [ "src", @@ -52,6 +53,7 @@ "license": "MIT", "dependencies": { "@huggingface/inference": "workspace:^", - "@huggingface/mcp-client": "workspace:^" + "@huggingface/mcp-client": "workspace:^", + "zod": "^3.24.4" } } diff --git a/packages/tiny-agents/pnpm-lock.yaml b/packages/tiny-agents/pnpm-lock.yaml new file mode 100644 index 0000000000..627fc108a6 --- /dev/null +++ b/packages/tiny-agents/pnpm-lock.yaml @@ -0,0 +1,28 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@huggingface/inference': + specifier: workspace:^ + version: link:../inference + '@huggingface/mcp-client': + specifier: workspace:^ + version: link:../mcp-client + zod: + specifier: ^3.24.4 + version: 3.24.4 + +packages: + + zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + +snapshots: + + zod@3.24.4: {} diff --git a/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json b/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json new file mode 100644 index 0000000000..ba78c614d9 --- /dev/null +++ b/packages/tiny-agents/src/agents/julien-c/flux-schnell-generator/agent.json @@ -0,0 +1,12 @@ +{ + "model": "Qwen/Qwen3-32B", + "provider": "novita", + "servers": [ + { + "type": "sse", + "config": { + "url": "https://evalstate-flux1-schnell.hf.space/gradio_api/mcp/sse" + } + } + ] +} diff --git a/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md b/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md new file mode 100644 index 0000000000..e880614bf2 --- /dev/null +++ b/packages/tiny-agents/src/agents/julien-c/local-coder/PROMPT.md @@ -0,0 +1,5 @@ +You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. Only terminate your turn when you are sure that the problem is solved, or if you need more info from the user to solve the problem. + +If you are not sure about anything pertaining to the user’s request, use your tools to read files and gather the relevant information: do NOT guess or make up an answer. + +You MUST plan extensively before each function call, and reflect extensively on the outcomes of the previous function calls. DO NOT do this entire process by making function calls only, as this can impair your ability to solve the problem and think insightfully. diff --git a/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json b/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json new file mode 100644 index 0000000000..31a926839d --- /dev/null +++ b/packages/tiny-agents/src/agents/julien-c/local-coder/agent.json @@ -0,0 +1,13 @@ +{ + "model": "Qwen/Qwen2.5-72B-Instruct", + "provider": "nebius", + "servers": [ + { + "type": "stdio", + "config": { + "command": "npx", + "args": ["@playwright/mcp@latest"] + } + } + ] +} diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 5bbe151aa6..9b19c9b916 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -1,57 +1,109 @@ #!/usr/bin/env node import * as readline from "node:readline/promises"; import { stdin, stdout } from "node:process"; -import { join } from "node:path"; -import { homedir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; +import { z } from "zod"; import type { InferenceProviderOrPolicy } from "@huggingface/inference"; +import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { version as packageVersion } from "../package.json"; +import { readFile } from "node:fs/promises"; +import type { ServerConfig } from "@huggingface/mcp-client"; -const SERVERS: ServerConfig[] = [ - { - // Filesystem "official" mcp-server with access to your Desktop - command: "npx", - args: [ - "-y", - "@modelcontextprotocol/server-filesystem", - process.platform === "darwin" ? join(homedir(), "Desktop") : homedir(), - ], - }, - { - // Playwright MCP - command: "npx", - args: ["@playwright/mcp@latest"], - }, -]; +const USAGE_HELP = ` +Usage: + tiny-agents [flags] + tiny-agents run "agent/id" + tiny-agents serve "agent/id" + +Available Commands: + run Run the Agent in command-line + serve Run the Agent as an OpenAI-compatible HTTP server + +Flags: + -h, --help help for tiny-agents + -v, --version Show version information +`.trim(); + +const CLI_COMMANDS = ["run", "serve"] as const; +function isValidCommand(command: string): command is (typeof CLI_COMMANDS)[number] { + return (CLI_COMMANDS as unknown as string[]).includes(command); +} + +const FILENAME_CONFIG = "agent.json"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const FILENAME_PROMPT = "PROMPT.md"; async function main() { const { - values: { url: urls }, + values: { help, version }, + positionals, } = parseArgs({ options: { - url: { - type: "string", - multiple: true, + help: { + type: "boolean", + short: "h", + }, + version: { + type: "boolean", + short: "v", }, }, + allowPositionals: true, }); - if (urls?.length) { - while (SERVERS.length) { - SERVERS.pop(); - } - for (const url of urls) { - try { - SERVERS.push(urlToServerConfig(url, process.env.HF_TOKEN)); - } catch (error) { - console.error(`Error adding server from URL "${url}": ${error.message}`); - } - } - } - - if (process.argv.includes("--version")) { + if (version) { console.log(packageVersion); process.exit(0); } + const command = positionals[0]; + const loadFrom = positionals[1]; + if (help) { + console.log(USAGE_HELP); + process.exit(0); + } + if (positionals.length !== 2 || !isValidCommand(command)) { + console.error(`You need to call run or serve, followed by an agent id (local path or Hub identifier).`); + console.log(USAGE_HELP); + process.exit(1); + } + + if (command === "serve") { + console.error(`Serve is not implemented yet, coming soon!`); + process.exit(1); + } else { + const srcDir = dirname(fileURLToPath(import.meta.url)); + const configPath = join(srcDir, "agents", loadFrom, FILENAME_CONFIG); + let configJson: string; + try { + configJson = await readFile(configPath, { encoding: "utf8" }); + } catch { + console.error(`Config file not found!`); + process.exit(1); + } + const ConfigSchema = z.object({ + model: z.string(), + provider: z.enum(PROVIDERS_OR_POLICIES), + servers: z.array(z.custom()), + }); + + let config: z.infer; + try { + const parsedConfig = JSON.parse(configJson); + config = ConfigSchema.parse(parsedConfig); + } catch (error) { + console.error("Invalid configuration file:", error instanceof Error ? error.message : error); + process.exit(1); + } + const agent = new Agent({ + provider: config.provider, + model: config.model, + apiKey: process.env.HF_TOKEN, + servers: config.servers, + }); + console.log(agent); + // TODO: hook main loop from mcp-client/cli.ts + } } main(); From 9ebc6851b7b4030e15f4135587f96d6197d53704 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 13 May 2025 20:38:16 +0200 Subject: [PATCH 03/15] Fix vibe coding errors... --- packages/tiny-agents/package.json | 1 - packages/tiny-agents/src/cli.ts | 42 ++++++++++++++++++------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json index 5833bf4f3f..60fdc107c7 100644 --- a/packages/tiny-agents/package.json +++ b/packages/tiny-agents/package.json @@ -24,7 +24,6 @@ "node": ">=18" }, "source": "index.ts", - "type": "module", "scripts": { "lint": "eslint --quiet --fix --ext .cjs,.ts .", "lint:check": "eslint --ext .cjs,.ts .", diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 9b19c9b916..8e8f826a68 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -1,15 +1,12 @@ #!/usr/bin/env node -import * as readline from "node:readline/promises"; -import { stdin, stdout } from "node:process"; import { dirname, join } from "node:path"; -import { fileURLToPath } from "node:url"; import { parseArgs } from "node:util"; +import { readFile } from "node:fs/promises"; import { z } from "zod"; -import type { InferenceProviderOrPolicy } from "@huggingface/inference"; import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; -import { version as packageVersion } from "../package.json"; -import { readFile } from "node:fs/promises"; +import { Agent } from "@huggingface/mcp-client"; import type { ServerConfig } from "@huggingface/mcp-client"; +import { version as packageVersion } from "../package.json"; const USAGE_HELP = ` Usage: @@ -35,6 +32,22 @@ const FILENAME_CONFIG = "agent.json"; // eslint-disable-next-line @typescript-eslint/no-unused-vars const FILENAME_PROMPT = "PROMPT.md"; +async function loadConfigFrom(loadFrom: string): Promise { + try { + /// First try it as a local directory path, then we will try as a path inside the repo itself + return await readFile(loadFrom, { encoding: "utf8" }); + } catch { + const srcDir = dirname(__filename); + const configPath = join(srcDir, "agents", loadFrom, FILENAME_CONFIG); + try { + return await readFile(configPath, { encoding: "utf8" }); + } catch { + console.error(`Config file not found! Loading from the HF Hub is not implemented yet`); + process.exit(1); + } + } +} + async function main() { const { values: { help, version }, @@ -72,18 +85,11 @@ async function main() { console.error(`Serve is not implemented yet, coming soon!`); process.exit(1); } else { - const srcDir = dirname(fileURLToPath(import.meta.url)); - const configPath = join(srcDir, "agents", loadFrom, FILENAME_CONFIG); - let configJson: string; - try { - configJson = await readFile(configPath, { encoding: "utf8" }); - } catch { - console.error(`Config file not found!`); - process.exit(1); - } + const configJson = await loadConfigFrom(loadFrom); + const ConfigSchema = z.object({ model: z.string(), - provider: z.enum(PROVIDERS_OR_POLICIES), + provider: z.enum(PROVIDERS_OR_POLICIES), servers: z.array(z.custom()), }); @@ -98,10 +104,12 @@ async function main() { const agent = new Agent({ provider: config.provider, model: config.model, - apiKey: process.env.HF_TOKEN, + apiKey: process.env.HF_TOKEN ?? "", servers: config.servers, }); + console.log(agent); + // TODO: hook main loop from mcp-client/cli.ts } } From b7d11a4234e59cd446393e9d483cf193fa8c22b8 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 13:03:51 +0200 Subject: [PATCH 04/15] Update pnpm-workspace.yaml --- pnpm-workspace.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 94ca7a9323..08e651bb73 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -13,3 +13,4 @@ packages: - "packages/space-header" - "packages/ollama-utils" - "packages/mcp-client" + - "packages/tiny-agents" From 1c0215e1bdca5ca68c13708c36e89ac88e1a0630 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 13:21:12 +0200 Subject: [PATCH 05/15] re-export --- packages/tiny-agents/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/tiny-agents/src/index.ts b/packages/tiny-agents/src/index.ts index 8b13789179..4f4ef44e7a 100644 --- a/packages/tiny-agents/src/index.ts +++ b/packages/tiny-agents/src/index.ts @@ -1 +1 @@ - +export { Agent } from "@huggingface/mcp-client"; From 3127f31f5e47a91db615aa730696616bab676bde Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 13:43:32 +0200 Subject: [PATCH 06/15] Define Zod validation for config files --- packages/tiny-agents/package.json | 3 +- packages/tiny-agents/pnpm-lock.yaml | 715 +++++++++++++++++++++++++- packages/tiny-agents/src/cli.ts | 4 +- packages/tiny-agents/src/lib/types.ts | 37 ++ 4 files changed, 751 insertions(+), 8 deletions(-) create mode 100644 packages/tiny-agents/src/lib/types.ts diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json index 60fdc107c7..14442e33e2 100644 --- a/packages/tiny-agents/package.json +++ b/packages/tiny-agents/package.json @@ -53,6 +53,7 @@ "dependencies": { "@huggingface/inference": "workspace:^", "@huggingface/mcp-client": "workspace:^", - "zod": "^3.24.4" + "@modelcontextprotocol/sdk": "^1.11.4", + "zod": "^3.25.7" } } diff --git a/packages/tiny-agents/pnpm-lock.yaml b/packages/tiny-agents/pnpm-lock.yaml index 627fc108a6..5b09a016f9 100644 --- a/packages/tiny-agents/pnpm-lock.yaml +++ b/packages/tiny-agents/pnpm-lock.yaml @@ -14,15 +14,720 @@ importers: '@huggingface/mcp-client': specifier: workspace:^ version: link:../mcp-client + '@modelcontextprotocol/sdk': + specifier: ^1.11.4 + version: 1.11.4 zod: - specifier: ^3.24.4 - version: 3.24.4 + specifier: ^3.25.7 + version: 3.25.7 packages: - zod@3.24.4: - resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + '@modelcontextprotocol/sdk@1.11.4': + resolution: {integrity: sha512-OTbhe5slIjiOtLxXhKalkKGhIQrwvhgCDs/C2r8kcBTy5HR/g43aDQU0l7r8O0VGbJPTNJvDc7ZdQMdQDJXmbw==} + engines: {node: '>=18'} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.2: + resolution: {integrity: sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-uri@3.0.6: + resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + pkce-challenge@5.0.0: + resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + engines: {node: '>=16.20.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + + zod@3.25.7: + resolution: {integrity: sha512-YGdT1cVRmKkOg6Sq7vY7IkxdphySKnXhaUmFI4r4FcuFVNgpCb9tZfNwXbT6BPjD5oz0nubFsoo9pIqKrDcCvg==} snapshots: - zod@3.24.4: {} + '@modelcontextprotocol/sdk@1.11.4': + dependencies: + ajv: 8.17.1 + content-type: 1.0.5 + cors: 2.8.5 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + express: 5.1.0 + express-rate-limit: 7.5.0(express@5.1.0) + pkce-challenge: 5.0.0 + raw-body: 3.0.0 + zod: 3.25.7 + zod-to-json-schema: 3.24.5(zod@3.25.7) + transitivePeerDependencies: + - supports-color + + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.6 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + depd@2.0.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + eventsource-parser@3.0.2: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.2 + + express-rate-limit@7.5.0(express@5.1.0): + dependencies: + express: 5.1.0 + + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + fast-deep-equal@3.1.3: {} + + fast-uri@3.0.6: {} + + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + forwarded@0.2.0: {} + + fresh@2.0.0: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-promise@4.0.0: {} + + isexe@2.0.0: {} + + json-schema-traverse@1.0.0: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + mime-db@1.54.0: {} + + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + + ms@2.1.3: {} + + negotiator@1.0.0: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + parseurl@1.3.3: {} + + path-key@3.1.1: {} + + path-to-regexp@8.2.0: {} + + pkce-challenge@5.0.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + require-from-string@2.0.2: {} + + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + statuses@2.0.1: {} + + toidentifier@1.0.1: {} + + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + + unpipe@1.0.0: {} + + vary@1.1.2: {} + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrappy@1.0.2: {} + + zod-to-json-schema@3.24.5(zod@3.25.7): + dependencies: + zod: 3.25.7 + + zod@3.25.7: {} diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 8e8f826a68..001b6d3e7f 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -5,8 +5,8 @@ import { readFile } from "node:fs/promises"; import { z } from "zod"; import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { Agent } from "@huggingface/mcp-client"; -import type { ServerConfig } from "@huggingface/mcp-client"; import { version as packageVersion } from "../package.json"; +import { ServerConfigSchema } from "./lib/types"; const USAGE_HELP = ` Usage: @@ -90,7 +90,7 @@ async function main() { const ConfigSchema = z.object({ model: z.string(), provider: z.enum(PROVIDERS_OR_POLICIES), - servers: z.array(z.custom()), + servers: z.array(ServerConfigSchema), }); let config: z.infer; diff --git a/packages/tiny-agents/src/lib/types.ts b/packages/tiny-agents/src/lib/types.ts new file mode 100644 index 0000000000..6c3d7bce85 --- /dev/null +++ b/packages/tiny-agents/src/lib/types.ts @@ -0,0 +1,37 @@ +import { z } from "zod"; + +export const ServerConfigSchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("stdio"), + config: z.object({ + command: z.string(), + args: z.array(z.string()).optional(), + env: z.record(z.string()).optional(), + cwd: z.string().optional(), + }), + }), + z.object({ + type: z.literal("http"), + config: z.object({ + url: z.union([z.string(), z.string().url()]), + options: z + .object({ + /** + * Session ID for the connection. This is used to identify the session on the server. + * When not provided and connecting to a server that supports session IDs, the server will generate a new session ID. + */ + sessionId: z.string().optional(), + }) + .optional(), + }), + }), + z.object({ + type: z.literal("sse"), + config: z.object({ + url: z.union([z.string(), z.string().url()]), + options: z.object({}).optional(), + }), + }), +]); + +export type ServerConfig = z.infer; From bb2f36eb3371bb2ef685c1124e018a3334f7d707 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:08:08 +0200 Subject: [PATCH 07/15] Fetch prompt.md from disk cc @gary149 --- packages/tiny-agents/src/cli.ts | 76 ++++++++++++++++----------- packages/tiny-agents/src/lib/utils.ts | 19 +++++++ 2 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 packages/tiny-agents/src/lib/utils.ts diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 001b6d3e7f..8878594891 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -7,6 +7,7 @@ import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { Agent } from "@huggingface/mcp-client"; import { version as packageVersion } from "../package.json"; import { ServerConfigSchema } from "./lib/types"; +import { ANSI, debug, error } from "./lib/utils"; const USAGE_HELP = ` Usage: @@ -32,17 +33,28 @@ const FILENAME_CONFIG = "agent.json"; // eslint-disable-next-line @typescript-eslint/no-unused-vars const FILENAME_PROMPT = "PROMPT.md"; -async function loadConfigFrom(loadFrom: string): Promise { +async function loadConfigFrom(loadFrom: string): Promise<{ configJson: string; prompt?: string }> { try { /// First try it as a local directory path, then we will try as a path inside the repo itself - return await readFile(loadFrom, { encoding: "utf8" }); + return { + configJson: await readFile(loadFrom, { encoding: "utf8" }), + }; } catch { const srcDir = dirname(__filename); - const configPath = join(srcDir, "agents", loadFrom, FILENAME_CONFIG); + const configDir = join(srcDir, "agents", loadFrom); try { - return await readFile(configPath, { encoding: "utf8" }); + let prompt: string | undefined; + try { + prompt = await readFile(join(configDir, FILENAME_PROMPT), { encoding: "utf8" }); + } catch { + debug(`PROMPT.md not found in ${configDir}, continuing without prompt template`); + } + return { + configJson: await readFile(join(configDir, FILENAME_CONFIG), { encoding: "utf8" }), + prompt, + }; } catch { - console.error(`Config file not found! Loading from the HF Hub is not implemented yet`); + error(`Config file not found! Loading from the HF Hub is not implemented yet`); process.exit(1); } } @@ -76,39 +88,41 @@ async function main() { process.exit(0); } if (positionals.length !== 2 || !isValidCommand(command)) { - console.error(`You need to call run or serve, followed by an agent id (local path or Hub identifier).`); + error(`You need to call run or serve, followed by an agent id (local path or Hub identifier).`); console.log(USAGE_HELP); process.exit(1); } - if (command === "serve") { - console.error(`Serve is not implemented yet, coming soon!`); - process.exit(1); - } else { - const configJson = await loadConfigFrom(loadFrom); + const { configJson, prompt } = await loadConfigFrom(loadFrom); - const ConfigSchema = z.object({ - model: z.string(), - provider: z.enum(PROVIDERS_OR_POLICIES), - servers: z.array(ServerConfigSchema), - }); + const ConfigSchema = z.object({ + model: z.string(), + provider: z.enum(PROVIDERS_OR_POLICIES), + servers: z.array(ServerConfigSchema), + }); - let config: z.infer; - try { - const parsedConfig = JSON.parse(configJson); - config = ConfigSchema.parse(parsedConfig); - } catch (error) { - console.error("Invalid configuration file:", error instanceof Error ? error.message : error); - process.exit(1); - } - const agent = new Agent({ - provider: config.provider, - model: config.model, - apiKey: process.env.HF_TOKEN ?? "", - servers: config.servers, - }); + let config: z.infer; + try { + const parsedConfig = JSON.parse(configJson); + config = ConfigSchema.parse(parsedConfig); + } catch (err) { + error("Invalid configuration file:", err instanceof Error ? err.message : err); + process.exit(1); + } + + const agent = new Agent({ + provider: config.provider, + model: config.model, + apiKey: process.env.HF_TOKEN ?? "", + servers: config.servers, + prompt, + }); - console.log(agent); + if (command === "serve") { + error(`Serve is not implemented yet, coming soon!`); + process.exit(1); + } else { + console.debug(agent); // TODO: hook main loop from mcp-client/cli.ts } diff --git a/packages/tiny-agents/src/lib/utils.ts b/packages/tiny-agents/src/lib/utils.ts new file mode 100644 index 0000000000..069aa75717 --- /dev/null +++ b/packages/tiny-agents/src/lib/utils.ts @@ -0,0 +1,19 @@ +import { inspect } from "util"; + +export function debug(...args: unknown[]): void { + if (process.env.DEBUG) { + console.debug(inspect(args, { depth: Infinity, colors: true })); + } +} + +export function error(...args: unknown[]): void { + console.error(ANSI.RED + args.join("") + ANSI.RESET); +} + +export const ANSI = { + BLUE: "\x1b[34m", + GRAY: "\x1b[90m", + GREEN: "\x1b[32m", + RED: "\x1b[31m", + RESET: "\x1b[0m", +}; From 4e592abedb74a8988402dfc403614f2d2ad69794 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:14:24 +0200 Subject: [PATCH 08/15] hook main loop from mcp-client/cli.ts --- packages/tiny-agents/src/cli.ts | 9 ++- packages/tiny-agents/src/lib/mainCliLoop.ts | 85 +++++++++++++++++++++ 2 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 packages/tiny-agents/src/lib/mainCliLoop.ts diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 8878594891..8fadd9f602 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -7,7 +7,8 @@ import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { Agent } from "@huggingface/mcp-client"; import { version as packageVersion } from "../package.json"; import { ServerConfigSchema } from "./lib/types"; -import { ANSI, debug, error } from "./lib/utils"; +import { debug, error } from "./lib/utils"; +import { mainCliLoop } from "./lib/mainCliLoop"; const USAGE_HELP = ` Usage: @@ -122,9 +123,9 @@ async function main() { error(`Serve is not implemented yet, coming soon!`); process.exit(1); } else { - console.debug(agent); - - // TODO: hook main loop from mcp-client/cli.ts + debug(agent); + // main loop from mcp-client/cli.ts + await mainCliLoop(agent); } } diff --git a/packages/tiny-agents/src/lib/mainCliLoop.ts b/packages/tiny-agents/src/lib/mainCliLoop.ts new file mode 100644 index 0000000000..75c20bd88b --- /dev/null +++ b/packages/tiny-agents/src/lib/mainCliLoop.ts @@ -0,0 +1,85 @@ +import * as readline from "node:readline/promises"; +import { stdin, stdout } from "node:process"; +import type { Agent } from "@huggingface/mcp-client"; +import { ANSI } from "./utils"; + +/** + * From mcp-client/cli.ts + */ +export async function mainCliLoop(agent: Agent): Promise { + const rl = readline.createInterface({ input: stdin, output: stdout }); + let abortController = new AbortController(); + let waitingForInput = false; + async function waitForInput() { + waitingForInput = true; + const input = await rl.question("> "); + waitingForInput = false; + return input; + } + rl.on("SIGINT", async () => { + if (waitingForInput) { + // close the whole process + await agent.cleanup(); + stdout.write("\n"); + rl.close(); + } else { + // otherwise, it means a request is underway + abortController.abort(); + abortController = new AbortController(); + stdout.write("\n"); + stdout.write(ANSI.GRAY); + stdout.write("Ctrl+C a second time to exit"); + stdout.write(ANSI.RESET); + stdout.write("\n"); + } + }); + process.on("uncaughtException", (err) => { + stdout.write("\n"); + rl.close(); + throw err; + }); + + await agent.loadTools(); + + stdout.write(ANSI.BLUE); + stdout.write(`Agent loaded with ${agent.availableTools.length} tools:\n`); + stdout.write(agent.availableTools.map((t) => `- ${t.function.name}`).join("\n")); + stdout.write(ANSI.RESET); + stdout.write("\n"); + + while (true) { + const input = await waitForInput(); + for await (const chunk of agent.run(input, { abortSignal: abortController.signal })) { + if ("choices" in chunk) { + const delta = chunk.choices[0]?.delta; + if (delta.content) { + stdout.write(delta.content); + } + if (delta.tool_calls) { + stdout.write(ANSI.GRAY); + for (const deltaToolCall of delta.tool_calls) { + if (deltaToolCall.id) { + stdout.write(`\n`); + } + if (deltaToolCall.function.name) { + stdout.write(deltaToolCall.function.name + " "); + } + if (deltaToolCall.function.arguments) { + stdout.write(deltaToolCall.function.arguments); + } + } + stdout.write(ANSI.RESET); + } + } else { + /// Tool call info + stdout.write("\n\n"); + stdout.write(ANSI.GREEN); + stdout.write(`Tool[${chunk.name}] ${chunk.tool_call_id}\n`); + stdout.write(chunk.content); + stdout.write(ANSI.RESET); + stdout.write("\n\n"); + } + } + stdout.write("\n"); + } +} From 30c2c94c23968c4434f8670bce2c95d530617263 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:15:51 +0200 Subject: [PATCH 09/15] Create .prettierignore --- packages/tiny-agents/.prettierignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 packages/tiny-agents/.prettierignore diff --git a/packages/tiny-agents/.prettierignore b/packages/tiny-agents/.prettierignore new file mode 100644 index 0000000000..cac0c69496 --- /dev/null +++ b/packages/tiny-agents/.prettierignore @@ -0,0 +1,4 @@ +pnpm-lock.yaml +# In order to avoid code samples to have tabs, they don't display well on npm +README.md +dist \ No newline at end of file From 4c628fb182bf5dcd9513823dfd9c97e12e96bfaa Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:17:06 +0200 Subject: [PATCH 10/15] Create .eslintignore --- packages/tiny-agents/.eslintignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 packages/tiny-agents/.eslintignore diff --git a/packages/tiny-agents/.eslintignore b/packages/tiny-agents/.eslintignore new file mode 100644 index 0000000000..53c37a1660 --- /dev/null +++ b/packages/tiny-agents/.eslintignore @@ -0,0 +1 @@ +dist \ No newline at end of file From b417aa524cc5f32acea66187cfe31ecaa382fff5 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:24:43 +0200 Subject: [PATCH 11/15] Fix CI? --- packages/tiny-agents/package.json | 1 + packages/tiny-agents/pnpm-lock.yaml | 3 +++ packages/tiny-agents/src/lib/mainCliLoop.ts | 5 +++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/tiny-agents/package.json b/packages/tiny-agents/package.json index 14442e33e2..432cb2a75a 100644 --- a/packages/tiny-agents/package.json +++ b/packages/tiny-agents/package.json @@ -53,6 +53,7 @@ "dependencies": { "@huggingface/inference": "workspace:^", "@huggingface/mcp-client": "workspace:^", + "@huggingface/tasks": "workspace:^", "@modelcontextprotocol/sdk": "^1.11.4", "zod": "^3.25.7" } diff --git a/packages/tiny-agents/pnpm-lock.yaml b/packages/tiny-agents/pnpm-lock.yaml index 5b09a016f9..3e9aca9ca8 100644 --- a/packages/tiny-agents/pnpm-lock.yaml +++ b/packages/tiny-agents/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@huggingface/mcp-client': specifier: workspace:^ version: link:../mcp-client + '@huggingface/tasks': + specifier: workspace:^ + version: link:../tasks '@modelcontextprotocol/sdk': specifier: ^1.11.4 version: 1.11.4 diff --git a/packages/tiny-agents/src/lib/mainCliLoop.ts b/packages/tiny-agents/src/lib/mainCliLoop.ts index 75c20bd88b..cdc20056ce 100644 --- a/packages/tiny-agents/src/lib/mainCliLoop.ts +++ b/packages/tiny-agents/src/lib/mainCliLoop.ts @@ -1,7 +1,8 @@ import * as readline from "node:readline/promises"; import { stdin, stdout } from "node:process"; -import type { Agent } from "@huggingface/mcp-client"; import { ANSI } from "./utils"; +import type { ChatCompletionStreamOutput } from "@huggingface/tasks"; +import type { Agent } from "../index"; /** * From mcp-client/cli.ts @@ -51,7 +52,7 @@ export async function mainCliLoop(agent: Agent): Promise { const input = await waitForInput(); for await (const chunk of agent.run(input, { abortSignal: abortController.signal })) { if ("choices" in chunk) { - const delta = chunk.choices[0]?.delta; + const delta = (chunk as ChatCompletionStreamOutput).choices[0]?.delta; if (delta.content) { stdout.write(delta.content); } From f29eeca9be6c42867649d3c2a490ab5ea0fd43f1 Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:30:30 +0200 Subject: [PATCH 12/15] tests --- .../tiny-agents/test/ServerConfigSchema.spec.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/tiny-agents/test/ServerConfigSchema.spec.ts diff --git a/packages/tiny-agents/test/ServerConfigSchema.spec.ts b/packages/tiny-agents/test/ServerConfigSchema.spec.ts new file mode 100644 index 0000000000..f7455e40d6 --- /dev/null +++ b/packages/tiny-agents/test/ServerConfigSchema.spec.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from "vitest"; +import { ServerConfigSchema } from "../src/lib/types"; + +describe("ServerConfigSchema", () => { + it("You can parse a server config", async () => { + const config = ServerConfigSchema.parse({ + type: "stdio", + config: { + command: "npx", + args: ["@playwright/mcp@latest"], + }, + }); + expect(config).toBeDefined(); + expect(config.type).toBe("stdio"); + }); +}); From 71babb53f0ea37a13dcd836e2857def2541582cd Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:32:20 +0200 Subject: [PATCH 13/15] Create tiny-agents-publish.yml --- .github/workflows/tiny-agents-publish.yml | 77 +++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 .github/workflows/tiny-agents-publish.yml diff --git a/.github/workflows/tiny-agents-publish.yml b/.github/workflows/tiny-agents-publish.yml new file mode 100644 index 0000000000..2a55acde7e --- /dev/null +++ b/.github/workflows/tiny-agents-publish.yml @@ -0,0 +1,77 @@ +name: Tiny Agents - Version and Release + +on: + workflow_dispatch: + inputs: + newversion: + type: choice + description: "Semantic Version Bump Type" + default: patch + options: + - patch + - minor + - major + bypass_deps_check: + type: boolean + description: "Bypass dependency checking" + default: false + +concurrency: + group: "push-to-main" # Consider changing this if tiny-agents has its own release concurrency group + +defaults: + run: + working-directory: packages/tiny-agents + +jobs: + version_and_release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + token: ${{ secrets.BOT_ACCESS_TOKEN }} + - run: npm install -g corepack@latest && corepack enable + - uses: actions/setup-node@v3 + with: + node-version: "20" + cache: "pnpm" + cache-dependency-path: | + packages/tiny-agents/pnpm-lock.yaml + packages/doc-internal/pnpm-lock.yaml + registry-url: "https://registry.npmjs.org" + - run: pnpm install + - run: git config --global user.name machineuser + - run: git config --global user.email infra+machineuser@huggingface.co + - run: | + PACKAGE_VERSION=$(node -p "require('./package.json').version") + BUMPED_VERSION=$(node -p "require('semver').inc('$PACKAGE_VERSION', '${{ github.event.inputs.newversion }}')") + # Update package.json with the new version + node -e "const fs = require('fs'); const package = JSON.parse(fs.readFileSync('./package.json')); package.version = '$BUMPED_VERSION'; fs.writeFileSync('./package.json', JSON.stringify(package, null, '\t') + '\n');" + pnpm --filter doc-internal run fix-cdn-versions + git add ../.. + git commit -m "🔖 @huggingface/tiny-agents $BUMPED_VERSION" + git tag "tiny-agents-v$BUMPED_VERSION" + + # Add checks for dependencies if needed, similar to hub-publish.yml + - if: ${{ !github.event.inputs.bypass_deps_check }} + name: "Check Deps are published before publishing this package" + run: pnpm -w check-deps inference && pnpm -w check-deps tasks # Review if these specific deps apply to tiny-agents + + - run: pnpm publish --no-git-checks . + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - run: (git pull --rebase && git push --follow-tags) || (git pull --rebase && git push --follow-tags) + # hack - reuse actions/setup-node@v3 just to set a new registry + - uses: actions/setup-node@v3 + with: + node-version: "20" + registry-url: "https://npm.pkg.github.com" + # Disable for now, until github supports PATs for writing github packages (https://github.com/github/roadmap/issues/558) + # - run: pnpm publish --no-git-checks . + # env: + # NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Update Doc" + uses: peter-evans/repository-dispatch@v2 + with: + event-type: doc-build + token: ${{ secrets.BOT_ACCESS_TOKEN }} From b60cfed053a26aab221dae6f9e45204ec907dd7a Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 14:53:57 +0200 Subject: [PATCH 14/15] README --- packages/tiny-agents/README.md | 44 ++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/packages/tiny-agents/README.md b/packages/tiny-agents/README.md index 231972999c..bbf00d887f 100644 --- a/packages/tiny-agents/README.md +++ b/packages/tiny-agents/README.md @@ -1,14 +1,14 @@ # @huggingface/tiny-agents -A lightweight, composable agent framework for AI applications built on Hugging Face's JS stack. +![meme](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/blog/tiny-agents/legos.png) + +A squad of lightweight composable AI applications built on Hugging Face's Inference Client and MCP stack. ## Installation ```bash npm install @huggingface/tiny-agents # or -yarn add @huggingface/tiny-agents -# or pnpm add @huggingface/tiny-agents ``` @@ -30,8 +30,44 @@ Available Commands: serve Run the Agent as an OpenAI-compatible HTTP server ``` +## Define your own agent + +The simplest way to create your own agent is to create a folder containing an `agent.json` file: + +```bash +mkdir my-agent +touch my-agent/agent.json +``` + +```json +{ + "model": "Qwen/Qwen2.5-72B-Instruct", // model id + "provider": "nebius", // or you can also use a local endpoint base url with `endpointUrl` + "servers": [ + { + "type": "stdio", + "config": { + "command": "npx", + "args": ["@playwright/mcp@latest"] + } + } + ] +} +``` + +`servers` is a list of MCP servers (we support Stdio, SSE, and HTTP servers). + +Optionally, you can add a `PROMPT.md` file to override the default Agent prompt. + +Then just point tiny-agents to your local folder: + +```bash +npx @huggingface/tiny-agents run ./my-agent +``` + +Voilà! 🔥 -## Programmatic Usage +### Advanced: Programmatic Usage ```typescript import { Agent } from '@huggingface/tiny-agents'; From b7f61d3bef9f2730d6ffb1ca61ee7334e13f499b Mon Sep 17 00:00:00 2001 From: Julien Chaumond Date: Tue, 20 May 2025 15:06:24 +0200 Subject: [PATCH 15/15] handle local directory --- packages/tiny-agents/README.md | 5 ++++- packages/tiny-agents/src/cli.ts | 24 +++++++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/tiny-agents/README.md b/packages/tiny-agents/README.md index bbf00d887f..e22fc3b41e 100644 --- a/packages/tiny-agents/README.md +++ b/packages/tiny-agents/README.md @@ -55,7 +55,7 @@ touch my-agent/agent.json } ``` -`servers` is a list of MCP servers (we support Stdio, SSE, and HTTP servers). +Where `servers` is a list of MCP servers (we support Stdio, SSE, and HTTP servers). Optionally, you can add a `PROMPT.md` file to override the default Agent prompt. @@ -67,6 +67,9 @@ npx @huggingface/tiny-agents run ./my-agent Voilà! 🔥 +> [!NOTE] +> Note: you can open a PR in the huggingface.js repo to share your agent with the community, just upload it inside the `src/agents/` directory. + ### Advanced: Programmatic Usage ```typescript diff --git a/packages/tiny-agents/src/cli.ts b/packages/tiny-agents/src/cli.ts index 8fadd9f602..d33fcb5073 100644 --- a/packages/tiny-agents/src/cli.ts +++ b/packages/tiny-agents/src/cli.ts @@ -1,7 +1,7 @@ #!/usr/bin/env node import { dirname, join } from "node:path"; import { parseArgs } from "node:util"; -import { readFile } from "node:fs/promises"; +import { lstat, readFile } from "node:fs/promises"; import { z } from "zod"; import { PROVIDERS_OR_POLICIES } from "@huggingface/inference"; import { Agent } from "@huggingface/mcp-client"; @@ -36,11 +36,29 @@ const FILENAME_PROMPT = "PROMPT.md"; async function loadConfigFrom(loadFrom: string): Promise<{ configJson: string; prompt?: string }> { try { - /// First try it as a local directory path, then we will try as a path inside the repo itself + /// First try it as a local file path, then as a local directory, then we will try as a path inside the repo itself return { configJson: await readFile(loadFrom, { encoding: "utf8" }), }; } catch { + if ((await lstat(loadFrom)).isDirectory()) { + /// local directory + try { + let prompt: string | undefined; + try { + prompt = await readFile(join(loadFrom, FILENAME_PROMPT), { encoding: "utf8" }); + } catch { + debug(`PROMPT.md not found in ${loadFrom}, continuing without prompt template`); + } + return { + configJson: await readFile(join(loadFrom, FILENAME_CONFIG), { encoding: "utf8" }), + prompt, + }; + } catch { + error(`Config file not found in specified local directory.`); + process.exit(1); + } + } const srcDir = dirname(__filename); const configDir = join(srcDir, "agents", loadFrom); try { @@ -55,7 +73,7 @@ async function loadConfigFrom(loadFrom: string): Promise<{ configJson: string; p prompt, }; } catch { - error(`Config file not found! Loading from the HF Hub is not implemented yet`); + error(`Config file not found in tiny-agents repo! Loading from the HF Hub is not implemented yet`); process.exit(1); } }