mcpmon 0.1.0 → 0.3.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.
@@ -18,6 +18,9 @@ jobs:
18
18
  runs-on: ubuntu-latest
19
19
  permissions:
20
20
  contents: write
21
+ id-token: write
22
+ outputs:
23
+ version: ${{ steps.bump.outputs.version }}
21
24
  steps:
22
25
  - uses: actions/checkout@v4
23
26
  with:
@@ -27,6 +30,11 @@ jobs:
27
30
  with:
28
31
  python-version: "3.12"
29
32
 
33
+ - uses: actions/setup-node@v4
34
+ with:
35
+ node-version: '20'
36
+ # NOTE: No registry-url - npm OIDC trusted publishers require no auth token present
37
+
30
38
  - name: Get current version
31
39
  id: current
32
40
  run: |
@@ -35,27 +43,86 @@ jobs:
35
43
 
36
44
  - name: Bump version
37
45
  id: bump
46
+ env:
47
+ CURRENT_VERSION: ${{ steps.current.outputs.version }}
48
+ BUMP_TYPE: ${{ inputs.bump }}
38
49
  run: |
39
- IFS='.' read -r major minor patch <<< "${{ steps.current.outputs.version }}"
40
- case "${{ inputs.bump }}" in
50
+ IFS='.' read -r major minor patch <<< "$CURRENT_VERSION"
51
+ case "$BUMP_TYPE" in
41
52
  major) major=$((major + 1)); minor=0; patch=0 ;;
42
53
  minor) minor=$((minor + 1)); patch=0 ;;
43
54
  patch) patch=$((patch + 1)) ;;
44
55
  esac
45
56
  NEW_VERSION="$major.$minor.$patch"
46
57
  echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
47
- sed -i "s/version = \"${{ steps.current.outputs.version }}\"/version = \"$NEW_VERSION\"/" pyproject.toml
58
+ sed -i "s/version = \"$CURRENT_VERSION\"/version = \"$NEW_VERSION\"/" pyproject.toml
59
+ npm version "$NEW_VERSION" --no-git-tag-version
48
60
 
49
61
  - name: Commit and tag
62
+ env:
63
+ NEW_VERSION: ${{ steps.bump.outputs.version }}
50
64
  run: |
51
65
  git config user.name "github-actions[bot]"
52
66
  git config user.email "github-actions[bot]@users.noreply.github.com"
53
- git add pyproject.toml
54
- git commit -m "(release): v${{ steps.bump.outputs.version }}"
55
- git tag "v${{ steps.bump.outputs.version }}"
67
+ git add pyproject.toml package.json
68
+ git commit -m "(release): v$NEW_VERSION"
69
+ git tag "v$NEW_VERSION"
56
70
  git push && git push --tags
57
71
 
58
72
  - name: Create release
59
- run: gh release create "v${{ steps.bump.outputs.version }}" --generate-notes
60
73
  env:
