nomad-mcp 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.
- nomad_mcp-0.1.0/LICENSE +21 -0
- nomad_mcp-0.1.0/PKG-INFO +205 -0
- nomad_mcp-0.1.0/README.md +188 -0
- nomad_mcp-0.1.0/pyproject.toml +38 -0
- nomad_mcp-0.1.0/setup.cfg +4 -0
- nomad_mcp-0.1.0/src/nomad/__init__.py +2 -0
- nomad_mcp-0.1.0/src/nomad/cli.py +124 -0
- nomad_mcp-0.1.0/src/nomad/config.py +366 -0
- nomad_mcp-0.1.0/src/nomad/result.py +97 -0
- nomad_mcp-0.1.0/src/nomad/schema.py +103 -0
- nomad_mcp-0.1.0/src/nomad/security.py +274 -0
- nomad_mcp-0.1.0/src/nomad/server.py +115 -0
- nomad_mcp-0.1.0/src/nomad/ssh.py +265 -0
- nomad_mcp-0.1.0/src/nomad/tools/__init__.py +1 -0
- nomad_mcp-0.1.0/src/nomad/tools/commands.py +255 -0
- nomad_mcp-0.1.0/src/nomad/tools/init.py +694 -0
- nomad_mcp-0.1.0/src/nomad/tools/network.py +871 -0
- nomad_mcp-0.1.0/src/nomad/tools/sync.py +627 -0
- nomad_mcp-0.1.0/src/nomad/tools/tasks.py +920 -0
- nomad_mcp-0.1.0/src/nomad/truncate.py +61 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/PKG-INFO +205 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/SOURCES.txt +36 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/dependency_links.txt +1 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/entry_points.txt +2 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/requires.txt +5 -0
- nomad_mcp-0.1.0/src/nomad_mcp.egg-info/top_level.txt +1 -0
- nomad_mcp-0.1.0/tests/test_cli.py +46 -0
- nomad_mcp-0.1.0/tests/test_commands.py +435 -0
- nomad_mcp-0.1.0/tests/test_config.py +462 -0
- nomad_mcp-0.1.0/tests/test_init.py +589 -0
- nomad_mcp-0.1.0/tests/test_network.py +723 -0
- nomad_mcp-0.1.0/tests/test_result.py +104 -0
- nomad_mcp-0.1.0/tests/test_security.py +255 -0
- nomad_mcp-0.1.0/tests/test_server.py +106 -0
- nomad_mcp-0.1.0/tests/test_ssh.py +249 -0
- nomad_mcp-0.1.0/tests/test_sync.py +693 -0
- nomad_mcp-0.1.0/tests/test_tasks.py +1021 -0
- nomad_mcp-0.1.0/tests/test_truncate.py +81 -0
nomad_mcp-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tsunam1
|
|
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.
|
nomad_mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nomad-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local MCP Server to manage remote development execution, sync, and tasks.
|
|
5
|
+
Author: Tsunam1
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: mcp>=1.0.0
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-mock>=3.0.0; extra == "dev"
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# nomad
|
|
19
|
+
|
|
20
|
+
[中文说明](README.zh-CN.md)
|
|
21
|
+
|
|
22
|
+
nomad is a local MCP server for agentic remote development.
|
|
23
|
+
|
|
24
|
+
It helps an AI coding agent work with a remote machine while keeping the source
|
|
25
|
+
of truth on your local workstation: sync code with `rsync`, run short commands
|
|
26
|
+
over SSH, manage long-running jobs in remote `tmux` sessions, diagnose network
|
|
27
|
+
issues, and pull generated artifacts back into the local project.
|
|
28
|
+
|
|
29
|
+
nomad is not tied to a specific MCP client. Any agent environment that can start
|
|
30
|
+
a stdio MCP server with a command and arguments can use it.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- Multi-target remote workspaces per local project.
|
|
35
|
+
- Project-local `.nomad.json` configuration with schema hints exposed through MCP.
|
|
36
|
+
- SSH preflight checks and read-only network diagnostics.
|
|
37
|
+
- Incremental `rsync` push with `.gitignore` conversion and `--delete` dry-run protection.
|
|
38
|
+
- Remote artifact pull into project-owned local directories.
|
|
39
|
+
- Short remote command execution with output truncation.
|
|
40
|
+
- Long-running remote task management through `tmux`.
|
|
41
|
+
- Optional persistent reverse SSH tunnel for sharing a local proxy with remote jobs.
|
|
42
|
+
- Path guards, dangerous-command checks, and secret redaction for safer agent workflows.
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
- Python 3.11+
|
|
47
|
+
- `ssh`
|
|
48
|
+
- `rsync`
|
|
49
|
+
- `tmux` on remote machines when using long-running tasks
|
|
50
|
+
- Key-based SSH access to your remote targets
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
Run directly with `uvx`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uvx nomad-mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Or install it as an isolated global command with `pipx`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pipx install nomad-mcp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## MCP Client Configuration
|
|
67
|
+
|
|
68
|
+
Use nomad as a stdio MCP server. The exact config file depends on your client.
|
|
69
|
+
|
|
70
|
+
Recommended no-install configuration:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"nomad": {
|
|
76
|
+
"command": "uvx",
|
|
77
|
+
"args": ["nomad-mcp"]
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For TOML-based clients:
|
|
84
|
+
|
|
85
|
+
```toml
|
|
86
|
+
[mcp_servers.nomad]
|
|
87
|
+
command = "uvx"
|
|
88
|
+
args = ["nomad-mcp"]
|
|
89
|
+
startup_timeout_sec = 120
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
If you installed with `pipx`, use the installed command instead:
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"mcpServers": {
|
|
97
|
+
"nomad": {
|
|
98
|
+
"command": "nomad",
|
|
99
|
+
"args": []
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
You can also print config snippets with:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
nomad client-config
|
|
109
|
+
nomad client-config --runner nomad --format toml
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Quick Start
|
|
113
|
+
|
|
114
|
+
1. Open an MCP-enabled agent in your local project directory.
|
|
115
|
+
2. Ask it to call `init_discover`.
|
|
116
|
+
3. Choose an SSH target and remote workspace path.
|
|
117
|
+
4. Ask it to save a `.nomad.json` config with `init_save_config`.
|
|
118
|
+
5. Push code with `sync_push`.
|
|
119
|
+
6. Run short commands with `run_remote`.
|
|
120
|
+
7. Run long jobs with `task_start`, then monitor them with `task_status` or `task_list`.
|
|
121
|
+
8. Pull remote artifacts with `sync_pull`.
|
|
122
|
+
|
|
123
|
+
## Example `.nomad.json`
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"project_name": "my_project",
|
|
128
|
+
"mode": "remote",
|
|
129
|
+
"default_target": "devbox",
|
|
130
|
+
"targets": {
|
|
131
|
+
"devbox": {
|
|
132
|
+
"description": "Primary remote development machine",
|
|
133
|
+
"ssh_host": "devbox",
|
|
134
|
+
"remote_path": "/data/my_project",
|
|
135
|
+
"local_subpath": null,
|
|
136
|
+
"auto_create_remote_path": true,
|
|
137
|
+
"network": {
|
|
138
|
+
"use_proxy_for_ssh": false,
|
|
139
|
+
"jump_host": null,
|
|
140
|
+
"reverse_tunnel": {
|
|
141
|
+
"enabled": false,
|
|
142
|
+
"proxy_scheme": "socks5"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"sync": {
|
|
146
|
+
"respect_gitignore": true,
|
|
147
|
+
"extra_excludes": []
|
|
148
|
+
},
|
|
149
|
+
"runtime": {
|
|
150
|
+
"interpreter": null,
|
|
151
|
+
"extra_env": {}
|
|
152
|
+
},
|
|
153
|
+
"limits": {
|
|
154
|
+
"command_timeout_seconds": 60,
|
|
155
|
+
"max_output_lines": 200,
|
|
156
|
+
"max_output_bytes": 10240
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`run_remote` uses `limits.command_timeout_seconds`. For downloads, builds,
|
|
164
|
+
training, fuzzing, and other slow work, prefer `task_start` so the job runs in a
|
|
165
|
+
remote tmux session and can be checked later.
|
|
166
|
+
|
|
167
|
+
## Tools
|
|
168
|
+
|
|
169
|
+
- `init_discover`: inspect the local workspace, SSH aliases, and proxy settings.
|
|
170
|
+
- `init_verify_and_probe`: verify SSH reachability and probe remote hardware/runtimes.
|
|
171
|
+
- `init_save_config`: validate and save `.nomad.json`.
|
|
172
|
+
- `init_probe_target`: refresh hardware/runtime information for a target.
|
|
173
|
+
- `sync_push`: push local code to the remote workspace.
|
|
174
|
+
- `sync_pull`: pull a remote file or directory into local `remote_artifacts/`.
|
|
175
|
+
- `run_remote`: run a short command in the remote workspace.
|
|
176
|
+
- `task_start`: start a long-running tmux task.
|
|
177
|
+
- `task_status`: inspect one task and return a log tail.
|
|
178
|
+
- `task_list`: list project-owned tasks across targets.
|
|
179
|
+
- `task_kill`: stop a task without deleting its logs.
|
|
180
|
+
- `net_diagnose`: run read-only SSH/network diagnostics.
|
|
181
|
+
- `tunnel_start`, `tunnel_status`, `tunnel_stop`: manage persistent reverse tunnels.
|
|
182
|
+
|
|
183
|
+
## Safety Notes
|
|
184
|
+
|
|
185
|
+
nomad can execute commands over SSH and synchronize files with `rsync`. Use it
|
|
186
|
+
only with trusted local projects and trusted remote machines.
|
|
187
|
+
|
|
188
|
+
The server includes guardrails such as local/remote path checks, dangerous-command
|
|
189
|
+
blocking, `.nomad.json` sync exclusion, secret redaction, output truncation, and
|
|
190
|
+
`rsync --delete` dry-run protection. These guardrails reduce risk, but they do not
|
|
191
|
+
turn an untrusted agent or remote machine into a trusted one.
|
|
192
|
+
|
|
193
|
+
## Development
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
python -m pip install -e .[dev]
|
|
197
|
+
nomad --version
|
|
198
|
+
nomad doctor
|
|
199
|
+
python -m pytest
|
|
200
|
+
python -m compileall -q src tests
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# nomad
|
|
2
|
+
|
|
3
|
+
[中文说明](README.zh-CN.md)
|
|
4
|
+
|
|
5
|
+
nomad is a local MCP server for agentic remote development.
|
|
6
|
+
|
|
7
|
+
It helps an AI coding agent work with a remote machine while keeping the source
|
|
8
|
+
of truth on your local workstation: sync code with `rsync`, run short commands
|
|
9
|
+
over SSH, manage long-running jobs in remote `tmux` sessions, diagnose network
|
|
10
|
+
issues, and pull generated artifacts back into the local project.
|
|
11
|
+
|
|
12
|
+
nomad is not tied to a specific MCP client. Any agent environment that can start
|
|
13
|
+
a stdio MCP server with a command and arguments can use it.
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Multi-target remote workspaces per local project.
|
|
18
|
+
- Project-local `.nomad.json` configuration with schema hints exposed through MCP.
|
|
19
|
+
- SSH preflight checks and read-only network diagnostics.
|
|
20
|
+
- Incremental `rsync` push with `.gitignore` conversion and `--delete` dry-run protection.
|
|
21
|
+
- Remote artifact pull into project-owned local directories.
|
|
22
|
+
- Short remote command execution with output truncation.
|
|
23
|
+
- Long-running remote task management through `tmux`.
|
|
24
|
+
- Optional persistent reverse SSH tunnel for sharing a local proxy with remote jobs.
|
|
25
|
+
- Path guards, dangerous-command checks, and secret redaction for safer agent workflows.
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
- Python 3.11+
|
|
30
|
+
- `ssh`
|
|
31
|
+
- `rsync`
|
|
32
|
+
- `tmux` on remote machines when using long-running tasks
|
|
33
|
+
- Key-based SSH access to your remote targets
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Run directly with `uvx`:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx nomad-mcp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or install it as an isolated global command with `pipx`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pipx install nomad-mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## MCP Client Configuration
|
|
50
|
+
|
|
51
|
+
Use nomad as a stdio MCP server. The exact config file depends on your client.
|
|
52
|
+
|
|
53
|
+
Recommended no-install configuration:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"mcpServers": {
|
|
58
|
+
"nomad": {
|
|
59
|
+
"command": "uvx",
|
|
60
|
+
"args": ["nomad-mcp"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For TOML-based clients:
|
|
67
|
+
|
|
68
|
+
```toml
|
|
69
|
+
[mcp_servers.nomad]
|
|
70
|
+
command = "uvx"
|
|
71
|
+
args = ["nomad-mcp"]
|
|
72
|
+
startup_timeout_sec = 120
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If you installed with `pipx`, use the installed command instead:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"nomad": {
|
|
81
|
+
"command": "nomad",
|
|
82
|
+
"args": []
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You can also print config snippets with:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
nomad client-config
|
|
92
|
+
nomad client-config --runner nomad --format toml
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Quick Start
|
|
96
|
+
|
|
97
|
+
1. Open an MCP-enabled agent in your local project directory.
|
|
98
|
+
2. Ask it to call `init_discover`.
|
|
99
|
+
3. Choose an SSH target and remote workspace path.
|
|
100
|
+
4. Ask it to save a `.nomad.json` config with `init_save_config`.
|
|
101
|
+
5. Push code with `sync_push`.
|
|
102
|
+
6. Run short commands with `run_remote`.
|
|
103
|
+
7. Run long jobs with `task_start`, then monitor them with `task_status` or `task_list`.
|
|
104
|
+
8. Pull remote artifacts with `sync_pull`.
|
|
105
|
+
|
|
106
|
+
## Example `.nomad.json`
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"project_name": "my_project",
|
|
111
|
+
"mode": "remote",
|
|
112
|
+
"default_target": "devbox",
|
|
113
|
+
"targets": {
|
|
114
|
+
"devbox": {
|
|
115
|
+
"description": "Primary remote development machine",
|
|
116
|
+
"ssh_host": "devbox",
|
|
117
|
+
"remote_path": "/data/my_project",
|
|
118
|
+
"local_subpath": null,
|
|
119
|
+
"auto_create_remote_path": true,
|
|
120
|
+
"network": {
|
|
121
|
+
"use_proxy_for_ssh": false,
|
|
122
|
+
"jump_host": null,
|
|
123
|
+
"reverse_tunnel": {
|
|
124
|
+
"enabled": false,
|
|
125
|
+
"proxy_scheme": "socks5"
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
"sync": {
|
|
129
|
+
"respect_gitignore": true,
|
|
130
|
+
"extra_excludes": []
|
|
131
|
+
},
|
|
132
|
+
"runtime": {
|
|
133
|
+
"interpreter": null,
|
|
134
|
+
"extra_env": {}
|
|
135
|
+
},
|
|
136
|
+
"limits": {
|
|
137
|
+
"command_timeout_seconds": 60,
|
|
138
|
+
"max_output_lines": 200,
|
|
139
|
+
"max_output_bytes": 10240
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
`run_remote` uses `limits.command_timeout_seconds`. For downloads, builds,
|
|
147
|
+
training, fuzzing, and other slow work, prefer `task_start` so the job runs in a
|
|
148
|
+
remote tmux session and can be checked later.
|
|
149
|
+
|
|
150
|
+
## Tools
|
|
151
|
+
|
|
152
|
+
- `init_discover`: inspect the local workspace, SSH aliases, and proxy settings.
|
|
153
|
+
- `init_verify_and_probe`: verify SSH reachability and probe remote hardware/runtimes.
|
|
154
|
+
- `init_save_config`: validate and save `.nomad.json`.
|
|
155
|
+
- `init_probe_target`: refresh hardware/runtime information for a target.
|
|
156
|
+
- `sync_push`: push local code to the remote workspace.
|
|
157
|
+
- `sync_pull`: pull a remote file or directory into local `remote_artifacts/`.
|
|
158
|
+
- `run_remote`: run a short command in the remote workspace.
|
|
159
|
+
- `task_start`: start a long-running tmux task.
|
|
160
|
+
- `task_status`: inspect one task and return a log tail.
|
|
161
|
+
- `task_list`: list project-owned tasks across targets.
|
|
162
|
+
- `task_kill`: stop a task without deleting its logs.
|
|
163
|
+
- `net_diagnose`: run read-only SSH/network diagnostics.
|
|
164
|
+
- `tunnel_start`, `tunnel_status`, `tunnel_stop`: manage persistent reverse tunnels.
|
|
165
|
+
|
|
166
|
+
## Safety Notes
|
|
167
|
+
|
|
168
|
+
nomad can execute commands over SSH and synchronize files with `rsync`. Use it
|
|
169
|
+
only with trusted local projects and trusted remote machines.
|
|
170
|
+
|
|
171
|
+
The server includes guardrails such as local/remote path checks, dangerous-command
|
|
172
|
+
blocking, `.nomad.json` sync exclusion, secret redaction, output truncation, and
|
|
173
|
+
`rsync --delete` dry-run protection. These guardrails reduce risk, but they do not
|
|
174
|
+
turn an untrusted agent or remote machine into a trusted one.
|
|
175
|
+
|
|
176
|
+
## Development
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
python -m pip install -e .[dev]
|
|
180
|
+
nomad --version
|
|
181
|
+
nomad doctor
|
|
182
|
+
python -m pytest
|
|
183
|
+
python -m compileall -q src tests
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nomad-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Local MCP Server to manage remote development execution, sync, and tasks."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Tsunam1" }
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
dependencies = [
|
|
21
|
+
"mcp>=1.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.optional-dependencies]
|
|
25
|
+
dev = [
|
|
26
|
+
"pytest>=7.0.0",
|
|
27
|
+
"pytest-mock>=3.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.scripts]
|
|
31
|
+
nomad = "nomad.cli:main"
|
|
32
|
+
|
|
33
|
+
[tool.pytest.ini_options]
|
|
34
|
+
testpaths = ["tests"]
|
|
35
|
+
addopts = "-ra"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command line entry point for nomad.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import json
|
|
8
|
+
import shutil
|
|
9
|
+
import sys
|
|
10
|
+
from importlib.util import find_spec
|
|
11
|
+
|
|
12
|
+
from nomad import __version__
|
|
13
|
+
from nomad.schema import get_config_schema_hints
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
PACKAGE_NAME = "nomad-mcp"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def main(argv: list[str] | None = None) -> int | None:
|
|
20
|
+
"""Runs helper CLI commands, or starts the MCP server when no args are given."""
|
|
21
|
+
args_list = list(sys.argv[1:] if argv is None else argv)
|
|
22
|
+
if not args_list:
|
|
23
|
+
from nomad.server import main as server_main
|
|
24
|
+
|
|
25
|
+
server_main()
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
parser = _build_parser()
|
|
29
|
+
args = parser.parse_args(args_list)
|
|
30
|
+
|
|
31
|
+
if args.version:
|
|
32
|
+
print(__version__)
|
|
33
|
+
return 0
|
|
34
|
+
|
|
35
|
+
if args.command == "doctor":
|
|
36
|
+
return _doctor()
|
|
37
|
+
if args.command == "schema":
|
|
38
|
+
print(json.dumps(get_config_schema_hints(), indent=2))
|
|
39
|
+
return 0
|
|
40
|
+
if args.command == "client-config":
|
|
41
|
+
print(_client_config(args.runner, args.format))
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
parser.print_help()
|
|
45
|
+
return 0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
49
|
+
parser = argparse.ArgumentParser(
|
|
50
|
+
prog="nomad",
|
|
51
|
+
description="Local MCP server for agentic remote development.",
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument("--version", action="store_true", help="Print nomad version.")
|
|
54
|
+
|
|
55
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
56
|
+
subparsers.add_parser("doctor", help="Check local runtime dependencies.")
|
|
57
|
+
subparsers.add_parser("schema", help="Print .nomad.json schema hints as JSON.")
|
|
58
|
+
|
|
59
|
+
config_parser = subparsers.add_parser(
|
|
60
|
+
"client-config", help="Print an MCP client configuration snippet."
|
|
61
|
+
)
|
|
62
|
+
config_parser.add_argument(
|
|
63
|
+
"--runner",
|
|
64
|
+
choices=("uvx", "nomad"),
|
|
65
|
+
default="uvx",
|
|
66
|
+
help="Use uvx package execution or an already-installed nomad command.",
|
|
67
|
+
)
|
|
68
|
+
config_parser.add_argument(
|
|
69
|
+
"--format",
|
|
70
|
+
choices=("json", "toml"),
|
|
71
|
+
default="json",
|
|
72
|
+
help="Output config format.",
|
|
73
|
+
)
|
|
74
|
+
return parser
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _doctor() -> int:
|
|
78
|
+
checks = [
|
|
79
|
+
("python>=3.11", sys.version_info >= (3, 11), sys.version.split()[0]),
|
|
80
|
+
("mcp package", find_spec("mcp") is not None, "import mcp"),
|
|
81
|
+
("ssh", shutil.which("ssh") is not None, shutil.which("ssh") or "missing"),
|
|
82
|
+
("rsync", shutil.which("rsync") is not None, shutil.which("rsync") or "missing"),
|
|
83
|
+
]
|
|
84
|
+
ok = True
|
|
85
|
+
for name, passed, detail in checks:
|
|
86
|
+
ok = ok and passed
|
|
87
|
+
mark = "ok" if passed else "missing"
|
|
88
|
+
print(f"{mark:7} {name}: {detail}")
|
|
89
|
+
print("note remote tmux is required only when using task_start/task_status.")
|
|
90
|
+
return 0 if ok else 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _client_config(runner: str, output_format: str) -> str:
|
|
94
|
+
if runner == "uvx":
|
|
95
|
+
command = "uvx"
|
|
96
|
+
args = [PACKAGE_NAME]
|
|
97
|
+
else:
|
|
98
|
+
command = "nomad"
|
|
99
|
+
args = []
|
|
100
|
+
|
|
101
|
+
if output_format == "toml":
|
|
102
|
+
rendered_args = ", ".join(json.dumps(arg) for arg in args)
|
|
103
|
+
return (
|
|
104
|
+
"[mcp_servers.nomad]\n"
|
|
105
|
+
f"command = {json.dumps(command)}\n"
|
|
106
|
+
f"args = [{rendered_args}]\n"
|
|
107
|
+
"startup_timeout_sec = 120"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return json.dumps(
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"nomad": {
|
|
114
|
+
"command": command,
|
|
115
|
+
"args": args,
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
indent=2,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
raise SystemExit(main())
|