zero-config-cli-bridge 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 +164 -0
- package/dist/executor.d.ts +7 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +42 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +98 -0
- package/dist/schema.js.map +1 -0
- package/dist/security.d.ts +2 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +21 -0
- package/dist/security.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# zero-config-cli-bridge
|
|
2
|
+
|
|
3
|
+
**Zero Setup. Zero API Keys. Just your local CLI.**
|
|
4
|
+
|
|
5
|
+
> Still copy-pasting `GITHUB_TOKEN` into `.env` files?
|
|
6
|
+
> Still wondering why your AI agent can't see your private repos?
|
|
7
|
+
> Stop. Your machine already has everything it needs.
|
|
8
|
+
|
|
9
|
+
`zero-config-cli-bridge` is an MCP (Model Context Protocol) server that exposes your **already-authenticated local CLI tools** directly to LLM agents — no API keys, no OAuth flows, no configuration.
|
|
10
|
+
|
|
11
|
+
If `gh issue list` works in your terminal, it works in Claude Desktop. That's it.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Option A: npx (no install required)
|
|
19
|
+
npx -y zero-config-cli-bridge
|
|
20
|
+
|
|
21
|
+
# Option B: global install
|
|
22
|
+
npm install -g zero-config-cli-bridge
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Prerequisites:** `gh` CLI installed and authenticated (`gh auth login`)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Claude Desktop Setup
|
|
30
|
+
|
|
31
|
+
Add to `~/AppData/Roaming/Claude/claude_desktop_config.json` (Windows) or
|
|
32
|
+
`~/Library/Application Support/Claude/claude_desktop_config.json` (macOS):
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"mcpServers": {
|
|
37
|
+
"zero-config-cli-bridge": {
|
|
38
|
+
"command": "npx",
|
|
39
|
+
"args": ["-y", "zero-config-cli-bridge"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Restart Claude Desktop. Done.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## What you can do
|
|
50
|
+
|
|
51
|
+
Ask Claude naturally:
|
|
52
|
+
|
|
53
|
+
- *"List the open issues in cli/cli"*
|
|
54
|
+
- *"Show me the last 20 PRs merged into main"*
|
|
55
|
+
- *"Which issues are labeled bug and unassigned?"*
|
|
56
|
+
|
|
57
|
+
Claude will call the MCP tools, hit your local `gh` binary with your existing authentication, and return real data — without ever seeing your token.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Available Tools
|
|
62
|
+
|
|
63
|
+
| Tool | Command | Description |
|
|
64
|
+
|------|---------|-------------|
|
|
65
|
+
| `gh_issue_list` | `gh issue list` | List issues with filters |
|
|
66
|
+
| `gh_pr_list` | `gh pr list` | List pull requests with filters |
|
|
67
|
+
|
|
68
|
+
### Arguments
|
|
69
|
+
|
|
70
|
+
**gh_issue_list**
|
|
71
|
+
| Argument | Type | Description |
|
|
72
|
+
|----------|------|-------------|
|
|
73
|
+
| `repo` | string | `OWNER/REPO` format. Omit to use current directory's remote. |
|
|
74
|
+
| `limit` | number | Max results (default: 30) |
|
|
75
|
+
| `state` | string | `open` / `closed` / `all` |
|
|
76
|
+
| `label` | string | Filter by label |
|
|
77
|
+
| `assignee` | string | Filter by assignee login |
|
|
78
|
+
|
|
79
|
+
**gh_pr_list**
|
|
80
|
+
| Argument | Type | Description |
|
|
81
|
+
|----------|------|-------------|
|
|
82
|
+
| `repo` | string | `OWNER/REPO` format |
|
|
83
|
+
| `limit` | number | Max results (default: 30) |
|
|
84
|
+
| `state` | string | `open` / `closed` / `merged` |
|
|
85
|
+
| `base` | string | Filter by base branch |
|
|
86
|
+
| `assignee` | string | Filter by assignee login |
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## Security: Layered Defense
|
|
91
|
+
|
|
92
|
+
This server is **read-only by design**. Destructive operations are blocked at multiple layers:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
Request
|
|
96
|
+
│
|
|
97
|
+
▼
|
|
98
|
+
┌─────────────────────────────────────────────┐
|
|
99
|
+
│ Layer 1: LLM Self-Governance │
|
|
100
|
+
│ The tool schema explicitly describes │
|
|
101
|
+
│ read-only intent. Claude refuses mutating │
|
|
102
|
+
│ requests before calling the tool at all. │
|
|
103
|
+
└─────────────────┬───────────────────────────┘
|
|
104
|
+
│ (if bypassed)
|
|
105
|
+
▼
|
|
106
|
+
┌─────────────────────────────────────────────┐
|
|
107
|
+
│ Layer 2: Keyword Validator (security.ts) │
|
|
108
|
+
│ Blocks: create, delete, edit, rm, │
|
|
109
|
+
│ update, close, reopen, merge │
|
|
110
|
+
│ Returns: "Mutating commands are blocked" │
|
|
111
|
+
└─────────────────┬───────────────────────────┘
|
|
112
|
+
│ (if bypassed)
|
|
113
|
+
▼
|
|
114
|
+
┌─────────────────────────────────────────────┐
|
|
115
|
+
│ Layer 3: gh CLI Error │
|
|
116
|
+
│ Invalid commands fail at the gh binary │
|
|
117
|
+
│ level with a non-zero exit code. │
|
|
118
|
+
└─────────────────────────────────────────────┘
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Verified in production:** TEST 3 on Claude Desktop confirmed Layer 1 blocked a `delete` injection attempt before the tool was even invoked.
|
|
122
|
+
|
|
123
|
+
### Additional Safeguards
|
|
124
|
+
|
|
125
|
+
- **Timeout:** Processes are killed with `SIGKILL` after 3000ms — no hanging on interactive prompts
|
|
126
|
+
- **Output truncation:** stdout/stderr capped at 2000 characters to prevent context exhaustion. Long outputs append `...[Output truncated. Use grep/jq to filter]`
|
|
127
|
+
- **CI mode:** `CI=true` is injected to suppress interactive prompts
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## How It Works
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
Claude Desktop
|
|
135
|
+
│ MCP (stdio JSON-RPC)
|
|
136
|
+
▼
|
|
137
|
+
zero-config-cli-bridge
|
|
138
|
+
│ child_process.spawn
|
|
139
|
+
▼
|
|
140
|
+
gh CLI (your local binary)
|
|
141
|
+
│ uses ~/.config/gh/hosts.yml
|
|
142
|
+
▼
|
|
143
|
+
GitHub API
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Your token never leaves your machine. The bridge just connects Claude to a process that was already authorized.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Roadmap
|
|
151
|
+
|
|
152
|
+
- [ ] `gh issue view` / `gh pr view`
|
|
153
|
+
- [ ] `gh run list` / `gh release list`
|
|
154
|
+
- [ ] Dynamic schema generation from `gh --help`
|
|
155
|
+
- [ ] Support for `git`, `docker`, `kubectl`
|
|
156
|
+
- [ ] Configurable allow-list for additional read-only commands
|
|
157
|
+
|
|
158
|
+
PRs welcome.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAWD,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAqCtE"}
|
package/dist/executor.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
const MAX_OUTPUT = 2000;
|
|
3
|
+
const TRUNCATION_MSG = '\n...[Output truncated. Use grep/jq to filter]';
|
|
4
|
+
const TIMEOUT_MS = 3000;
|
|
5
|
+
function truncate(output) {
|
|
6
|
+
if (output.length <= MAX_OUTPUT)
|
|
7
|
+
return output;
|
|
8
|
+
return output.slice(0, MAX_OUTPUT) + TRUNCATION_MSG;
|
|
9
|
+
}
|
|
10
|
+
export function executeCommand(command) {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
const proc = spawn('sh', ['-c', command], {
|
|
13
|
+
env: { ...process.env, CI: 'true' },
|
|
14
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
15
|
+
});
|
|
16
|
+
let stdoutBuf = '';
|
|
17
|
+
let stderrBuf = '';
|
|
18
|
+
proc.stdout.on('data', (chunk) => {
|
|
19
|
+
stdoutBuf += chunk.toString();
|
|
20
|
+
});
|
|
21
|
+
proc.stderr.on('data', (chunk) => {
|
|
22
|
+
stderrBuf += chunk.toString();
|
|
23
|
+
});
|
|
24
|
+
const timer = setTimeout(() => {
|
|
25
|
+
proc.kill('SIGKILL');
|
|
26
|
+
reject(new Error(`Command timed out after ${TIMEOUT_MS}ms: ${command}`));
|
|
27
|
+
}, TIMEOUT_MS);
|
|
28
|
+
proc.on('close', (code) => {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
resolve({
|
|
31
|
+
stdout: truncate(stdoutBuf),
|
|
32
|
+
stderr: truncate(stderrBuf),
|
|
33
|
+
exitCode: code ?? 1,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
proc.on('error', (err) => {
|
|
37
|
+
clearTimeout(timer);
|
|
38
|
+
reject(err);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQtC,MAAM,UAAU,GAAG,IAAI,CAAC;AACxB,MAAM,cAAc,GAAG,gDAAgD,CAAC;AACxE,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB,SAAS,QAAQ,CAAC,MAAc;IAC9B,IAAI,MAAM,CAAC,MAAM,IAAI,UAAU;QAAE,OAAO,MAAM,CAAC;IAC/C,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,GAAG,cAAc,CAAC;AACtD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE;YACxC,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE,EAAE,MAAM,EAAE;YACnC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;SAClC,CAAC,CAAC;QAEH,IAAI,SAAS,GAAG,EAAE,CAAC;QACnB,IAAI,SAAS,GAAG,EAAE,CAAC;QAEnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACvC,SAAS,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACrB,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,UAAU,OAAO,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3E,CAAC,EAAE,UAAU,CAAC,CAAC;QAEf,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAmB,EAAE,EAAE;YACvC,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC;gBACN,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;gBAC3B,MAAM,EAAE,QAAQ,CAAC,SAAS,CAAC;gBAC3B,QAAQ,EAAE,IAAI,IAAI,CAAC;aACpB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE;YAC9B,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { executeCommand } from './executor.js';
|
|
6
|
+
import { validateReadOnly } from './security.js';
|
|
7
|
+
import { buildGhCommand, getToolDefinitions } from './schema.js';
|
|
8
|
+
const server = new Server({ name: 'zero-config-cli-bridge', version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
9
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
10
|
+
return { tools: getToolDefinitions() };
|
|
11
|
+
});
|
|
12
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
13
|
+
const { name: toolName, arguments: rawArgs } = request.params;
|
|
14
|
+
const args = (rawArgs ?? {});
|
|
15
|
+
const command = buildGhCommand(toolName, args);
|
|
16
|
+
// Security check: block mutating commands
|
|
17
|
+
try {
|
|
18
|
+
validateReadOnly(command);
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22
|
+
return {
|
|
23
|
+
content: [{ type: 'text', text: msg }],
|
|
24
|
+
isError: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
// Execute command
|
|
28
|
+
let result;
|
|
29
|
+
try {
|
|
30
|
+
result = await executeCommand(command);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34
|
+
return {
|
|
35
|
+
content: [{ type: 'text', text: `Execution error: ${msg}` }],
|
|
36
|
+
isError: true,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const output = result.stdout ||
|
|
40
|
+
result.stderr ||
|
|
41
|
+
`(no output, exit code ${result.exitCode})`;
|
|
42
|
+
const isError = result.exitCode !== 0;
|
|
43
|
+
return {
|
|
44
|
+
content: [{ type: 'text', text: output }],
|
|
45
|
+
isError,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
async function main() {
|
|
49
|
+
const transport = new StdioServerTransport();
|
|
50
|
+
await server.connect(transport);
|
|
51
|
+
// Server is running on stdio - no console output to avoid corrupting MCP protocol
|
|
52
|
+
}
|
|
53
|
+
main().catch((err) => {
|
|
54
|
+
process.stderr.write(`Fatal error: ${err}\n`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
57
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface ToolDefinition {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
inputSchema: {
|
|
5
|
+
type: 'object';
|
|
6
|
+
properties: Record<string, {
|
|
7
|
+
type: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}>;
|
|
10
|
+
required?: string[];
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export declare function getToolDefinitions(): ToolDefinition[];
|
|
14
|
+
export declare function buildGhCommand(toolName: string, args: Record<string, unknown>): string;
|
|
15
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAClE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;CACH;AAED,wBAAgB,kBAAkB,IAAI,cAAc,EAAE,CAqErD;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CA+BR"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
export function getToolDefinitions() {
|
|
2
|
+
return [
|
|
3
|
+
{
|
|
4
|
+
name: 'gh_issue_list',
|
|
5
|
+
description: 'List GitHub issues for a repository using the local `gh` CLI. ' +
|
|
6
|
+
'Requires gh to be installed and authenticated.',
|
|
7
|
+
inputSchema: {
|
|
8
|
+
type: 'object',
|
|
9
|
+
properties: {
|
|
10
|
+
repo: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'Repository in OWNER/REPO format (e.g. "cli/cli"). ' +
|
|
13
|
+
'If omitted, uses the current directory\'s git remote.',
|
|
14
|
+
},
|
|
15
|
+
limit: {
|
|
16
|
+
type: 'number',
|
|
17
|
+
description: 'Maximum number of issues to fetch (default: 30).',
|
|
18
|
+
},
|
|
19
|
+
state: {
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'Filter by state: "open" (default), "closed", or "all".',
|
|
22
|
+
},
|
|
23
|
+
label: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Filter by label name.',
|
|
26
|
+
},
|
|
27
|
+
assignee: {
|
|
28
|
+
type: 'string',
|
|
29
|
+
description: 'Filter by assignee login.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'gh_pr_list',
|
|
36
|
+
description: 'List GitHub pull requests for a repository using the local `gh` CLI. ' +
|
|
37
|
+
'Requires gh to be installed and authenticated.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
repo: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Repository in OWNER/REPO format (e.g. "cli/cli"). ' +
|
|
44
|
+
'If omitted, uses the current directory\'s git remote.',
|
|
45
|
+
},
|
|
46
|
+
limit: {
|
|
47
|
+
type: 'number',
|
|
48
|
+
description: 'Maximum number of PRs to fetch (default: 30).',
|
|
49
|
+
},
|
|
50
|
+
state: {
|
|
51
|
+
type: 'string',
|
|
52
|
+
description: 'Filter by state: "open" (default), "closed", or "merged".',
|
|
53
|
+
},
|
|
54
|
+
base: {
|
|
55
|
+
type: 'string',
|
|
56
|
+
description: 'Filter by base branch name.',
|
|
57
|
+
},
|
|
58
|
+
assignee: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
description: 'Filter by assignee login.',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
export function buildGhCommand(toolName, args) {
|
|
68
|
+
const parts = ['gh'];
|
|
69
|
+
if (toolName === 'gh_issue_list') {
|
|
70
|
+
parts.push('issue', 'list');
|
|
71
|
+
}
|
|
72
|
+
else if (toolName === 'gh_pr_list') {
|
|
73
|
+
parts.push('pr', 'list');
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw new Error(`Unknown tool: ${toolName}`);
|
|
77
|
+
}
|
|
78
|
+
if (args['repo'] !== undefined) {
|
|
79
|
+
parts.push('--repo', String(args['repo']));
|
|
80
|
+
}
|
|
81
|
+
if (args['limit'] !== undefined) {
|
|
82
|
+
parts.push('--limit', String(args['limit']));
|
|
83
|
+
}
|
|
84
|
+
if (args['state'] !== undefined) {
|
|
85
|
+
parts.push('--state', String(args['state']));
|
|
86
|
+
}
|
|
87
|
+
if (toolName === 'gh_issue_list' && args['label'] !== undefined) {
|
|
88
|
+
parts.push('--label', String(args['label']));
|
|
89
|
+
}
|
|
90
|
+
if (args['assignee'] !== undefined) {
|
|
91
|
+
parts.push('--assignee', String(args['assignee']));
|
|
92
|
+
}
|
|
93
|
+
if (toolName === 'gh_pr_list' && args['base'] !== undefined) {
|
|
94
|
+
parts.push('--base', String(args['base']));
|
|
95
|
+
}
|
|
96
|
+
return parts.join(' ');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAWA,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAWtD"}
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const BLOCKED_KEYWORDS = [
|
|
2
|
+
'create',
|
|
3
|
+
'delete',
|
|
4
|
+
'edit',
|
|
5
|
+
'rm',
|
|
6
|
+
'update',
|
|
7
|
+
'close',
|
|
8
|
+
'reopen',
|
|
9
|
+
'merge',
|
|
10
|
+
];
|
|
11
|
+
export function validateReadOnly(command) {
|
|
12
|
+
const lower = command.toLowerCase();
|
|
13
|
+
for (const keyword of BLOCKED_KEYWORDS) {
|
|
14
|
+
// Match keyword as a whole word (surrounded by non-alphanumeric chars or at boundaries)
|
|
15
|
+
const pattern = new RegExp(`(?<![a-z0-9])${keyword}(?![a-z0-9])`);
|
|
16
|
+
if (pattern.test(lower)) {
|
|
17
|
+
throw new Error('Error: Mutating commands are blocked in MVP version. Use read-only commands.');
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG;IACvB,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,OAAO;CACR,CAAC;AAEF,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,KAAK,MAAM,OAAO,IAAI,gBAAgB,EAAE,CAAC;QACvC,wFAAwF;QACxF,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,gBAAgB,OAAO,cAAc,CAAC,CAAC;QAClE,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "zero-config-cli-bridge",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero Setup. Zero API Keys. Expose your local authenticated CLIs as MCP tools.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"zero-config-cli-bridge": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && node -e \"import('fs').then(({readFileSync,writeFileSync})=>{const p='dist/index.js';writeFileSync(p,'#!/usr/bin/env node\\n'+readFileSync(p,'utf8'))})\" && chmod +x dist/index.js",
|
|
17
|
+
"start": "node dist/index.js"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"github",
|
|
23
|
+
"gh",
|
|
24
|
+
"cli",
|
|
25
|
+
"llm",
|
|
26
|
+
"claude",
|
|
27
|
+
"zero-config",
|
|
28
|
+
"agent",
|
|
29
|
+
"tool"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"typescript": "^5.3.0",
|
|
40
|
+
"@types/node": "^20.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|