- Pin every basic-tier entry to an exact published version (npm @x.y.z,
uvx @x.y.z, docker :tag). Unpinned npx -y <pkg> means a package
compromised AFTER we ship auto-upgrades into every user; a pin bounds
supply-chain compromise to versions we actually reviewed.
- Drop firecrawl from the seed (19 entries). npm publish rights are held
solely by hello_sideguide/sideguide.dev, which has no visible
relationship to firecrawl.dev, while the package is presented as
official. Publisher identity we cannot tie to the vendor is exactly
what this catalog must not execute on a user's machine. Retained in the
research pool pending confirmation.
- postgres: ship --access-mode=restricted, not unrestricted. A curated
catalog must not default to handing an LLM write access to your DB.
- Add last_release (ISO date, from the live registry) so the UI can show
freshness; postgres-mcp and mcp-obsidian are both ~14mo stale.
Phase 1 of the MCP server catalog: pure, GUI-free core functions plus the
seed data/catalog.json (20 servers). No GUI wiring in this PR -- bcc.py is
untouched; a follow-up PR adds the picker dialog.
- load_catalog(): strict json.loads ONLY. The lenient repair pipeline
(repair_json_text / parse_pasted_json*) is never used on catalog bytes,
by design and by comment, so a signature always authenticates exactly
what gets parsed.
- validate_catalog(): rejects the whole file (not per-entry) on: bad
schema/version types, missing tier-appropriate fields (basic needs
config.command+args, link-only needs docs_url and no config), a
command allowlist (npx/uvx/docker/node/python/python3 only), -e/--eval/-c
denial for node/python, --privileged and root/$HOME volume-mount denial
for docker, non-empty env_required values (hard rejection -- secrets
never ship in the catalog), secret-looking args (reuses
_TOKEN_PREFIXES/_is_secret_value rather than reimplementing), non-https
URL fields, and non-ASCII code points in id/command/args (homoglyph
defence).
- verify_catalog_signature(): Ed25519 via the cryptography package,
domain-separated message (the literal prefix "bcc-catalog-v1|" + raw
bytes), accepts a match against any key in CATALOG_PUBKEYS
(rotation-ready), never raises.
- resolve_catalog(): picks the highest version among bundled/cached/remote
candidates that EACH independently pass verify + validate -- the bundled
catalog gets no implicit trust, closing the hole where an unsigned
payload merged to main would win on being local. Anti-rollback (never
regress below the best verified candidate already in hand) and
anti-freeze (reject a jump of more than 1000 versions) built in.
- catalog_entry_to_paste_json() / config_has_unfilled_placeholders(): small
pure helpers the future GUI dialog will use to feed a catalog pick into
the existing paste-import path and to gate Save on unfilled placeholder
tokens.
data/catalog.json: the provided 20-server seed, with a signed_at field
added at the top level (lives inside the signed payload once real signing
lands in #62). Wired into bcc.spec's PyInstaller datas so it bundles into
the frozen app.
Security requirements from the issue, and where they landed:
- Catalog bytes never touch the lenient JSON repair path -- enforced by
load_catalog()'s strict json.loads and a comment warning against wiring
it in later.
- env_required values are a hard rejection when non-empty, not a warning.
- Secret-looking args are rejected at validation time, reusing the
existing secret-detection helpers instead of duplicating them.
- Non-ASCII id/command/args rejected (typosquat/homoglyph defence).
- URL fields restricted to https://.
- Ed25519 signature verification is domain-separated and never raises.
- The bundled catalog is verified at runtime exactly like remote/cached --
no implicit trust for being local.
- Anti-rollback and anti-freeze bounds on resolve_catalog's version
comparison.
Tests: 42 new tests added to tests/test_core.py (full suite: 239 passed,
1 pre-existing unrelated skip). ruff check and ruff format --check both
clean.
Sets live in the config under _bccServerSets (bcc-owned, ignored by
Claude, travels with the file). Apply enables exactly the set's members
and parks the rest; vanished members are reported, not fatal. GUI row:
set combo + Apply + Save set… + delete.
Closes#52
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First release actually shipping the v1.2.0 feature set (the v1.2.0
tag's release run was cancelled and produced no assets) plus the
audit fixes #32-#40 and the Windows process-tree kill (#13).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Popen.kill() only terminated the direct child, so runner-style commands
(npx -> node -> server) leaked the real server process on every Windows
spawn test. taskkill /PID <pid> /T /F walks the descendant tree. Also
sets CREATE_NO_WINDOW on the spawned test process so the windowed exe
doesn't flash a console per test.
Closes#13
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
python3 is not a command on a stock Windows install; caught by the new
windows-latest CI job. Probe 'python' there and accept warn (found on
augmented PATH) as proof of resolution.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The status bar is rewritten on every action, so the appended MSIX
warning vanished on first interaction. A dedicated warn banner under
the top bar stays visible.
Closes#35
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The self-hosted Windows runner's PowerShell execution policy rejects
setup-python's install script, so Windows mirrors release.yml: py -3.12
+ venv (the version the release binaries ship with). Linux keeps the
full 3.10/3.12/3.13 setup-python matrix.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Linux (and any non-desktop platform): refuse instead of 'pkill claude',
which substring-matched running Claude Code CLI sessions and relaunched
the CLI, not a desktop app. New restart_supported() gates the button.
- macOS: wait (<=5s) for the old instance to exit before 'open -a Claude'
so the relaunch can't re-activate the dying process. Runs off the UI
thread via a RestartWorker.
- Windows: verify the Start-menu shortcut exists BEFORE taskkill, so an
MSIX/Store install is never killed without a relaunch path.
Closes#33
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Closing the About dialog mid-check GC'd the dialog and the running
QThread with it -> 'QThread: Destroyed while thread is still running'.
A class-level keepalive set now holds each worker until finished;
stale results to a destroyed receiver are dropped by Qt.
Closes#32
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All network calls are monkeypatched (urllib.request.urlopen) — no live
network in tests. Covers older/newer/equal comparisons, v-prefix,
differing-length tuples, malformed input on both sides, and
fetch_latest_release success/timeout/network-failure/malformed-response
paths.
- Add a real QMenuBar (Help -> About...).
- AboutDialog: app icon, name, version (core.__version__), and links to the
repo/issues/license opened via QDesktopServices.openUrl (system browser,
never in-app navigation). macOS unsigned-app note.
- "Check for updates" button + UpdateCheckWorker (QThread) runs
core.fetch_latest_release() off the UI thread; shows "up to date" or
"vX.Y.Z available" with a button to open the releases page. No binary
download, ever.
- Optional quiet startup auto-check, throttled to once/day via a QSettings
timestamp, off-thread, silent on failure, toggle lives in the About dialog.
Mocks subprocess.run/Popen and patches sys.platform per-OS branch (darwin,
win32, linux) -- never actually kills or launches anything. Covers: correct
command sequence per platform, pkill/taskkill exiting non-zero (nothing to
kill) is NOT treated as failure, a failed relaunch IS reported as failure,
and profile_targets_claude_desktop() correctly distinguishes Claude Desktop
configs from Claude Code / legacy settings.json profiles.
Includes the required regression case: rewrite a file with different
content, force the original mtime back via os.utime, and assert the
fingerprint still differs (because size changed).
MainWindow._loaded_mtime -> _loaded_stat (core.ConfigStat), populated
via core.config_fingerprint() at load/save time. The save-path stale
check now compares the full fingerprint (mtime AND size) instead of
bare mtime equality.
After a successful save, shows a 'Restart Claude Desktop' button next to
the status line. Only shown when the saved profile targets Claude Desktop
(core.profile_targets_claude_desktop) -- never for Claude Code, which has
no GUI process to bounce. Clicking it calls core.restart_claude_desktop()
and reports success/failure. The button hides again on further edits or
when switching/reloading profiles.
Two prior attempts to fix this line via a literal or -escaped
non-breaking space both silently reverted back to a no-op regular-space
replace during transcription. Using chr(0xA0) instead removes any
non-ASCII or backslash-escape character from the source line entirely.
Platform-abstracted restart: macOS uses pkill -x Claude + open -a Claude,
Windows uses taskkill + relaunch via the Start-menu shortcut, Linux uses
pkill claude + a detached Popen relaunch. Not finding a running process is
not an error -- only a failed relaunch is reported as failure.
Also adds profile_targets_claude_desktop() to distinguish Claude Desktop
profiles (claude_desktop_config.json) from Claude Code profiles, used to
gate the restart button in the GUI.
- Add `__version__ = "1.1.0"` as the single source of truth (matches pyproject.toml).
- Add parse_version()/is_newer_version() for numeric (never lexical) version
comparison, handling v-prefix, pre-release suffixes, and malformed input.
- Add fetch_latest_release(): reads the public Gitea releases API
(anonymous, no token) and returns {version, url} or None on any failure.
Never downloads or touches a binary — metadata only.
The previous commit accidentally normalized the non-breaking space
(U+00A0) in _normalize_unicode's replace() call to a regular space
during a copy/paste, turning that replace() into a no-op. Use an
explicit escape instead of the literal character so it can't
be silently corrupted again.
Bare mtime equality can miss a concurrent external write that lands
within the filesystem's mtime resolution (same-second writes), or
where the writer restores the original mtime. Add config_fingerprint()
returning a (mtime, size) ConfigStat pair; the stale-file check in
bcc.py now compares both fields instead of mtime alone. config_mtime()
is kept as-is (still used/tested independently).
The crashed/exited/stderr spawn tests used 0.3-0.5s timeouts. On a slow CI
runner, interpreter startup can exceed that, so the process is still starting
when the timeout fires, gets killed, and is misclassified "ok" (still running)
instead of "crashed"/"exited". Bump those three to 2.0s. The two "ok" paths
(sleep-60, stdin-block) stay at 0.3s since timing out there means success.
Adds `args_secret_warning(data)` to bcc_core — returns a warning string
when any arg positional value looks like a raw credential (token prefix,
value following a secret-named flag, or URL with embedded user:pass like
postgres://user:pass@host). `--flag=value` inline forms are intentionally
skipped (the flag name already labels the value).
Adds `secret_warn` QLabel in ServerEditor's stdio page; shown/hidden by
`_check_args()` on every field change, and cleared on deselect or
stdio→remote type switch. Non-blocking — save path is not touched.
Closes#1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Records the file mtime at load time; before write_config() fires, re-checks
it. If it changed (e.g. `claude mcp add`, a second BCC window, or Claude
itself writing ~/.claude.json), StaleDialog prompts with the changed top-level
key names and a masked server-section diff. "Merge & save" applies the user's
in-memory server edits on top of the current on-disk file (preserving external
non-server changes); "Overwrite anyway" proceeds as before.
- bcc_core: config_mtime(), external_change_summary(), _server_sections()
helper extracted from backup_diff for reuse
- bcc.py: StaleDialog, MainWindow._loaded_mtime tracked through load/save
- tests: 7 new tests (config_mtime, external_change_summary variants, AC merge test)
Closes#4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The diff preview in RestoreDialog was serializing the full config dict,
exposing secret env values and token args in cleartext on a pasteable surface.
- Add _redact_server_data / _redact_servers_block helpers that apply
redact_args to args and mask env values for is_secret_key() keys
- Rewrite backup_diff to compare only {mcpServers, _disabledMcpServers}
sections (sanitized), not the whole file — also avoids double-serializing
multi-MB ~/.claude.json for a servers-only diff
- Add clarifying comment in _restore_from_backup about why full_config
is the right base after confirm-discard
- Add test: backup_diff with secret args/env → MASK in output, raw values absent
- Add test: restore_backup restores _disabledMcpServers correctly
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add list_backups / backup_label / backup_diff / restore_backup to bcc_core,
RestoreDialog to bcc.py, and a "Restore…" button in the profile top bar.
Restore is selective: only mcpServers and _disabledMcpServers are replaced;
all other keys in the config (history, project state) are preserved verbatim.
Goes through write_config() so a pre-restore backup is always created first.
Closes#3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two bugs caught in supervisor review:
1. Stderr was silently dropped when the Details panel was closed during a
crash. _on_spawn_done appended to diag_text only when the panel was
already open, and refresh_dependency() clobbered that text on the next
field change anyway.
Fix: stash the result in self._last_spawn; _full_diag_text() appends
the stderr section whenever diag text is generated; _on_spawn_done
auto-opens the panel on non-ok outcomes (same pattern as the existing
auto_open for missing commands).
2. _drain stopped reading once _STDERR_CAP (4 KB) was reached. A process
that writes more than 4 KB then blocked on a full pipe buffer, never
exited, and was misclassified as "ok" instead of "crashed".
Fix: drain to EOF unconditionally; keep only the first _STDERR_CAP bytes.
Regression test: 64 KB stderr + exit(3) → outcome "crashed".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add spawn_test() to bcc_core — spawns a stdio server for up to 3 s,
captures stderr, and reports ok/exited/crashed/not_found. Key design
decisions driven by real MCP server behaviour:
- stdin=PIPE (never written): servers block on JSON-RPC input and stay
alive, so "still running after timeout" reliably signals a healthy
start. stdin=DEVNULL would send EOF, causing well-behaved servers to
exit 0 and be misclassified as "exited".
- Command resolved via shutil.which(augmented_path()) before Popen so
subprocess PATH resolution is unambiguous across platforms.
- start_new_session=True on POSIX + os.killpg on timeout: kills the
whole process group, not just the launcher (npx, uvx), which would
otherwise orphan the actual node/python grandchild process.
- stdout=DEVNULL: draining a PIPE we don't read would deadlock at ~64 KB.
- stderr drained in a daemon thread, capped at 4 KB.
GUI: SpawnTester(QThread) wraps spawn_test; "Test launch" button in
ServerEditor dep row (stdio only, visible when command resolves ok/warn).
Result colours match the existing dep-status palette (green/amber/red).
Stderr appended to the diagnostics panel if it is open.
7 new unit tests cover all outcomes and the stdin-open regression guard.
Ran python bcc.py locally: button appears for stdio servers whose command
resolves, is hidden for remote servers and missing-command servers.
Closes#2
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Env/header values whose key looks secret (TOKEN, API_KEY, PASSWORD,
AUTH, ...) render as •••••••• via a display-only delegate; a
'Show secrets' toggle reveals them. Underlying data, editing, and
save are untouched.
- The Add dialog switches the value field to password echo when the
key name looks secret.
- Copy-diagnostics now redacts secrets from args (--token <v>,
--api-key=<v>, and well-known token prefixes like ghp_/sk-/xoxb-),
since those reports get pasted into public bug reports. Env and
header values were already omitted from diagnostics.
Claude Code stores user-scope MCP servers in ~/.claude.json (what
'claude mcp add' writes); ~/.claude/settings.json is for permissions
and hooks and rejects an mcpServers key with a schema error, so BCC
was reading (and writing) servers where Claude Code never looks.
If servers are found parked in settings.json, that file is still
listed as 'Claude Code (legacy settings.json)' so they can be copied
into the real config via Copy to. Docs updated; verified against
docs.claude.com/en/docs/claude-code/settings.
'-apple-system' is a web convention, not a real font family — Qt scans
every installed font trying to resolve it (the 'Populating font family
aliases' warning at startup). Qt already defaults to the native system
UI font on each platform, so don't name UI fonts at all. The diag
panel's 'SF Mono' (not system-installed on macOS) becomes Menlo.
- Label now explains the model with an example: a flag and its value go
on separate lines. Placeholder shows the common uv pattern.
- split_suspicious_args() flags lines that contain whitespace plus a
dash-prefixed token ('--directory /path') — legit single args with
spaces ('My Documents') are never touched. Quotes are respected.
- The editor shows a warning under the args box with a 'Fix: split onto
separate lines' button; the fix goes through the undo stack.
If a config file on disk fails strict JSON parsing, BCC now runs it
through the same repair pipeline as pasted snippets and shows a dialog
listing the parse error, each fix it would apply, and a preview of the
resulting file. The user chooses: Repair & load (marks the profile
dirty; the file is only rewritten on Save, after the broken original
is backed up) or Cancel. Unsalvageable files keep the old error path.
repair_config_file() in bcc_core never writes to disk itself.
The one-arg-per-line model read as odd text wrapping — a path on its
own line looked like a wrapped continuation of the previous argument.
A line-number gutter makes each argument visibly its own item.
ArgsEdit also owns the NoWrap setting now.
- Cell editors in env/header tables got the global QLineEdit style
(6px vertical padding, 7px radius) crammed into a short row, clipping
the text to an unreadable sliver. Give table editors a compact flat
style and bump default row height to 34px.
- Editor select-all now uses dim-orange/white selection colors inside
cells for contrast against the dark field.
- Arguments box no longer soft-wraps: one arg per line means a wrapped
path looks like two args. Long lines scroll horizontally instead.
Editing a table cell select-alls its text; without an explicit
selection-color the highlighted text rendered near-invisible against
the accent-orange selection background. Applies to all line edits,
text areas, and combo boxes.
- Horizontal splitter between the server list and the editor panel
- Vertical splitter between the Active and Disabled tables (drops the
fixed 170px cap on the disabled list)
- Editor fields (args, env/header tables) now grow with the window
instead of being pinned to fixed max heights
- Splitter positions and window geometry persist across launches via
QSettings; handles highlight on hover/drag
Pasted config snippets no longer have to be valid JSON. repair_json_text()
auto-fixes markdown fences, surrounding prose, // /* */ # comments,
trailing and missing commas, smart quotes, single quotes, unquoted keys,
Python/JS literals, and unclosed braces. parse_pasted_json_verbose()
reports every repair applied; the paste dialog now parses as you type
and previews exactly what will be added and what was fixed.
Also includes ruff lint fixes and formatting across bcc.py/bcc_core.py.
Pillow's ICO saver stores all sizes as PNG-compressed ("Vista icon"
format). PyInstaller's Windows resource-updater cannot embed
PNG-compressed entries for small sizes and silently falls back to its
default gear icon.
Fix build_icons.py to write the ICO manually: BMP DIB for sizes ≤ 128 px,
PNG only for the 256 px entry (where Windows Explorer expects PNG).
Regenerate icons/app.ico with the new code.
Also set upx=False in bcc.spec for the Windows/Linux EXE; UPX is another
known cause of icon resources being stripped from PE files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 22:42:02 -04:00
17 changed files with 6708 additions and 302 deletions
Requires Python 3.10+. Runtime dependency: `PySide6>=6.6`. Tooling config (ruff, pytest, project metadata) lives in `pyproject.toml`. CI (`.github/workflows/ci.yml`) runs lint + tests on every push/PR; releases build on tag push (`release.yml`).
## Architecture
@@ -27,9 +31,9 @@ The codebase is split into two layers:
**`bcc_core.py`** — All logic with no GUI imports. Contains:
-`Profile` / `ServerEntry` dataclasses (the data model)
-`discover_profiles()` — scans the platform's app-support directory for `Claude*` folders (Claude Desktop) **and** always adds `~/.claude/settings.json` (Claude Code, cross-platform)
-`discover_profiles()` — scans the platform's app-support directory for `Claude*` folders (Claude Desktop) **and** always adds `~/.claude.json` (Claude Code user scope — what `claude mcp add` writes). `~/.claude/settings.json` is NOT a server config (it rejects `mcpServers` with a schema error) and is only surfaced, labelled legacy, if servers are found parked in it. Project-scope `.mcp.json` files can be opened via Add config…
-`load_config` / `extract_servers` / `apply_servers` / `write_config` — the read/write pipeline; writes are atomic with rotating timestamped backups in `.bcc_backups/`
-`parse_pasted_json()` — accepts three JSON shapes (full config, inner map, or bare server object)
-`parse_pasted_json()` / `parse_pasted_json_verbose()` — accepts three JSON shapes (full config, inner map, or bare server object). Input does not have to be valid JSON: `repair_json_text()` auto-fixes markdown fences, surrounding prose, `//``/* */``#` comments, trailing/missing commas, smart quotes, single quotes, unquoted keys, Python/JS literals, and unclosed braces. The verbose variant also returns human-readable notes describing every repair applied (shown live in the paste dialog)
-`check_dependency()` / `diagnostics_text()` / `pin_command_path()` — PATH resolution logic; distinguishes "found on normal PATH" (ok) vs "found only on augmented PATH" (warn) vs "not found" (missing). Uses an `lru_cache`-memoized `augmented_path()` that extends the inherited PATH with common runtime locations (nvm, homebrew, cargo, volta, etc.)
-`test_remote()` — synchronous HTTP reachability check, intended to run off the UI thread
| 5 | 8 | Duplicate-name conflict on paste/import | P1 | VERIFY FIRST: check `paste_json`/`dropEvent` in bcc.py for silent overwrite; file findings on the issue before coding |
| 6 | 5 | Cross-client support (Cursor/Windsurf/VS Code) | P1 | VS Code uses `servers` not `mcpServers` — needs a read/write adapter, not just paths |
| 7 | 6 | In-app MCP log viewer | P1 | Platform log paths in the doc |
| 8 | 7 | Windows MSIX path detection | P1 | Can't test locally on macOS — unit-test the detection logic, note that in the PR |
| 9 | 9 | Restart Claude Desktop button | P1 | Platform-specific process handling; scope to Desktop only |
| 10 | 10 | Server catalog / one-click add | P2 | Design pass first — post a proposal as an issue comment before building |
## Workflow rules
- **Branch per issue** off `main`: `feat/<issue-number>-short-slug` (or
`fix/`). One issue per PR. Reference the issue in the PR description
"notes":"Part of the official modelcontextprotocol/servers reference monorepo (star count is for the whole repo). Clients that support MCP 'roots' can also grant directories dynamically instead of via args.",
"last_release":"2026-07-10"
},
{
"id":"fetch",
"display":"Fetch",
"description":"Fetches a URL and converts the page to clean markdown so Claude can read web content.",
"notes":"Can access local/internal IPs, so treat as a mild security risk on untrusted networks. Add '--ignore-robots-txt' or '--user-agent=...' as extra args if needed.",
"last_release":"2026-07-10"
},
{
"id":"memory",
"display":"Memory",
"description":"Gives Claude a persistent knowledge-graph memory that survives across conversations.",
"notes":"The old '@modelcontextprotocol/server-github' npm package is discontinued (deprecated April 2025). GitHub now ships a Docker-based local server (requires Docker installed/running) plus a hosted remote server at https://api.githubcopilot.com/mcp/ that supports OAuth or PAT auth without Docker."
},
{
"id":"playwright",
"display":"Playwright",
"description":"Lets Claude drive a real browser (click, type, navigate, screenshot) using Playwright's accessibility-tree snapshots.",
"notes":"Maintained by the Playwright team at Microsoft. Add '--isolated' for a throwaway profile, or '--browser firefox|webkit|msedge' to change engine. A persistent browser profile is used by default so logins carry over between sessions.",
"last_release":"2026-07-09"
},
{
"id":"chrome-devtools",
"display":"Chrome DevTools",
"description":"Lets Claude control Chrome and use real DevTools features: performance traces, network inspection, console logs, screenshots.",
"notes":"Maintained by the Google Chrome DevTools team; only officially supports Google Chrome / Chrome for Testing. Exposes the browser's content to the MCP client, so avoid sensitive sites while connected. Add '--slim --headless' for a minimal 3-tool basic-automation mode.",
"last_release":"2026-07-03"
},
{
"id":"postgres",
"display":"Postgres MCP Pro",
"description":"Lets Claude query, inspect schema, and analyze/tune performance of a PostgreSQL database.",
"notes":"The official reference Postgres server was archived by the MCP team; this community server (Crystal DBA) is the most capable/most-referenced replacement, adding index tuning and EXPLAIN-plan analysis. Use --access-mode=restricted for read-only/production use. Docker image also available (crystaldba/postgres-mcp). Catalog ships --access-mode=restricted (read-only); switch to unrestricted yourself if you want writes.",
"last_release":"2025-05-16"
},
{
"id":"n8n",
"display":"n8n",
"description":"Build, validate, and deploy n8n workflows with full node documentation for the AI.",
"notes":"Set MCP_MODE=stdio (required for Claude Desktop, prevents debug logs from breaking the protocol). N8N_API_URL/N8N_API_KEY are optional — without them you still get full node documentation, validation, and template search; with them you get live workflow create/update/execute against your own n8n instance. A hosted free-tier alternative exists at dashboard.n8n-mcp.com.",
"last_release":"2026-07-09"
},
{
"id":"notion",
"display":"Notion",
"description":"Read, search, and edit Notion pages, databases, and comments from your AI assistant.",
"notes":"Notion is prioritizing its hosted remote MCP (OAuth, https://mcp.notion.com/mcp) and may eventually sunset this local package, but the stdio server still works today and is the simplest way to get a static config with an internal-integration token.",
"last_release":"2026-06-22"
},
{
"id":"obsidian",
"display":"Obsidian",
"description":"Read, search, and edit notes in your Obsidian vault.",
"notes":"Requires the Obsidian Local REST API community plugin installed and enabled in Obsidian; copy the API key from the plugin settings. OBSIDIAN_HOST defaults to 127.0.0.1 and OBSIDIAN_PORT to 27124 if omitted.",
"last_release":"2025-04-01"
},
{
"id":"brave-search",
"display":"Brave Search",
"description":"Search the web, news, images, and videos using Brave's independent search index.",
"notes":"Official Brave server; replaced the old archived modelcontextprotocol/servers brave-search entry (now in modelcontextprotocol/servers-archived). Get an API key from the Brave Search API dashboard.",
"last_release":"2026-06-15"
},
{
"id":"tavily",
"display":"Tavily",
"description":"AI-optimized web search, extract, map, and crawl API built for LLM agents.",
"notes":"Tavily also offers a hosted remote MCP endpoint (mcp.tavily.com) with OAuth as an alternative to running the local npx server.",
"last_release":"2026-07-10"
},
{
"id":"home-assistant",
"display":"Home Assistant",
"description":"Control smart home devices, query states, and troubleshoot automations in Home Assistant.",
"category":"smart-home",
"homepage":"https://github.com/voska/hass-mcp",
"stars":308,
"official":false,
"setup":"basic",
"config":{
"command":"docker",
"args":[
"run",
"-i",
"--rm",
"-e",
"HA_URL",
"-e",
"HA_TOKEN",
"voska/hass-mcp:0.5.0"
]
},
"placeholders":{},
"env_required":{
"HA_URL":"",
"HA_TOKEN":""
},
"docs_url":"https://github.com/voska/hass-mcp",
"notes":"HA_URL example: http://homeassistant.local:8123 (use http://host.docker.internal:8123 if HA runs in Docker on the same machine). HA_TOKEN is a Home Assistant long-lived access token from your profile page. A more actively developed alternative is the community 'HA-MCP' integration (homeassistant-ai/ha-mcp, ~3.9k stars), but it installs inside Home Assistant itself via HACS rather than as an external stdio process, so it doesn't fit this catalog's launch-line format."
},
{
"id":"kubernetes",
"display":"Kubernetes",
"description":"Lets Claude inspect and manage Kubernetes/OpenShift resources — pods, deployments, logs, Helm releases — using your local kubeconfig.",
"notes":"Not an official Kubernetes SIG project, but a Go-native (no kubectl dependency) implementation maintained under the 'containers' GitHub org (Podman/Red Hat-adjacent) that's widely regarded as the most capable K8s MCP server, supporting Kubernetes and OpenShift. Uses your existing ~/.kube/config automatically; add --read-only to prevent writes.",
"last_release":"2026-07-10"
},
{
"id":"aws-api-mcp-server",
"display":"AWS API MCP Server (AWS Labs)",
"description":"Lets your AI assistant run AWS CLI commands to inspect and manage AWS resources across virtually every AWS service.",
"notes":"AWS credentials are NOT set in this MCP config — configure them beforehand via `aws configure` (or set AWS_API_MCP_PROFILE_NAME to pick a named profile) so boto3's standard credential chain can find them. Optional env vars: AWS_REGION (default us-east-1), READ_OPERATIONS_ONLY=true to block all mutating AWS calls. AWS notes this server is being superseded by a newer unified AWS MCP server referenced in their agent-toolkit docs.",
"last_release":"2026-06-25"
},
{
"id":"grafana",
"display":"Grafana",
"description":"Query dashboards, datasources, alerts and incidents in Grafana from your AI assistant.",
"notes":"Slack's own MCP server went GA Feb 17, 2026 (streamable HTTP at https://mcp.slack.com/mcp, OAuth). No stdio one-liner is published because it's a hosted, permissioned connector. A well-known community alternative, korotovsky/slack-mcp-server (~1.6k GitHub stars, MIT, not an official Slack product), supports stdio/SSE/HTTP with bot or browser-session tokens and no app-install requirement if a stdio option is preferred."
c.parse_pasted_json_verbose("this is just prose with no json at all")
deftest_junk_object_still_rejected():
withpytest.raises(ValueError):
c.parse_pasted_json_verbose('{"junk": 5}')
deftest_empty_raises():
withpytest.raises(ValueError):
c.parse_pasted_json_verbose("")
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.