@aiwerk/mcp-bridge 1.0.0

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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +117 -0
  3. package/bin/mcp-bridge.js +9 -0
  4. package/bin/mcp-bridge.ts +335 -0
  5. package/package.json +42 -0
  6. package/scripts/install-server.ps1 +300 -0
  7. package/scripts/install-server.sh +357 -0
  8. package/servers/apify/README.md +40 -0
  9. package/servers/apify/config.json +13 -0
  10. package/servers/apify/env_vars +1 -0
  11. package/servers/apify/install.ps1 +3 -0
  12. package/servers/apify/install.sh +4 -0
  13. package/servers/candidates.md +13 -0
  14. package/servers/github/README.md +40 -0
  15. package/servers/github/config.json +21 -0
  16. package/servers/github/env_vars +1 -0
  17. package/servers/github/install.ps1 +3 -0
  18. package/servers/github/install.sh +4 -0
  19. package/servers/google-maps/README.md +40 -0
  20. package/servers/google-maps/config.json +17 -0
  21. package/servers/google-maps/env_vars +1 -0
  22. package/servers/google-maps/install.ps1 +3 -0
  23. package/servers/google-maps/install.sh +4 -0
  24. package/servers/hetzner/README.md +41 -0
  25. package/servers/hetzner/config.json +16 -0
  26. package/servers/hetzner/env_vars +1 -0
  27. package/servers/hetzner/install.ps1 +3 -0
  28. package/servers/hetzner/install.sh +4 -0
  29. package/servers/hostinger/README.md +40 -0
  30. package/servers/hostinger/config.json +17 -0
  31. package/servers/hostinger/env_vars +1 -0
  32. package/servers/hostinger/install.ps1 +3 -0
  33. package/servers/hostinger/install.sh +4 -0
  34. package/servers/index.json +125 -0
  35. package/servers/linear/README.md +40 -0
  36. package/servers/linear/config.json +16 -0
  37. package/servers/linear/env_vars +1 -0
  38. package/servers/linear/install.ps1 +3 -0
  39. package/servers/linear/install.sh +4 -0
  40. package/servers/miro/README.md +40 -0
  41. package/servers/miro/config.json +19 -0
  42. package/servers/miro/env_vars +1 -0
  43. package/servers/miro/install.ps1 +3 -0
  44. package/servers/miro/install.sh +4 -0
  45. package/servers/notion/README.md +42 -0
  46. package/servers/notion/config.json +17 -0
  47. package/servers/notion/env_vars +1 -0
  48. package/servers/notion/install.ps1 +3 -0
  49. package/servers/notion/install.sh +4 -0
  50. package/servers/stripe/README.md +40 -0
  51. package/servers/stripe/config.json +19 -0
  52. package/servers/stripe/env_vars +1 -0
  53. package/servers/stripe/install.ps1 +3 -0
  54. package/servers/stripe/install.sh +4 -0
  55. package/servers/tavily/README.md +40 -0
  56. package/servers/tavily/config.json +17 -0
  57. package/servers/tavily/env_vars +1 -0
  58. package/servers/tavily/install.ps1 +3 -0
  59. package/servers/tavily/install.sh +4 -0
  60. package/servers/todoist/README.md +40 -0
  61. package/servers/todoist/config.json +17 -0
  62. package/servers/todoist/env_vars +1 -0
  63. package/servers/todoist/install.ps1 +3 -0
  64. package/servers/todoist/install.sh +4 -0
  65. package/servers/wise/README.md +41 -0
  66. package/servers/wise/config.json +16 -0
  67. package/servers/wise/env_vars +1 -0
  68. package/servers/wise/install.ps1 +3 -0
  69. package/servers/wise/install.sh +4 -0
  70. package/src/config.ts +168 -0
  71. package/src/index.ts +44 -0
  72. package/src/mcp-router.ts +366 -0
  73. package/src/protocol.ts +69 -0
  74. package/src/schema-convert.ts +178 -0
  75. package/src/standalone-server.ts +385 -0
  76. package/src/tool-naming.ts +51 -0
  77. package/src/transport-base.ts +199 -0
  78. package/src/transport-sse.ts +230 -0
  79. package/src/transport-stdio.ts +312 -0
  80. package/src/transport-streamable-http.ts +188 -0
  81. package/src/types.ts +88 -0
  82. package/src/update-checker.ts +155 -0
  83. package/tests/collision.test.ts +60 -0
  84. package/tests/env-resolve.test.ts +68 -0
  85. package/tests/mcp-router.test.ts +301 -0
  86. package/tests/schema-convert.test.ts +70 -0
  87. package/tests/transport-base.test.ts +214 -0
  88. package/tsconfig.json +15 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AIWerk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # @aiwerk/mcp-bridge
