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.
- package/README.md +323 -0
- package/dist/cli.mjs +978 -209
- package/dist/client/index.d.mts +24 -10
- package/dist/client/index.mjs +28 -21
- package/package.json +14 -11
- package/LICENSE +0 -21
package/dist/client/index.d.mts
CHANGED
|
@@ -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
|
|
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 `
|
|
78
|
-
interface
|
|
79
|
-
type HasTools = keyof
|
|
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
|
|
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
|
|
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
|
|
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<
|
|
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,
|
|
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 };
|
package/dist/client/index.mjs
CHANGED
|
@@ -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
|
|
7
|
-
return path.join(os.homedir(), ".
|
|
6
|
+
function getMuxedDir() {
|
|
7
|
+
return path.join(os.homedir(), ".muxed");
|
|
8
8
|
}
|
|
9
9
|
function getSocketPath() {
|
|
10
|
-
return path.join(
|
|
10
|
+
return path.join(getMuxedDir(), "muxed.sock");
|
|
11
11
|
}
|
|
12
12
|
function getPidPath() {
|
|
13
|
-
return path.join(
|
|
13
|
+
return path.join(getMuxedDir(), "muxed.pid");
|
|
14
14
|
}
|
|
15
|
-
function
|
|
16
|
-
fs.mkdirSync(
|
|
15
|
+
function ensureMuxedDir() {
|
|
16
|
+
fs.mkdirSync(getMuxedDir(), { recursive: true });
|
|
17
17
|
}
|
|
18
18
|
function getLockPath() {
|
|
19
|
-
return path.join(
|
|
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
|
|
38
|
+
function isMuxedProcess(pid) {
|
|
39
39
|
try {
|
|
40
40
|
const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf-8");
|
|
41
|
-
return cmdline.includes("
|
|
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
|
-
|
|
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) || !
|
|
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 (!
|
|
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) && !
|
|
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
|
|
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 = "
|
|
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 `
|
|
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
|
|
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
|
|
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
|
|
356
|
+
return new MuxedClient(sendRequest);
|
|
350
357
|
}
|
|
351
|
-
export {
|
|
358
|
+
export { MuxedClient, MuxedError, createClient };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "muxed",
|
|
3
|
-
"version": "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.
|