Skip to content

Commit 60f90f1

Browse files
committed
feat: add Chrome DevTools MCP server and generalize MCP setup
Add Chrome DevTools MCP (chrome-devtools-mcp) as an opt-in MCP server for browser debugging during frontend development. Generalize the MCP setup to support multiple servers from config rather than hardcoding only Serena. Key changes: - Add chrome-devtools to mcpConfig in core config - Rename setup/serena.py to setup/mcp.py, generate .mcp.json with all configured servers - Allow extra MCP servers in McpConfig schema (extra="allow") - Default enabledMcpjsonServers to ["serena"] only -- chrome-devtools is opt-in to avoid unnecessary token usage for non-frontend devs - Fix bug: bootstrap was passing ReposConfig to setup_serena_config instead of ClaudeCodeConfig - Check command validates .mcp.json contains all expected servers - Bump version to 0.6.0
1 parent f631678 commit 60f90f1

9 files changed

Lines changed: 74 additions & 31 deletions

File tree

core/claude-code-config.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"Bash(ls:*)",
1919
"Bash(cat:*)",
2020
"WebSearch",
21-
"mcp__serena__*"
21+
"mcp__serena__*",
22+
"mcp__chrome-devtools__*"
2223
]
2324
}
2425
},
@@ -41,6 +42,12 @@
4142
"--project",
4243
"${PWD}"
4344
]
45+
},
46+
"chrome-devtools": {
47+
"command": "npx",
48+
"args": [
49+
"chrome-devtools-mcp@latest"
50+
]
4451
}
4552
}
4653
}

