gemini-mcp-cli 1.0.0__py3-none-any.whl
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.
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gemini-mcp-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: FastMCP server wrapping Google's Gemini CLI — use Gemini models from any MCP client
|
|
5
|
+
Project-URL: Homepage, https://github.com/jxsprt/gemini-mcp-server
|
|
6
|
+
Project-URL: Repository, https://github.com/jxsprt/gemini-mcp-server
|
|
7
|
+
Project-URL: Issues, https://github.com/jxsprt/gemini-mcp-server/issues
|
|
8
|
+
Author-email: Jaspreet Singh <jaspreetsinghintp@gmail.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: fastmcp,gemini,gemini-cli,mcp,mcp-server
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: fastmcp>=3.0.0
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Gemini CLI MCP Server
|
|
27
|
+
|
|
28
|
+
[](https://github.com/jxsprt/gemini-mcp-server/actions/workflows/ci.yml)
|
|
29
|
+
[](https://pypi.org/project/gemini-mcp-server/)
|
|
30
|
+
[](LICENSE)
|
|
31
|
+
[](https://github.com/NousResearch/hermes-agent/pull/22878)
|
|
32
|
+
|
|
33
|
+
A lightweight [FastMCP](https://gofastmcp.com) server that wraps Google's [Gemini CLI](https://github.com/google-gemini/gemini-cli) as MCP tools. Works with any MCP client — Hermes Agent, Claude Code, Claude Desktop, Cursor, etc.
|
|
34
|
+
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
- **Node.js** — for the Gemini CLI
|
|
38
|
+
- **Python 3.11+** — for the MCP server
|
|
39
|
+
- **Gemini CLI** — installed and authenticated
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g @google/gemini-cli
|
|
43
|
+
gemini --version # Verify install
|
|
44
|
+
gemini # Run once to complete OAuth login
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Clone the repo
|
|
51
|
+
git clone https://github.com/jxsprt/gemini-mcp-server.git
|
|
52
|
+
cd gemini-mcp-server
|
|
53
|
+
|
|
54
|
+
# Create venv and install deps
|
|
55
|
+
python3 -m venv .venv
|
|
56
|
+
.venv/bin/pip install -r requirements.txt
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
Add to your MCP client's config:
|
|
62
|
+
|
|
63
|
+
### Hermes Agent (`~/.hermes/config.yaml`)
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
mcp_servers:
|
|
67
|
+
gemini-cli:
|
|
68
|
+
command: "/path/to/gemini-mcp-server/.venv/bin/python3"
|
|
69
|
+
args: ["/path/to/gemini-mcp-server/server.py"]
|
|
70
|
+
timeout: 240
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Restart your Hermes gateway. Tools will be available as `mcp_gemini_cli_*`.
|
|
74
|
+
|
|
75
|
+
### Claude Desktop (`claude_desktop_config.json`)
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"gemini-cli": {
|
|
81
|
+
"command": "/path/to/gemini-mcp-server/.venv/bin/python3",
|
|
82
|
+
"args": ["/path/to/gemini-mcp-server/server.py"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Claude Code
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
fastmcp install claude-code /path/to/gemini-mcp-server/server.py
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Cursor
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
fastmcp install cursor /path/to/gemini-mcp-server/server.py -e .
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Tools
|
|
101
|
+
|
|
102
|
+
### `gemini_prompt`
|
|
103
|
+
|
|
104
|
+
Send any prompt to Gemini CLI non-interactively. Supports model selection and JSON output.
|
|
105
|
+
|
|
106
|
+
**Safe by default** — uses `--approval-mode auto-edit` (auto-approves file edits, prompts for shell). Pass `dangerous=true` for full `yolo` mode.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
gemini_prompt(prompt="Explain TCP handshake", model="gemini-2.5-flash")
|
|
110
|
+
gemini_prompt(prompt="Refactor this module", dangerous=True) # full auto-approval
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Parameter | Type | Required | Default | Description |
|
|
114
|
+
|-----------|------|----------|---------|-------------|
|
|
115
|
+
| `prompt` | string | ✅ | — | Prompt text (max ~100K chars) |
|
|
116
|
+
| `model` | string | ❌ | CLI default | Model name (e.g., `gemini-3-flash-preview`) |
|
|
117
|
+
| `output_format` | string | ❌ | `text` | `text`, `json`, or `stream-json` |
|
|
118
|
+
| `dangerous` | bool | ❌ | `false` | Use `--approval-mode yolo` (auto-approves shell) |
|
|
119
|
+
|
|
120
|
+
### `gemini_plan`
|
|
121
|
+
|
|
122
|
+
Read-only audit and review mode. Uses `--approval-mode plan` — **guarantees no file mutations or shell execution**. Safe for whole-repo analysis and code reviews.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
gemini_plan(prompt="Review this auth module for security issues")
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
| Parameter | Type | Required | Default | Description |
|
|
129
|
+
|-----------|------|----------|---------|-------------|
|
|
130
|
+
| `prompt` | string | ✅ | — | Analysis question or review request |
|
|
131
|
+
| `model` | string | ❌ | CLI default | Gemini model |
|
|
132
|
+
| `include_directories` | string | ❌ | — | Comma-separated additional dirs |
|
|
133
|
+
|
|
134
|
+
### `gemini_version`
|
|
135
|
+
|
|
136
|
+
Returns the installed Gemini CLI version.
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
gemini_version() # → "0.41.2"
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Verification
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
cd /path/to/gemini-mcp-server
|
|
146
|
+
.venv/bin/python -c "
|
|
147
|
+
import server
|
|
148
|
+
print('Version:', server.gemini_version())
|
|
149
|
+
print('Prompt:', server.gemini_prompt('Say hello in one word'))
|
|
150
|
+
print('Plan:', server.gemini_plan('What tools does this server have?'))
|
|
151
|
+
"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## How It Works
|
|
155
|
+
|
|
156
|
+
The server shells out to `gemini -p "prompt" --approval-mode yolo` for each tool call. It does NOT use the Google Gen AI Python SDK — it goes through the Gemini CLI binary (OAuth-authenticated).
|
|
157
|
+
|
|
158
|
+
**Key design decisions:**
|
|
159
|
+
- **Stdio transport** — runs as a subprocess of your MCP client
|
|
160
|
+
- **No hardcoded paths** — discovers `gemini` on PATH
|
|
161
|
+
- **No API keys in config** — uses the CLI's existing OAuth session
|
|
162
|
+
- **Retry-friendly** — generous timeouts (120s default, 240s for plan mode) to handle Gemini's capacity retries
|
|
163
|
+
|
|
164
|
+
## Notes
|
|
165
|
+
|
|
166
|
+
- **Capacity errors**: Gemini's reasoning models can hit rate limits. The CLI retries up to 7 times with backoff. If it fails, try again later or use a different model.
|
|
167
|
+
- **Plan mode is safe**: `--approval-mode plan` explicitly prevents the agent from writing files or executing shell commands. Read-only, guaranteed.
|
|
168
|
+
- **1M token context**: Gemini CLI supports the full 1M token context window of Gemini models.
|
|
169
|
+
|
|
170
|
+
## License
|
|
171
|
+
|
|
172
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
gemini_mcp_server/__init__.py,sha256=EYzg5PB1KqwRyAaQ7-cR3UdyragGjktce-zvWbX5U2M,225
|
|
2
|
+
gemini_mcp_server/__main__.py,sha256=dHOSZ1BdFFbUPYxDhEGuiU5i_GmkxtEZW8olFqr8CIU,129
|
|
3
|
+
gemini_mcp_server/server.py,sha256=nwd2s1f-6jxrhTM0P-WJTV0Jo7M6q__ArNcsi_HERZs,9282
|
|
4
|
+
gemini_mcp_cli-1.0.0.dist-info/METADATA,sha256=buaUIkYTShMKN0XbA-QJ-NCzGZ3NzEimLwheNuvlmvU,6010
|
|
5
|
+
gemini_mcp_cli-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
6
|
+
gemini_mcp_cli-1.0.0.dist-info/entry_points.txt,sha256=bWFu_7C87aMqg0wsNdX1vq20Ww77eYpYjHZu2UxqVcA,54
|
|
7
|
+
gemini_mcp_cli-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gemini-mcp-server — FastMCP server wrapping Google's Gemini CLI.
|
|
3
|
+
|
|
4
|
+
Exposes Gemini CLI as MCP tools consumable by any MCP client
|
|
5
|
+
(Hermes Agent, Claude Code, Claude Desktop, Cursor, etc.).
|
|
6
|
+
|
|
7
|
+
Powered by FastMCP (https://gofastmcp.com).
|
|
8
|
+
|
|
9
|
+
Tools:
|
|
10
|
+
- gemini_prompt → Send a prompt to Gemini CLI (non-interactive, full power)
|
|
11
|
+
- gemini_plan → Read-only audit/review mode (--approval-mode plan)
|
|
12
|
+
- gemini_version → Get Gemini CLI version
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
import shutil
|
|
18
|
+
import subprocess
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
from fastmcp import FastMCP
|
|
22
|
+
|
|
23
|
+
# ── Logging ────────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
logging.basicConfig(
|
|
26
|
+
level=logging.INFO,
|
|
27
|
+
format="%(asctime)s [%(levelname)s] %(message)s",
|
|
28
|
+
stream=__import__("sys").stderr,
|
|
29
|
+
)
|
|
30
|
+
logger = logging.getLogger("gemini-mcp")
|
|
31
|
+
|
|
32
|
+
# ── Server ─────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
mcp = FastMCP("Gemini CLI MCP Server")
|
|
35
|
+
|
|
36
|
+
# ── Configuration (env vars with sensible defaults) ────────────────────────
|
|
37
|
+
|
|
38
|
+
_GEMINI_CLI_PATH = os.environ.get("GEMINI_CLI_PATH")
|
|
39
|
+
DEFAULT_TIMEOUT = int(os.environ.get("GEMINI_TIMEOUT", "120"))
|
|
40
|
+
PLAN_TIMEOUT = int(os.environ.get("GEMINI_PLAN_TIMEOUT", "240"))
|
|
41
|
+
MAX_PROMPT_LENGTH = int(os.environ.get("GEMINI_MAX_PROMPT", "100000"))
|
|
42
|
+
HTTP_ENABLED = os.environ.get("GEMINI_MCP_HTTP", "").lower() in ("1", "true", "yes")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _get_gemini_bin() -> str:
|
|
46
|
+
"""Locate the gemini CLI binary. Returns full path or raises RuntimeError."""
|
|
47
|
+
if _GEMINI_CLI_PATH:
|
|
48
|
+
path = _GEMINI_CLI_PATH
|
|
49
|
+
if os.path.isfile(path) and os.access(path, os.X_OK):
|
|
50
|
+
return path
|
|
51
|
+
raise RuntimeError(
|
|
52
|
+
f"GEMINI_CLI_PATH set to '{path}' but file not found or not executable. "
|
|
53
|
+
"Fix the path or unset it to auto-discover."
|
|
54
|
+
)
|
|
55
|
+
path = shutil.which("gemini")
|
|
56
|
+
if path is None:
|
|
57
|
+
raise RuntimeError(
|
|
58
|
+
"Gemini CLI not found on PATH. Install it with: npm install -g @google/gemini-cli, "
|
|
59
|
+
"or set the GEMINI_CLI_PATH environment variable."
|
|
60
|
+
)
|
|
61
|
+
return path
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── Helper ─────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run_gemini(args: list[str], timeout: int = DEFAULT_TIMEOUT) -> dict:
|
|
68
|
+
"""Run `gemini <args>` and return structured result.
|
|
69
|
+
|
|
70
|
+
Returns dict with keys: success, output, error.
|
|
71
|
+
"""
|
|
72
|
+
gemini_bin = _get_gemini_bin()
|
|
73
|
+
cmd = [gemini_bin] + args
|
|
74
|
+
logger.info("Running: %s", " ".join(cmd))
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
result = subprocess.run(
|
|
78
|
+
cmd,
|
|
79
|
+
capture_output=True,
|
|
80
|
+
text=True,
|
|
81
|
+
timeout=timeout,
|
|
82
|
+
)
|
|
83
|
+
stdout = result.stdout.strip()
|
|
84
|
+
stderr = result.stderr.strip()
|
|
85
|
+
|
|
86
|
+
if result.returncode != 0:
|
|
87
|
+
logger.warning("Non-zero exit %d: %s", result.returncode, stderr[:200])
|
|
88
|
+
return {
|
|
89
|
+
"success": False,
|
|
90
|
+
"output": stdout,
|
|
91
|
+
"error": stderr or f"Exit code {result.returncode}",
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
logger.info("Success (%d chars)", len(result.stdout))
|
|
95
|
+
return {"success": True, "output": stdout, "error": None}
|
|
96
|
+
|
|
97
|
+
except subprocess.TimeoutExpired:
|
|
98
|
+
logger.warning("Timed out after %ds", timeout)
|
|
99
|
+
return {
|
|
100
|
+
"success": False,
|
|
101
|
+
"output": "",
|
|
102
|
+
"error": f"Command timed out after {timeout}s",
|
|
103
|
+
}
|
|
104
|
+
except FileNotFoundError:
|
|
105
|
+
msg = f"Gemini CLI not found at {gemini_bin}"
|
|
106
|
+
logger.error(msg)
|
|
107
|
+
return {"success": False, "output": "", "error": msg}
|
|
108
|
+
except Exception as e:
|
|
109
|
+
logger.exception("Unexpected error")
|
|
110
|
+
return {"success": False, "output": "", "error": str(e)}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ── Tools ──────────────────────────────────────────────────────────────────
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@mcp.tool(
|
|
117
|
+
name="gemini_prompt",
|
|
118
|
+
description=(
|
|
119
|
+
"Send a prompt to Gemini CLI in non-interactive mode. "
|
|
120
|
+
"The full power of Gemini models — coding, research, Q&A, analysis. "
|
|
121
|
+
"Supports optional model selection and output format."
|
|
122
|
+
),
|
|
123
|
+
)
|
|
124
|
+
def gemini_prompt(
|
|
125
|
+
prompt: str,
|
|
126
|
+
model: Optional[str] = None,
|
|
127
|
+
output_format: Optional[str] = None,
|
|
128
|
+
dangerous: bool = False,
|
|
129
|
+
) -> str:
|
|
130
|
+
"""Execute a prompt via Gemini CLI.
|
|
131
|
+
|
|
132
|
+
By default uses --approval-mode auto-edit (safe for file edits in working dir).
|
|
133
|
+
Pass dangerous=True to use --approval-mode yolo (auto-approves ALL actions
|
|
134
|
+
including arbitrary shell commands).
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
prompt: The prompt text to send (max ~100K chars).
|
|
138
|
+
model: Gemini model to use (e.g., gemini-3-flash-preview, gemini-2.5-pro).
|
|
139
|
+
Defaults to gemini CLI's built-in default.
|
|
140
|
+
output_format: 'text' (default), 'json', or 'stream-json'.
|
|
141
|
+
JSON includes token/cost stats in the envelope.
|
|
142
|
+
dangerous: If True, uses --approval-mode yolo (auto-approves shell cmds).
|
|
143
|
+
Defaults to False (--approval-mode auto-edit).
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Gemini CLI output as a string.
|
|
147
|
+
"""
|
|
148
|
+
if len(prompt) > MAX_PROMPT_LENGTH:
|
|
149
|
+
prompt = prompt[:MAX_PROMPT_LENGTH]
|
|
150
|
+
|
|
151
|
+
approval = "yolo" if dangerous else "auto_edit"
|
|
152
|
+
args = ["-p", prompt, "--approval-mode", approval]
|
|
153
|
+
|
|
154
|
+
if model:
|
|
155
|
+
args += ["-m", model]
|
|
156
|
+
if output_format:
|
|
157
|
+
args += ["-o", output_format]
|
|
158
|
+
|
|
159
|
+
result = _run_gemini(args, timeout=DEFAULT_TIMEOUT)
|
|
160
|
+
|
|
161
|
+
if result["success"]:
|
|
162
|
+
return result["output"] or "(empty response)"
|
|
163
|
+
else:
|
|
164
|
+
error_msg = result["error"] or "unknown error"
|
|
165
|
+
return f"Error: {error_msg}"
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@mcp.tool(
|
|
169
|
+
name="gemini_plan",
|
|
170
|
+
description=(
|
|
171
|
+
"Read-only audit, code review, and research via Gemini CLI. "
|
|
172
|
+
"Uses --approval-mode plan which guarantees NO file mutations or shell execution. "
|
|
173
|
+
"Safe for whole-repo analysis, code reviews, vulnerability assessments, "
|
|
174
|
+
"architecture audits, and deep research."
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
def gemini_plan(
|
|
178
|
+
prompt: str,
|
|
179
|
+
model: Optional[str] = None,
|
|
180
|
+
include_directories: Optional[str] = None,
|
|
181
|
+
) -> str:
|
|
182
|
+
"""Run Gemini CLI in read-only plan mode — no mutations possible.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
prompt: Analysis question or review request.
|
|
186
|
+
model: Gemini model (optional, defaults to gemini CLI default).
|
|
187
|
+
include_directories: Comma-separated additional dirs to include in workspace.
|
|
188
|
+
Only includes subdirectories of the current working directory
|
|
189
|
+
for safety.
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Gemini CLI's analysis/plan output.
|
|
193
|
+
"""
|
|
194
|
+
if len(prompt) > MAX_PROMPT_LENGTH:
|
|
195
|
+
prompt = prompt[:MAX_PROMPT_LENGTH]
|
|
196
|
+
|
|
197
|
+
args = ["-p", prompt, "--approval-mode", "plan"]
|
|
198
|
+
|
|
199
|
+
if model:
|
|
200
|
+
args += ["-m", model]
|
|
201
|
+
|
|
202
|
+
# Validate include_directories — restrict to CWD subdirectories
|
|
203
|
+
if include_directories:
|
|
204
|
+
cwd = os.getcwd()
|
|
205
|
+
validated = []
|
|
206
|
+
for d in include_directories.split(","):
|
|
207
|
+
d = d.strip()
|
|
208
|
+
abs_d = os.path.abspath(os.path.join(cwd, d)) if not os.path.isabs(d) else d
|
|
209
|
+
if abs_d.startswith(cwd.rstrip("/") + "/") or abs_d == cwd:
|
|
210
|
+
validated.append(d)
|
|
211
|
+
else:
|
|
212
|
+
logger.warning("Ignored out-of-workspace path: %s", d)
|
|
213
|
+
if validated:
|
|
214
|
+
args += ["--include-directories", ",".join(validated)]
|
|
215
|
+
|
|
216
|
+
result = _run_gemini(args, timeout=PLAN_TIMEOUT)
|
|
217
|
+
|
|
218
|
+
if result["success"]:
|
|
219
|
+
return result["output"] or "(empty response)"
|
|
220
|
+
else:
|
|
221
|
+
error_msg = result["error"] or "unknown error"
|
|
222
|
+
return f"Error: {error_msg}"
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@mcp.tool(
|
|
226
|
+
name="gemini_version",
|
|
227
|
+
description="Get the installed Gemini CLI version.",
|
|
228
|
+
)
|
|
229
|
+
def gemini_version() -> str:
|
|
230
|
+
"""Get Gemini CLI version info."""
|
|
231
|
+
try:
|
|
232
|
+
gemini_bin = _get_gemini_bin()
|
|
233
|
+
result = subprocess.run(
|
|
234
|
+
[gemini_bin, "--version"],
|
|
235
|
+
capture_output=True,
|
|
236
|
+
text=True,
|
|
237
|
+
timeout=10,
|
|
238
|
+
)
|
|
239
|
+
output = (result.stdout or result.stderr or "").strip()
|
|
240
|
+
return output or "(no version info)"
|
|
241
|
+
except subprocess.TimeoutExpired:
|
|
242
|
+
return "Error: Command timed out after 10s"
|
|
243
|
+
except RuntimeError as e:
|
|
244
|
+
return f"Error: {e}"
|
|
245
|
+
except Exception as e:
|
|
246
|
+
return f"Error: {e}"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ── Entrypoint ─────────────────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def main() -> None:
|
|
253
|
+
"""CLI entry point for pip-installed package."""
|
|
254
|
+
if HTTP_ENABLED:
|
|
255
|
+
logger.info("Starting HTTP transport on port 8000")
|
|
256
|
+
mcp.run(transport="http")
|
|
257
|
+
else:
|
|
258
|
+
logger.info("Starting stdio transport")
|
|
259
|
+
mcp.run()
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
if __name__ == "__main__":
|
|
263
|
+
main()
|