@dotmcp/tunnel 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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import('../dist/index.js');
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Tunnel Configuration Types and Validation
3
+ */
4
+ export interface ServerConfig {
5
+ id: string;
6
+ command?: string;
7
+ args?: string[];
8
+ url?: string;
9
+ env?: Record<string, string>;
10
+ cwd?: string;
11
+ }
12
+ export interface TunnelConfig {
13
+ key: string;
14
+ servers: ServerConfig[];
15
+ relay?: string;
16
+ }
17
+ export interface ValidationResult {
18
+ valid: boolean;
19
+ errors: string[];
20
+ }
21
+ export declare function validateConfig(config: TunnelConfig): ValidationResult;
22
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB,CA+BrE"}
package/dist/config.js ADDED
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * Tunnel Configuration Types and Validation
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateConfig = validateConfig;
7
+ function validateConfig(config) {
8
+ const errors = [];
9
+ if (!config.key) {
10
+ errors.push('Missing required field: key');
11
+ }
12
+ else if (!config.key.startsWith('dott_')) {
13
+ errors.push('Invalid key format: must start with "dott_"');
14
+ }
15
+ if (!config.servers || !Array.isArray(config.servers)) {
16
+ errors.push('Missing required field: servers (must be an array)');
17
+ }
18
+ else if (config.servers.length === 0) {
19
+ errors.push('At least one server must be configured');
20
+ }
21
+ else {
22
+ config.servers.forEach((server, idx) => {
23
+ if (!server.id) {
24
+ errors.push(`Server ${idx}: missing required field: id`);
25
+ }
26
+ if (!server.command && !server.url) {
27
+ errors.push(`Server ${idx} (${server.id || 'unnamed'}): must specify either command or url`);
28
+ }
29
+ if (server.command && server.url) {
30
+ errors.push(`Server ${idx} (${server.id || 'unnamed'}): cannot specify both command and url`);
31
+ }
32
+ });
33
+ }
34
+ return {
35
+ valid: errors.length === 0,
36
+ errors
37
+ };
38
+ }
39
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA;;GAEG;;AAsBH,wCA+BC;AA/BD,SAAgB,cAAc,CAAC,MAAoB;IAC/C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC/D,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QACpD,MAAM,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;IACtE,CAAC;SAAM,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;IAC1D,CAAC;SAAM,CAAC;QACJ,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YACnC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,8BAA8B,CAAC,CAAC;YAC7D,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,MAAM,CAAC,EAAE,IAAI,SAAS,uCAAuC,CAAC,CAAC;YACjG,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,GAAG,EAAE,CAAC;gBAC/B,MAAM,CAAC,IAAI,CAAC,UAAU,GAAG,KAAK,MAAM,CAAC,EAAE,IAAI,SAAS,wCAAwC,CAAC,CAAC;YAClG,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,OAAO;QACH,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM;KACT,CAAC;AACN,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * dotMCP Tunnel - CLI Entry Point
3
+ *
4
+ * A daemon that connects local MCP servers to dotmcp.io
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/dist/index.js ADDED
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * dotMCP Tunnel - CLI Entry Point
4
+ *
5
+ * A daemon that connects local MCP servers to dotmcp.io
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const commander_1 = require("commander");
9
+ const fs_1 = require("fs");
10
+ const yaml_1 = require("yaml");
11
+ const tunnel_1 = require("./tunnel");
12
+ const config_1 = require("./config");
13
+ const logger_1 = require("./logger");
14
+ const logger = new logger_1.Logger();
15
+ const program = new commander_1.Command();
16
+ program
17
+ .name('dotmcp-tunnel')
18
+ .description('Connect local MCP servers to dotmcp.io')
19
+ .version('1.0.0');
20
+ program
21
+ .option('-c, --config <path>', 'Path to config file', '~/.dotmcp/tunnel.yaml')
22
+ .option('-v, --verbose', 'Enable verbose logging')
23
+ .action(async (options) => {
24
+ const configPath = options.config.replace('~', process.env.HOME || '');
25
+ if (!(0, fs_1.existsSync)(configPath)) {
26
+ logger.error(`Config file not found: ${configPath}`);
27
+ logger.info('\nCreate a config file with the following format:');
28
+ logger.info(`
29
+ key: "dott_your_tunnel_key_here"
30
+
31
+ servers:
32
+ - id: "my-server"
33
+ command: "npx"
34
+ args: ["-y", "@modelcontextprotocol/server-everything"]
35
+ `);
36
+ process.exit(1);
37
+ }
38
+ try {
39
+ const configContent = (0, fs_1.readFileSync)(configPath, 'utf-8');
40
+ const config = (0, yaml_1.parse)(configContent);
41
+ const validation = (0, config_1.validateConfig)(config);
42
+ if (!validation.valid) {
43
+ logger.error('Invalid config:');
44
+ validation.errors.forEach(e => logger.error(` - ${e}`));
45
+ process.exit(1);
46
+ }
47
+ logger.info('Starting dotmcp-tunnel...');
48
+ const tunnel = new tunnel_1.Tunnel(config, logger, options.verbose || false);
49
+ // Handle shutdown signals
50
+ const shutdown = async () => {
51
+ logger.info('\nShutting down...');
52
+ await tunnel.stop();
53
+ process.exit(0);
54
+ };
55
+ process.on('SIGINT', shutdown);
56
+ process.on('SIGTERM', shutdown);
57
+ await tunnel.start();
58
+ }
59
+ catch (error) {
60
+ logger.error(`Failed to start: ${error.message}`);
61
+ process.exit(1);
62
+ }
63
+ });
64
+ program.parse(process.argv);
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAEH,yCAAoC;AACpC,2BAA8C;AAC9C,+BAA0C;AAC1C,qCAAkC;AAClC,qCAAwD;AACxD,qCAAkC;AAElC,MAAM,MAAM,GAAG,IAAI,eAAM,EAAE,CAAC;AAE5B,MAAM,OAAO,GAAG,IAAI,mBAAO,EAAE,CAAC;AAE9B,OAAO;KACF,IAAI,CAAC,eAAe,CAAC;KACrB,WAAW,CAAC,wCAAwC,CAAC;KACrD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,OAAO;KACF,MAAM,CAAC,qBAAqB,EAAE,qBAAqB,EAAE,uBAAuB,CAAC;KAC7E,MAAM,CAAC,eAAe,EAAE,wBAAwB,CAAC;KACjD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACtB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAEvE,IAAI,CAAC,IAAA,eAAU,EAAC,UAAU,CAAC,EAAE,CAAC;QAC1B,MAAM,CAAC,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;QACjE,MAAM,CAAC,IAAI,CAAC;;;;;;;CAOvB,CAAC,CAAC;QACS,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,CAAC;QACD,MAAM,aAAa,GAAG,IAAA,iBAAY,EAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,MAAM,GAAiB,IAAA,YAAS,EAAC,aAAa,CAAC,CAAC;QAEtD,MAAM,UAAU,GAAG,IAAA,uBAAc,EAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YACpB,MAAM,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAChC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;YACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAEzC,MAAM,MAAM,GAAG,IAAI,eAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC;QAEpE,0BAA0B;QAC1B,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;YACxB,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAClC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,CAAC,CAAC;QAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;QAEhC,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IAEzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,MAAM,CAAC,KAAK,CAAC,oBAAqB,KAAe,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACL,CAAC,CAAC,CAAC;AAEP,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Simple Logger for Tunnel CLI
3
+ */
4
+ export declare class Logger {
5
+ private verbose;
6
+ setVerbose(verbose: boolean): void;
7
+ info(message: string): void;
8
+ success(message: string): void;
9
+ error(message: string): void;
10
+ warn(message: string): void;
11
+ debug(message: string): void;
12
+ serverLog(serverId: string, message: string): void;
13
+ serverError(serverId: string, message: string): void;
14
+ }
15
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,qBAAa,MAAM;IACf,OAAO,CAAC,OAAO,CAAS;IAExB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI9B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI5B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAI3B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAM5B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;CAGvD"}
package/dist/logger.js ADDED
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Simple Logger for Tunnel CLI
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Logger = void 0;
7
+ class Logger {
8
+ verbose = false;
9
+ setVerbose(verbose) {
10
+ this.verbose = verbose;
11
+ }
12
+ info(message) {
13
+ console.log(`[tunnel] ${message}`);
14
+ }
15
+ success(message) {
16
+ console.log(`[tunnel] ✓ ${message}`);
17
+ }
18
+ error(message) {
19
+ console.error(`[tunnel] ✗ ${message}`);
20
+ }
21
+ warn(message) {
22
+ console.warn(`[tunnel] ⚠ ${message}`);
23
+ }
24
+ debug(message) {
25
+ if (this.verbose) {
26
+ console.log(`[tunnel] [debug] ${message}`);
27
+ }
28
+ }
29
+ serverLog(serverId, message) {
30
+ console.log(`[tunnel:${serverId}] ${message}`);
31
+ }
32
+ serverError(serverId, message) {
33
+ console.error(`[tunnel:${serverId}] ✗ ${message}`);
34
+ }
35
+ }
36
+ exports.Logger = Logger;
37
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAEH,MAAa,MAAM;IACP,OAAO,GAAG,KAAK,CAAC;IAExB,UAAU,CAAC,OAAgB;QACvB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC;IAED,IAAI,CAAC,OAAe;QAChB,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC;IACvC,CAAC;IAED,OAAO,CAAC,OAAe;QACnB,OAAO,CAAC,GAAG,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CAAC,OAAe;QACjB,OAAO,CAAC,KAAK,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,CAAC,OAAe;QAChB,OAAO,CAAC,IAAI,CAAC,cAAc,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,OAAe;QACjB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,oBAAoB,OAAO,EAAE,CAAC,CAAC;QAC/C,CAAC;IACL,CAAC;IAED,SAAS,CAAC,QAAgB,EAAE,OAAe;QACvC,OAAO,CAAC,GAAG,CAAC,WAAW,QAAQ,KAAK,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,WAAW,CAAC,QAAgB,EAAE,OAAe;QACzC,OAAO,CAAC,KAAK,CAAC,WAAW,QAAQ,OAAO,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;CACJ;AApCD,wBAoCC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Process Manager
3
+ *
4
+ * Manages spawning and communication with local MCP server processes via stdio.
5
+ */
6
+ import { ServerConfig } from './config.js';
7
+ import { Logger } from './logger.js';
8
+ export declare class ProcessManager {
9
+ private config;
10
+ private logger;
11
+ private process;
12
+ private pendingRequests;
13
+ private buffer;
14
+ private running;
15
+ constructor(config: ServerConfig, logger: Logger);
16
+ start(): Promise<void>;
17
+ private handleStdout;
18
+ private handleResponse;
19
+ sendRequest(request: any): Promise<any>;
20
+ stop(): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=process-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,qBAAa,cAAc;IACvB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,eAAe,CAGR;IACf,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,OAAO,CAAS;gBAEZ,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM;IAK1C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwD5B,OAAO,CAAC,YAAY;IAmBpB,OAAO,CAAC,cAAc;IAehB,WAAW,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IA+BvC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAuB9B"}
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ /**
3
+ * Process Manager
4
+ *
5
+ * Manages spawning and communication with local MCP server processes via stdio.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.ProcessManager = void 0;
9
+ const child_process_1 = require("child_process");
10
+ class ProcessManager {
11
+ config;
12
+ logger;
13
+ process = null;
14
+ pendingRequests = new Map();
15
+ buffer = '';
16
+ running = false;
17
+ constructor(config, logger) {
18
+ this.config = config;
19
+ this.logger = logger;
20
+ }
21
+ async start() {
22
+ if (!this.config.command) {
23
+ throw new Error('No command specified');
24
+ }
25
+ return new Promise((resolve, reject) => {
26
+ this.logger.serverLog(this.config.id, `Starting: ${this.config.command} ${(this.config.args || []).join(' ')}`);
27
+ this.process = (0, child_process_1.spawn)(this.config.command, this.config.args || [], {
28
+ cwd: this.config.cwd,
29
+ env: { ...process.env, ...this.config.env },
30
+ stdio: ['pipe', 'pipe', 'pipe']
31
+ });
32
+ this.running = true;
33
+ // Handle stdout (JSON-RPC responses from MCP server)
34
+ this.process.stdout?.on('data', (data) => {
35
+ this.handleStdout(data.toString());
36
+ });
37
+ // Handle stderr (logs from MCP server)
38
+ this.process.stderr?.on('data', (data) => {
39
+ const lines = data.toString().trim().split('\n');
40
+ lines.forEach((line) => {
41
+ this.logger.debug(`[${this.config.id}:stderr] ${line}`);
42
+ });
43
+ });
44
+ this.process.on('error', (error) => {
45
+ this.logger.serverError(this.config.id, `Process error: ${error.message}`);
46
+ reject(error);
47
+ });
48
+ this.process.on('close', (code) => {
49
+ this.running = false;
50
+ if (code !== 0 && code !== null) {
51
+ this.logger.serverError(this.config.id, `Process exited with code ${code}`);
52
+ }
53
+ // Reject all pending requests
54
+ for (const [id, pending] of this.pendingRequests) {
55
+ pending.reject(new Error('Process terminated'));
56
+ }
57
+ this.pendingRequests.clear();
58
+ });
59
+ // Give the process a moment to start
60
+ setTimeout(() => {
61
+ if (this.running) {
62
+ resolve();
63
+ }
64
+ }, 500);
65
+ });
66
+ }
67
+ handleStdout(data) {
68
+ this.buffer += data;
69
+ // Try to parse complete JSON-RPC messages (newline-delimited)
70
+ const lines = this.buffer.split('\n');
71
+ this.buffer = lines.pop() || '';
72
+ for (const line of lines) {
73
+ if (!line.trim())
74
+ continue;
75
+ try {
76
+ const message = JSON.parse(line);
77
+ this.handleResponse(message);
78
+ }
79
+ catch (error) {
80
+ this.logger.debug(`[${this.config.id}:stdout] ${line}`);
81
+ }
82
+ }
83
+ }
84
+ handleResponse(message) {
85
+ if (message.id !== undefined) {
86
+ const pending = this.pendingRequests.get(message.id);
87
+ if (pending) {
88
+ this.pendingRequests.delete(message.id);
89
+ if (message.error) {
90
+ pending.reject(new Error(message.error.message || 'Unknown error'));
91
+ }
92
+ else {
93
+ pending.resolve(message);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ async sendRequest(request) {
99
+ if (!this.process || !this.running) {
100
+ throw new Error('Process not running');
101
+ }
102
+ return new Promise((resolve, reject) => {
103
+ const id = request.id;
104
+ // Set timeout for request
105
+ const timeout = setTimeout(() => {
106
+ this.pendingRequests.delete(id);
107
+ reject(new Error('Request timeout'));
108
+ }, 30000);
109
+ this.pendingRequests.set(id, {
110
+ resolve: (value) => {
111
+ clearTimeout(timeout);
112
+ resolve(value);
113
+ },
114
+ reject: (error) => {
115
+ clearTimeout(timeout);
116
+ reject(error);
117
+ }
118
+ });
119
+ // Write request to stdin
120
+ const message = JSON.stringify(request) + '\n';
121
+ this.process.stdin?.write(message);
122
+ });
123
+ }
124
+ async stop() {
125
+ if (this.process && this.running) {
126
+ this.logger.serverLog(this.config.id, 'Stopping process...');
127
+ // Try graceful shutdown first
128
+ this.process.kill('SIGTERM');
129
+ // Force kill after timeout
130
+ await new Promise((resolve) => {
131
+ const timeout = setTimeout(() => {
132
+ if (this.process && this.running) {
133
+ this.process.kill('SIGKILL');
134
+ }
135
+ resolve();
136
+ }, 5000);
137
+ this.process.on('close', () => {
138
+ clearTimeout(timeout);
139
+ resolve();
140
+ });
141
+ });
142
+ }
143
+ }
144
+ }
145
+ exports.ProcessManager = ProcessManager;
146
+ //# sourceMappingURL=process-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../src/process-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Relay Client
3
+ *
4
+ * Manages WebSocket connection to dotmcp.io relay server.
5
+ * Handles reconnection, heartbeats, and request/response routing.
6
+ */
7
+ import { Logger } from './logger';
8
+ export interface RelayClientOptions {
9
+ serverId: string;
10
+ relayUrl: string;
11
+ tunnelKey: string;
12
+ logger: Logger;
13
+ onRequest: (request: any) => Promise<any>;
14
+ }
15
+ export declare class RelayClient {
16
+ private options;
17
+ private ws;
18
+ private reconnectAttempts;
19
+ private maxReconnectAttempts;
20
+ private reconnectDelay;
21
+ private heartbeatInterval;
22
+ private connected;
23
+ private shuttingDown;
24
+ constructor(options: RelayClientOptions);
25
+ connect(): Promise<void>;
26
+ private handleMessage;
27
+ private send;
28
+ private startHeartbeat;
29
+ private stopHeartbeat;
30
+ private scheduleReconnect;
31
+ disconnect(): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=relay-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-client.d.ts","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,WAAW,kBAAkB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CAC7C;AAED,qBAAa,WAAW;IACpB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAM;IAClC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,iBAAiB,CAA+B;IACxD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAAS;gBAEjB,OAAO,EAAE,kBAAkB;IAIjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAsDhB,aAAa;IAiC3B,OAAO,CAAC,IAAI;IAMZ,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,iBAAiB;IA0BnB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;CASpC"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Relay Client
4
+ *
5
+ * Manages WebSocket connection to dotmcp.io relay server.
6
+ * Handles reconnection, heartbeats, and request/response routing.
7
+ */
8
+ var __importDefault = (this && this.__importDefault) || function (mod) {
9
+ return (mod && mod.__esModule) ? mod : { "default": mod };
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.RelayClient = void 0;
13
+ // @ts-ignore - ws types will be installed at build time
14
+ const ws_1 = __importDefault(require("ws"));
15
+ class RelayClient {
16
+ options;
17
+ ws = null;
18
+ reconnectAttempts = 0;
19
+ maxReconnectAttempts = 10;
20
+ reconnectDelay = 1000;
21
+ heartbeatInterval = null;
22
+ connected = false;
23
+ shuttingDown = false;
24
+ constructor(options) {
25
+ this.options = options;
26
+ }
27
+ async connect() {
28
+ return new Promise((resolve, reject) => {
29
+ const url = `${this.options.relayUrl}/connect/${this.options.serverId}`;
30
+ this.options.logger.debug(`Connecting to ${url}`);
31
+ this.ws = new ws_1.default(url, {
32
+ headers: {
33
+ 'X-Tunnel-Key': this.options.tunnelKey
34
+ }
35
+ });
36
+ this.ws.on('open', () => {
37
+ this.connected = true;
38
+ this.reconnectAttempts = 0;
39
+ this.startHeartbeat();
40
+ resolve();
41
+ });
42
+ this.ws.on('message', async (data) => {
43
+ try {
44
+ const message = JSON.parse(data.toString());
45
+ await this.handleMessage(message);
46
+ }
47
+ catch (error) {
48
+ this.options.logger.serverError(this.options.serverId, `Error handling message: ${error.message}`);
49
+ }
50
+ });
51
+ this.ws.on('close', (code, reason) => {
52
+ this.options.logger.warn(`WebSocket closed: code=${code}, reason=${reason?.toString() || 'none'}, connected=${this.connected}`);
53
+ this.connected = false;
54
+ this.stopHeartbeat();
55
+ if (!this.shuttingDown) {
56
+ this.scheduleReconnect();
57
+ }
58
+ });
59
+ this.ws.on('error', (error) => {
60
+ this.options.logger.serverError(this.options.serverId, `WebSocket error: ${error.message}`);
61
+ if (!this.connected) {
62
+ reject(error);
63
+ }
64
+ });
65
+ });
66
+ }
67
+ async handleMessage(message) {
68
+ switch (message.type) {
69
+ case 'connected':
70
+ this.options.logger.debug(`Connected with ID: ${message.connectionId}`);
71
+ break;
72
+ case 'heartbeat_ack':
73
+ this.options.logger.debug('Heartbeat acknowledged');
74
+ break;
75
+ case 'request':
76
+ // Forward request to local MCP server
77
+ try {
78
+ const response = await this.options.onRequest(message.payload);
79
+ this.send({
80
+ type: 'response',
81
+ requestId: message.requestId,
82
+ payload: response
83
+ });
84
+ }
85
+ catch (error) {
86
+ this.send({
87
+ type: 'error',
88
+ requestId: message.requestId,
89
+ error: error.message
90
+ });
91
+ }
92
+ break;
93
+ default:
94
+ this.options.logger.debug(`Unknown message type: ${message.type}`);
95
+ }
96
+ }
97
+ send(message) {
98
+ if (this.ws && this.connected) {
99
+ this.ws.send(JSON.stringify(message));
100
+ }
101
+ }
102
+ startHeartbeat() {
103
+ this.heartbeatInterval = setInterval(() => {
104
+ this.send({ type: 'heartbeat', timestamp: Date.now() });
105
+ }, 30000);
106
+ }
107
+ stopHeartbeat() {
108
+ if (this.heartbeatInterval) {
109
+ clearInterval(this.heartbeatInterval);
110
+ this.heartbeatInterval = null;
111
+ }
112
+ }
113
+ scheduleReconnect() {
114
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
115
+ this.options.logger.serverError(this.options.serverId, `Max reconnect attempts (${this.maxReconnectAttempts}) reached. Giving up.`);
116
+ return;
117
+ }
118
+ const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
119
+ this.reconnectAttempts++;
120
+ this.options.logger.warn(`Connection lost. Reconnecting in ${delay / 1000}s (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})...`);
121
+ setTimeout(async () => {
122
+ try {
123
+ await this.connect();
124
+ this.options.logger.success(`Reconnected to relay`);
125
+ }
126
+ catch (error) {
127
+ // Will trigger another reconnect via close handler
128
+ }
129
+ }, delay);
130
+ }
131
+ async disconnect() {
132
+ this.shuttingDown = true;
133
+ this.stopHeartbeat();
134
+ if (this.ws) {
135
+ this.ws.close();
136
+ this.ws = null;
137
+ }
138
+ }
139
+ }
140
+ exports.RelayClient = RelayClient;
141
+ //# sourceMappingURL=relay-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-client.js","sourceRoot":"","sources":["../src/relay-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Main Tunnel Class
3
+ *
4
+ * Orchestrates connections to relay and management of local MCP server processes.
5
+ */
6
+ import { TunnelConfig } from './config';
7
+ import { Logger } from './logger';
8
+ export declare class Tunnel {
9
+ private config;
10
+ private logger;
11
+ private relayClients;
12
+ private processManagers;
13
+ private running;
14
+ private verbose;
15
+ constructor(config: TunnelConfig, logger: Logger, verbose?: boolean);
16
+ start(): Promise<void>;
17
+ private startServer;
18
+ private forwardToHttp;
19
+ stop(): Promise<void>;
20
+ }
21
+ //# sourceMappingURL=tunnel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.d.ts","sourceRoot":"","sources":["../src/tunnel.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAgB,MAAM,UAAU,CAAC;AAGtD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,qBAAa,MAAM;IACf,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAU;gBAEb,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,UAAQ;IAS3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAyBd,WAAW;YAyCX,aAAa;IAcrB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA0B9B"}
package/dist/tunnel.js ADDED
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * Main Tunnel Class
4
+ *
5
+ * Orchestrates connections to relay and management of local MCP server processes.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.Tunnel = void 0;
9
+ const relay_client_1 = require("./relay-client");
10
+ const process_manager_1 = require("./process-manager");
11
+ class Tunnel {
12
+ config;
13
+ logger;
14
+ relayClients = new Map();
15
+ processManagers = new Map();
16
+ running = false;
17
+ verbose;
18
+ constructor(config, logger, verbose = false) {
19
+ this.config = config;
20
+ this.logger = logger;
21
+ this.verbose = verbose;
22
+ if (verbose) {
23
+ logger.setVerbose(true);
24
+ }
25
+ }
26
+ async start() {
27
+ this.running = true;
28
+ const relayUrl = this.config.relay || 'wss://relay.dotmcp.io';
29
+ this.logger.info(`Connecting ${this.config.servers.length} server(s) to ${relayUrl}`);
30
+ for (const serverConfig of this.config.servers) {
31
+ await this.startServer(serverConfig, relayUrl);
32
+ }
33
+ this.logger.success('All servers connected. Press Ctrl+C to stop.');
34
+ // Keep the process running
35
+ await new Promise((resolve) => {
36
+ const checkRunning = () => {
37
+ if (!this.running) {
38
+ resolve();
39
+ }
40
+ else {
41
+ setTimeout(checkRunning, 1000);
42
+ }
43
+ };
44
+ checkRunning();
45
+ });
46
+ }
47
+ async startServer(serverConfig, relayUrl) {
48
+ const { id } = serverConfig;
49
+ try {
50
+ // Start the local MCP server process (if command-based)
51
+ let processManager = null;
52
+ if (serverConfig.command) {
53
+ processManager = new process_manager_1.ProcessManager(serverConfig, this.logger);
54
+ this.processManagers.set(id, processManager);
55
+ await processManager.start();
56
+ }
57
+ // Create relay client for this server
58
+ const relayClient = new relay_client_1.RelayClient({
59
+ serverId: id,
60
+ relayUrl,
61
+ tunnelKey: this.config.key,
62
+ logger: this.logger,
63
+ onRequest: async (request) => {
64
+ // Forward request to local MCP server
65
+ if (processManager) {
66
+ return await processManager.sendRequest(request);
67
+ }
68
+ else if (serverConfig.url) {
69
+ // HTTP transport - forward to running server
70
+ return await this.forwardToHttp(serverConfig.url, request);
71
+ }
72
+ throw new Error('No transport configured');
73
+ }
74
+ });
75
+ this.relayClients.set(id, relayClient);
76
+ await relayClient.connect();
77
+ this.logger.success(`Server "${id}" connected`);
78
+ }
79
+ catch (error) {
80
+ this.logger.serverError(id, `Failed to start: ${error.message}`);
81
+ throw error;
82
+ }
83
+ }
84
+ async forwardToHttp(url, request) {
85
+ const response = await fetch(url, {
86
+ method: 'POST',
87
+ headers: { 'Content-Type': 'application/json' },
88
+ body: JSON.stringify(request)
89
+ });
90
+ if (!response.ok) {
91
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
92
+ }
93
+ return await response.json();
94
+ }
95
+ async stop() {
96
+ this.running = false;
97
+ this.logger.info('Stopping all connections...');
98
+ // Close relay connections
99
+ for (const [id, client] of this.relayClients) {
100
+ try {
101
+ await client.disconnect();
102
+ this.logger.debug(`Disconnected relay for ${id}`);
103
+ }
104
+ catch (error) {
105
+ this.logger.serverError(id, `Error disconnecting: ${error.message}`);
106
+ }
107
+ }
108
+ // Stop local processes
109
+ for (const [id, manager] of this.processManagers) {
110
+ try {
111
+ await manager.stop();
112
+ this.logger.debug(`Stopped process for ${id}`);
113
+ }
114
+ catch (error) {
115
+ this.logger.serverError(id, `Error stopping process: ${error.message}`);
116
+ }
117
+ }
118
+ this.logger.success('Tunnel stopped');
119
+ }
120
+ }
121
+ exports.Tunnel = Tunnel;
122
+ //# sourceMappingURL=tunnel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tunnel.js","sourceRoot":"","sources":["../src/tunnel.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@dotmcp/tunnel",
3
+ "version": "1.0.0",
4
+ "description": "CLI daemon to connect local MCP servers to dotmcp.io",
5
+ "author": "dotMCP <hello@dotmcp.io>",
6
+ "homepage": "https://dotmcp.io",
7
+ "keywords": [
8
+ "mcp",
9
+ "tunnel",
10
+ "dotmcp",
11
+ "model-context-protocol"
12
+ ],
13
+ "bin": {
14
+ "dotmcp-tunnel": "./bin/dotmcp-tunnel.js"
15
+ },
16
+ "main": "dist/index.js",
17
+ "types": "dist/index.d.ts",
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "ts-node src/index.ts",
21
+ "start": "node dist/index.js"
22
+ },
23
+ "dependencies": {
24
+ "commander": "^11.1.0",
25
+ "ws": "^8.14.2",
26
+ "yaml": "^2.3.4",
27
+ "chalk": "^5.3.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^20.10.0",
31
+ "@types/ws": "^8.5.10",
32
+ "typescript": "^5.3.2",
33
+ "ts-node": "^10.9.2"
34
+ },
35
+ "engines": {
36
+ "node": ">=18.0.0"
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "bin"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/dotmcp/tunnel"
45
+ },
46
+ "license": "BUSL-1.1"
47
+ }