mcp-gov 1.0.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.
package/README.md ADDED
@@ -0,0 +1,1247 @@
1
+ # MCP Governance System
2
+
3
+ A permission control and audit logging system for Model Context Protocol (MCP) servers. Provides fine-grained governance over tool operations with **automatic rule generation**, **wrap/unwrap capabilities**, and structured audit logs.
4
+
5
+ ## Quick Start (2 Steps)
6
+
7
+ ```bash
8
+ # 1. Install globally
9
+ npm install -g mcp-gov
10
+
11
+ # 2. Wrap your MCP servers (auto-generates rules with safe defaults)
12
+ mcp-gov-wrap --config ~/.config/claude/config.json
13
+ ```
14
+
15
+ That's it! Rules are automatically generated at `~/.mcp-gov/rules.json` with safe defaults:
16
+ - ✅ **Allow**: read, write operations
17
+ - ❌ **Deny**: delete, admin, execute operations
18
+
19
+ **To unwrap (restore original config):**
20
+ ```bash
21
+ mcp-gov-unwrap --config ~/.config/claude/config.json
22
+ ```
23
+
24
+ **To customize rules:**
25
+ Edit `~/.mcp-gov/rules.json` - changes take effect immediately!
26
+
27
+ ## Features
28
+
29
+ - **Auto-Discovery**: Automatically discovers MCP server tools and generates governance rules with safe defaults
30
+ - **Smart Delta Updates**: Detects new servers and adds rules while preserving your customizations
31
+ - **Permission Control**: Fine-grained rules for read, write, delete, execute, and admin operations
32
+ - **Operation Detection**: Automatic classification of 160+ keywords across 5 operation types
33
+ - **Safe by Default**: Denies destructive operations (delete/admin/execute) unless explicitly allowed
34
+ - **Audit Logging**: Structured JSON logs to stderr with timestamps, tool names, and status
35
+ - **Zero Configuration**: Works out of the box - no manual rule writing required
36
+ - **MCP-Compliant**: Works seamlessly with any MCP client (Claude Code, Droid, etc.)
37
+ - **Middleware Pattern**: Wraps existing MCP servers without modifying tool logic
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ # From npm (recommended)
43
+ npm install -g mcp-gov
44
+
45
+ # Or from source
46
+ git clone https://github.com/yourusername/mcp-gov.git
47
+ cd mcp-gov
48
+ npm install
49
+ sudo npm link
50
+ ```
51
+
52
+ **Verify installation:**
53
+ ```bash
54
+ mcp-gov-wrap --help
55
+ mcp-gov-unwrap --help
56
+ mcp-gov-proxy --help
57
+ ```
58
+
59
+ ## The 3 Commands
60
+
61
+ ### 1. `mcp-gov-wrap` - Wrap MCP Servers
62
+
63
+ Automatically wraps MCP servers with governance proxy and generates rules.
64
+
65
+ ```bash
66
+ # Minimal (uses all defaults)
67
+ mcp-gov-wrap --config ~/.config/claude/config.json
68
+
69
+ # With custom rules file
70
+ mcp-gov-wrap --config ~/.config/claude/config.json --rules ~/my-rules.json
71
+
72
+ # Wrap and launch a tool after
73
+ mcp-gov-wrap --config ~/.config/claude/config.json --tool "claude chat"
74
+ ```
75
+
76
+ **What it does:**
77
+ - ✅ Auto-detects unwrapped MCP servers in your config
78
+ - ✅ Wraps them with `mcp-gov-proxy`
79
+ - ✅ Stores original config in `_original` field (for unwrapping later)
80
+ - ✅ Auto-generates rules at `~/.mcp-gov/rules.json` if missing
81
+ - ✅ Creates timestamped backup before modifying config
82
+ - ✅ Supports both flat and Claude Code config formats
83
+
84
+ **First run output:**
85
+ ```
86
+ Loaded config in flat format
87
+ Found 4 MCP servers
88
+
89
+ No rules file found - generating with safe defaults...
90
+ Discovering tools from 4 server(s)...
91
+ Discovering github...
92
+ ✓ Found 15 tool(s), generated 5 rule(s)
93
+
94
+ ✓ Generated rules file: ~/.mcp-gov/rules.json
95
+
96
+ Safe defaults applied:
97
+ ✓ Allow: read, write
98
+ ✗ Deny: delete, admin, execute
99
+
100
+ Server status:
101
+ Total: 4
102
+ Already wrapped: 0
103
+ Need wrapping: 4
104
+
105
+ Wrapping 4 server(s)...
106
+ ✓ Created backup: ~/.config/claude/config.json.backup-20260123-123456
107
+ ✓ Updated config file: ~/.config/claude/config.json
108
+
109
+ ✓ Wrapping complete!
110
+
111
+ To customize governance rules, edit: ~/.mcp-gov/rules.json
112
+ ```
113
+
114
+ ### 2. `mcp-gov-unwrap` - Restore Original Config
115
+
116
+ Unwraps MCP servers by restoring the original configuration from `_original` field.
117
+
118
+ ```bash
119
+ # Minimal (just unwrap)
120
+ mcp-gov-unwrap --config ~/.config/claude/config.json
121
+
122
+ # Unwrap and launch a tool after
123
+ mcp-gov-unwrap --config ~/.config/claude/config.json --tool "claude chat"
124
+ ```
125
+
126
+ **What it does:**
127
+ - ✅ Auto-detects wrapped MCP servers (servers with `_original` field)
128
+ - ✅ Restores original `command` and `args` from `_original`
129
+ - ✅ Removes `_original` field and proxy wrapper
130
+ - ✅ Preserves `env` variables unchanged
131
+ - ✅ Creates timestamped backup before modifying config
132
+ - ✅ Idempotent (safe to run multiple times)
133
+
134
+ **Output:**
135
+ ```
136
+ Loaded config in flat format
137
+ Found 4 MCP servers
138
+
139
+ Server status:
140
+ Total: 4
141
+ Wrapped (can unwrap): 4
142
+ Already unwrapped: 0
143
+
144
+ Servers to unwrap:
145
+ - github
146
+ - filesystem
147
+ - memory
148
+
149
+ Unwrapping 4 server(s)...
150
+ ✓ Created backup: ~/.config/claude/config.json.backup-20260123-213121
151
+ ✓ Updated config file: ~/.config/claude/config.json
152
+
153
+ ✓ Unwrapping complete!
154
+ ```
155
+
156
+ **Use cases:**
157
+ - Test governance on/off quickly
158
+ - Temporarily disable governance for debugging
159
+ - Restore original config if something goes wrong
160
+
161
+ ### 3. `mcp-gov-proxy` - Low-Level Proxy (Advanced)
162
+
163
+ The underlying proxy used by `mcp-gov-wrap`. Normally you don't call this directly.
164
+
165
+ ```bash
166
+ mcp-gov-proxy \
167
+ --service filesystem \
168
+ --target "npx -y @modelcontextprotocol/server-filesystem" \
169
+ --rules ~/.mcp-gov/rules.json
170
+ ```
171
+
172
+ **What it does:**
173
+ - Intercepts JSON-RPC `tools/call` messages
174
+ - Checks permissions against rules
175
+ - Allows or denies operations
176
+ - Logs all operations to stderr (structured JSON)
177
+
178
+ **Parameters:**
179
+ - `--service` (recommended): Service name for rule matching (e.g., "filesystem", "github")
180
+ - `--target` (required): Command to spawn the target MCP server
181
+ - `--rules` (required): Path to rules.json file
182
+
183
+ **Important:** When wrapping with `mcp-gov-wrap`, the `--service` parameter is automatically added using the server name from your config (e.g., `mcpServers.filesystem` → `--service filesystem`). This ensures governance rules match correctly.
184
+
185
+ This command is automatically invoked by `mcp-gov-wrap` - you typically don't need to use it directly.
186
+
187
+ ## Rules Configuration
188
+
189
+ ### Location
190
+
191
+ Rules are stored at: **`~/.mcp-gov/rules.json`**
192
+
193
+ This file is **auto-generated** on first run with safe defaults. You can edit it to customize permissions.
194
+
195
+ ### Format
196
+
197
+ ```json
198
+ {
199
+ "_comment": "Auto-generated governance rules. Edit as needed.",
200
+ "_location": "/home/user/.mcp-gov/rules.json",
201
+ "rules": [
202
+ {
203
+ "service": "github",
204
+ "operations": ["read"],
205
+ "permission": "allow",
206
+ "reason": "Allow read operations (optional)"
207
+ },
208
+ {
209
+ "service": "github",
210
+ "operations": ["write"],
211
+ "permission": "allow"
212
+ },
213
+ {
214
+ "service": "github",
215
+ "operations": ["delete"],
216
+ "permission": "deny",
217
+ "reason": "Delete operations denied by default for safety"
218
+ }
219
+ ]
220
+ }
221
+ ```
222
+
223
+ ### Fields
224
+
225
+ - **`service`** - MCP server name (matches config key)
226
+ - **`operations`** - Array of operation types: `read`, `write`, `delete`, `execute`, `admin`
227
+ - **`permission`** - `allow` or `deny`
228
+ - **`reason`** (optional) - Human-readable explanation
229
+
230
+ ### Operation Types
231
+
232
+ The system automatically classifies 160+ keywords into 5 operation types:
233
+
234
+ | Type | Examples | Default |
235
+ |------|----------|---------|
236
+ | **read** | get, list, search, query, fetch | ✅ Allow |
237
+ | **write** | create, update, modify, save, add | ✅ Allow |
238
+ | **delete** | delete, remove, drop, purge, destroy | ❌ Deny |
239
+ | **execute** | execute, run, eval, invoke, trigger | ❌ Deny |
240
+ | **admin** | admin, sudo, grant, revoke, configure | ❌ Deny |
241
+
242
+ ### Editing Rules
243
+
244
+ ```bash
245
+ # Edit rules file
246
+ vim ~/.mcp-gov/rules.json
247
+
248
+ # Changes take effect immediately - no restart needed!
249
+ ```
250
+
251
+ ### Example: Allow Deletes for Specific Service
252
+
253
+ ```json
254
+ {
255
+ "rules": [
256
+ {
257
+ "service": "github",
258
+ "operations": ["delete"],
259
+ "permission": "allow",
260
+ "reason": "Trusted service - allow deletes"
261
+ }
262
+ ]
263
+ }
264
+ ```
265
+
266
+ ### Example: Deny All Writes
267
+
268
+ ```json
269
+ {
270
+ "rules": [
271
+ {
272
+ "service": "github",
273
+ "operations": ["write"],
274
+ "permission": "deny",
275
+ "reason": "Read-only mode"
276
+ }
277
+ ]
278
+ }
279
+ ```
280
+
281
+ ## Complete Workflow
282
+
283
+ ```bash
284
+ # 1. Install
285
+ npm install -g mcp-gov
286
+
287
+ # 2. Wrap servers (auto-generates rules)
288
+ mcp-gov-wrap --config ~/.config/claude/config.json
289
+
290
+ # 3. (Optional) Customize rules
291
+ vim ~/.mcp-gov/rules.json
292
+
293
+ # 4. Use your MCP client normally
294
+ claude chat
295
+
296
+ # 5. (Optional) Unwrap to restore original config
297
+ mcp-gov-unwrap --config ~/.config/claude/config.json
298
+ ```
299
+
300
+ ## Adding New Servers (Delta Approach)
301
+
302
+ When you add a new MCP server, just run `mcp-gov-wrap` again - it **automatically detects** new servers and adds rules:
303
+
304
+ ```bash
305
+ # 1. Add a new server (using your client's native command)
306
+ claude mcp add slack --command "npx" --args "-y @modelcontextprotocol/server-slack"
307
+
308
+ # 2. Run wrap again - detects the new server
309
+ mcp-gov-wrap --config ~/.config/claude/config.json
310
+ ```
311
+
312
+ **What happens:**
313
+ ```
314
+ Discovered 1 new server(s) not in rules:
315
+ - slack
316
+
317
+ Generating safe defaults for new servers...
318
+ ✓ Added 3 rule(s) for slack
319
+
320
+ ✓ Updated rules file: ~/.mcp-gov/rules.json
321
+
322
+ Your existing github rules are preserved!
323
+ ```
324
+
325
+ **Smart delta approach:**
326
+ - ✅ Detects servers not in `rules.json`
327
+ - ✅ Adds rules **only for new servers**
328
+ - ✅ **Preserves your customizations** to existing rules
329
+ - ✅ Never overwrites manual edits
330
+
331
+ ## Usage Examples
332
+
333
+ ### Viewing Audit Logs
334
+
335
+ Audit logs are written to stderr in JSON format. Capture them for monitoring:
336
+
337
+ ```bash
338
+ # Redirect stderr to a log file
339
+ mcp-gov-wrap \
340
+ --config ~/.config/claude/config.json \
341
+ --tool "claude chat" \
342
+ 2>> ~/.mcp-gov/audit.log
343
+
344
+ # In another terminal, monitor logs in real-time
345
+ tail -f ~/.mcp-gov/audit.log | jq '.'
346
+ ```
347
+
348
+ Example audit log entries:
349
+
350
+ ```json
351
+ {"timestamp":"2026-01-23T14:30:45.123Z","tool":"github_list_repos","service":"github","operation":"read","status":"allowed"}
352
+ {"timestamp":"2026-01-23T14:31:12.456Z","tool":"github_delete_repo","service":"github","operation":"delete","status":"denied","reason":"Destructive operations require manual approval"}
353
+ ```
354
+
355
+ ### Updating Rules
356
+
357
+ To change governance rules:
358
+
359
+ 1. Edit `~/.mcp-gov/rules.json`
360
+ 2. No need to restart or re-wrap - changes take effect immediately
361
+ 3. Next tool call will use the updated rules
362
+
363
+ ```bash
364
+ # Edit rules
365
+ vim ~/.mcp-gov/rules.json
366
+
367
+ # No restart needed - just continue using your client
368
+ ```
369
+
370
+ ### Checking Server Status
371
+
372
+ View which servers are wrapped:
373
+
374
+ ```bash
375
+ # Show wrapped config
376
+ cat ~/.config/claude/config.json | jq '.mcpServers'
377
+
378
+ # Or for Claude Code format
379
+ cat ~/.config/claude/config.json | jq '.projects.default.mcpServers'
380
+ ```
381
+
382
+ Wrapped servers will have `"command": "mcp-gov-proxy"` instead of their original command.
383
+
384
+ ### Restoring Original Config
385
+
386
+ **Easiest way - use unwrap:**
387
+
388
+ ```bash
389
+ mcp-gov-unwrap --config ~/.config/claude/config.json
390
+ ```
391
+
392
+ **Or restore from backup manually:**
393
+
394
+ ```bash
395
+ # List available backups
396
+ ls -lt ~/.config/claude/*.backup-* | head -5
397
+
398
+ # Restore from a backup
399
+ cp ~/.config/claude/config.json.backup-20260123-143022 ~/.config/claude/config.json
400
+ ```
401
+
402
+ ### Direct Proxy Usage (Advanced)
403
+
404
+ For testing or custom integrations, use the proxy directly:
405
+
406
+ ```bash
407
+ # Run a single server through the proxy
408
+ mcp-gov-proxy \
409
+ --target "npx -y @modelcontextprotocol/server-github" \
410
+ --rules ~/.mcp-gov/rules.json
411
+
412
+ # Pipe input/output for testing
413
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | \
414
+ mcp-gov-proxy \
415
+ --target "npx -y @modelcontextprotocol/server-github" \
416
+ --rules ~/.mcp-gov/rules.json
417
+ ```
418
+
419
+ ## Quick Start
420
+
421
+ ### 1. Create a governed server
422
+
423
+ ```javascript
424
+ import { GovernedMCPServer } from './src/index.js';
425
+
426
+ // Define permission rules
427
+ const rules = {
428
+ github: {
429
+ read: 'allow',
430
+ write: 'allow',
431
+ delete: 'deny', // Block destructive operations
432
+ admin: 'deny'
433
+ }
434
+ };
435
+
436
+ // Create server
437
+ const server = new GovernedMCPServer(
438
+ { name: 'my-server', version: '1.0.0' },
439
+ rules
440
+ );
441
+
442
+ // Register tools
443
+ server.registerTool(
444
+ {
445
+ name: 'github_list_repos',
446
+ description: 'List repositories',
447
+ inputSchema: { type: 'object', properties: {} }
448
+ },
449
+ async (args) => {
450
+ // Your tool logic here
451
+ return {
452
+ content: [{ type: 'text', text: 'Repositories listed' }]
453
+ };
454
+ }
455
+ );
456
+
457
+ // Start server
458
+ await server.start();
459
+ ```
460
+
461
+ ### 2. Run the GitHub example
462
+
463
+ ```bash
464
+ # Set up environment
465
+ cd examples/github
466
+ cp .env.example .env
467
+ # Edit .env and add your GitHub token
468
+
469
+ # Run the server
470
+ node server.js
471
+ ```
472
+
473
+ ### 3. Configure Claude Desktop
474
+
475
+ Add to `~/.config/Claude/claude_desktop_config.json`:
476
+
477
+ ```json
478
+ {
479
+ "mcpServers": {
480
+ "github-governed": {
481
+ "command": "node",
482
+ "args": ["/path/to/mcp-gov/examples/github/server.js"],
483
+ "env": {
484
+ "GITHUB_TOKEN": "your_token_here"
485
+ }
486
+ }
487
+ }
488
+ }
489
+ ```
490
+
491
+ ## Troubleshooting
492
+
493
+ ### Rules Auto-Generation
494
+
495
+ **Behavior:** When `--rules` is not provided or the file doesn't exist, rules are **automatically generated**.
496
+
497
+ The wrapper will:
498
+ 1. Discover tools from each MCP server
499
+ 2. Classify operations (read/write/delete/admin/execute)
500
+ 3. Generate rules with safe defaults
501
+ 4. Save to `~/.mcp-gov/rules.json` (or specified path)
502
+
503
+ **To disable auto-generation:** Always provide a rules file explicitly with `--rules`.
504
+
505
+ **To regenerate rules:** Delete `~/.mcp-gov/rules.json` and run the wrapper again.
506
+
507
+ ### Config file errors
508
+
509
+ **Error:** `Error: Config file not found` or `Error: Invalid config format`
510
+
511
+ **Solution:** Check your config file path and format:
512
+
513
+ ```bash
514
+ # Verify file exists
515
+ ls -la ~/.config/claude/config.json
516
+
517
+ # Check JSON is valid
518
+ jq '.' ~/.config/claude/config.json
519
+
520
+ # Check for mcpServers key
521
+ jq 'keys' ~/.config/claude/config.json
522
+ ```
523
+
524
+ For Claude Code, the config uses `projects.default.mcpServers` or `projects.<project-name>.mcpServers`.
525
+ For other clients, look for top-level `mcpServers`.
526
+
527
+ ### Wrapper says "No servers found"
528
+
529
+ **Error:** `Found 0 MCP servers`
530
+
531
+ **Solution:** Check your config format:
532
+
533
+ ```bash
534
+ # Claude Code format
535
+ cat ~/.config/claude/config.json | jq '.projects'
536
+
537
+ # Flat format
538
+ cat ~/.config/claude/config.json | jq '.mcpServers'
539
+ ```
540
+
541
+ Make sure you've added at least one server using your client's native command first.
542
+
543
+ ### Proxy not blocking operations
544
+
545
+ **Problem:** Operations that should be denied are being allowed.
546
+
547
+ **Solutions:**
548
+
549
+ 1. **Verify rules file syntax:**
550
+ ```bash
551
+ jq '.' ~/.mcp-gov/rules.json
552
+ ```
553
+
554
+ 2. **Check service name matches:**
555
+ Tool names like `github_delete_repo` are parsed as service `github`, operation `delete`.
556
+ Your rules should use `"service": "github"`.
557
+
558
+ 3. **Check operation detection:**
559
+ ```bash
560
+ node -e "import('./src/operation-detector.js').then(m => console.log(m.detectOperation('github_delete_repo')))"
561
+ ```
562
+
563
+ 4. **Review audit logs:**
564
+ Check stderr output to see what's being detected:
565
+ ```bash
566
+ # Logs show what service/operation was detected
567
+ tail ~/.mcp-gov/audit.log | jq '.'
568
+ ```
569
+
570
+ ### Path issues (Windows)
571
+
572
+ **Problem:** Paths with backslashes or spaces cause errors.
573
+
574
+ **Solution:** Use forward slashes (works on Windows too) or quote paths:
575
+
576
+ ```powershell
577
+ # Forward slashes work on Windows
578
+ mcp-gov-wrap --config ~/.config/claude/config.json --rules ~/.mcp-gov/rules.json --tool "claude chat"
579
+
580
+ # Or use quotes for paths with spaces
581
+ mcp-gov-wrap --config "C:\Program Files\Claude\config.json" --rules "C:\mcp-gov\rules.json" --tool "claude chat"
582
+ ```
583
+
584
+ ### Commands fail after wrapping
585
+
586
+ **Problem:** MCP client fails to start or servers don't respond after wrapping.
587
+
588
+ **Solutions:**
589
+
590
+ 1. **Test original command:**
591
+ ```bash
592
+ # Test the server command directly
593
+ npx -y @modelcontextprotocol/server-github
594
+ ```
595
+
596
+ 2. **Test proxy directly:**
597
+ ```bash
598
+ # If original works, test through proxy
599
+ mcp-gov-proxy --target "npx -y @modelcontextprotocol/server-github" --rules ~/.mcp-gov/rules.json
600
+ ```
601
+
602
+ 3. **Restore from backup:**
603
+ ```bash
604
+ # If wrapping broke config, restore backup
605
+ ls -lt ~/.config/claude/*.backup.* | head -1
606
+ cp <backup-file> ~/.config/claude/config.json
607
+ ```
608
+
609
+ 4. **Check for double-wrapping:**
610
+ ```bash
611
+ # Verify servers aren't double-wrapped
612
+ cat ~/.config/claude/config.json | jq '.mcpServers'
613
+
614
+ # Should see "mcp-gov-proxy" as command, NOT nested proxies
615
+ ```
616
+
617
+ ### Binary not found after installation
618
+
619
+ **Error:** `command not found: mcp-gov-proxy`
620
+
621
+ **Solution:** Add npm global bin to PATH:
622
+
623
+ **Linux/macOS:**
624
+ ```bash
625
+ # Find global bin directory
626
+ npm prefix -g
627
+
628
+ # Add to PATH (add to ~/.bashrc or ~/.zshrc)
629
+ export PATH="$(npm prefix -g)/bin:$PATH"
630
+ ```
631
+
632
+ **Windows:**
633
+ ```powershell
634
+ # Find global bin directory
635
+ npm prefix -g
636
+
637
+ # Add to PATH in System Environment Variables
638
+ # Or use full path:
639
+ $env:PATH += ";$(npm prefix -g)\bin"
640
+ ```
641
+
642
+ ### Permission denied on Linux/macOS
643
+
644
+ **Error:** `Permission denied` when running binaries.
645
+
646
+ **Solution:** Make binaries executable:
647
+
648
+ ```bash
649
+ chmod +x $(npm prefix -g)/lib/node_modules/mcp-gov/bin/mcp-gov-proxy.js
650
+ chmod +x $(npm prefix -g)/lib/node_modules/mcp-gov/bin/mcp-gov-wrap.js
651
+ ```
652
+
653
+ ### Audit logs not appearing
654
+
655
+ **Problem:** No logs in stderr or log file.
656
+
657
+ **Solutions:**
658
+
659
+ 1. **Ensure stderr is redirected:**
660
+ ```bash
661
+ mcp-gov-wrap ... 2>> ~/.mcp-gov/audit.log
662
+ ```
663
+
664
+ 2. **Check tools are being called:**
665
+ Use your MCP client to call a tool and verify it triggers the proxy.
666
+
667
+ 3. **Test proxy directly:**
668
+ ```bash
669
+ echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | \
670
+ mcp-gov-proxy --target "npx -y @modelcontextprotocol/server-github" --rules ~/.mcp-gov/rules.json \
671
+ 2>&1 | tee test.log
672
+ ```
673
+
674
+ ### Performance issues
675
+
676
+ **Problem:** Tool calls seem slow after adding governance.
677
+
678
+ **Expected:** Proxy overhead should be < 50ms per call.
679
+
680
+ **Solutions:**
681
+
682
+ 1. **Check rules file size:**
683
+ Very large rules files (1000+ rules) may slow parsing.
684
+
685
+ 2. **Test without proxy:**
686
+ Compare performance with and without governance to isolate the issue.
687
+
688
+ 3. **Check network latency:**
689
+ If calling external APIs, most delay is network, not governance.
690
+
691
+ For more help, see [TESTING.md](TESTING.md) or open an issue on GitHub.
692
+
693
+ ## Operation Detection
694
+
695
+ The system automatically classifies tools based on keywords in their names:
696
+
697
+ | Operation | Keywords | Default Policy |
698
+ |-----------|----------|----------------|
699
+ | **admin** | admin, superuser, configure, migrate, deploy | Conservative |
700
+ | **delete** | delete, remove, destroy, purge, erase | Conservative |
701
+ | **execute** | execute, run, invoke, trigger, send | Conservative |
702
+ | **write** | create, add, update, modify, edit, write | Moderate |
703
+ | **read** | get, list, fetch, query, search, view | Permissive |
704
+
705
+ Priority order: admin → delete → execute → write → read
706
+
707
+ ## Permission Rules
708
+
709
+ ### Rules File Format (CLI Tools)
710
+
711
+ For the auto-wrap CLI tools, rules are defined in `rules.json`:
712
+
713
+ ```json
714
+ {
715
+ "rules": [
716
+ {
717
+ "service": "github",
718
+ "operations": ["delete", "admin"],
719
+ "permission": "deny",
720
+ "reason": "Destructive operations require manual approval"
721
+ },
722
+ {
723
+ "service": "github",
724
+ "operations": ["read", "write"],
725
+ "permission": "allow"
726
+ }
727
+ ]
728
+ }
729
+ ```
730
+
731
+ **Fields:**
732
+ - `service`: Service name (extracted from tool name prefix, e.g., `github_list_repos` → `github`)
733
+ - `operations`: Array of operation types: `["read", "write", "delete", "execute", "admin"]`
734
+ - `permission`: Either `"allow"` or `"deny"`
735
+ - `reason` (optional): Human-readable explanation for deny rules
736
+
737
+ ### Multiple Service Examples
738
+
739
+ ```json
740
+ {
741
+ "rules": [
742
+ {
743
+ "service": "github",
744
+ "operations": ["delete", "admin"],
745
+ "permission": "deny",
746
+ "reason": "Destructive operations require manual approval"
747
+ },
748
+ {
749
+ "service": "google",
750
+ "operations": ["delete"],
751
+ "permission": "deny",
752
+ "reason": "Prevent accidental deletion of Google Drive files"
753
+ },
754
+ {
755
+ "service": "aws",
756
+ "operations": ["admin", "execute"],
757
+ "permission": "deny",
758
+ "reason": "AWS administrative actions require explicit approval"
759
+ },
760
+ {
761
+ "service": "database",
762
+ "operations": ["delete", "admin"],
763
+ "permission": "deny",
764
+ "reason": "Database modifications must be reviewed"
765
+ },
766
+ {
767
+ "service": "slack",
768
+ "operations": ["delete"],
769
+ "permission": "deny",
770
+ "reason": "Cannot delete messages once sent"
771
+ }
772
+ ]
773
+ }
774
+ ```
775
+
776
+ ### Default Behavior
777
+
778
+ - **Default policy:** `allow` (if no rule matches, operation is allowed)
779
+ - Operations not listed in rules default to allowed
780
+ - Service names are automatically extracted from tool names
781
+ - Rules are evaluated at runtime (no restart needed after changes)
782
+
783
+ ### Programmatic API Format
784
+
785
+ For the `GovernedMCPServer` class, rules use a different format:
786
+
787
+ ```javascript
788
+ const rules = {
789
+ serviceName: {
790
+ read: 'allow',
791
+ write: 'allow',
792
+ delete: 'deny',
793
+ execute: 'allow',
794
+ admin: 'deny'
795
+ }
796
+ };
797
+ ```
798
+
799
+ ## Audit Logs
800
+
801
+ ### Log Format
802
+
803
+ All tool operations are logged to stderr in JSON format by the proxy:
804
+
805
+ ```json
806
+ {
807
+ "timestamp": "2026-01-23T14:30:45.123Z",
808
+ "tool": "github_delete_repo",
809
+ "service": "github",
810
+ "operation": "delete",
811
+ "status": "denied",
812
+ "reason": "Destructive operations require manual approval"
813
+ }
814
+ ```
815
+
816
+ **Fields:**
817
+ - `timestamp`: ISO 8601 timestamp (UTC)
818
+ - `tool`: Full tool name as called by the MCP client
819
+ - `service`: Extracted service name (e.g., `github` from `github_delete_repo`)
820
+ - `operation`: Operation type (`read`, `write`, `delete`, `execute`, `admin`)
821
+ - `status`: Either `allowed` or `denied`
822
+ - `reason`: Optional explanation (only for denied operations)
823
+
824
+ ### Capturing Logs
825
+
826
+ Redirect stderr to capture audit logs:
827
+
828
+ ```bash
829
+ # Append to log file
830
+ mcp-gov-wrap \
831
+ --config ~/.config/claude/config.json \
832
+ --tool "claude chat" \
833
+ 2>> ~/.mcp-gov/audit.log
834
+ ```
835
+
836
+ Or with your alias (recommended):
837
+
838
+ ```bash
839
+ # Capture stderr to log
840
+ claude-gov 2>> ~/.mcp-gov/audit.log
841
+ ```
842
+
843
+ ### Real-Time Monitoring
844
+
845
+ Monitor logs in real-time using `tail` and `jq`:
846
+
847
+ ```bash
848
+ # Watch logs with pretty formatting
849
+ tail -f ~/.mcp-gov/audit.log | jq '.'
850
+
851
+ # Watch only denied operations
852
+ tail -f ~/.mcp-gov/audit.log | jq -r 'select(.status=="denied")'
853
+
854
+ # Count operations by service
855
+ tail -f ~/.mcp-gov/audit.log | jq -r '.service' | sort | uniq -c
856
+
857
+ # Show timestamp and tool name
858
+ tail -f ~/.mcp-gov/audit.log | jq -r '"\(.timestamp) \(.tool) \(.status)"'
859
+ ```
860
+
861
+ ### Log Analysis
862
+
863
+ Analyze historical audit logs:
864
+
865
+ ```bash
866
+ # Count total operations
867
+ wc -l ~/.mcp-gov/audit.log
868
+
869
+ # Count denied operations
870
+ grep '"status":"denied"' ~/.mcp-gov/audit.log | wc -l
871
+
872
+ # List unique tools called
873
+ jq -r '.tool' ~/.mcp-gov/audit.log | sort | uniq
874
+
875
+ # Operations by service
876
+ jq -r '.service' ~/.mcp-gov/audit.log | sort | uniq -c | sort -rn
877
+
878
+ # Denied operations with reasons
879
+ jq -r 'select(.status=="denied") | "\(.tool): \(.reason)"' ~/.mcp-gov/audit.log
880
+
881
+ # Operations in the last hour
882
+ jq -r --arg hour "$(date -u +%Y-%m-%dT%H)" 'select(.timestamp | startswith($hour))' ~/.mcp-gov/audit.log
883
+
884
+ # Group by hour
885
+ jq -r '.timestamp[0:13]' ~/.mcp-gov/audit.log | uniq -c
886
+ ```
887
+
888
+ ### Monitoring Best Practices
889
+
890
+ 1. **Rotate logs** to prevent unlimited growth:
891
+ ```bash
892
+ # Add to cron (daily rotation)
893
+ 0 0 * * * mv ~/.mcp-gov/audit.log ~/.mcp-gov/audit.$(date +\%Y\%m\%d).log && touch ~/.mcp-gov/audit.log
894
+ ```
895
+
896
+ 2. **Alert on anomalies** using a simple script:
897
+ ```bash
898
+ #!/bin/bash
899
+ # alert-on-denied.sh
900
+ tail -f ~/.mcp-gov/audit.log | while read line; do
901
+ status=$(echo $line | jq -r '.status')
902
+ if [ "$status" = "denied" ]; then
903
+ tool=$(echo $line | jq -r '.tool')
904
+ reason=$(echo $line | jq -r '.reason')
905
+ echo "ALERT: Denied operation: $tool ($reason)"
906
+ # Send notification, email, etc.
907
+ fi
908
+ done
909
+ ```
910
+
911
+ 3. **Export to external systems**:
912
+ ```bash
913
+ # Forward to syslog
914
+ tail -f ~/.mcp-gov/audit.log | logger -t mcp-gov
915
+
916
+ # Forward to monitoring service
917
+ tail -f ~/.mcp-gov/audit.log | curl -X POST -H "Content-Type: application/json" \
918
+ -d @- https://your-monitoring-service.com/logs
919
+ ```
920
+
921
+ 4. **Backup audit logs** regularly:
922
+ ```bash
923
+ # Daily backup
924
+ tar -czf ~/backups/mcp-audit-$(date +%Y%m%d).tar.gz ~/.mcp-gov/*.log
925
+ ```
926
+
927
+ ### Log Retention
928
+
929
+ Consider your retention requirements:
930
+
931
+ - **Development**: 7-30 days
932
+ - **Production**: 90-365 days (or per compliance requirements)
933
+ - **Compliance**: May require indefinite retention and immutability
934
+
935
+ ### Privacy Considerations
936
+
937
+ Audit logs may contain sensitive information:
938
+
939
+ - Tool names might reveal user intent
940
+ - Service names show what systems are being accessed
941
+ - Timestamps reveal usage patterns
942
+
943
+ Ensure logs are:
944
+ - Stored securely (appropriate file permissions)
945
+ - Encrypted at rest if required
946
+ - Access-controlled
947
+ - Included in backup strategies
948
+
949
+ ### Example: Continuous Monitoring Dashboard
950
+
951
+ ```bash
952
+ #!/bin/bash
953
+ # mcp-dashboard.sh - Simple audit log dashboard
954
+
955
+ watch -n 5 '
956
+ echo "=== MCP Governance Dashboard ==="
957
+ echo ""
958
+ echo "Total Operations: $(wc -l < ~/.mcp-gov/audit.log)"
959
+ echo "Allowed: $(grep -c "\"status\":\"allowed\"" ~/.mcp-gov/audit.log || echo 0)"
960
+ echo "Denied: $(grep -c "\"status\":\"denied\"" ~/.mcp-gov/audit.log || echo 0)"
961
+ echo ""
962
+ echo "=== Top Services ==="
963
+ jq -r ".service" ~/.mcp-gov/audit.log | sort | uniq -c | sort -rn | head -5
964
+ echo ""
965
+ echo "=== Recent Denied Operations ==="
966
+ jq -r "select(.status==\"denied\") | \"\(.timestamp) \(.tool)\"" ~/.mcp-gov/audit.log | tail -5
967
+ '
968
+ ```
969
+
970
+ ## Architecture
971
+
972
+ ### Auto-Wrap System Architecture
973
+
974
+ ```
975
+ ┌──────────────────────────────────────────────────────────────┐
976
+ │ User Workflow │
977
+ └──────────────────────────────────────────────────────────────┘
978
+
979
+ │ 1. Add servers with native command
980
+ │ (claude mcp add server ...)
981
+
982
+ ┌──────────────────────────────────────────────────────────────┐
983
+ │ ~/.config/claude/config.json │
984
+ │ { │
985
+ │ "mcpServers": { │
986
+ │ "github": { │
987
+ │ "command": "npx", │
988
+ │ "args": ["-y", "@modelcontextprotocol/server-github"] │
989
+ │ } │
990
+ │ } │
991
+ │ } │
992
+ └──────────────────────────────────────────────────────────────┘
993
+
994
+ │ 2. Run wrapper
995
+ │ (mcp-gov-wrap --config ... --rules ... --tool ...)
996
+
997
+ ┌──────────────────────────────────────────────────────────────┐
998
+ │ mcp-gov-wrap │
999
+ │ ┌────────────────────────────────────────────────────────┐ │
1000
+ │ │ 1. Read config file (detect format) │ │
1001
+ │ │ 2. Load rules from ~/.mcp-gov/rules.json │ │
1002
+ │ │ 3. Detect unwrapped servers │ │
1003
+ │ │ 4. Create timestamped backup │ │
1004
+ │ │ 5. Wrap servers: replace command with mcp-gov-proxy │ │
1005
+ │ │ 6. Execute tool command (e.g., "claude chat") │ │
1006
+ │ └────────────────────────────────────────────────────────┘ │
1007
+ └──────────────────────────────────────────────────────────────┘
1008
+
1009
+ │ 3. Config now wrapped
1010
+
1011
+ ┌──────────────────────────────────────────────────────────────┐
1012
+ │ ~/.config/claude/config.json (wrapped) │
1013
+ │ { │
1014
+ │ "mcpServers": { │
1015
+ │ "github": { │
1016
+ │ "command": "mcp-gov-proxy", │
1017
+ │ "args": [ │
1018
+ │ "--service", "github", │
1019
+ │ "--target", "npx -y @modelcontextprotocol/...", │
1020
+ │ "--rules", "/home/user/.mcp-gov/rules.json" │
1021
+ │ ] │
1022
+ │ } │
1023
+ │ } │
1024
+ │ } │
1025
+ └──────────────────────────────────────────────────────────────┘
1026
+
1027
+ │ 4. Client launches with governed servers
1028
+
1029
+ ┌──────────────────────────────────────────────────────────────┐
1030
+ │ MCP Client │
1031
+ │ (Claude Code, Droid, etc.) │
1032
+ └──────────────────────────────────────────────────────────────┘
1033
+
1034
+ │ 5. Client makes tool call
1035
+ │ (tools/call: github_delete_repo)
1036
+
1037
+ ┌──────────────────────────────────────────────────────────────┐
1038
+ │ mcp-gov-proxy │
1039
+ │ ┌────────────────────────────────────────────────────────┐ │
1040
+ │ │ JSON-RPC Message Interception │ │
1041
+ │ │ ↓ │ │
1042
+ │ │ Operation Detection │ │
1043
+ │ │ - Parse tool name: github_delete_repo │ │
1044
+ │ │ - Extract service: github │ │
1045
+ │ │ - Extract operation: delete │ │
1046
+ │ │ ↓ │ │
1047
+ │ │ Permission Check │ │
1048
+ │ │ - Load rules.json │ │
1049
+ │ │ - Check service="github", operation="delete" │ │
1050
+ │ │ - Decision: DENY │ │
1051
+ │ │ ↓ │ │
1052
+ │ │ Audit Logging (stderr) │ │
1053
+ │ │ {"timestamp":"...","tool":"github_delete_repo", │ │
1054
+ │ │ "service":"github","operation":"delete", │ │
1055
+ │ │ "status":"denied","reason":"..."} │ │
1056
+ │ │ ↓ │ │
1057
+ │ │ Response │ │
1058
+ │ │ - If ALLOW: Forward to target server │ │
1059
+ │ │ - If DENY: Return error to client │ │
1060
+ │ └────────────────────────────────────────────────────────┘ │
1061
+ └──────────────────────────────────────────────────────────────┘
1062
+
1063
+ │ 6a. If ALLOWED: Forward to target
1064
+
1065
+ ┌──────────────────────────────────────────────────────────────┐
1066
+ │ Target MCP Server │
1067
+ │ (npx -y @modelcontextprotocol/server-github) │
1068
+ │ ┌────────────────────────────────────────────────────────┐ │
1069
+ │ │ Execute tool logic │ │
1070
+ │ │ - Call GitHub API │ │
1071
+ │ │ - Return result │ │
1072
+ │ └────────────────────────────────────────────────────────┘ │
1073
+ └──────────────────────────────────────────────────────────────┘
1074
+
1075
+ │ 7. Result flows back to client
1076
+
1077
+ ┌──────────────────────────────────────────────────────────────┐
1078
+ │ MCP Client │
1079
+ │ Receives result or error │
1080
+ └──────────────────────────────────────────────────────────────┘
1081
+ ```
1082
+
1083
+ ### Component Responsibilities
1084
+
1085
+ **mcp-gov-wrap (Wrapper)**
1086
+ - Config file discovery and parsing
1087
+ - Server detection (wrapped vs unwrapped)
1088
+ - Automatic wrapping with mcp-gov-proxy
1089
+ - Config backup management
1090
+ - Tool command execution
1091
+
1092
+ **mcp-gov-proxy (Proxy)**
1093
+ - JSON-RPC message interception
1094
+ - Operation detection and classification
1095
+ - Permission checking against rules
1096
+ - Audit logging to stderr
1097
+ - Message forwarding to target servers
1098
+
1099
+ **rules.json**
1100
+ - Centralized governance policies
1101
+ - Service-specific operation rules
1102
+ - Allow/deny decisions with reasons
1103
+ - Hot-reloadable (no restart needed)
1104
+
1105
+ **Target MCP Servers**
1106
+ - Unchanged original servers
1107
+ - No code modifications needed
1108
+ - Work through proxy transparently
1109
+
1110
+ ### Data Flow
1111
+
1112
+ 1. **Setup**: User adds servers with native commands → mcp-gov-wrap wraps them
1113
+ 2. **Runtime**: Client calls tool → Proxy intercepts → Checks permission → Forwards or denies
1114
+ 3. **Audit**: Every operation logged to stderr with timestamp, service, operation, status
1115
+ 4. **Updates**: Change rules.json → Takes effect immediately on next call
1116
+
1117
+ ### Programmatic API Architecture (Alternative)
1118
+
1119
+ For building custom governed servers:
1120
+
1121
+ ```
1122
+ ┌─────────────────┐
1123
+ │ MCP Client │ (Claude Desktop)
1124
+ └────────┬────────┘
1125
+ │ MCP Protocol (stdio)
1126
+
1127
+ ┌─────────────────┐
1128
+ │ GovernedMCP │ 1. Check permission
1129
+ │ Server │ 2. Log operation
1130
+ │ (SDK) │ 3. Execute handler
1131
+ └────────┬────────┘
1132
+
1133
+
1134
+ ┌─────────────────┐
1135
+ │ Tool Handlers │ (GitHub API, etc.)
1136
+ └─────────────────┘
1137
+ ```
1138
+
1139
+ ## Examples
1140
+
1141
+ ### Auto-Wrap System
1142
+
1143
+ - **[Auto-Wrap Example](examples/auto-wrap-example.md)**: Complete step-by-step walkthrough
1144
+ - Installation and setup
1145
+ - Creating rules files
1146
+ - Adding and wrapping servers
1147
+ - Daily workflow and monitoring
1148
+
1149
+ - **[Multi-Client Example](examples/multi-client-example.md)**: Using governance with multiple MCP clients
1150
+ - Claude Code, Droid, and custom clients
1151
+ - Shared rules across clients
1152
+ - Centralized audit logging
1153
+ - Environment-specific configurations
1154
+
1155
+ ### Programmatic API
1156
+
1157
+ - **[GitHub Example](examples/github/)**: Complete working example with:
1158
+ - GitHub API integration (list repos, delete repo)
1159
+ - Permission rules configuration
1160
+ - Environment variable management
1161
+ - Claude Desktop integration
1162
+
1163
+ ## Platform Compatibility
1164
+
1165
+ mcp-gov is designed to work seamlessly across Linux, macOS, and Windows. The system handles platform-specific path conventions and behaviors automatically.
1166
+
1167
+ ### Supported Platforms
1168
+
1169
+ - **Linux**: Full support with Unix path separators (`/`) and LF line endings
1170
+ - **macOS**: Full support including .app bundles, case-sensitive filesystems, and system symlinks
1171
+ - **Windows**: Full support with drive letters (`C:\`), UNC paths (`\\server\share`), backslashes, and CRLF line endings
1172
+
1173
+ ### Path Handling
1174
+
1175
+ The proxy and wrapper tools automatically handle platform-specific path formats:
1176
+
1177
+ **Linux/macOS:**
1178
+ ```bash
1179
+ # Unix-style paths with forward slashes
1180
+ mcp-gov-proxy --target "node server.js" --rules /home/user/rules.json
1181
+ mcp-gov-wrap --config ~/.config/claude/config.json --rules ~/.mcp-gov/rules.json --tool "claude chat"
1182
+ ```
1183
+
1184
+ **Windows:**
1185
+ ```powershell
1186
+ # Windows-style paths with backslashes or forward slashes
1187
+ mcp-gov-proxy --target "node server.js" --rules C:\Users\user\rules.json
1188
+ mcp-gov-wrap --config %USERPROFILE%\.config\claude\config.json --rules %USERPROFILE%\.mcp-gov\rules.json --tool "claude chat"
1189
+ ```
1190
+
1191
+ ### Special Considerations
1192
+
1193
+ **macOS:**
1194
+ - Application bundles (`.app` directories) are handled correctly
1195
+ - System symlinks like `/tmp` → `/private/tmp` work transparently
1196
+ - Both case-sensitive and case-insensitive APFS filesystems are supported
1197
+
1198
+ **Windows:**
1199
+ - UNC network paths (`\\server\share\path`) are fully supported
1200
+ - Paths with spaces are handled correctly (e.g., `C:\Program Files`)
1201
+ - Both forward slashes and backslashes work in paths
1202
+ - Drive letters in absolute paths (e.g., `C:\`, `D:\`) are preserved
1203
+
1204
+ **Line Endings:**
1205
+ - Config files can use either LF (Unix/macOS) or CRLF (Windows) line endings
1206
+ - JSON parsing handles both formats automatically
1207
+ - Backup files preserve the original line ending format
1208
+
1209
+ ### Testing
1210
+
1211
+ The test suite includes platform-specific test cases for Windows and macOS scenarios, even when run on Linux. To run platform tests:
1212
+
1213
+ ```bash
1214
+ npm run test:platform
1215
+ ```
1216
+
1217
+ These tests verify path handling, line ending compatibility, and platform-specific behaviors without requiring multiple operating systems for development.
1218
+
1219
+ ## Development
1220
+
1221
+ ```bash
1222
+ # Run all tests
1223
+ npm test
1224
+
1225
+ # Run specific test suites
1226
+ npm run test:proxy
1227
+ npm run test:wrapper
1228
+ npm run test:platform
1229
+ npm run test:integration
1230
+
1231
+ # Test operation detection
1232
+ node -e "import('./src/operation-detector.js').then(m => console.log(m.detectOperation('github_list_repos')))"
1233
+
1234
+ # Test permission checking
1235
+ node -e "import('./src/index.js').then(m => {
1236
+ const s = new m.GovernedMCPServer({name:'test',version:'1.0'}, {github:{delete:'deny'}});
1237
+ console.log(s.checkPermission('github_delete_repo'));
1238
+ })"
1239
+ ```
1240
+
1241
+ ## License
1242
+
1243
+ MIT
1244
+
1245
+ ## Contributing
1246
+
1247
+ Contributions welcome! Please open an issue or PR.