sshand 0.1.0__tar.gz
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.
- sshand-0.1.0/LICENSE +21 -0
- sshand-0.1.0/PKG-INFO +344 -0
- sshand-0.1.0/README.md +291 -0
- sshand-0.1.0/host_config.py +191 -0
- sshand-0.1.0/platform_utils.py +179 -0
- sshand-0.1.0/pyproject.toml +45 -0
- sshand-0.1.0/server.py +941 -0
- sshand-0.1.0/setup.cfg +4 -0
- sshand-0.1.0/setup_wizard.py +494 -0
- sshand-0.1.0/ssh_client.py +380 -0
- sshand-0.1.0/sshand.egg-info/PKG-INFO +344 -0
- sshand-0.1.0/sshand.egg-info/SOURCES.txt +14 -0
- sshand-0.1.0/sshand.egg-info/dependency_links.txt +1 -0
- sshand-0.1.0/sshand.egg-info/entry_points.txt +2 -0
- sshand-0.1.0/sshand.egg-info/requires.txt +8 -0
- sshand-0.1.0/sshand.egg-info/top_level.txt +5 -0
sshand-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Murad
|
|
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.
|
sshand-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sshand
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server — give any AI agent SSH access to remote Linux/Unix machines
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2025 Murad
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Project-URL: Homepage, https://github.com/muradmalik23/sshand
|
|
28
|
+
Project-URL: Repository, https://github.com/muradmalik23/sshand
|
|
29
|
+
Project-URL: Issues, https://github.com/muradmalik23/sshand/issues
|
|
30
|
+
Project-URL: Changelog, https://github.com/muradmalik23/sshand/blob/main/CHANGELOG.md
|
|
31
|
+
Keywords: mcp,ssh,ai,agent,claude,openai,llm,sshand
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: Intended Audience :: System Administrators
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: System :: Systems Administration
|
|
41
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
42
|
+
Requires-Python: >=3.10
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
Requires-Dist: mcp[cli]>=1.6.0
|
|
46
|
+
Requires-Dist: paramiko>=3.4.0
|
|
47
|
+
Requires-Dist: pyyaml>=6.0
|
|
48
|
+
Requires-Dist: pydantic>=2.7.0
|
|
49
|
+
Provides-Extra: dev
|
|
50
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
51
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# SSHand
|
|
55
|
+
|
|
56
|
+
An open MCP server that gives **any AI agent** SSH access to remote Linux/Unix machines — shell commands, file read/write, and SFTP transfers.
|
|
57
|
+
|
|
58
|
+
Works with **Claude.ai**, **Claude Desktop**, **ChatGPT**, **Cursor**, **VS Code Copilot**, **OpenAI Agents SDK**, and any other [MCP-compatible client](https://modelcontextprotocol.io/clients).
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# 1. Install
|
|
66
|
+
pip install sshand
|
|
67
|
+
|
|
68
|
+
# 2. Run the interactive setup wizard
|
|
69
|
+
sshand setup
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The wizard walks you through adding your first host, testing the connection, and copying the right config snippet for whichever AI client you use.
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Installation options
|
|
77
|
+
|
|
78
|
+
### Option A — pip (recommended for most users)
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install sshand # from PyPI
|
|
82
|
+
pip install -e . # from source, editable install
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Option B — uvx (zero-install, no venv needed)
|
|
86
|
+
|
|
87
|
+
[uv](https://docs.astral.sh/uv/) is the modern Python package manager. With it installed you can run SSHand without any manual install step:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uvx sshand # run server directly
|
|
91
|
+
uvx sshand setup # run setup wizard
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This is the cleanest option to recommend to non-technical users.
|
|
95
|
+
|
|
96
|
+
### Option C — plain Python (no install)
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
git clone https://github.com/YOUR_GITHUB_USERNAME/sshand
|
|
100
|
+
cd sshand
|
|
101
|
+
pip install -r requirements.txt
|
|
102
|
+
python server.py # start server
|
|
103
|
+
python setup_wizard.py # run wizard
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Connecting to your AI client
|
|
109
|
+
|
|
110
|
+
### Claude.ai and ChatGPT (web + desktop)
|
|
111
|
+
|
|
112
|
+
For Claude.ai web and ChatGPT, SSHand runs as an **HTTP server** and you connect it through each app's native integrations UI.
|
|
113
|
+
|
|
114
|
+
**→ See [INTEGRATIONS.md](INTEGRATIONS.md) for the full step-by-step guide**, including how to expose the server publicly using ngrok or Cloudflare Tunnel, and how to add it to Claude.ai Integrations and ChatGPT Desktop.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Claude Desktop
|
|
119
|
+
|
|
120
|
+
Add to your `claude_desktop_config.json`:
|
|
121
|
+
|
|
122
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
123
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"mcpServers": {
|
|
128
|
+
"ssh": {
|
|
129
|
+
"command": "uvx",
|
|
130
|
+
"args": ["sshand"],
|
|
131
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Or with plain Python:
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"mcpServers": {
|
|
142
|
+
"ssh": {
|
|
143
|
+
"command": "python",
|
|
144
|
+
"args": ["/absolute/path/to/sshand/server.py"],
|
|
145
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/sshand/hosts.yaml" }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Restart Claude Desktop after saving.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Cursor
|
|
156
|
+
|
|
157
|
+
Create or update `.cursor/mcp.json` in your project (or the global Cursor MCP settings):
|
|
158
|
+
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"mcpServers": {
|
|
162
|
+
"ssh": {
|
|
163
|
+
"command": "uvx",
|
|
164
|
+
"args": ["sshand"],
|
|
165
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
### VS Code (GitHub Copilot Chat)
|
|
174
|
+
|
|
175
|
+
Add to `.vscode/mcp.json` or your workspace `settings.json`:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"mcp": {
|
|
180
|
+
"servers": {
|
|
181
|
+
"ssh": {
|
|
182
|
+
"type": "stdio",
|
|
183
|
+
"command": "uvx",
|
|
184
|
+
"args": ["sshand"],
|
|
185
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
### OpenAI Agents SDK
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Terminal 1 — keep this running
|
|
198
|
+
sshand --transport http --port 8000
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from agents import Agent
|
|
203
|
+
from agents.mcp import MCPServerStreamableHttp
|
|
204
|
+
|
|
205
|
+
ssh_server = MCPServerStreamableHttp(url="http://localhost:8000/mcp")
|
|
206
|
+
agent = Agent(name="ops-agent", mcp_servers=[ssh_server])
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### Any other MCP client (HTTP)
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
sshand --transport http --port 8000
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
MCP endpoint: `http://localhost:8000/mcp`
|
|
218
|
+
|
|
219
|
+
For remote access, put a reverse proxy (nginx / Caddy) in front with TLS. Never expose port 8000 directly on a public interface.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Host inventory
|
|
224
|
+
|
|
225
|
+
Hosts are stored in `hosts.yaml` (set a different path with the `SSH_MCP_HOSTS_FILE` env var). You can edit the file directly or let your agent call `ssh_add_host`.
|
|
226
|
+
|
|
227
|
+
Copy `hosts.yaml.example` to `hosts.yaml` to get started:
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
cp hosts.yaml.example hosts.yaml
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Auth types
|
|
234
|
+
|
|
235
|
+
**Key file** — most common, most secure:
|
|
236
|
+
```yaml
|
|
237
|
+
auth:
|
|
238
|
+
type: key
|
|
239
|
+
key_path: ~/.ssh/id_rsa # ~ is expanded automatically
|
|
240
|
+
passphrase: null # set if the key is encrypted
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
**Password** — convenient for dev/test, avoid on internet-facing servers:
|
|
244
|
+
```yaml
|
|
245
|
+
auth:
|
|
246
|
+
type: password
|
|
247
|
+
password: s3cr3t
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**SSH agent** — no credentials stored at all, delegates to the running `ssh-agent`:
|
|
251
|
+
```yaml
|
|
252
|
+
auth:
|
|
253
|
+
type: agent
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Available tools
|
|
259
|
+
|
|
260
|
+
| Tool | Description | Read-only? |
|
|
261
|
+
|------|-------------|------------|
|
|
262
|
+
| `ssh_list_hosts` | List all configured SSH targets | ✅ |
|
|
263
|
+
| `ssh_add_host` | Register a new SSH host | ❌ |
|
|
264
|
+
| `ssh_remove_host` | Remove a host from inventory | ❌ |
|
|
265
|
+
| `ssh_test_connection` | Verify auth works for a host | ✅ |
|
|
266
|
+
| `ssh_run_command` | Execute a shell command + capture output | ❌ |
|
|
267
|
+
| `ssh_read_file` | Read a remote file's contents | ✅ |
|
|
268
|
+
| `ssh_write_file` | Create or overwrite a remote file | ❌ |
|
|
269
|
+
| `ssh_list_directory` | Browse a remote directory | ✅ |
|
|
270
|
+
| `ssh_upload_file` | Push a local file to the remote host (SFTP) | ❌ |
|
|
271
|
+
| `ssh_download_file` | Pull a remote file to this machine (SFTP) | ✅ |
|
|
272
|
+
| `ssh_get_local_info` | Return OS and path style of the MCP server host | ✅ |
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Example conversations
|
|
277
|
+
|
|
278
|
+
> *"What servers do you have access to?"*
|
|
279
|
+
> → `ssh_list_hosts`
|
|
280
|
+
|
|
281
|
+
> *"Check disk usage on webserver"*
|
|
282
|
+
> → `ssh_run_command(alias='webserver', command='df -h')`
|
|
283
|
+
|
|
284
|
+
> *"Read the nginx config on the web box"*
|
|
285
|
+
> → `ssh_read_file(alias='webserver', remote_path='/etc/nginx/nginx.conf')`
|
|
286
|
+
|
|
287
|
+
> *"Tail the last 100 lines of syslog on bastion"*
|
|
288
|
+
> → `ssh_run_command(alias='bastion', command='tail -n 100 /var/log/syslog')`
|
|
289
|
+
|
|
290
|
+
> *"Deploy this config file to devbox"*
|
|
291
|
+
> → `ssh_write_file(alias='devbox', remote_path='/etc/myapp/config.yaml', content='...')`
|
|
292
|
+
|
|
293
|
+
> *"Download today's DB backup from webserver"*
|
|
294
|
+
> → `ssh_download_file(alias='webserver', remote_path='/backups/db-today.sql.gz', local_path='/tmp/db-today.sql.gz')`
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## CLI reference
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
sshand [subcommand] [options]
|
|
302
|
+
|
|
303
|
+
Subcommands:
|
|
304
|
+
setup Interactive first-run wizard
|
|
305
|
+
|
|
306
|
+
Options:
|
|
307
|
+
--transport {stdio,http} Transport (default: stdio)
|
|
308
|
+
--port INT HTTP port (default: 8000)
|
|
309
|
+
--host STR HTTP bind address (default: 127.0.0.1)
|
|
310
|
+
|
|
311
|
+
Examples:
|
|
312
|
+
sshand # start stdio server
|
|
313
|
+
sshand setup # first-run wizard
|
|
314
|
+
sshand --transport http # start HTTP server on :8000
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## Security notes
|
|
320
|
+
|
|
321
|
+
- Keep `hosts.yaml` out of version control if it contains passwords — it is excluded by the included `.gitignore`. Copy `hosts.yaml.example` to `hosts.yaml` to get started.
|
|
322
|
+
- Prefer key-based or agent auth over password auth for any internet-facing host.
|
|
323
|
+
- The HTTP transport binds to `127.0.0.1` by default.
|
|
324
|
+
- When exposing the HTTP server publicly (for Claude.ai / ChatGPT web), use TLS and consider adding authentication via a reverse proxy.
|
|
325
|
+
- `ssh_run_command` is marked `destructiveHint: true` — MCP clients that respect annotations will prompt before running potentially dangerous commands.
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## Project structure
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
sshand/
|
|
333
|
+
├── server.py # FastMCP server — all 11 tools + CLI entry point
|
|
334
|
+
├── ssh_client.py # Async paramiko wrapper (command exec + SFTP)
|
|
335
|
+
├── host_config.py # YAML host inventory manager
|
|
336
|
+
├── setup_wizard.py # Interactive first-run setup wizard
|
|
337
|
+
├── platform_utils.py # Windows SSH agent helpers
|
|
338
|
+
├── hosts.yaml.example # Safe template — copy to hosts.yaml and fill in your values
|
|
339
|
+
├── hosts.yaml # Your SSH targets (gitignored — copy from hosts.yaml.example)
|
|
340
|
+
├── INTEGRATIONS.md # Guide: Claude.ai and ChatGPT native extensions
|
|
341
|
+
├── pyproject.toml # Package metadata + pip/uvx install config
|
|
342
|
+
├── requirements.txt # Plain pip install fallback
|
|
343
|
+
└── README.md
|
|
344
|
+
```
|
sshand-0.1.0/README.md
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
# SSHand
|
|
2
|
+
|
|
3
|
+
An open MCP server that gives **any AI agent** SSH access to remote Linux/Unix machines — shell commands, file read/write, and SFTP transfers.
|
|
4
|
+
|
|
5
|
+
Works with **Claude.ai**, **Claude Desktop**, **ChatGPT**, **Cursor**, **VS Code Copilot**, **OpenAI Agents SDK**, and any other [MCP-compatible client](https://modelcontextprotocol.io/clients).
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 1. Install
|
|
13
|
+
pip install sshand
|
|
14
|
+
|
|
15
|
+
# 2. Run the interactive setup wizard
|
|
16
|
+
sshand setup
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The wizard walks you through adding your first host, testing the connection, and copying the right config snippet for whichever AI client you use.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Installation options
|
|
24
|
+
|
|
25
|
+
### Option A — pip (recommended for most users)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install sshand # from PyPI
|
|
29
|
+
pip install -e . # from source, editable install
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Option B — uvx (zero-install, no venv needed)
|
|
33
|
+
|
|
34
|
+
[uv](https://docs.astral.sh/uv/) is the modern Python package manager. With it installed you can run SSHand without any manual install step:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uvx sshand # run server directly
|
|
38
|
+
uvx sshand setup # run setup wizard
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
This is the cleanest option to recommend to non-technical users.
|
|
42
|
+
|
|
43
|
+
### Option C — plain Python (no install)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/YOUR_GITHUB_USERNAME/sshand
|
|
47
|
+
cd sshand
|
|
48
|
+
pip install -r requirements.txt
|
|
49
|
+
python server.py # start server
|
|
50
|
+
python setup_wizard.py # run wizard
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Connecting to your AI client
|
|
56
|
+
|
|
57
|
+
### Claude.ai and ChatGPT (web + desktop)
|
|
58
|
+
|
|
59
|
+
For Claude.ai web and ChatGPT, SSHand runs as an **HTTP server** and you connect it through each app's native integrations UI.
|
|
60
|
+
|
|
61
|
+
**→ See [INTEGRATIONS.md](INTEGRATIONS.md) for the full step-by-step guide**, including how to expose the server publicly using ngrok or Cloudflare Tunnel, and how to add it to Claude.ai Integrations and ChatGPT Desktop.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### Claude Desktop
|
|
66
|
+
|
|
67
|
+
Add to your `claude_desktop_config.json`:
|
|
68
|
+
|
|
69
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
70
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"ssh": {
|
|
76
|
+
"command": "uvx",
|
|
77
|
+
"args": ["sshand"],
|
|
78
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or with plain Python:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"ssh": {
|
|
90
|
+
"command": "python",
|
|
91
|
+
"args": ["/absolute/path/to/sshand/server.py"],
|
|
92
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/sshand/hosts.yaml" }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Restart Claude Desktop after saving.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### Cursor
|
|
103
|
+
|
|
104
|
+
Create or update `.cursor/mcp.json` in your project (or the global Cursor MCP settings):
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"mcpServers": {
|
|
109
|
+
"ssh": {
|
|
110
|
+
"command": "uvx",
|
|
111
|
+
"args": ["sshand"],
|
|
112
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### VS Code (GitHub Copilot Chat)
|
|
121
|
+
|
|
122
|
+
Add to `.vscode/mcp.json` or your workspace `settings.json`:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcp": {
|
|
127
|
+
"servers": {
|
|
128
|
+
"ssh": {
|
|
129
|
+
"type": "stdio",
|
|
130
|
+
"command": "uvx",
|
|
131
|
+
"args": ["sshand"],
|
|
132
|
+
"env": { "SSH_MCP_HOSTS_FILE": "/absolute/path/to/hosts.yaml" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### OpenAI Agents SDK
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Terminal 1 — keep this running
|
|
145
|
+
sshand --transport http --port 8000
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from agents import Agent
|
|
150
|
+
from agents.mcp import MCPServerStreamableHttp
|
|
151
|
+
|
|
152
|
+
ssh_server = MCPServerStreamableHttp(url="http://localhost:8000/mcp")
|
|
153
|
+
agent = Agent(name="ops-agent", mcp_servers=[ssh_server])
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### Any other MCP client (HTTP)
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
sshand --transport http --port 8000
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
MCP endpoint: `http://localhost:8000/mcp`
|
|
165
|
+
|
|
166
|
+
For remote access, put a reverse proxy (nginx / Caddy) in front with TLS. Never expose port 8000 directly on a public interface.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Host inventory
|
|
171
|
+
|
|
172
|
+
Hosts are stored in `hosts.yaml` (set a different path with the `SSH_MCP_HOSTS_FILE` env var). You can edit the file directly or let your agent call `ssh_add_host`.
|
|
173
|
+
|
|
174
|
+
Copy `hosts.yaml.example` to `hosts.yaml` to get started:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
cp hosts.yaml.example hosts.yaml
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Auth types
|
|
181
|
+
|
|
182
|
+
**Key file** — most common, most secure:
|
|
183
|
+
```yaml
|
|
184
|
+
auth:
|
|
185
|
+
type: key
|
|
186
|
+
key_path: ~/.ssh/id_rsa # ~ is expanded automatically
|
|
187
|
+
passphrase: null # set if the key is encrypted
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**Password** — convenient for dev/test, avoid on internet-facing servers:
|
|
191
|
+
```yaml
|
|
192
|
+
auth:
|
|
193
|
+
type: password
|
|
194
|
+
password: s3cr3t
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
**SSH agent** — no credentials stored at all, delegates to the running `ssh-agent`:
|
|
198
|
+
```yaml
|
|
199
|
+
auth:
|
|
200
|
+
type: agent
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Available tools
|
|
206
|
+
|
|
207
|
+
| Tool | Description | Read-only? |
|
|
208
|
+
|------|-------------|------------|
|
|
209
|
+
| `ssh_list_hosts` | List all configured SSH targets | ✅ |
|
|
210
|
+
| `ssh_add_host` | Register a new SSH host | ❌ |
|
|
211
|
+
| `ssh_remove_host` | Remove a host from inventory | ❌ |
|
|
212
|
+
| `ssh_test_connection` | Verify auth works for a host | ✅ |
|
|
213
|
+
| `ssh_run_command` | Execute a shell command + capture output | ❌ |
|
|
214
|
+
| `ssh_read_file` | Read a remote file's contents | ✅ |
|
|
215
|
+
| `ssh_write_file` | Create or overwrite a remote file | ❌ |
|
|
216
|
+
| `ssh_list_directory` | Browse a remote directory | ✅ |
|
|
217
|
+
| `ssh_upload_file` | Push a local file to the remote host (SFTP) | ❌ |
|
|
218
|
+
| `ssh_download_file` | Pull a remote file to this machine (SFTP) | ✅ |
|
|
219
|
+
| `ssh_get_local_info` | Return OS and path style of the MCP server host | ✅ |
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Example conversations
|
|
224
|
+
|
|
225
|
+
> *"What servers do you have access to?"*
|
|
226
|
+
> → `ssh_list_hosts`
|
|
227
|
+
|
|
228
|
+
> *"Check disk usage on webserver"*
|
|
229
|
+
> → `ssh_run_command(alias='webserver', command='df -h')`
|
|
230
|
+
|
|
231
|
+
> *"Read the nginx config on the web box"*
|
|
232
|
+
> → `ssh_read_file(alias='webserver', remote_path='/etc/nginx/nginx.conf')`
|
|
233
|
+
|
|
234
|
+
> *"Tail the last 100 lines of syslog on bastion"*
|
|
235
|
+
> → `ssh_run_command(alias='bastion', command='tail -n 100 /var/log/syslog')`
|
|
236
|
+
|
|
237
|
+
> *"Deploy this config file to devbox"*
|
|
238
|
+
> → `ssh_write_file(alias='devbox', remote_path='/etc/myapp/config.yaml', content='...')`
|
|
239
|
+
|
|
240
|
+
> *"Download today's DB backup from webserver"*
|
|
241
|
+
> → `ssh_download_file(alias='webserver', remote_path='/backups/db-today.sql.gz', local_path='/tmp/db-today.sql.gz')`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## CLI reference
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
sshand [subcommand] [options]
|
|
249
|
+
|
|
250
|
+
Subcommands:
|
|
251
|
+
setup Interactive first-run wizard
|
|
252
|
+
|
|
253
|
+
Options:
|
|
254
|
+
--transport {stdio,http} Transport (default: stdio)
|
|
255
|
+
--port INT HTTP port (default: 8000)
|
|
256
|
+
--host STR HTTP bind address (default: 127.0.0.1)
|
|
257
|
+
|
|
258
|
+
Examples:
|
|
259
|
+
sshand # start stdio server
|
|
260
|
+
sshand setup # first-run wizard
|
|
261
|
+
sshand --transport http # start HTTP server on :8000
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## Security notes
|
|
267
|
+
|
|
268
|
+
- Keep `hosts.yaml` out of version control if it contains passwords — it is excluded by the included `.gitignore`. Copy `hosts.yaml.example` to `hosts.yaml` to get started.
|
|
269
|
+
- Prefer key-based or agent auth over password auth for any internet-facing host.
|
|
270
|
+
- The HTTP transport binds to `127.0.0.1` by default.
|
|
271
|
+
- When exposing the HTTP server publicly (for Claude.ai / ChatGPT web), use TLS and consider adding authentication via a reverse proxy.
|
|
272
|
+
- `ssh_run_command` is marked `destructiveHint: true` — MCP clients that respect annotations will prompt before running potentially dangerous commands.
|
|
273
|
+
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## Project structure
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
sshand/
|
|
280
|
+
├── server.py # FastMCP server — all 11 tools + CLI entry point
|
|
281
|
+
├── ssh_client.py # Async paramiko wrapper (command exec + SFTP)
|
|
282
|
+
├── host_config.py # YAML host inventory manager
|
|
283
|
+
├── setup_wizard.py # Interactive first-run setup wizard
|
|
284
|
+
├── platform_utils.py # Windows SSH agent helpers
|
|
285
|
+
├── hosts.yaml.example # Safe template — copy to hosts.yaml and fill in your values
|
|
286
|
+
├── hosts.yaml # Your SSH targets (gitignored — copy from hosts.yaml.example)
|
|
287
|
+
├── INTEGRATIONS.md # Guide: Claude.ai and ChatGPT native extensions
|
|
288
|
+
├── pyproject.toml # Package metadata + pip/uvx install config
|
|
289
|
+
├── requirements.txt # Plain pip install fallback
|
|
290
|
+
└── README.md
|
|
291
|
+
```
|