regcode 0.9.0__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {regcode-0.9.0 → regcode-0.10.0}/GOALS.md +39 -0
- {regcode-0.9.0 → regcode-0.10.0}/PKG-INFO +2 -1
- regcode-0.10.0/config.yaml.template +56 -0
- {regcode-0.9.0 → regcode-0.10.0}/pyproject.toml +2 -1
- {regcode-0.9.0 → regcode-0.10.0}/regcode/__init__.py +1 -1
- {regcode-0.9.0 → regcode-0.10.0}/regcode/cli.py +84 -1
- {regcode-0.9.0 → regcode-0.10.0}/regcode/config.py +36 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/main.py +112 -1
- regcode-0.10.0/regcode/mcp_client.py +595 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tui.py +13 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_cli.py +1 -0
- regcode-0.10.0/tests/test_mcp.py +376 -0
- regcode-0.10.0/tests/test_mcp_status.py +188 -0
- {regcode-0.9.0 → regcode-0.10.0}/uv.lock +109 -10
- regcode-0.9.0/config.yaml.template +0 -16
- {regcode-0.9.0 → regcode-0.10.0}/.gitignore +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/.gitlab-ci.yml +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/.python-version +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/.qwen/settings.json +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/AGENTS.md +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/README.md +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/main.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/clipboard.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/conversation_manager.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/dependencies.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/monty_sandbox.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/paste.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/permissions.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/sandbox.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/__init__.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/base.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/builtins.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/registry.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/review_notes.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/web_crawl.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tools/web_search.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/regcode/tracing.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/__init__.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/conftest.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_api.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_clipboard.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_config.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_context_manager.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_dependencies.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_main.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_monty_sandbox.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_osv_cve.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_provider_extra.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_review_notes.py +0 -0
- {regcode-0.9.0 → regcode-0.10.0}/tests/test_web_search.py +0 -0
|
@@ -505,6 +505,45 @@ All phases complete. 50/50 tests passing. Lint clean.
|
|
|
505
505
|
|
|
506
506
|
- ✅ WebSearchTool: connects to SearXNG, defaults to https://search.regmi.dev, no extra deps needed
|
|
507
507
|
|
|
508
|
+
## MCP Support (2026-07-05)
|
|
509
|
+
|
|
510
|
+
- ✅ `mcp` package added to `pyproject.toml` dependencies
|
|
511
|
+
- ✅ `McpServerConfig` Pydantic model in `regcode/config.py` — supports both stdio (command+args+env) and HTTP (url+headers) transports, with per-tool timeout
|
|
512
|
+
- ✅ `regcode/mcp_client.py` — MCP client with two transport strategies:
|
|
513
|
+
• **stdio** — uses MCP library's `stdio_client` for command-based servers (npx, uvx)
|
|
514
|
+
• **HTTP blocking JSON-RPC** — uses `httpx.Client` directly for servers that return 200 OK with JSON body (not 202 + SSE). Supports session-ID propagation via `mcp-session-id` header, credential injection via custom headers.
|
|
515
|
+
• Discovers tools via `tools/list` JSON-RPC call, registers them into Agent's `ToolRegistry` with `mcp_{server}_{tool}` naming
|
|
516
|
+
• Credential redaction in error messages
|
|
517
|
+
- ✅ `Agent.__init__` calls `_discover_mcp_tools()` at startup — reads `mcp_servers` from config, connects to each server, registers tools. Gracefully skips if `mcp` package not installed or connection fails.
|
|
518
|
+
- ✅ `Agent.reload_mcp()` method — hot-reloads MCP connections (closes old, discovers new) without restart
|
|
519
|
+
- ✅ `Agent._mcp_status()` — returns formatted text showing all MCP servers, their status, and discovered tools
|
|
520
|
+
- ✅ `Agent.cleanup()` includes MCP session close
|
|
521
|
+
- ✅ `McpClient.get_server_info()` — returns introspection data for all configured servers (name, transport, connected status, error, tool list with params)
|
|
522
|
+
- ✅ `/mcp` slash command in TUI — lists all configured MCP servers, shows connection status, enumerates discovered tools with descriptions and parameters
|
|
523
|
+
- ✅ CLI `configure` command generates `mcp_servers` section in config.yaml (interactive prompt for stdio/HTTP servers)
|
|
524
|
+
- ✅ `config.yaml.template` includes commented MCP server examples for stdio (npx, uvx) and HTTP transport
|
|
525
|
+
- ✅ 31 MCP config tests: `TestMcpServerConfig`, `TestConfigMcpServers`, `TestSanitizeName`, `TestRedactCredentials`, `TestMcpClientAgentIntegration`, `TestMcpClientConnectAll`
|
|
526
|
+
- ✅ 7 MCP status tests: `test_get_server_info_*`, `test_mcp_status_format*`
|
|
527
|
+
- ✅ All 231 tests pass, lint clean
|
|
528
|
+
|
|
529
|
+
Example config.yaml MCP section:
|
|
530
|
+
```yaml
|
|
531
|
+
mcp_servers:
|
|
532
|
+
github:
|
|
533
|
+
command: npx
|
|
534
|
+
args: ["-y", "@modelcontextprotocol/server-github"]
|
|
535
|
+
env:
|
|
536
|
+
GITHUB_TOKEN: ghp_xxxxx
|
|
537
|
+
timeout: 60
|
|
538
|
+
filesystem:
|
|
539
|
+
command: uvx
|
|
540
|
+
args: ["mcp-server-filesystem", "/tmp"]
|
|
541
|
+
remote_api:
|
|
542
|
+
url: https://mcp.example.com/v1/mcp
|
|
543
|
+
headers:
|
|
544
|
+
Authorization: Bearer sk-xxxxx
|
|
545
|
+
```
|
|
546
|
+
|
|
508
547
|
## Completed (2026-06-30 — TUI fixes)
|
|
509
548
|
|
|
510
549
|
1. ✅ AGENTS.md injection — Agent now reads AGENTS.md/agents.md from CWD or allowed directory and injects it into system prompt (both `_build_system_prompt` and `_build_system_prompt_no_tools`). Appended after base system_prompt, before tool descriptions.
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: regcode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Minimalistic coding agent with Python API and CLI
|
|
5
5
|
Requires-Python: >=3.10
|
|
6
6
|
Requires-Dist: boto3>=1.43.40
|
|
7
7
|
Requires-Dist: click>=8.0
|
|
8
8
|
Requires-Dist: crawl4ai>=0.4.0
|
|
9
9
|
Requires-Dist: litellm>=1.83.17
|
|
10
|
+
Requires-Dist: mcp>=1.0.0
|
|
10
11
|
Requires-Dist: mlflow>=3.14.0
|
|
11
12
|
Requires-Dist: pathspec>=1.1.1
|
|
12
13
|
Requires-Dist: pydantic-monty>=0.0.18
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
agent:
|
|
2
|
+
# Context window management
|
|
3
|
+
enable_compaction: true
|
|
4
|
+
compaction_threshold: 0.8
|
|
5
|
+
context_window: 128000
|
|
6
|
+
|
|
7
|
+
provider:
|
|
8
|
+
api_key: "${OPENAI_API_KEY}"
|
|
9
|
+
model: "openai/gpt-4o"
|
|
10
|
+
max_tokens: 4096
|
|
11
|
+
temperature: 0.7
|
|
12
|
+
|
|
13
|
+
# ── MCP Servers (optional) ──────────────────────────────────────
|
|
14
|
+
#
|
|
15
|
+
# Add MCP servers to extend the agent with external tools.
|
|
16
|
+
# The agent connects on startup, discovers available tools,
|
|
17
|
+
# and registers them as `mcp_{server}_{tool_name}`.
|
|
18
|
+
#
|
|
19
|
+
# Two transport types are supported:
|
|
20
|
+
#
|
|
21
|
+
# 1. Stdio (command-based, e.g. npx, uvx):
|
|
22
|
+
# command — executable to run
|
|
23
|
+
# args — arguments passed to the command
|
|
24
|
+
# env — environment variables (only these are passed,
|
|
25
|
+
# NOT the full shell environment)
|
|
26
|
+
#
|
|
27
|
+
# 2. HTTP (StreamableHTTP URL):
|
|
28
|
+
# url — server endpoint
|
|
29
|
+
# headers — HTTP headers sent with each request
|
|
30
|
+
#
|
|
31
|
+
# Example:
|
|
32
|
+
|
|
33
|
+
mcp_servers:
|
|
34
|
+
# GitHub server — uses npx to run the official MCP server
|
|
35
|
+
#github:
|
|
36
|
+
# command: "npx"
|
|
37
|
+
# args: ["-y", "@modelcontextprotocol/server-github"]
|
|
38
|
+
# env:
|
|
39
|
+
# GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_TOKEN}"
|
|
40
|
+
# timeout: 60
|
|
41
|
+
|
|
42
|
+
# Filesystem server — lets the agent read/write arbitrary files
|
|
43
|
+
# filesystem:
|
|
44
|
+
# command: "uvx"
|
|
45
|
+
# args: ["mcp-server-filesystem", "/tmp"]
|
|
46
|
+
|
|
47
|
+
# Remote HTTP server
|
|
48
|
+
# remote_api:
|
|
49
|
+
# url: "https://mcp.example.com/v1/mcp"
|
|
50
|
+
# headers:
|
|
51
|
+
# Authorization: "Bearer ${MCP_API_KEY}"
|
|
52
|
+
|
|
53
|
+
tools:
|
|
54
|
+
code_review: true
|
|
55
|
+
security_scan: false
|
|
56
|
+
sandbox: false
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "regcode"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.10.0"
|
|
4
4
|
description = "Minimalistic coding agent with Python API and CLI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -18,6 +18,7 @@ dependencies = [
|
|
|
18
18
|
"pathspec>=1.1.1",
|
|
19
19
|
"pyperclip>=1.9.0",
|
|
20
20
|
"boto3>=1.43.40",
|
|
21
|
+
"mcp>=1.0.0",
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
[project.scripts]
|
|
@@ -139,8 +139,84 @@ def configure(output_path):
|
|
|
139
139
|
else:
|
|
140
140
|
click.echo(" Using default provider settings.")
|
|
141
141
|
|
|
142
|
+
# --- MCP servers config ---
|
|
143
|
+
yes = _confirm("Configure MCP servers? (optional)")
|
|
144
|
+
if yes:
|
|
145
|
+
mcp_section: dict[str, dict] = {}
|
|
146
|
+
while True:
|
|
147
|
+
server_name = click.prompt(
|
|
148
|
+
"Server name (e.g. github, filesystem)",
|
|
149
|
+
default="",
|
|
150
|
+
)
|
|
151
|
+
if not server_name:
|
|
152
|
+
break
|
|
153
|
+
|
|
154
|
+
transport = click.prompt(
|
|
155
|
+
"Transport type",
|
|
156
|
+
type=click.Choice(["stdio", "http"]),
|
|
157
|
+
default="stdio",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
if transport == "stdio":
|
|
161
|
+
command = click.prompt("Command (e.g. npx, uvx)")
|
|
162
|
+
args_str = click.prompt(
|
|
163
|
+
"Arguments (comma-separated, e.g. -y,package-name)",
|
|
164
|
+
default="",
|
|
165
|
+
)
|
|
166
|
+
args = [
|
|
167
|
+
a.strip()
|
|
168
|
+
for a in args_str.split(",")
|
|
169
|
+
if a.strip()
|
|
170
|
+
] if args_str else []
|
|
171
|
+
|
|
172
|
+
env_str = click.prompt(
|
|
173
|
+
"Environment variables (KEY=VALUE lines, empty to skip)",
|
|
174
|
+
default="",
|
|
175
|
+
)
|
|
176
|
+
env = {}
|
|
177
|
+
for line in env_str.splitlines():
|
|
178
|
+
line = line.strip()
|
|
179
|
+
if line and "=" in line:
|
|
180
|
+
k, v = line.split("=", 1)
|
|
181
|
+
env[k.strip()] = v.strip()
|
|
182
|
+
|
|
183
|
+
mcp_section[server_name] = {
|
|
184
|
+
"command": command,
|
|
185
|
+
"args": args,
|
|
186
|
+
"env": env if env else None,
|
|
187
|
+
}
|
|
188
|
+
else:
|
|
189
|
+
url = click.prompt("Server URL (e.g. https://example.com/mcp)")
|
|
190
|
+
headers_str = click.prompt(
|
|
191
|
+
"Headers (KEY=VALUE lines, empty to skip)",
|
|
192
|
+
default="",
|
|
193
|
+
)
|
|
194
|
+
headers = {}
|
|
195
|
+
for line in headers_str.splitlines():
|
|
196
|
+
line = line.strip()
|
|
197
|
+
if line and "=" in line:
|
|
198
|
+
k, v = line.split("=", 1)
|
|
199
|
+
headers[k.strip()] = v.strip()
|
|
200
|
+
|
|
201
|
+
mcp_section[server_name] = {
|
|
202
|
+
"url": url,
|
|
203
|
+
"headers": headers if headers else None,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if mcp_section:
|
|
207
|
+
from regcode.config import McpServerConfig
|
|
208
|
+
|
|
209
|
+
config.mcp_servers = {
|
|
210
|
+
name: McpServerConfig.model_validate(
|
|
211
|
+
{k: v for k, v in cfg.items() if v is not None}
|
|
212
|
+
)
|
|
213
|
+
for name, cfg in mcp_section.items()
|
|
214
|
+
}
|
|
215
|
+
else:
|
|
216
|
+
click.echo(" Using default MCP settings (no servers).")
|
|
217
|
+
|
|
142
218
|
# --- Write config.yaml ---
|
|
143
|
-
data = {
|
|
219
|
+
data: dict = {
|
|
144
220
|
"agent": {
|
|
145
221
|
"context_window": config.agent.context_window,
|
|
146
222
|
"compaction_threshold": config.agent.compaction_threshold,
|
|
@@ -157,6 +233,13 @@ def configure(output_path):
|
|
|
157
233
|
"tool_budget": config.tool_budget,
|
|
158
234
|
}
|
|
159
235
|
|
|
236
|
+
# Add MCP servers section if configured
|
|
237
|
+
if hasattr(config, "mcp_servers") and config.mcp_servers:
|
|
238
|
+
data["mcp_servers"] = {
|
|
239
|
+
name: cfg.model_dump(exclude_none=True)
|
|
240
|
+
for name, cfg in config.mcp_servers.items()
|
|
241
|
+
}
|
|
242
|
+
|
|
160
243
|
with open(output_path, "w") as f:
|
|
161
244
|
yaml.dump(data, f, default_flow_style=False)
|
|
162
245
|
click.echo(f"\nconfig.yaml written to {output_path}")
|
|
@@ -38,11 +38,46 @@ default_system_prompt = dedent(
|
|
|
38
38
|
Start directly with your review content. Do not address user or
|
|
39
39
|
use fillers like "Now I have enough information to provide a review." or
|
|
40
40
|
"Here is my review:"
|
|
41
|
+
|
|
42
|
+
## Code implementation
|
|
43
|
+
If you are asked to implement code, do what a responsible developer would do:
|
|
44
|
+
- researh what library versions are used
|
|
45
|
+
- if library versions are newer than your knowledge cutoff, research first.
|
|
46
|
+
- write code
|
|
47
|
+
- check if tests exist. Always write tests for your code if they don't exist.
|
|
48
|
+
- Make sure to comply to users coding pattern and style.
|
|
41
49
|
"""
|
|
42
50
|
)
|
|
43
51
|
|
|
44
52
|
logger = logging.getLogger(__name__)
|
|
45
53
|
|
|
54
|
+
class McpServerConfig(BaseModel):
|
|
55
|
+
"""Configuration for a single MCP server.
|
|
56
|
+
|
|
57
|
+
Supports two transport types:
|
|
58
|
+
- Stdio: command-based (npx, uvx, or any executable)
|
|
59
|
+
- HTTP: URL-based (StreamableHTTP)
|
|
60
|
+
|
|
61
|
+
Exactly one of ``command`` or ``url`` must be provided.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
model_config = ConfigDict(extra="allow")
|
|
65
|
+
|
|
66
|
+
# Stdio transport
|
|
67
|
+
command: str | None = None
|
|
68
|
+
args: list[str] = []
|
|
69
|
+
env: dict[str, str] = {}
|
|
70
|
+
connect_timeout: int = 60
|
|
71
|
+
|
|
72
|
+
# HTTP transport
|
|
73
|
+
url: str | None = None
|
|
74
|
+
headers: dict[str, str] = {}
|
|
75
|
+
connect_timeout_http: int = 60
|
|
76
|
+
|
|
77
|
+
# Per-tool-call timeout
|
|
78
|
+
timeout: int = 120
|
|
79
|
+
|
|
80
|
+
|
|
46
81
|
class ToolSetConfig(BaseModel):
|
|
47
82
|
"""Configuration for a set of tools."""
|
|
48
83
|
|
|
@@ -126,6 +161,7 @@ class Config(BaseModel):
|
|
|
126
161
|
permissions: list[AgentPermission] = [AgentPermission.READ, AgentPermission.EXECUTE]
|
|
127
162
|
toolset_config: ToolSetConfig = ToolSetConfig()
|
|
128
163
|
debug_mode: bool = False
|
|
164
|
+
mcp_servers: dict[str, McpServerConfig] = {}
|
|
129
165
|
|
|
130
166
|
@classmethod
|
|
131
167
|
def load(cls, path: str | None = None) -> "Config":
|
|
@@ -121,6 +121,16 @@ class Agent:
|
|
|
121
121
|
for tool in create_default_tools():
|
|
122
122
|
self.registry.register(tool)
|
|
123
123
|
|
|
124
|
+
# Discover and register MCP tools
|
|
125
|
+
self._mcp_client = None
|
|
126
|
+
mcp_results = self._discover_mcp_tools()
|
|
127
|
+
if mcp_results:
|
|
128
|
+
logger.info(
|
|
129
|
+
"MCP discovery complete: %d servers, %d tools",
|
|
130
|
+
len(mcp_results),
|
|
131
|
+
sum(len(t) for t in mcp_results.values()),
|
|
132
|
+
)
|
|
133
|
+
|
|
124
134
|
# Apply permission filtering after tools are registered
|
|
125
135
|
self._filter_tools_by_permissions()
|
|
126
136
|
|
|
@@ -135,6 +145,105 @@ class Agent:
|
|
|
135
145
|
if not has_all:
|
|
136
146
|
self.registry.disable_tool(tool_name)
|
|
137
147
|
|
|
148
|
+
def _discover_mcp_tools(self) -> dict[str, list[str]]:
|
|
149
|
+
"""Discover and register MCP tools from configured servers.
|
|
150
|
+
|
|
151
|
+
Reads ``mcp_servers`` from config, connects to each server,
|
|
152
|
+
discovers available tools, and registers them into the agent's
|
|
153
|
+
tool registry with the prefix ``mcp_{server}_{tool}``.
|
|
154
|
+
|
|
155
|
+
Returns a dict mapping server name → list of registered tool names.
|
|
156
|
+
"""
|
|
157
|
+
if not self.config.mcp_servers:
|
|
158
|
+
return {}
|
|
159
|
+
|
|
160
|
+
from regcode.mcp_client import McpClient
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
self._mcp_client = McpClient(
|
|
164
|
+
mcp_servers_config={
|
|
165
|
+
name: cfg.model_dump(exclude_none=True)
|
|
166
|
+
for name, cfg in self.config.mcp_servers.items()
|
|
167
|
+
},
|
|
168
|
+
registry=self.registry,
|
|
169
|
+
)
|
|
170
|
+
results = self._mcp_client.connect_all()
|
|
171
|
+
return results
|
|
172
|
+
except Exception:
|
|
173
|
+
logger.error(
|
|
174
|
+
"MCP discovery failed: %s",
|
|
175
|
+
sys.exc_info()[1],
|
|
176
|
+
exc_info=True,
|
|
177
|
+
)
|
|
178
|
+
return {}
|
|
179
|
+
|
|
180
|
+
def reload_mcp(self) -> dict[str, list[str]]:
|
|
181
|
+
"""Reload MCP servers and re-register their tools.
|
|
182
|
+
|
|
183
|
+
Closes any existing connections, discovers new tools, and
|
|
184
|
+
registers them into the registry. Useful after changing
|
|
185
|
+
``mcp_servers`` in config without restarting the agent.
|
|
186
|
+
|
|
187
|
+
Returns a dict mapping server name → list of registered tool names.
|
|
188
|
+
"""
|
|
189
|
+
# Close existing connections
|
|
190
|
+
if self._mcp_client is not None:
|
|
191
|
+
self._mcp_client.close()
|
|
192
|
+
self._mcp_client = None
|
|
193
|
+
|
|
194
|
+
return self._discover_mcp_tools()
|
|
195
|
+
|
|
196
|
+
def _mcp_status(self) -> str:
|
|
197
|
+
"""Return formatted text showing all MCP servers and their tools.
|
|
198
|
+
|
|
199
|
+
Used by the /mcp slash command for debugging and visibility.
|
|
200
|
+
"""
|
|
201
|
+
if self._mcp_client is None:
|
|
202
|
+
return "No MCP servers configured."
|
|
203
|
+
|
|
204
|
+
servers = self._mcp_client.get_server_info()
|
|
205
|
+
|
|
206
|
+
if not servers:
|
|
207
|
+
return "No MCP servers configured."
|
|
208
|
+
|
|
209
|
+
lines = ["═══ MCP Servers ═══"]
|
|
210
|
+
|
|
211
|
+
for server in servers:
|
|
212
|
+
if server["connected"]:
|
|
213
|
+
status = "[green]connected[/]"
|
|
214
|
+
else:
|
|
215
|
+
status = f"[red]{server['error']}[/]"
|
|
216
|
+
transport = server["transport"]
|
|
217
|
+
name = server["name"]
|
|
218
|
+
lines.append(
|
|
219
|
+
f"\n [bold]{name}[/] "
|
|
220
|
+
f"— transport: {transport}, status: {status}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if server["tools"]:
|
|
224
|
+
for tool in server["tools"]:
|
|
225
|
+
lines.append(f" • {tool['name']}")
|
|
226
|
+
desc = tool.get("description", "")
|
|
227
|
+
if desc:
|
|
228
|
+
# Truncate long descriptions
|
|
229
|
+
if len(desc) > 80:
|
|
230
|
+
desc = desc[:77] + "..."
|
|
231
|
+
lines.append(f" {desc}")
|
|
232
|
+
if tool.get("params"):
|
|
233
|
+
for p in tool["params"]:
|
|
234
|
+
pdef = f"{p['name']} ({p['type']})"
|
|
235
|
+
if p.get("description"):
|
|
236
|
+
if len(p["description"]) > 60:
|
|
237
|
+
pdef += f" — {p['description'][:57]}..."
|
|
238
|
+
else:
|
|
239
|
+
pdef += f" — {p['description']}"
|
|
240
|
+
lines.append(f" └─ {pdef}")
|
|
241
|
+
else:
|
|
242
|
+
lines.append(" (no tools discovered)")
|
|
243
|
+
|
|
244
|
+
lines.append("")
|
|
245
|
+
return "\n".join(lines)
|
|
246
|
+
|
|
138
247
|
def _format_tool_descriptions(self) -> str:
|
|
139
248
|
"""Format tool definitions for inclusion in system prompts.
|
|
140
249
|
|
|
@@ -1304,8 +1413,10 @@ class Agent:
|
|
|
1304
1413
|
)
|
|
1305
1414
|
|
|
1306
1415
|
def cleanup(self) -> None:
|
|
1307
|
-
"""Clean up sandbox resources."""
|
|
1416
|
+
"""Clean up sandbox and MCP resources."""
|
|
1308
1417
|
self.sandbox.cleanup()
|
|
1418
|
+
if self._mcp_client is not None:
|
|
1419
|
+
self._mcp_client.close()
|
|
1309
1420
|
|
|
1310
1421
|
def __enter__(self) -> "Agent":
|
|
1311
1422
|
"""Enter context manager."""
|