2
+
3
+ Standalone MCP server that multiplexes multiple MCP servers into one interface. Works with Claude Desktop, Cursor, Windsurf, Cline, or any MCP client.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @aiwerk/mcp-bridge
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Initialize config directory
15
+ mcp-bridge init
16
+
17
+ # Edit your config
18
+ vi ~/.mcp-bridge/config.json
19
+
20
+ # Install a server from the catalog
21
+ mcp-bridge install todoist
22
+
23
+ # Start (stdio mode for MCP clients)
24
+ mcp-bridge
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ Config location: `~/.mcp-bridge/config.json`
30
+
31
+ ```json
32
+ {
33
+ "mode": "router",
34
+ "servers": {
35
+ "todoist": {
36
+ "transport": "stdio",
37
+ "command": "npx",
38
+ "args": ["-y", "@doist/todoist-ai"],
39
+ "env": {
40
+ "TODOIST_API_KEY": "${TODOIST_API_TOKEN}"
41
+ },
42
+ "description": "Task management"
43
+ },
44
+ "github": {
45
+ "transport": "stdio",
46
+ "command": "docker",
47
+ "args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
48
+ "env": {
49
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
50
+ },
51
+ "description": "GitHub repos, issues, PRs"
52
+ }
53
+ },
54
+ "toolPrefix": true,
55
+ "connectionTimeoutMs": 5000,
56
+ "requestTimeoutMs": 60000
57
+ }
58
+ ```
59
+
60
+ Environment variables go in `~/.mcp-bridge/.env`:
61
+
62
+ ```
63
+ TODOIST_API_TOKEN=your-token-here
64
+ GITHUB_TOKEN=ghp_xxxxx
65
+ ```
66
+
67
+ ## Modes
68
+
69
+ **Router mode** (default) — exposes a single `mcp` meta-tool. The agent calls `mcp(server="todoist", action="list")` to discover tools, then `mcp(server="todoist", tool="get_tasks", params={...})` to call them. ~99% reduction in tool registration tokens.
70
+
71
+ **Direct mode** — registers all tools from all servers individually (`todoist_get_tasks`, `github_list_repos`, etc.). Better for few servers or simpler agents.
72
+
73
+ ## Claude Desktop
74
+
75
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "mcp-bridge": {
81
+ "command": "mcp-bridge",
82
+ "args": []
83
+ }
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## CLI
89
+
90
+ ```bash
91
+ mcp-bridge # stdio mode (default)
92
+ mcp-bridge --verbose # info-level logs to stderr
93
+ mcp-bridge --debug # full protocol logs to stderr
94
+ mcp-bridge --config ./config.json # custom config file
95
+
96
+ mcp-bridge init # create ~/.mcp-bridge/ with template
97
+ mcp-bridge install <server> # install from catalog
98
+ mcp-bridge catalog # list available servers
99
+ mcp-bridge servers # list configured servers
100
+ mcp-bridge search <query> # search catalog
101
+ mcp-bridge update --check # check for updates
102
+ mcp-bridge update # install updates
103
+ ```
104
+
105
+ ## Library Usage
106
+
107
+ ```typescript
108
+ import { McpRouter, StandaloneServer, loadConfig } from "@aiwerk/mcp-bridge";
109
+
110
+ const config = loadConfig({ configPath: "./config.json" });
111
+ const server = new StandaloneServer(config, console);
112
+ await server.startStdio();
113
+ ```
114
+
115
+ ## License
116
+
117
+ MIT
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ // Bootstrap: run the TypeScript entry point via tsx
4
+ import { register } from "node:module";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ register("tsx/esm", pathToFileURL("./"));
8
+
9
+ const { default: _ } = await import("./mcp-bridge.ts");
@@ -0,0 +1,335 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, existsSync } from "fs";
4
+ import { join, dirname, resolve } from "path";
5
+ import { fileURLToPath } from "url";
6
+ import { execSync } from "child_process";
7
+ import { loadConfig, initConfigDir, getConfigDir } from "../src/config.js";
8
+ import { StandaloneServer } from "../src/standalone-server.js";
9
+ import { PACKAGE_VERSION } from "../src/protocol.js";
10
+ import { checkForUpdate, runUpdate } from "../src/update-checker.js";
11
+ import type { Logger } from "../src/types.js";
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = dirname(__filename);
15
+
16
+ // -- Logger ---------------------------------------------------------------
17
+
18
+ type LogLevel = "error" | "warn" | "info" | "debug";
19
+
20
+ function createLogger(level: LogLevel): Logger {
21
+ const levels: Record<LogLevel, number> = { error: 0, warn: 1, info: 2, debug: 3 };
22
+ const threshold = levels[level];
23
+ const ts = () => new Date().toISOString().replace("T", " ").replace("Z", "");
24
+
25
+ return {
26
+ error: (...args: unknown[]) => {
27
+ if (threshold >= 0) process.stderr.write(`[${ts()}] [ERROR] ${args.map(String).join(" ")}\n`);
28
+ },
29
+ warn: (...args: unknown[]) => {
30
+ if (threshold >= 1) process.stderr.write(`[${ts()}] [WARN] ${args.map(String).join(" ")}\n`);
31
+ },
32
+ info: (...args: unknown[]) => {
33
+ if (threshold >= 2) process.stderr.write(`[${ts()}] [INFO] ${args.map(String).join(" ")}\n`);
34
+ },
35
+ debug: (...args: unknown[]) => {
36
+ if (threshold >= 3) process.stderr.write(`[${ts()}] [DEBUG] ${args.map(String).join(" ")}\n`);
37
+ },
38
+ };
39
+ }
40
+
41
+ // -- Arg parsing ----------------------------------------------------------
42
+
43
+ interface CliArgs {
44
+ command: "serve" | "init" | "install" | "catalog" | "servers" | "search" | "update" | "version" | "help";
45
+ sse: boolean;
46
+ http: boolean;
47
+ port: number;
48
+ configPath?: string;
49
+ verbose: boolean;
50
+ debug: boolean;
51
+ positional: string[];
52
+ checkOnly: boolean;
53
+ offline: boolean;
54
+ }
55
+
56
+ function parseArgs(argv: string[]): CliArgs {
57
+ const args: CliArgs = {
58
+ command: "serve",
59
+ sse: false,
60
+ http: false,
61
+ port: 3000,
62
+ verbose: false,
63
+ debug: false,
64
+ positional: [],
65
+ checkOnly: false,
66
+ offline: false,
67
+ };
68
+
69
+ let i = 0;
70
+ while (i < argv.length) {
71
+ const arg = argv[i];
72
+ switch (arg) {
73
+ case "--sse": args.sse = true; break;
74
+ case "--http": args.http = true; break;
75
+ case "--port":
76
+ i++;
77
+ args.port = parseInt(argv[i], 10);
78
+ if (isNaN(args.port)) { process.stderr.write("Error: --port requires a number\n"); process.exit(1); }
79
+ break;
80
+ case "--config":
81
+ i++;
82
+ args.configPath = resolve(argv[i]);
83
+ break;
84
+ case "--verbose": args.verbose = true; break;
85
+ case "--debug": args.debug = true; break;
86
+ case "--version": args.command = "version"; break;
87
+ case "--help": case "-h": args.command = "help"; break;
88
+ case "--check": args.checkOnly = true; break;
89
+ case "--offline": args.offline = true; break;
90
+ case "init": args.command = "init"; break;
91
+ case "install": args.command = "install"; break;
92
+ case "catalog": args.command = "catalog"; break;
93
+ case "servers": args.command = "servers"; break;
94
+ case "search": args.command = "search"; break;
95
+ case "update": args.command = "update"; break;
96
+ default:
97
+ if (!arg.startsWith("-")) {
98
+ args.positional.push(arg);
99
+ }
100
+ break;
101
+ }
102
+ i++;
103
+ }
104
+
105
+ return args;
106
+ }
107
+
108
+ // -- Commands -------------------------------------------------------------
109
+
110
+ function printVersion(): void {
111
+ process.stdout.write(`mcp-bridge ${PACKAGE_VERSION}\n`);
112
+ }
113
+
114
+ function printHelp(): void {
115
+ process.stdout.write(`
116
+ mcp-bridge v${PACKAGE_VERSION} — MCP server multiplexer
117
+
118
+ Usage:
119
+ mcp-bridge Start in stdio mode (default)
120
+ mcp-bridge --sse --port 3000 Start as SSE server
121
+ mcp-bridge --http --port 3000 Start as streamable-http server
122
+ mcp-bridge init Create ~/.mcp-bridge/ with config template
123
+ mcp-bridge install <server> Install a server from the catalog
124
+ mcp-bridge catalog [--offline] List available servers
125
+ mcp-bridge servers List configured servers
126
+ mcp-bridge search <query> Search catalog by keyword
127
+ mcp-bridge update [--check] Check for / install updates
128
+
129
+ Options:
130
+ --config PATH Custom config file (default: ~/.mcp-bridge/config.json)
131
+ --verbose Info-level logs to stderr
132
+ --debug Full protocol-level logs to stderr
133
+ --version Print version
134
+ --help Show this help
135
+
136
+ All logs go to stderr. Stdout is reserved for the MCP protocol (stdio mode).
137
+ `);
138
+ }
139
+
140
+ function cmdInit(logger: Logger): void {
141
+ initConfigDir(logger);
142
+ }
143
+
144
+ function cmdCatalog(logger: Logger, offline: boolean): void {
145
+ const catalogPath = join(__dirname, "..", "servers", "index.json");
146
+ if (!existsSync(catalogPath)) {
147
+ logger.error("Server catalog not found");
148
+ process.exit(1);
149
+ }
150
+
151
+ const catalog = JSON.parse(readFileSync(catalogPath, "utf-8"));
152
+ const servers = catalog.servers || {};
153
+
154
+ process.stdout.write("\nAvailable servers:\n\n");
155
+ process.stdout.write(" Server Transport Description\n");
156
+ process.stdout.write(" " + "─".repeat(60) + "\n");
157
+
158
+ for (const [name, info] of Object.entries(servers) as [string, any][]) {
159
+ const padded = name.padEnd(16);
160
+ const transport = (info.transport || "stdio").padEnd(13);
161
+ process.stdout.write(` ${padded}${transport}${info.description || ""}\n`);
162
+ }
163
+ process.stdout.write("\n");
164
+ }
165
+
166
+ function cmdServers(logger: Logger, configPath?: string): void {
167
+ try {
168
+ const config = loadConfig({ configPath, logger });
169
+ const servers = config.servers || {};
170
+ const entries = Object.entries(servers);
171
+
172
+ if (entries.length === 0) {
173
+ process.stdout.write("No servers configured.\n");
174
+ return;
175
+ }
176
+
177
+ process.stdout.write("\nConfigured servers:\n\n");
178
+ process.stdout.write(" Server Transport Description\n");
179
+ process.stdout.write(" " + "─".repeat(60) + "\n");
180
+
181
+ for (const [name, serverConfig] of entries) {
182
+ const padded = name.padEnd(16);
183
+ const transport = serverConfig.transport.padEnd(13);
184
+ process.stdout.write(` ${padded}${transport}${serverConfig.description || ""}\n`);
185
+ }
186
+ process.stdout.write("\n");
187
+ } catch (err) {
188
+ logger.error(err instanceof Error ? err.message : String(err));
189
+ process.exit(1);
190
+ }
191
+ }
192
+
193
+ function cmdSearch(query: string, logger: Logger): void {
194
+ const catalogPath = join(__dirname, "..", "servers", "index.json");
195
+ if (!existsSync(catalogPath)) {
196
+ logger.error("Server catalog not found");
197
+ process.exit(1);
198
+ }
199
+
200
+ const catalog = JSON.parse(readFileSync(catalogPath, "utf-8"));
201
+ const servers = catalog.servers || {};
202
+ const lowerQuery = query.toLowerCase();
203
+
204
+ const matches = Object.entries(servers).filter(([name, info]: [string, any]) => {
205
+ return name.toLowerCase().includes(lowerQuery) ||
206
+ (info.description || "").toLowerCase().includes(lowerQuery);
207
+ });
208
+
209
+ if (matches.length === 0) {
210
+ process.stdout.write(`No servers matching "${query}"\n`);
211
+ return;
212
+ }
213
+
214
+ process.stdout.write(`\nSearch results for "${query}":\n\n`);
215
+ for (const [i, [name, info]] of matches.entries() as any) {
216
+ process.stdout.write(` ${i + 1} ${name.padEnd(16)}${(info as any).description || ""}\n`);
217
+ }
218
+ process.stdout.write("\n");
219
+ }
220
+
221
+ function cmdInstall(serverName: string, logger: Logger): void {
222
+ const scriptPath = join(__dirname, "..", "scripts", "install-server.sh");
223
+ if (!existsSync(scriptPath)) {
224
+ logger.error("Install script not found");
225
+ process.exit(1);
226
+ }
227
+
228
+ try {
229
+ execSync(`bash "${scriptPath}" "${serverName}"`, { stdio: "inherit" });
230
+ } catch (err) {
231
+ process.exit(1);
232
+ }
233
+ }
234
+
235
+ async function cmdUpdate(logger: Logger, checkOnly: boolean): Promise<void> {
236
+ if (checkOnly) {
237
+ const info = await checkForUpdate(logger);
238
+ if (info.updateAvailable) {
239
+ process.stdout.write(`Update available: ${info.currentVersion} → ${info.latestVersion}\n`);
240
+ process.stdout.write(`Run 'mcp-bridge update' to install.\n`);
241
+ } else {
242
+ process.stdout.write(`mcp-bridge ${info.currentVersion} is up to date.\n`);
243
+ }
244
+ return;
245
+ }
246
+
247
+ const result = await runUpdate(logger);
248
+ process.stdout.write(result + "\n");
249
+ }
250
+
251
+ async function cmdServe(args: CliArgs, logger: Logger): Promise<void> {
252
+ let config;
253
+ try {
254
+ config = loadConfig({ configPath: args.configPath, logger });
255
+ } catch (err) {
256
+ logger.error(err instanceof Error ? err.message : String(err));
257
+ process.exit(1);
258
+ }
259
+
260
+ // HTTP modes: require auth
261
+ if ((args.sse || args.http) && !config.http?.auth?.token) {
262
+ logger.error("HTTP auth not configured. Set http.auth in config or use stdio mode.");
263
+ process.exit(1);
264
+ }
265
+
266
+ const server = new StandaloneServer(config, logger);
267
+
268
+ // Graceful shutdown
269
+ const shutdown = async () => {
270
+ await server.shutdown();
271
+ process.exit(0);
272
+ };
273
+ process.on("SIGTERM", shutdown);
274
+ process.on("SIGINT", shutdown);
275
+
276
+ if (args.sse || args.http) {
277
+ // SSE/HTTP mode: not yet implemented in standalone, show message
278
+ logger.error("SSE and HTTP server modes are not yet implemented. Use stdio mode (default).");
279
+ process.exit(1);
280
+ }
281
+
282
+ // Default: stdio mode
283
+ await server.startStdio();
284
+ }
285
+
286
+ // -- Main -----------------------------------------------------------------
287
+
288
+ async function main(): Promise<void> {
289
+ const args = parseArgs(process.argv.slice(2));
290
+ const logLevel: LogLevel = args.debug ? "debug" : args.verbose ? "info" : "warn";
291
+ const logger = createLogger(logLevel);
292
+
293
+ switch (args.command) {
294
+ case "version":
295
+ printVersion();
296
+ break;
297
+ case "help":
298
+ printHelp();
299
+ break;
300
+ case "init":
301
+ cmdInit(logger);
302
+ break;
303
+ case "catalog":
304
+ cmdCatalog(logger, args.offline);
305
+ break;
306
+ case "servers":
307
+ cmdServers(logger, args.configPath);
308
+ break;
309
+ case "search":
310
+ if (args.positional.length === 0) {
311
+ process.stderr.write("Usage: mcp-bridge search <query>\n");
312
+ process.exit(1);
313
+ }
314
+ cmdSearch(args.positional[0], logger);
315
+ break;
316
+ case "install":
317
+ if (args.positional.length === 0) {
318
+ process.stderr.write("Usage: mcp-bridge install <server>\n");
319
+ process.exit(1);
320
+ }
321
+ cmdInstall(args.positional[0], logger);
322
+ break;
323
+ case "update":
324
+ await cmdUpdate(logger, args.checkOnly);
325
+ break;
326
+ case "serve":
327
+ await cmdServe(args, logger);
328
+ break;
329
+ }
330
+ }
331
+
332
+ main().catch(err => {
333
+ process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}\n`);
334
+ process.exit(1);
335
+ });
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@aiwerk/mcp-bridge",
3
+ "version": "1.0.0",
4
+ "description": "Standalone MCP server that multiplexes multiple MCP servers into one interface",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "bin": {
8
+ "mcp-bridge": "./bin/mcp-bridge.js"
9
+ },
10
+ "license": "MIT",
11
+ "author": "AIWerk <kontakt@aiwerk.ch>",
12
+ "homepage": "https://github.com/AIWerk/mcp-bridge#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/AIWerk/mcp-bridge"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "model-context-protocol",
20
+ "bridge",
21
+ "multiplexer",
22
+ "ai",
23
+ "agent",
24
+ "tools"
25
+ ],
26
+ "engines": {
27
+ "node": ">=20.0.0"
28
+ },
29
+ "scripts": {
30
+ "build": "tsc",
31
+ "test": "node --import tsx --test tests/*.test.ts",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "dependencies": {
35
+ "@sinclair/typebox": "^0.34.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^22.0.0",
39
+ "tsx": "^4.0.0",
40
+ "typescript": "^5.7.0"
41
+ }
42
+ }