muxed 0.1.0 → 0.2.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.
@@ -6,6 +6,7 @@ type StdioServerConfig = {
6
6
  args?: string[];
7
7
  env?: Record<string, string>;
8
8
  cwd?: string;
9
+ timeout?: number;
9
10
  };
10
11
  type ClientCredentialsAuth = {
11
12
  type: 'client_credentials';
@@ -33,6 +34,7 @@ type HttpServerConfig = {
33
34
  maxRetries?: number;
34
35
  };
35
36
  auth?: OAuthConfig;
37
+ timeout?: number;
36
38
  };
37
39
  type ServerConfig = StdioServerConfig | HttpServerConfig;
38
40
  type DaemonConfig = {
@@ -67,22 +69,23 @@ type ServerState = {
67
69
  };
68
70
  //#endregion
69
71
  //#region src/client/socket.d.ts
70
- declare class TooldError extends Error {
72
+ declare class MuxedError extends Error {
71
73
  readonly code: number;
72
74
  readonly data?: unknown;
73
75
  constructor(code: number, message: string, data?: unknown);
74
76
  }
75
77
  //#endregion
76
78
  //#region src/client/index.d.ts
77
- /** Tool type map – empty by default, populated by `toold typegen`. */
78
- interface TooldToolMap {}
79
- type HasTools = keyof TooldToolMap extends never ? false : true;
79
+ /** Tool type map – empty by default, populated by `muxed typegen`. */
80
+ interface MuxedToolMap {}
81
+ type HasTools = keyof MuxedToolMap extends never ? false : true;
80
82
  type CreateClientOptions = {
81
- /** Path to toold.config.json. Uses default resolution if omitted. */configPath?: string; /** Skip auto-starting the daemon. Throws if daemon is not running. Default: true. */
83
+ /** Path to muxed.config.json. Uses default resolution if omitted. */configPath?: string; /** Skip auto-starting the daemon. Throws if daemon is not running. Default: true. */
82
84
  autoStart?: boolean;
83
85
  };
84
86
  type CallOptions = {
85
- /** Request timeout in milliseconds. */timeout?: number;
87
+ /** Request timeout in milliseconds. */timeout?: number; /** Dot-notation field paths to extract from the response. */
88
+ fields?: string[];
86
89
  };
87
90
  type CallResult = {
88
91
  content: Array<{
@@ -117,10 +120,20 @@ type ReloadResult = {
117
120
  removed: string[];
118
121
  changed: string[];
119
122
  };
123
+ type ValidationResult = {
124
+ valid: boolean;
125
+ errors: string[];
126
+ warnings: string[]; /** True when the tool's schema uses features not supported by dry-run validation. */
127
+ unsupported?: boolean;
128
+ tool?: {
129
+ name: string;
130
+ annotations?: Record<string, unknown>;
131
+ };
132
+ };
120
133
  type TaskStatus = Record<string, unknown>;
121
134
  type TaskResult = Record<string, unknown>;
122
135
  type TaskCancelResult = Record<string, unknown>;
123
- declare class TooldClient {
136
+ declare class MuxedClient {
124
137
  #private;
125
138
  /** @internal Use `createClient()` instead. */
126
139
  constructor(send: (method: string, params?: Record<string, unknown>) => Promise<unknown>);
@@ -134,7 +147,8 @@ declare class TooldClient {
134
147
  server: string;
135
148
  tool: Tool$1;
136
149
  }>>;
137
- call<K extends string>(name: HasTools extends true ? (K extends keyof TooldToolMap ? K : K & {}) : string, args?: K extends keyof TooldToolMap ? TooldToolMap[K]['input'] : Record<string, unknown>, options?: CallOptions): Promise<K extends keyof TooldToolMap ? TooldToolMap[K]['output'] : CallResult>;
150
+ call<K extends string>(name: HasTools extends true ? (K extends keyof MuxedToolMap ? K : K & {}) : string, args?: K extends keyof MuxedToolMap ? MuxedToolMap[K]['input'] : Record<string, unknown>, options?: CallOptions): Promise<K extends keyof MuxedToolMap ? MuxedToolMap[K]['output'] : CallResult>;
151
+ validate(name: string, args?: Record<string, unknown>): Promise<ValidationResult>;
138
152
  callAsync(name: string, args?: Record<string, unknown>): Promise<TaskHandle>;
139
153
  resources(server?: string): Promise<Array<{
140
154
  server: string;
@@ -166,6 +180,6 @@ declare class TooldClient {
166
180
  stop(): Promise<void>;
167
181
  close(): void;
168
182
  }
169
- declare function createClient(options?: CreateClientOptions): Promise<TooldClient>;
183
+ declare function createClient(options?: CreateClientOptions): Promise<MuxedClient>;
170
184
  //#endregion
171
- export { CallOptions, CallResult, CreateClientOptions, type DaemonConfig, DaemonStatus, type HttpServerConfig, type Prompt, ReloadResult, type Resource, type ServerConfig, type ServerState, type StdioServerConfig, TaskCancelResult, TaskHandle, TaskResult, TaskStatus, type Tool, TooldClient, TooldError, TooldToolMap, createClient };
185
+ export { CallOptions, CallResult, CreateClientOptions, type DaemonConfig, DaemonStatus, type HttpServerConfig, MuxedClient, MuxedError, MuxedToolMap, type Prompt, ReloadResult, type Resource, type ServerConfig, type ServerState, type StdioServerConfig, TaskCancelResult, TaskHandle, TaskResult, TaskStatus, type Tool, ValidationResult, createClient };
@@ -3,20 +3,20 @@ import { fork } from "node:child_process";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
6
- function getTooldDir() {
7
- return path.join(os.homedir(), ".toold");
6
+ function getMuxedDir() {
7
+ return path.join(os.homedir(), ".muxed");
8
8
  }
9
9
  function getSocketPath() {
10
- return path.join(getTooldDir(), "toold.sock");
10
+ return path.join(getMuxedDir(), "muxed.sock");
11
11
  }
12
12
  function getPidPath() {
13
- return path.join(getTooldDir(), "toold.pid");
13
+ return path.join(getMuxedDir(), "muxed.pid");
14
14
  }
15
- function ensureTooldDir() {
16
- fs.mkdirSync(getTooldDir(), { recursive: true });
15
+ function ensureMuxedDir() {
16
+ fs.mkdirSync(getMuxedDir(), { recursive: true });
17
17
  }
18
18
  function getLockPath() {
19
- return path.join(getTooldDir(), "toold.lock");
19
+ return path.join(getMuxedDir(), "muxed.lock");
20
20
  }
21
21
  function getDaemonPid() {
22
22
  try {
@@ -35,10 +35,10 @@ function isProcessAlive(pid) {
35
35
  return false;
36
36
  }
37
37
  }
38
- function isTooldProcess(pid) {
38
+ function isMuxedProcess(pid) {
39
39
  try {
40
40
  const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf-8");
41
- return cmdline.includes("toold") || cmdline.includes("node");
41
+ return cmdline.includes("muxed") || cmdline.includes("node");
42
42
  } catch {
43
43
  return isProcessAlive(pid);
44
44
  }
@@ -64,13 +64,13 @@ function tryConnectSocket(socketPath) {
64
64
  function acquireLock() {
65
65
  const lockPath = getLockPath();
66
66
  try {
67
- ensureTooldDir();
67
+ ensureMuxedDir();
68
68
  fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
69
69
  return true;
70
70
  } catch (err) {
71
71
  if (err.code === "EEXIST") try {
72
72
  const lockPid = parseInt(fs.readFileSync(lockPath, "utf-8").trim(), 10);
73
- if (!Number.isFinite(lockPid) || !isProcessAlive(lockPid) || !isTooldProcess(lockPid)) {
73
+ if (!Number.isFinite(lockPid) || !isProcessAlive(lockPid) || !isMuxedProcess(lockPid)) {
74
74
  fs.unlinkSync(lockPath);
75
75
  try {
76
76
  fs.writeFileSync(lockPath, String(process.pid), { flag: "wx" });
@@ -96,7 +96,7 @@ async function isDaemonRunning() {
96
96
  const pid = getDaemonPid();
97
97
  if (pid === null) return false;
98
98
  if (!isProcessAlive(pid)) return false;
99
- if (!isTooldProcess(pid)) return false;
99
+ if (!isMuxedProcess(pid)) return false;
100
100
  return tryConnectSocket(getSocketPath());
101
101
  }
102
102
  async function cleanupStaleFiles() {
@@ -113,7 +113,7 @@ async function cleanupStaleFiles() {
113
113
  releaseLock();
114
114
  return;
115
115
  }
116
- if (pid !== null && isProcessAlive(pid) && !isTooldProcess(pid)) {
116
+ if (pid !== null && isProcessAlive(pid) && !isMuxedProcess(pid)) {
117
117
  try {
118
118
  fs.unlinkSync(pidPath);
119
119
  } catch {}
@@ -178,12 +178,12 @@ async function daemonize(configPath) {
178
178
  releaseLock();
179
179
  }
180
180
  }
181
- var TooldError = class extends Error {
181
+ var MuxedError = class extends Error {
182
182
  code;
183
183
  data;
184
184
  constructor(code, message, data) {
185
185
  super(message);
186
- this.name = "TooldError";
186
+ this.name = "MuxedError";
187
187
  this.code = code;
188
188
  this.data = data;
189
189
  }
@@ -226,7 +226,7 @@ async function sendRequest(method, params) {
226
226
  const socket = net.createConnection(socketPath);
227
227
  let buffer = "";
228
228
  socket.on("error", (err) => {
229
- if (err.code === "ENOENT") reject(/* @__PURE__ */ new Error("Daemon is not running. Run `toold status` to check."));
229
+ if (err.code === "ENOENT") reject(/* @__PURE__ */ new Error("Daemon is not running. Run `muxed status` to check."));
230
230
  else if (err.code === "ECONNREFUSED") reject(/* @__PURE__ */ new Error("Daemon may have crashed. Try running a command to auto-restart it."));
231
231
  else reject(err);
232
232
  });
@@ -247,7 +247,7 @@ async function sendRequest(method, params) {
247
247
  socket.destroy();
248
248
  try {
249
249
  const response = JSON.parse(line);
250
- if (response.error) reject(new TooldError(response.error.code, response.error.message, response.error.data));
250
+ if (response.error) reject(new MuxedError(response.error.code, response.error.message, response.error.data));
251
251
  else resolve(response.result);
252
252
  } catch {
253
253
  reject(/* @__PURE__ */ new Error("Invalid response from daemon"));
@@ -255,7 +255,7 @@ async function sendRequest(method, params) {
255
255
  });
256
256
  });
257
257
  }
258
- var TooldClient = class {
258
+ var MuxedClient = class {
259
259
  #send;
260
260
  constructor(send) {
261
261
  this.#send = send;
@@ -276,7 +276,14 @@ var TooldClient = class {
276
276
  return await this.#send("tools/call", {
277
277
  name,
278
278
  arguments: args ?? {},
279
- ...options?.timeout ? { timeout: options.timeout } : {}
279
+ ...options?.timeout ? { timeout: options.timeout } : {},
280
+ ...options?.fields ? { fields: options.fields } : {}
281
+ });
282
+ }
283
+ async validate(name, args) {
284
+ return await this.#send("tools/validate", {
285
+ name,
286
+ arguments: args ?? {}
280
287
  });
281
288
  }
282
289
  async callAsync(name, args) {
@@ -346,6 +353,6 @@ var TooldClient = class {
346
353
  async function createClient(options) {
347
354
  const { configPath, autoStart = true } = options ?? {};
348
355
  if (autoStart) await ensureDaemon(configPath);
349
- return new TooldClient(sendRequest);
356
+ return new MuxedClient(sendRequest);
350
357
  }
351
- export { TooldClient, TooldError, createClient };
358
+ export { MuxedClient, MuxedError, createClient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "muxed",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server daemon and aggregator CLI – aggregate all your Model Context Protocol servers behind a single background daemon with lazy start, auto-reconnect, and idle shutdown",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,7 +19,8 @@
19
19
  "files": [
20
20
  "dist",
21
21
  "bin",
22
- "muxed.generated.d.ts"
22
+ "muxed.generated.d.ts",
23
+ "README.md"
23
24
  ],
24
25
  "engines": {
25
26
  "node": ">=20"
@@ -53,10 +54,20 @@
53
54
  },
54
55
  "author": "Georgiy Tarasov",
55
56
  "license": "MIT",
57
+ "scripts": {
58
+ "build": "obuild",
59
+ "dev": "node src/cli.ts",
60
+ "format": "prettier --write 'src/**/*.ts'",
61
+ "format:check": "prettier --check 'src/**/*.ts'",
62
+ "publish": "tsc --noEmit && prettier --check 'src/**/*.ts' && pnpm build && cp ../../README.md . && npm publish",
63
+ "test": "vitest",
64
+ "type-check": "tsc --noEmit"
65
+ },
56
66
  "dependencies": {
57
67
  "@modelcontextprotocol/sdk": "^1.26.0",
58
68
  "commander": "^14.0.0",
59
69
  "json-schema-to-typescript": "^15.0.4",
70
+ "posthog-node": "^4.18.0",
60
71
  "zod": "^4.3.6"
61
72
  },
62
73
  "devDependencies": {
@@ -66,13 +77,5 @@
66
77
  "prettier": "^3.8.1",
67
78
  "typescript": "^5.9.3",
68
79
  "vitest": "^4.0.17"
69
- },
70
- "scripts": {
71
- "build": "obuild",
72
- "dev": "node src/cli.ts",
73
- "format": "prettier --write 'src/**/*.ts'",
74
- "format:check": "prettier --check 'src/**/*.ts'",
75
- "test": "vitest",
76
- "type-check": "tsc --noEmit"
77
80
  }
78
- }
81
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Georgiy Tarasov
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.