Skip to content

feat(mcp): JSON-paste path in Add MCP server dialog#285

Merged
mbektas merged 1 commit into
plmbr:mainfrom
pjdoland:feat/278-mcp-json-paste
May 17, 2026
Merged

feat(mcp): JSON-paste path in Add MCP server dialog#285
mbektas merged 1 commit into
plmbr:mainfrom
pjdoland:feat/278-mcp-json-paste

Conversation

@pjdoland
Copy link
Copy Markdown
Collaborator

@pjdoland pjdoland commented May 17, 2026

Summary

@mbektas asked for a paste-friendly alternative to the field-by-field form when adding an MCP server. The dialog now has Scope at the top, a Form/JSON tab control below it, and a JSON textarea that parses common copy-paste shapes into the same IClaudeMCPAddInput shape the form already emits.

Solution

Dialog layout. Scope is the first field. Below it, a WAI-ARIA tablist toggles between Form and JSON. The Form panel is the existing inputs; the JSON panel is a single textarea with a representative placeholder.

Paste parser (src/components/claude-mcp-paste.ts). Accepts:

  • bare "server-key": { ... } (the shape that lands when you copy a single entry out of a .mcp.json)
  • wrapped { "server-key": { ... } }
  • full { "mcpServers": { "server-key": { ... } } }
  • stray leading or trailing comma (copy-from-larger-config artefact)

Multi-server payloads, empty objects, non-object server values, and unparseable input are rejected with guiding error messages. configToInput translates a parsed entry into IClaudeMCPAddInput, inferring transport from the shape, but honors an explicit transport field when present and rejects mismatches (stdio with no command; sse/http with no url) rather than silently downgrading. Six-agent review flagged the silent-downgrade as a real footgun.

Helpers moved to a sibling module (claude-mcp-paste.ts) so the unit tests don't drag the React tree in to exercise pure data functions.