61
74
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
75
+ NEW_VERSION: ${{ steps.bump.outputs.version }}
76
+ run: gh release create "v$NEW_VERSION" --generate-notes
77
+
78
+ # Publish to PyPI
79
+ - name: Install build
80
+ run: pip install build
81
+
82
+ - name: Build Python package
83
+ run: python -m build
84
+
85
+ - name: Publish to PyPI
86
+ uses: pypa/gh-action-pypi-publish@release/v1
87
+
88
+ # Publish to npm (OIDC trusted publisher)
89
+ - name: Upgrade npm for OIDC support
90
+ run: npm install -g npm@latest && npm --version
91
+
92
+ - name: Publish to npm
93
+ run: npm publish --provenance --access public
94
+
95
+ build-binaries:
96
+ needs: release
97
+ runs-on: ${{ matrix.os }}
98
+ permissions:
99
+ contents: write
100
+ strategy:
101
+ matrix:
102
+ include:
103
+ - os: ubuntu-latest
104
+ target: linux-x64
105
+ - os: macos-latest
106
+ target: darwin-arm64
107
+ - os: macos-15-intel
108
+ target: darwin-x64
109
+ steps:
110
+ - uses: actions/checkout@v4
111
+ with:
112
+ ref: v${{ needs.release.outputs.version }}
113
+
114
+ - uses: oven-sh/setup-bun@v2
115
+ with:
116
+ bun-version: latest
117
+
118
+ - name: Build binary
119
+ env:
120
+ TARGET: ${{ matrix.target }}
121
+ run: bun build --compile mcpmon.ts --outfile "mcpmon-$TARGET"
122
+
123
+ - name: Upload to release
124
+ env:
125
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
126
+ VERSION: ${{ needs.release.outputs.version }}
127
+ TARGET: ${{ matrix.target }}
128
+ run: gh release upload "v$VERSION" "mcpmon-$TARGET" --clobber
package/.nojekyll ADDED
File without changes
@@ -0,0 +1,44 @@
1
+ # ============================================================================
2
+ # .pre-commit-config.yaml
3
+ # Install: pip install pre-commit && pre-commit install
4
+ # Run manually: pre-commit run --all-files
5
+ # ============================================================================
6
+
7
+ repos:
8
+ # -------------------------------------------------------------------------
9
+ # SECRET DETECTION
10
+ # -------------------------------------------------------------------------
11
+
12
+ - repo: https://github.com/gitleaks/gitleaks
13
+ rev: v8.18.1
14
+ hooks:
15
+ - id: gitleaks
16
+ name: Detect secrets with gitleaks
17
+
18
+ # -------------------------------------------------------------------------
19
+ # GENERAL
20
+ # -------------------------------------------------------------------------
21
+
22
+ - repo: https://github.com/pre-commit/pre-commit-hooks
23
+ rev: v4.5.0
24
+ hooks:
25
+ - id: trailing-whitespace
26
+ - id: end-of-file-fixer
27
+ - id: check-yaml
28
+ - id: check-json
29
+ - id: check-toml
30
+ - id: check-added-large-files
31
+ args: ['--maxkb=500']
32
+ - id: detect-private-key
33
+ - id: detect-aws-credentials
34
+
35
+ # -------------------------------------------------------------------------
36
+ # PYTHON
37
+ # -------------------------------------------------------------------------
38
+
39
+ - repo: https://github.com/astral-sh/ruff-pre-commit
40
+ rev: v0.3.0
41
+ hooks:
42
+ - id: ruff
43
+ args: [--fix]
44
+ - id: ruff-format
package/README.md CHANGED
@@ -25,23 +25,57 @@ mcpmon --watch src/ -- python -m my_mcp_server
25
25
 
26
26
  ### Options
27
27
 
28
- - `--watch, -w` - Directory to watch (default: current directory)
29
- - `--ext, -e` - File extensions to watch, comma-separated (default: py)
28
+ | Option | Description |
29
+ |--------|-------------|
30
+ | `-w, --watch <dir>` | Directory to watch (default: `.`) |
31
+ | `-e, --ext <exts>` | Extensions to watch, comma-separated (default: `py`) |
32
+ | `-q, --quiet` | Only show errors |
33
+ | `-v, --verbose` | Show file change details |
34
+ | `--debug` | Show all debug output |
35
+ | `-t, --timestamps` | Include timestamps in output |
36
+ | `-l, --log-file <file>` | Also write logs to file |
37
+
38
+ ### Logging Levels
39
+
40
+ ```
41
+ --quiet Only errors
42
+ (default) Start, stop, restart events + PID
43
+ --verbose + file change details
44
+ --debug + everything (ignored files, spawning, exit codes)
45
+ ```
30
46
 
31
47
  ### Examples
32
48
 
33
49
  ```bash
34
- # Watch current directory for .py changes
50
+ # Basic usage - watch current directory for .py changes
35
51
  mcpmon -- python server.py
36
52
 
37
53
  # Watch src/ for .py and .json changes
38
54
  mcpmon --watch src/ --ext py,json -- python -m myserver
39
55
 
56
+ # With timestamps and verbose output
57
+ mcpmon --timestamps --verbose -- python server.py
58
+
59
+ # Log to file for debugging
60
+ mcpmon --debug --log-file mcpmon.log -- python server.py
61
+
40
62
  # With crucible-mcp
41
63
  mcpmon --watch src/crucible/ -- crucible-mcp
42
64
 
43
65
  # With sage-mcp
44
- mcpmon --watch ~/.sage/ --ext py,yaml -- sage-mcp
66
+ mcpmon --watch sage/ --ext py -- python -m sage.mcp_server
67
+ ```
68
+
69
+ ### Sample Output
70
+
71
+ ```
72
+ [mcpmon 16:08:50] Watching sage for .py changes
73
+ [mcpmon 16:08:50 pid:53307] Started: python -m sage.mcp_server
74
+ [mcpmon 16:08:54 pid:53307] Restarting...
75
+ [mcpmon 16:08:54 pid:53411] Started: python -m sage.mcp_server
76
+ [mcpmon 16:08:54 pid:53411] Restart #1 complete
77
+ [mcpmon 16:08:57] Received SIGTERM, shutting down...
78
+ [mcpmon 16:08:57] Shutdown complete (restarts: 1)
45
79
  ```
