@pyrokine/mcp-ssh 1.1.0 → 1.1.2

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/dist/index.js CHANGED
@@ -15,10 +15,10 @@
15
15
  */
16
16
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
17
17
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
18
- import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
19
- import { sessionManager } from './session-manager.js';
18
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
20
19
  import * as fileOps from './file-ops.js';
21
- import { parseSSHConfig, parseProxyJump } from './ssh-config.js';
20
+ import { sessionManager } from './session-manager.js';
21
+ import { parseProxyJump, parseSSHConfig } from './ssh-config.js';
22
22
  // 创建 MCP Server
23
23
  const server = new Server({
24
24
  name: 'ssh-mcp-pro',
@@ -133,6 +133,9 @@ const tools = [
133
133
 
134
134
  适用场景: SSH 以 root 登录,但需要以其他用户(如 caros)执行命令。
135
135
 
136
+ 默认加载目标用户的 shell 配置以获取环境变量(su -c 创建非交互式 shell,不会自动执行 rc 文件)。
137
+ 支持 bash(.bashrc)、zsh(.zshrc) 及其他 shell(.profile)。
138
+
136
139
  示例: ssh_exec_as_user(alias="server", command="whoami", targetUser="caros")`,
137
140
  inputSchema: {
138
141
  type: 'object',
@@ -141,6 +144,7 @@ const tools = [
141
144
  command: { type: 'string', description: '要执行的命令' },
142
145
  targetUser: { type: 'string', description: '目标用户名' },
143
146
  timeout: { type: 'number', description: '超时(毫秒)' },
147
+ loadProfile: { type: 'boolean', description: '是否加载 .bashrc(默认 true)' },
144
148
  },
145
149
  required: ['alias', 'command', 'targetUser'],
146
150
  },
@@ -396,7 +400,11 @@ rsync 可实现增量传输,对大目录同步效率更高。
396
400
  type: 'object',
397
401
  properties: {
398
402
  ptyId: { type: 'string', description: 'PTY 会话 ID' },
399
- mode: { type: 'string', enum: ['screen', 'raw'], description: '输出模式:screen(当前屏幕)或 raw(原始流),默认 screen' },
403
+ mode: {
404
+ type: 'string',
405
+ enum: ['screen', 'raw'],
406
+ description: '输出模式:screen(当前屏幕)或 raw(原始流),默认 screen',
407
+ },
400
408
  clear: { type: 'boolean', description: '(仅 raw 模式) 读取后是否清空缓冲区,默认 true' },
401
409
  },
402
410
  required: ['ptyId'],
@@ -607,7 +615,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
607
615
  username: user,
608
616
  password: args.password,
609
617
  privateKeyPath: keyPath,
610
- alias: args.alias || args.configHost,
618
+ alias: args.alias ||
619
+ args.configHost,
611
620
  env: args.env,
612
621
  keepaliveInterval: args.keepaliveInterval,
613
622
  jumpHost,
@@ -655,7 +664,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
655
664
  break;
656
665
  }
657
666
  case 'ssh_exec_as_user': {
658
- const execResult = await sessionManager.execAsUser(args.alias, args.command, args.targetUser, { timeout: args.timeout });
667
+ const execResult = await sessionManager.execAsUser(args.alias, args.command, args.targetUser, {
668
+ timeout: args.timeout,
669
+ loadProfile: args.loadProfile,
670
+ });
659
671
  result = execResult;
660
672
  break;
661
673
  }
@@ -688,8 +700,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
688
700
  success: false,
689
701
  error: err.message,
690
702
  });
691
- if (stopOnError)
703
+ if (stopOnError) {
692
704
  break;
705
+ }
693
706
  }
694
707
  }
695
708
  result = {
@@ -841,7 +854,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
841
854
  success: true,
842
855
  forwardId,
843
856
  type: 'local',
844
- message: `Local forward: ${args.localHost || '127.0.0.1'}:${args.localPort} -> ${args.remoteHost}:${args.remotePort}`,
857
+ message: `Local forward: ${args.localHost ||
858
+ '127.0.0.1'}:${args.localPort} -> ${args.remoteHost}:${args.remotePort}`,
845
859
  };
846
860
  break;
847
861
  }
@@ -851,7 +865,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
851
865
  success: true,
852
866
  forwardId,
853
867
  type: 'remote',
854
- message: `Remote forward: ${args.remoteHost || '127.0.0.1'}:${args.remotePort} -> ${args.localHost}:${args.localPort}`,
868
+ message: `Remote forward: ${args.remoteHost ||
869
+ '127.0.0.1'}:${args.remotePort} -> ${args.localHost}:${args.localPort}`,
855
870
  };
856
871
  break;
857
872
  }
@@ -8,7 +8,7 @@
8
8
  * - 会话持久化
9
9
  */
10
10
  import { Client, ClientChannel, SFTPWrapper } from 'ssh2';
11
- import { SSHConnectionConfig, SSHSessionInfo, ExecOptions, ExecResult, PersistedSession, PtyOptions, PtySessionInfo, PortForwardInfo } from './types.js';
11
+ import { ExecOptions, ExecResult, PersistedSession, PortForwardInfo, PtyOptions, PtySessionInfo, SSHConnectionConfig, SSHSessionInfo } from './types.js';
12
12
  interface SSHSession {
13
13
  client: Client;
14
14
  config: SSHConnectionConfig;
@@ -37,23 +37,10 @@ export declare class SessionManager {
37
37
  private maxReconnectAttempts;
38
38
  private defaultPtyBufferSize;
39
39
  constructor(persistPath?: string);
40
- private ensurePersistDir;
41
- /**
42
- * 生成连接别名
43
- */
44
- private generateAlias;
45
40
  /**
46
41
  * 建立 SSH 连接
47
42
  */
48
43
  connect(config: SSHConnectionConfig): Promise<string>;
49
- /**
50
- * 通过跳板机转发连接
51
- */
52
- private forwardConnection;
53
- /**
54
- * 检查连接是否存活
55
- */
56
- private isAlive;
57
44
  /**
58
45
  * 重新连接
59
46
  */
@@ -74,26 +61,20 @@ export declare class SessionManager {
74
61
  * 列出所有会话
75
62
  */
76
63
  listSessions(): SSHSessionInfo[];
77
- /**
78
- * 转义 shell 参数(使用单引号方式)
79
- */
80
- private escapeShellArg;
81
64
  /**
82
65
  * 执行命令
83
66
  */
84
67
  exec(alias: string, command: string, options?: ExecOptions): Promise<ExecResult>;
85
- /**
86
- * 校验用户名(只允许字母、数字、下划线、连字符)
87
- */
88
- private isValidUsername;
89
- /**
90
- * 校验环境变量名(只允许字母、数字、下划线,不能以数字开头)
91
- */
92
- private isValidEnvKey;
93
68
  /**
94
69
  * 以其他用户身份执行命令
70
+ * @param loadProfile 是否加载用户的 shell 配置(默认 true)。
71
+ * su -c 创建非交互式 shell,不会自动执行 rc 文件,
72
+ * 但大多数用户的环境变量设置在 rc 文件中,因此默认加载。
73
+ * 支持 bash(.bashrc)、zsh(.zshrc) 及其他 shell(.profile)。
95
74
  */
96
- execAsUser(alias: string, command: string, targetUser: string, options?: ExecOptions): Promise<ExecResult>;
75
+ execAsUser(alias: string, command: string, targetUser: string, options?: ExecOptions & {
76
+ loadProfile?: boolean;
77
+ }): Promise<ExecResult>;
97
78
  /**
98
79
  * 使用 sudo 执行命令
99
80
  */
@@ -102,18 +83,10 @@ export declare class SessionManager {
102
83
  * 获取 SFTP 客户端
103
84
  */
104
85
  getSftp(alias: string): Promise<SFTPWrapper>;
105
- /**
106
- * 持久化会话信息
107
- */
108
- private persistSessions;
109
86
  /**
110
87
  * 加载持久化的会话信息(仅用于显示,不自动重连)
111
88
  */
112
89
  loadPersistedSessions(): PersistedSession[];
113
- /**
114
- * 生成 PTY 会话 ID
115
- */
116
- private generatePtyId;
117
90
  /**
118
91
  * 启动持久化 PTY 会话
119
92
  */
@@ -122,10 +95,6 @@ export declare class SessionManager {
122
95
  * 向 PTY 写入数据
123
96
  */
124
97
  ptyWrite(ptyId: string, data: string): boolean;
125
- /**
126
- * 从终端仿真器获取当前屏幕内容
127
- */
128
- private getScreenContent;
129
98
  /**
130
99
  * 读取 PTY 输出
131
100
  * @param mode 'screen' 返回当前屏幕内容,'raw' 返回原始 ANSI 流
@@ -155,10 +124,6 @@ export declare class SessionManager {
155
124
  * 关闭所有 PTY 会话
156
125
  */
157
126
  ptyCloseAll(): number;
158
- /**
159
- * 生成端口转发 ID
160
- */
161
- private generateForwardId;
162
127
  /**
163
128
  * 创建本地端口转发
164
129
  * 本地监听 localHost:localPort,转发到远程 remoteHost:remotePort
@@ -169,6 +134,60 @@ export declare class SessionManager {
169
134
  * 远程监听 remoteHost:remotePort,转发到本地 localHost:localPort
170
135
  */
171
136
  forwardRemote(alias: string, remotePort: number, localHost: string, localPort: number, remoteHost?: string): Promise<string>;
137
+ /**
138
+ * 关闭端口转发
139
+ */
140
+ forwardClose(forwardId: string): boolean;
141
+ /**
142
+ * 列出所有端口转发
143
+ */
144
+ forwardList(): PortForwardInfo[];
145
+ private ensurePersistDir;
146
+ /**
147
+ * 生成连接别名
148
+ */
149
+ private generateAlias;
150
+ /**
151
+ * 通过跳板机转发连接
152
+ */
153
+ private forwardConnection;
154
+ /**
155
+ * 检查连接是否存活
156
+ */
157
+ private isAlive;
158
+ /**
159
+ * 转义 shell 参数(使用单引号方式)
160
+ */
161
+ private escapeShellArg;
162
+ /**
163
+ * 校验用户名(只允许字母、数字、下划线、连字符)
164
+ */
165
+ private isValidUsername;
166
+ /**
167
+ * 校验环境变量名(只允许字母、数字、下划线,不能以数字开头)
168
+ */
169
+ private isValidEnvKey;
170
+ /**
171
+ * 根据用户 shell 类型生成加载配置文件的命令
172
+ * bash → .bashrc, zsh → .zshrc, 其他 → .profile
173
+ */
174
+ private getLoadProfileCommand;
175
+ /**
176
+ * 持久化会话信息
177
+ */
178
+ private persistSessions;
179
+ /**
180
+ * 生成 PTY 会话 ID
181
+ */
182
+ private generatePtyId;
183
+ /**
184
+ * 从终端仿真器获取当前屏幕内容
185
+ */
186
+ private getScreenContent;
187
+ /**
188
+ * 生成端口转发 ID
189
+ */
190
+ private generateForwardId;
172
191
  /**
173
192
  * 确保 SSH session 有共享的 tcp connection dispatcher
174
193
  * 所有 remote forward 共用一个 dispatcher,根据 destIP/destPort 路由
@@ -180,14 +199,6 @@ export declare class SessionManager {
180
199
  * @param alias - session 的 map key
181
200
  */
182
201
  private removeTcpDispatcherIfEmpty;
183
- /**
184
- * 关闭端口转发
185
- */
186
- forwardClose(forwardId: string): boolean;
187
- /**
188
- * 列出所有端口转发
189
- */
190
- forwardList(): PortForwardInfo[];
191
202
  }
192
203
  export declare const sessionManager: SessionManager;
193
204
  export {};