A11y. The Form/JSON control uses role="tablist" (not radiogroup, which doesn't fit a UI that swaps the rendered subtree). Arrow-Left / Arrow-Right move between tabs with roving tabindex. A useEffect focuses the right input when the user actively switches modes but skips the first run so it doesn't steal focus from the Scope select on initial mount.

Testing

pytest tests/ --ignore=tests/test_claude_client.py -q (709 passed). jlpm tsc --noEmit, jlpm lint:check, jlpm jest all green; new jest suite claude-mcp-paste.test.ts is 23 cases covering each paste shape, the empty / multi-server / non-object / array / primitive rejections, the explicit-transport-mismatch rejections (the footgun fix), and IClaudeMCPAddInput mapping (stringification of env values, http inference, sse override, unrecognized transport falls back to shape).

Manual: opened the dialog locally, confirmed Scope sits at the top, tab semantics work with keyboard, paste of the issue's example JSON populates correctly.

Risks / follow-ups

  • The parser doesn't strip // comments. A future enhancement could pre-strip JSON-with-comments via a small regex pass, but the standard error message ("Invalid JSON: ...") is informative enough for now.
  • The Form panel state is preserved when the user toggles to JSON and back. That's intentional: a user who mis-types JSON and wants to switch to Form to debug shouldn't lose their work.
  • Skipped: a React Testing Library test for the dialog's mode-toggle wiring. The 23 unit tests pin the data layer; the UI wiring is a one-liner that I verified manually. Worth adding if regressions appear.

Closes #278

Mehmet asked for a paste-friendly alternative to filling in the form
field-by-field. The dialog now has:

- Scope moved to the top of the dialog (was the second field).
- A "Form" / "JSON" tabbed control below Scope.
- JSON mode renders a single textarea; submit parses the paste into the
  existing IClaudeMCPAddInput shape and forwards to the same add path
  the form uses.

The paste parser is lenient about copy-paste artefacts:
- bare `"server-key": { ... }` (the common copy-from-docs shape)
- wrapped `{ "server-key": { ... } }`
- full `{ "mcpServers": { "server-key": { ... } } }`
- stray leading or trailing comma
Multi-server payloads, empty objects, and non-object server values are
rejected with guiding error messages.

`configToInput` honors an explicit `transport` field when it matches
the rest of the config and rejects mismatches (stdio without command,
sse/http without url) rather than silently downgrading -- six-agent
review flagged the silent-downgrade as a real footgun.

Pure helpers (`parseMcpJsonEntry`, `configToInput`, `parseKVLines`)
moved to `src/components/claude-mcp-paste.ts` so the unit tests don't
need to drag in the React tree to exercise them.

The Form / JSON toggle is a proper WAI-ARIA tablist with arrow-key
navigation, roving `tabindex`, and focus management on mode change.
A `useEffect` skips the first run so the initial mount doesn't steal
focus from the Scope select.

Tests: 23 jest cases in `tests/ts/claude-mcp-paste.test.ts` covering
the parser branches, the IClaudeMCPAddInput mapping, and the explicit-
transport mismatch rejections that came out of the agent review.

Closes plmbr#278
@pjdoland pjdoland added the enhancement New feature or request label May 17, 2026
Copy link
Copy Markdown
Collaborator

@mbektas mbektas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@mbektas mbektas added this to the 5.0.x milestone May 17, 2026
@mbektas mbektas merged commit 6382cce into plmbr:main May 17, 2026
4 of 6 checks passed
pjdoland added a commit to pjdoland/notebook-intelligence that referenced this pull request May 22, 2026
Promotes the [Unreleased] CHANGELOG snapshot to [5.0.0] - 2026-05-22
and expands it to cover everything merged into upstream/main after
PR plmbr#287's docs refresh. Bumps package.json to 5.0.0.

CHANGELOG additions cover the post-plmbr#287 surface:

- Settings tabs: plugin marketplace picker (plmbr#284), plugin marketplace
  details + Update button (plmbr#303), per-workspace MCP disable (plmbr#286),
  JSON-paste path in Add MCP server (plmbr#285).
- Launchers: hide-with-policy (plmbr#288), brand icons for Codex / opencode
  (plmbr#325, plmbr#333), per-launch directory picker (plmbr#332).
- Chat sidebar and agentic UX: workspace @-mention in Claude mode
  (plmbr#327), reload-open-files-on-disk (plmbr#330), steered system prompt
  away from over-eager notebook creation (plmbr#336).
- Skills: multi-manifest support (plmbr#321), tracks-upstream for user-
  imported skills (plmbr#322), HTTP kill switch for the reconciler (plmbr#291).
- Accessibility: full sub-section covering plmbr#305-plmbr#320.
- Security: shell-tool sandbox (plmbr#290), Claude UI-bridge sandbox (plmbr#323),
  0o600 on encrypted token (plmbr#293), env-secret scrubbing (plmbr#295), MCP
  config shape validation (plmbr#299), XSS allowlist (plmbr#296), Copilot WS
  auth + origin (plmbr#301), GHE host detection (plmbr#292), fastmcp -> mcp SDK
  swap (plmbr#324).
- Fixed: session listing unification (plmbr#310), session preview unwrap
  (plmbr#331), down-area runtime throw (plmbr#330 follow-up), WS message-handler
  leak (plmbr#294).
- Removed: fastmcp dependency, history.jsonl session gate.

Adds a Migration note covering the five behavior changes operators
should review before upgrading from 4.x: fastmcp swap, path
sandboxes, history.jsonl gate removal, workspace @-mention pointer
shape, and the Copilot WebSocket auth/origin tightening.

Two reviewer rounds (six personas each) applied:
- Round 1 caught security overclaims (plmbr#293, plmbr#299, plmbr#323), the
  plmbr#284/plmbr#303 mis-attribution, missing migration note, 3 em dashes,
  and the stale `fastmcp==2.x.*` recommendation in the admin guide.
- Round 2 caught the missing plmbr#301 migration bullet, missing version-
  matrix 5.0.x row, missing README TOC entry, and a couple of style
  nits (sub-heading overpromise, orphan bullet).

Skipped (deferred to future PRs):
- README first-run tour mention.
- Admin guide HTTP kill-switch row in Failure-modes table.
- Terminal drag-drop trust-model precision update after plmbr#327.
- Cipher description nit in plmbr#293 (Fernet AES-128-CBC+HMAC, not
  AES-GCM).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow copy-pasting MCP config

2 participants