46
80
 
47
81
  ## MCP Config
@@ -65,3 +99,24 @@ Use mcpmon in your `.mcp.json` for hot reload during development:
65
99
  2. Watches specified directory for file changes
66
100
  3. On change: SIGTERM → wait 2s → SIGKILL → restart
67
101
  4. Claude Code automatically reconnects to the restarted server
102
+
103
+ ## Dual Implementation
104
+
105
+ mcpmon ships as both:
106
+ - **Bun/TypeScript** (`mcpmon.ts`) - Zero dependencies, fast startup
107
+ - **Python** (`mcpmon.py`) - Uses `watchfiles` for robust file watching
108
+
109
+ Both implementations have feature parity.
110
+
111
+ ## Development
112
+
113
+ ```bash
114
+ # Install dev dependencies (Python)
115
+ pip install -e ".[dev]"
116
+
117
+ # Run Python tests (27 tests)
118
+ pytest tests/ -v
119
+
120
+ # Run Bun/TS tests (12 tests)
121
+ bun test
122
+ ```
package/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Allow running mcpmon as `python -m mcpmon`."""
2
+
3
+ from mcpmon import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
package/index.html ADDED
@@ -0,0 +1,414 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>mcpmon - Hot reload for MCP servers</title>
7
+ <meta name="description" content="Hot reload for MCP servers. Like nodemon, but for MCP.">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=VT323&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
11
+ <style>
12
+ :root {
13
+ --bg-primary: #0a0a0f;
14
+ --bg-secondary: #12121a;
15
+ --bg-tertiary: #1a1a2e;
16
+ --neon-cyan: #00f5ff;
17
+ --neon-magenta: #ff00aa;
18
+ --neon-yellow: #ffe600;
19
+ --neon-green: #00ff88;
20
+ --text-primary: #e0e0e0;
21
+ --text-secondary: #888888;
22
+ }
23
+
24
+ * {
25
+ margin: 0;
26
+ padding: 0;
27
+ box-sizing: border-box;
28
+ }
29
+
30
+ body {
31
+ background: var(--bg-primary);
32
+ color: var(--text-primary);
33
+ font-family: 'VT323', monospace;
34
+ font-size: 18px;
35
+ line-height: 1.6;
36
+ min-height: 100vh;
37
+ overflow-x: hidden;
38
+ }
39
+
40
+ /* Scanlines overlay */
41
+ body::before {
42
+ content: '';
43
+ position: fixed;
44
+ top: 0;
45
+ left: 0;
46
+ width: 100%;
47
+ height: 100%;
48
+ background: repeating-linear-gradient(
49
+ 0deg,
50
+ rgba(0, 0, 0, 0.1),
51
+ rgba(0, 0, 0, 0.1) 1px,
52
+ transparent 1px,
53
+ transparent 2px
54
+ );
55
+ pointer-events: none;
56
+ z-index: 1000;
57
+ }
58
+
59
+ /* CRT vignette */
60
+ body::after {
61
+ content: '';
62
+ position: fixed;
63
+ top: 0;
64
+ left: 0;
65
+ width: 100%;
66
+ height: 100%;
67
+ background: radial-gradient(
68
+ ellipse at center,
69
+ transparent 0%,
70
+ transparent 60%,
71
+ rgba(0, 0, 0, 0.4) 100%
72
+ );
73
+ pointer-events: none;
74
+ z-index: 999;
75
+ }
76
+
77
+ .container {
78
+ max-width: 900px;
79
+ margin: 0 auto;
80
+ padding: 2rem;
81
+ }
82
+
83
+ /* Header */
84
+ header {
85
+ display: flex;
86
+ justify-content: space-between;
87
+ align-items: center;
88
+ padding: 1rem 0;
89
+ border-bottom: 2px solid var(--bg-tertiary);
90
+ margin-bottom: 3rem;
91
+ }
92
+
93
+ .logo {
94
+ font-family: 'Press Start 2P', monospace;
95
+ font-size: 14px;
96
+ color: var(--neon-cyan);
97
+ text-decoration: none;
98
+ }
99
+
100
+ .github-link {
101
+ color: var(--text-secondary);
102
+ text-decoration: none;
103
+ font-size: 20px;
104
+ transition: color 0.2s;
105
+ }
106
+
107
+ .github-link:hover {
108
+ color: var(--neon-cyan);
109
+ }
110
+
111
+ /* Hero */
112
+ .hero {
113
+ text-align: center;
114
+ padding: 2rem 0 3rem;
115
+ }
116
+
117
+ .ascii-art {
118
+ font-family: 'Fira Code', monospace;
119
+ font-size: 14px;
120
+ color: var(--neon-cyan);
121
+ text-shadow: 0 0 10px var(--neon-cyan), 0 0 20px var(--neon-cyan);
122
+ margin-bottom: 2rem;
123
+ line-height: 1.2;
124
+ white-space: pre;
125
+ }
126
+
127
+ @media (max-width: 600px) {
128
+ .ascii-art {
129
+ font-size: 10px;
130
+ }
131
+ }
132
+
133
+ .tagline {
134
+ font-family: 'VT323', monospace;
135
+ font-size: 28px;
136
+ color: var(--text-primary);
137
+ margin-bottom: 0.5rem;
138
+ }
139
+
140
+ .subtext {
141
+ font-size: 22px;
142
+ color: var(--text-secondary);
143
+ }
144
+
145
+ /* Terminal */
146
+ .terminal {
147
+ background: var(--bg-secondary);
148
+ border: 2px solid var(--bg-tertiary);
149
+ border-radius: 4px;
150
+ padding: 1.5rem;
151
+ margin: 3rem 0;
152
+ font-family: 'Fira Code', monospace;
153
+ font-size: 14px;
154
+ overflow-x: auto;
155
+ box-shadow:
156
+ 0 0 20px rgba(0, 245, 255, 0.1),
157
+ inset 0 0 60px rgba(0, 0, 0, 0.3);
158
+ }
159
+
160
+ .terminal-header {
161
+ display: flex;
162
+ gap: 6px;
163
+ margin-bottom: 1rem;
164
+ padding-bottom: 0.75rem;
165
+ border-bottom: 1px solid var(--bg-tertiary);
166
+ }
167
+
168
+ .terminal-dot {
169
+ width: 12px;
170
+ height: 12px;
171
+ border-radius: 50%;
172
+ }
173
+
174
+ .terminal-dot.red { background: #ff5f56; }
175
+ .terminal-dot.yellow { background: #ffbd2e; }
176
+ .terminal-dot.green { background: #27ca40; }
177
+
178
+ .terminal-line {
179
+ margin: 0.25rem 0;
180
+ opacity: 0;
181
+ animation: typeIn 0.3s ease forwards;
182
+ }
183
+
184
+ .terminal-line:nth-child(1) { animation-delay: 0.2s; }
185
+ .terminal-line:nth-child(2) { animation-delay: 0.6s; }
186
+ .terminal-line:nth-child(3) { animation-delay: 1.0s; }
187
+ .terminal-line:nth-child(4) { animation-delay: 1.8s; }
188
+ .terminal-line:nth-child(5) { animation-delay: 2.2s; }
189
+ .terminal-line:nth-child(6) { animation-delay: 2.6s; }
190
+
191
+ @keyframes typeIn {
192
+ from { opacity: 0; transform: translateX(-10px); }
193
+ to { opacity: 1; transform: translateX(0); }
194
+ }
195
+
196
+ .prompt { color: var(--neon-green); }
197
+ .cmd { color: var(--text-primary); }
198
+ .output { color: var(--neon-cyan); }
199
+ .file { color: var(--neon-yellow); }
200
+
201
+ /* Install cards */
202
+ .install-section {
203
+ margin: 3rem 0;
204
+ }
205
+
206
+ .section-title {
207
+ font-family: 'Press Start 2P', monospace;
208
+ font-size: 12px;
209
+ color: var(--neon-cyan);
210
+ margin-bottom: 1.5rem;
211
+ }
212
+
213
+ .install-grid {
214
+ display: grid;
215
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
216
+ gap: 1rem;
217
+ }
218
+
219
+ .install-card {
220
+ background: var(--bg-secondary);
221
+ border: 2px solid var(--bg-tertiary);
222
+ padding: 1.5rem;
223
+ text-align: center;
224
+ transition: all 0.2s;
225
+ }
226
+
227
+ .install-card:hover {
228
+ border-color: var(--neon-cyan);
229
+ box-shadow: 0 0 20px rgba(0, 245, 255, 0.2);
230
+ }
231
+
232
+ .install-card h3 {
233
+ font-family: 'Press Start 2P', monospace;
234
+ font-size: 11px;
235
+ color: var(--text-primary);
236
+ margin-bottom: 1rem;
237
+ }
238
+
239
+ .install-card code {
240
+ display: block;
241
+ background: var(--bg-primary);
242
+ padding: 0.75rem;
243
+ font-family: 'Fira Code', monospace;
244
+ font-size: 13px;
245
+ color: var(--neon-green);
246
+ border: 1px solid var(--bg-tertiary);
247
+ }
248
+
249
+ .install-card .note {
250
+ font-size: 14px;
251
+ color: var(--text-secondary);
252
+ margin-top: 0.75rem;
253
+ }
254
+
255
+ /* Usage */
256
+ .usage-section {
257
+ margin: 3rem 0;
258
+ }
259
+
260
+ .code-block {
261
+ background: var(--bg-secondary);
262
+ border: 2px solid var(--bg-tertiary);
263
+ padding: 1.5rem;
264
+ font-family: 'Fira Code', monospace;
265
+ font-size: 13px;
266
+ overflow-x: auto;
267
+ }
268
+
269
+ .code-block .comment { color: var(--text-secondary); }
270
+ .code-block .key { color: var(--neon-magenta); }
271
+ .code-block .string { color: var(--neon-green); }
272
+ .code-block .bracket { color: var(--text-primary); }
273
+
274
+ /* Footer */
275
+ footer {
276
+ margin-top: 4rem;
277
+ padding: 2rem 0;
278
+ border-top: 2px solid var(--bg-tertiary);
279
+ text-align: center;
280
+ }
281
+
282
+ .footer-links {
283
+ display: flex;
284
+ justify-content: center;
285
+ gap: 2rem;
286
+ margin-bottom: 1.5rem;
287
+ }
288
+
289
+ .footer-links a {
290
+ color: var(--text-secondary);
291
+ text-decoration: none;
292
+ font-size: 18px;
293
+ transition: color 0.2s;
294
+ }
295
+
296
+ .footer-links a:hover {
297
+ color: var(--neon-cyan);
298
+ }
299
+
300
+ .footer-family {
301
+ font-size: 16px;
302
+ color: var(--text-secondary);
303
+ }
304
+
305
+ .footer-family a {
306
+ color: var(--neon-cyan);
307
+ text-decoration: none;
308
+ }
309
+
310
+ .footer-family a:hover {
311
+ text-decoration: underline;
312
+ }
313
+ </style>
314
+ </head>
315
+ <body>
316
+ <div class="container">
317
+ <header>
318
+ <a href="/" class="logo">mcpmon</a>
319
+ <a href="https://github.com/b17z/mcpmon" class="github-link" aria-label="GitHub">[GitHub]</a>
320
+ </header>
321
+
322
+ <main>
323
+ <section class="hero">
324
+ <pre class="ascii-art">
325
+ ░█▄█░█▀▀░█▀█░█▄█░█▀█░█▀█
326
+ ░█░█░█░░░█▀▀░█░█░█░█░█░█
327
+ ░▀░▀░▀▀▀░▀░░░▀░▀░▀▀▀░▀░▀</pre>
328
+ <p class="tagline">Hot reload for MCP servers.</p>
329
+ <p class="subtext">Change code. Server restarts. Context stays.</p>
330
+ </section>
331
+
332
+ <section class="terminal">
333
+ <div class="terminal-header">
334
+ <span class="terminal-dot red"></span>
335
+ <span class="terminal-dot yellow"></span>
336
+ <span class="terminal-dot green"></span>
337
+ </div>
338
+ <div class="terminal-content">
339
+ <div class="terminal-line"><span class="prompt">$</span> <span class="cmd">mcpmon --timestamps --verbose --watch src/ -- python -m my_server</span></div>
340
+ <div class="terminal-line"><span class="output">[mcpmon 14:32:01]</span> Watching src/ for .py changes</div>
341
+ <div class="terminal-line"><span class="output">[mcpmon 14:32:01 <span class="file">pid:48291</span>]</span> Started: python -m my_server</div>
342
+ <div class="terminal-line"><span class="output">[mcpmon 14:32:15]</span> File modified: <span class="file">tools.py</span></div>
343
+ <div class="terminal-line"><span class="output">[mcpmon 14:32:15 <span class="file">pid:48291</span>]</span> Restarting...</div>
344
+ <div class="terminal-line"><span class="output">[mcpmon 14:32:15 <span class="file">pid:48342</span>]</span> Restart #1 complete</div>
345
+ </div>
346
+ </section>
347
+
348
+ <section class="install-section">
349
+ <h2 class="section-title">LOGGING LEVELS</h2>
350
+ <div class="install-grid">
351
+ <div class="install-card">
352
+ <h3>--quiet</h3>
353
+ <code>errors only</code>
354
+ </div>
355
+ <div class="install-card">
356
+ <h3>default</h3>
357
+ <code>start/stop + PIDs</code>
358
+ </div>
359
+ <div class="install-card">
360
+ <h3>--verbose</h3>
361
+ <code>+ file changes</code>
362
+ </div>
363
+ <div class="install-card">
364
+ <h3>--debug</h3>
365
+ <code>+ everything</code>
366
+ </div>
367
+ </div>
368
+ </section>
369
+
370
+ <section class="install-section">
371
+ <h2 class="section-title">INSTALL</h2>
372
+ <div class="install-grid">
373
+ <div class="install-card">
374
+ <h3>Python</h3>
375
+ <code>pip install mcpmon</code>
376
+ <p class="note">requires watchfiles</p>
377
+ </div>
378
+ <div class="install-card">
379
+ <h3>Bun</h3>
380
+ <code>bunx mcpmon</code>
381
+ <p class="note">no install needed</p>
382
+ </div>
383
+ <div class="install-card">
384
+ <h3>Binary</h3>
385
+ <code>download & run</code>
386
+ <p class="note">no dependencies</p>
387
+ </div>
388
+ </div>
389
+ </section>
390
+
391
+ <section class="usage-section">
392
+ <h2 class="section-title">MCP CONFIG</h2>
393
+ <pre class="code-block"><span class="bracket">{</span>
394
+ <span class="key">"mcpServers"</span><span class="bracket">:</span> <span class="bracket">{</span>
395
+ <span class="key">"my-server"</span><span class="bracket">:</span> <span class="bracket">{</span>
396
+ <span class="key">"command"</span><span class="bracket">:</span> <span class="string">"mcpmon"</span><span class="bracket">,</span>
397
+ <span class="key">"args"</span><span class="bracket">:</span> <span class="bracket">[</span><span class="string">"--watch"</span><span class="bracket">,</span> <span class="string">"src/"</span><span class="bracket">,</span> <span class="string">"--"</span><span class="bracket">,</span> <span class="string">"python"</span><span class="bracket">,</span> <span class="string">"-m"</span><span class="bracket">,</span> <span class="string">"my_server"</span><span class="bracket">]</span>
398
+ <span class="bracket">}</span>
399
+ <span class="bracket">}</span>
400
+ <span class="bracket">}</span></pre>
401
+ </section>
402
+ </main>
403
+
404
+ <footer>
405
+ <div class="footer-links">
406
+ <a href="https://github.com/b17z/mcpmon">GitHub</a>
407
+ <a href="https://pypi.org/project/mcpmon/">PyPI</a>
408
+ <a href="https://www.npmjs.com/package/mcpmon">npm</a>
409
+ </div>
410
+ <p class="footer-family">part of: <a href="https://github.com/b17z/sage">sage</a> | <a href="https://github.com/b17z/crucible">crucible</a></p>
411
+ </footer>
412
+ </div>
413
+ </body>
414
+ </html>