Files
mcp-ssh/src/McpSsh.Server/Terminal/TerminalConnection.cs
2026-05-24 20:45:12 +00:00

96 lines
2.6 KiB
C#

using Renci.SshNet;
using Renci.SshNet.Common;
using McpSsh.Server.Ssh;
namespace McpSsh.Server.Terminal;
public interface ITerminalConnection : IDisposable
{
bool DataAvailable { get; }
string ReadAvailable();
void Write(string input);
}
public interface ITerminalConnectionFactory
{
ITerminalConnection Create(TerminalStartRequest request);
}
public sealed class SshNetTerminalConnectionFactory : ITerminalConnectionFactory
{
private const int ShellBufferSize = 16 * 1024;
private readonly ISshKeyResolver _keyResolver;
public SshNetTerminalConnectionFactory(ISshKeyResolver keyResolver)
{
_keyResolver = keyResolver;
}
public ITerminalConnection Create(TerminalStartRequest request)
{
try
{
var keyPath = _keyResolver.ResolveKeyPath(request.KeyPath);
var keyFile = string.IsNullOrEmpty(request.KeyPassphrase)
? new PrivateKeyFile(keyPath)
: new PrivateKeyFile(keyPath, request.KeyPassphrase);
var client = new SshClient(request.Host, request.Port, request.Username, keyFile);
client.Connect();
var stream = client.CreateShellStream(
"xterm-256color",
(uint)request.Cols,
(uint)request.Rows,
width: 0,
height: 0,
bufferSize: ShellBufferSize);
return new SshNetTerminalConnection(client, stream, keyFile);
}
catch (SshAuthenticationException ex)
{
throw new SshToolException("ssh_authentication_failed", "SSH key authentication failed. Check the username, key path, and key passphrase.", ex);
}
catch (SshException ex)
{
throw new SshToolException("ssh_error", ex.Message, ex);
}
}
private sealed class SshNetTerminalConnection : ITerminalConnection
{
private readonly SshClient _client;
private readonly ShellStream _stream;
private readonly PrivateKeyFile _keyFile;
public SshNetTerminalConnection(SshClient client, ShellStream stream, PrivateKeyFile keyFile)
{
_client = client;
_stream = stream;
_keyFile = keyFile;
}
public bool DataAvailable => _stream.DataAvailable;
public string ReadAvailable()
{
return _stream.Read();
}
public void Write(string input)
{
_stream.Write(input);
}
public void Dispose()
{
_stream.Dispose();
_client.Dispose();
_keyFile.Dispose();
}
}
}