packages/smartem-workspace/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "smartem-workspace"
7-
version = "0.5.0"
7+
version = "0.6.0"
88
description = "CLI tool to automate SmartEM multi-repo workspace setup"
99
readme = "README.md"
1010
license = "Apache-2.0"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""SmartEM workspace setup CLI tool."""
22

3-
__version__ = "0.5.0"
3+
__version__ = "0.6.0"

packages/smartem-workspace/smartem_workspace/commands/check.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def run_claude_checks(workspace_path: Path, claude_config: ClaudeCodeConfig) ->
290290
return CheckReport("claude", results)
291291

292292

293-
def run_serena_checks(workspace_path: Path) -> CheckReport:
293+
def run_serena_checks(workspace_path: Path, claude_config: ClaudeCodeConfig | None = None) -> CheckReport:
294294
results = []
295295

296296
serena_dir = workspace_path / ".serena"
@@ -305,6 +305,26 @@ def run_serena_checks(workspace_path: Path) -> CheckReport:
305305
mcp_json = workspace_path / ".mcp.json"
306306
if mcp_json.exists():
307307
results.append(check_json_valid(mcp_json, ".mcp.json"))
308+
309+
if claude_config:
310+
try:
311+
mcp_data = json.loads(mcp_json.read_text())
312+
defined_servers = set(mcp_data.get("mcpServers", {}).keys())
313+
expected_servers = set(claude_config.mcpConfig.model_dump().keys())
314+
missing = expected_servers - defined_servers
315+
if missing:
316+
results.append(
317+
CheckResult(
318+
".mcp.json servers",
319+
"warning",
320+
f"Missing servers: {', '.join(sorted(missing))}. Re-run setup to add them.",
321+
fixable=False,
322+
)
323+
)
324+
else:
325+
results.append(CheckResult(".mcp.json servers", "ok", f"{len(defined_servers)} server(s) configured"))
326+
except (json.JSONDecodeError, KeyError):
327+
pass
308328
else:
309329
results.append(CheckResult(".mcp.json", "warning", "Missing", fixable=False))
310330

@@ -357,7 +377,7 @@ def run_checks(
357377
reports.append(run_claude_checks(workspace_path, claude_config))
358378

359379
if scope in (CheckScope.ALL, CheckScope.SERENA) and workspace_path:
360-
reports.append(run_serena_checks(workspace_path))
380+
reports.append(run_serena_checks(workspace_path, claude_config))
361381

362382
if scope in (CheckScope.ALL, CheckScope.REPOS) and workspace_path and config:
363383
reports.append(run_repos_checks(workspace_path, config))

packages/smartem-workspace/smartem_workspace/config/claude-code-config.json

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,22 @@
44
"description": "Claude Code integration configuration for SmartEM workspace",
55
"claudeConfig": {
66
"skills": [
7-
{ "name": "database", "path": "claude-code/shared/skills/database" },
7+
{ "name": "database-admin", "path": "claude-code/shared/skills/database-admin" },
88
{ "name": "devops", "path": "claude-code/shared/skills/devops" },
9-
{ "name": "tech-writer", "path": "claude-code/shared/skills/tech-writer" },
9+
{ "name": "technical-writer", "path": "claude-code/shared/skills/technical-writer" },
1010
{ "name": "git", "path": "claude-code/shared/skills/git" },
11-
{ "name": "playwright", "path": "claude-code/smartem-frontend/skills/playwright" }
11+
{ "name": "github", "path": "claude-code/shared/skills/github" },
12+
{ "name": "ascii-art", "path": "claude-code/shared/skills/ascii-art" },
13+
{ "name": "playwright-skill", "path": "claude-code/smartem-frontend/skills/playwright-skill" }
1214
],
1315
"defaultPermissions": {
1416
"allow": [
1517
"Bash(git:*)",
1618
"Bash(ls:*)",
1719
"Bash(cat:*)",
1820
"WebSearch",
19-
"mcp__serena__*"
21+
"mcp__serena__*",
22+
"mcp__chrome-devtools__*"
2023
]
2124
}
2225
},
@@ -39,6 +42,12 @@
3942
"--project",
4043
"${PWD}"
4144
]
45+
},
46+
"chrome-devtools": {
47+
"command": "npx",
48+
"args": [
49+
"chrome-devtools-mcp@latest"
50+
]
4251
}
4352
}
4453
}

packages/smartem-workspace/smartem_workspace/config/schema.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,13 @@ class McpServerConfig(BaseModel):
9393

9494

9595
class McpConfig(BaseModel):
96-
"""MCP servers configuration."""
96+
"""MCP servers configuration.
97+
98+
Serena is required; additional servers (e.g. chrome-devtools) are accepted
99+
via extra="allow" so the schema doesn't need updating for each new server.
100+
"""
101+
102+
model_config = ConfigDict(extra="allow")
97103

98104
serena: McpServerConfig
99105

packages/smartem-workspace/smartem_workspace/setup/bootstrap.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
)
1414
from smartem_workspace.setup.claude import setup_claude_config
1515
from smartem_workspace.setup.repos import clone_repos
16-
from smartem_workspace.setup.serena import setup_serena_config
16+
from smartem_workspace.setup.mcp import setup_mcp_config
1717
from smartem_workspace.setup.workspace import display_next_steps, setup_workspace_structure
1818

1919
console = Console()
@@ -113,9 +113,9 @@ def bootstrap_workspace(
113113
if not skip_claude and claude_config:
114114
setup_claude_config(claude_config, workspace_path)
115115

116-
if not skip_serena:
116+
if not skip_serena and claude_config:
117117
project_name = workspace_path.name or "smartem-workspace"
118-
setup_serena_config(config, workspace_path, project_name)
118+
setup_mcp_config(claude_config, workspace_path, project_name)
119119

120120
display_next_steps(workspace_path)
121121

packages/smartem-workspace/smartem_workspace/setup/claude.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ def setup_claude_config(
7373
if not settings_path.exists():
7474
settings = {
7575
"permissions": config.claudeConfig.defaultPermissions.model_dump(),
76+
"enabledMcpjsonServers": ["serena"],
7677
}
7778
settings_path.write_text(json.dumps(settings, indent=2))
7879
console.print(f" [green]Created {settings_path.name}[/green]")

packages/smartem-workspace/smartem_workspace/setup/serena.py renamed to packages/smartem-workspace/smartem_workspace/setup/mcp.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,33 @@
1-
"""Serena MCP server configuration setup."""
1+
"""MCP server configuration setup."""
22

33
import json
44
from pathlib import Path
55

66
import yaml
77
from rich.console import Console
88

9-
from smartem_workspace.config.schema import ReposConfig
9+
from smartem_workspace.config.schema import ClaudeCodeConfig, McpServerConfig
1010

1111
console = Console()
1212

1313

14-
def setup_serena_config(
15-
config: ReposConfig,
14+
def setup_mcp_config(
15+
config: ClaudeCodeConfig,
1616
workspace_path: Path,
1717
project_name: str = "smartem-workspace",
1818
) -> bool:
1919
"""
20-
Set up Serena MCP server configuration.
20+
Set up MCP server configuration.
2121
2222
Creates:
23-
- .serena/project.yml
24-
- .mcp.json
23+
- .serena/project.yml (Serena-specific config)
24+
- .mcp.json (all MCP servers from config)
2525
2626
Returns:
2727
True if successful
2828
"""
2929
console.print()
30-
console.print("[bold]Setting up Serena MCP configuration...[/bold]")
30+
console.print("[bold]Setting up MCP configuration...[/bold]")
3131

3232
serena_dir = workspace_path / ".serena"
3333
serena_dir.mkdir(parents=True, exist_ok=True)
@@ -50,19 +50,19 @@ def setup_serena_config(
5050

5151
mcp_json_path = workspace_path / ".mcp.json"
5252
if not mcp_json_path.exists():
53-
mcp_config = {
54-
"mcpServers": {
55-
"serena": {
56-
"command": config.mcpConfig.serena.command,
57-
"args": [arg.replace("${PWD}", str(workspace_path)) for arg in config.mcpConfig.serena.args],
58-
}
53+
mcp_servers = {}
54+
for name, server_config in config.mcpConfig.model_dump().items():
55+
if not isinstance(server_config, dict) or "command" not in server_config:
56+
continue
57+
mcp_servers[name] = {
58+
"command": server_config["command"],
59+
"args": [arg.replace("${PWD}", str(workspace_path)) for arg in server_config.get("args", [])],
5960
}
60-
}
6161

62-
mcp_json_path.write_text(json.dumps(mcp_config, indent=2))
63-
console.print(f" [green]Created {mcp_json_path.name}[/green]")
62+
mcp_json_path.write_text(json.dumps({"mcpServers": mcp_servers}, indent=2))
63+
console.print(f" [green]Created {mcp_json_path.name} ({len(mcp_servers)} servers)[/green]")
6464
else:
6565
console.print(f" [dim]{mcp_json_path.name} already exists[/dim]")
6666

67-
console.print("[green]Serena configuration complete[/green]")
67+
console.print("[green]MCP configuration complete[/green]")
6868
return True

0 commit comments

Comments
 (0)