Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
8427cb9
feat(web,core): import client configs and registry server.json (#1348)
cliffhall Jun 23, 2026
da21e36
feat(web): collapse the server.json textarea into a File Contents dis…
cliffhall Jun 23, 2026
40061fc
feat(web): auto-collapse File Contents disclosure after content loads
cliffhall Jun 23, 2026
d0d8327
feat(web): flash File Contents hover highlight before auto-collapse
cliffhall Jun 23, 2026
425ca44
feat(web): rename "Import server.json" to "Import from registry config"
cliffhall Jun 23, 2026
dc3a1ca
feat(web): rename "Import config" to "Import client config"
cliffhall Jun 23, 2026
27b6c30
refactor(web,core): move import orchestration into core helpers + web…
cliffhall Jun 23, 2026
6fdc0d9
feat(web): rename "Import client config" to "Import from client config"
cliffhall Jun 23, 2026
ba7cb10
feat(web): remove the Cancel button from the registry import modal
cliffhall Jun 23, 2026
3484819
feat(web): hide validation results + name override until content is p…
cliffhall Jun 23, 2026
893dd62
feat(web): make Validation Results a collapsible disclosure
cliffhall Jun 23, 2026
5513db5
feat(web): Server Name disclosure with read-only derived name + override
cliffhall Jun 23, 2026
e008c06
feat(web): scroll to and highlight a freshly-added server
cliffhall Jun 23, 2026
eaf8e4f
fix(web): highlighted card fills its cell and its controls work on fi…
cliffhall Jun 23, 2026
e675680
fix(web,core): address review — OCI env, untrack fixture, rename vali…
cliffhall Jun 23, 2026
0648936
feat(web): highlight all servers from a batch import, not just the last
cliffhall Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ test-servers/build
/.playwright-mcp/

# Local debugging artifacts dropped at the repo root (e.g. attached to
# Claude Code conversations): screenshots and exported Inspector history
# dumps. Should never be part of the source tree.
# Claude Code conversations): screenshots, exported Inspector history dumps,
# and ad-hoc registry/client config fixtures used for manual import testing.
# Should never be part of the source tree.
/*.png
/inspector-history-*.json
/*.server.json
5 changes: 5 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ inspector/
│ ├── json/ # JSON utilities and parameter/argument conversion
│ ├── logging/ # Silent pino logger singleton
│ ├── mcp/ # InspectorClient runtime + state stores
│ │ ├── import/ # Config import strategies (#1348): client-config parsers
│ │ │ # (Claude Desktop/Cursor/Cline/VS Code), registry
│ │ │ # server.json parser, strategy registry + well-known
│ │ │ # paths, strategy-agnostic merge. Pure/isomorphic;
│ │ │ # used by the web file-upload path + /api/import-source.
│ │ ├── node/ # Node stdio transport factory
│ │ ├── remote/ # Browser HTTP/SSE transport + remote logger/fetch
│ │ │ └── node/ # Hono-based remote server backend (used by remote/ above)
Expand Down
42 changes: 42 additions & 0 deletions clients/web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions clients/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@dnd-kit/sortable": "^8.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@emotion/react": "^11.14.0",
"@gfazioli/mantine-border-animate": "^2.0.3",
"@hono/node-server": "^1.19.14",
"@mantine/core": "^8.3.17",
"@mantine/form": "^8.3.17",
Expand Down
82 changes: 68 additions & 14 deletions clients/web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ import {
type ServerConfigModalMode,
} from "./components/groups/ServerConfigModal/ServerConfigModal";
import { ServerSettingsModal } from "./components/groups/ServerSettingsModal/ServerSettingsModal";
import { ServerImportConfigModal } from "./components/groups/ServerImportConfigModal/ServerImportConfigModal";
import { ServerImportJsonModal } from "./components/groups/ServerImportJsonModal/ServerImportJsonModal";
import { ConnectionInfoModal } from "./components/groups/ConnectionInfoModal/ConnectionInfoModal";
import { OutputValidationModal } from "./components/groups/OutputValidationModal/OutputValidationModal";
import { UrlElicitationErrorModal } from "./components/groups/UrlElicitationErrorModal/UrlElicitationErrorModal";
Expand Down Expand Up @@ -489,6 +491,7 @@ function App() {
updateServerSettings,
removeServer,
reorderServers,
importSource,
} = useServers({
baseUrl:
typeof window !== "undefined"
Expand All @@ -503,6 +506,23 @@ function App() {
mode: ServerConfigModalMode;
targetId?: string;
} | null>(null);
// Import-flow modals (#1348): "Import from client config" (other-client
// config merge) and "Import from registry config" (registry single-server
// import).
const [importConfigOpen, setImportConfigOpen] = useState(false);
const [importJsonOpen, setImportJsonOpen] = useState(false);
// Ids of freshly-added servers (manual or import) — their cards draw an
// animated border (and the first scrolls into view) until clicked. A batch
// import accumulates all of its ids here; opening an add/import modal starts a
// fresh batch. (#1348)
const [highlightedServerIds, setHighlightedServerIds] = useState<string[]>(
[],
);
const clearHighlight = useCallback(
(id: string) =>
setHighlightedServerIds((ids) => ids.filter((x) => x !== id)),
[],
);
const [settingsModalTargetId, setSettingsModalTargetId] = useState<
string | undefined
>(undefined);
Expand Down Expand Up @@ -2083,14 +2103,6 @@ function App() {
);
}, [logs, activeServerId]);

// Action stubs — these UI affordances exist but require additional
// wiring (server CRUD, history pinning, app sandbox round-trip, log
// export). Tracked separately; the noop keeps the prop interface
// satisfied without lying about behavior.
const todoNoop = useCallback(() => {
/* TODO: not wired yet */
}, []);

// Download the current server list as a canonical mcp.json file. Uses the
// in-memory `servers` list (kept in sync with disk by useServers' refresh-
// after-mutate flow) so there's no extra HTTP roundtrip. Serialization
Expand Down Expand Up @@ -2156,6 +2168,20 @@ function App() {

// Submit handler for the Add / Edit / Clone modal. Add and Clone both go
// through addServer; Edit uses updateServer (which supports id rename).
// Add a server, then mark it as the freshly-added one so the list scrolls to
// it and highlights it. Used by manual add/clone and both import flows; edits
// and conflict-overwrites (updateServer) intentionally don't highlight.
// Accumulates into the current highlight batch (a multi-server import adds
// each id), deduped. The batch is reset to empty when an add/import modal
// opens (see the menu handlers).
const addServerHighlighted = useCallback(
async (id: string, config: MCPServerConfig) => {
await addServer(id, config);
setHighlightedServerIds((ids) => (ids.includes(id) ? ids : [...ids, id]));
},
[addServer],
);

// On rename of the active server, keep activeServerId pointed at the new id.
const onConfigSubmit = useCallback(
async (id: string, config: MCPServerConfig) => {
Expand All @@ -2168,9 +2194,9 @@ function App() {
return;
}
// add or clone
await addServer(id, config);
await addServerHighlighted(id, config);
},
[configModal, addServer, updateServer, activeServerId],
[configModal, addServerHighlighted, updateServer, activeServerId],
);

// Derive the existingIds list the modal uses for uniqueness validation.
Expand Down Expand Up @@ -2399,14 +2425,26 @@ function App() {
onDisconnect={() => {
void onDisconnect();
}}
onServerAdd={() => setConfigModal({ mode: "add" })}
onServerImportConfig={todoNoop}
onServerImportJson={todoNoop}
onServerAdd={() => {
setHighlightedServerIds([]);
setConfigModal({ mode: "add" });
}}
onServerImportConfig={() => {
setHighlightedServerIds([]);
setImportConfigOpen(true);
}}
onServerImportJson={() => {
setHighlightedServerIds([]);
setImportJsonOpen(true);
}}
onServerExport={onServerExport}
onConnectionInfo={() => setConnectionInfoModalOpen(true)}
onServerSettings={(id) => setSettingsModalTargetId(id)}
onServerEdit={(id) => setConfigModal({ mode: "edit", targetId: id })}
onServerClone={(id) => setConfigModal({ mode: "clone", targetId: id })}
onServerClone={(id) => {
setHighlightedServerIds([]);
setConfigModal({ mode: "clone", targetId: id });
}}
onServerRemove={(id) => {
const target = servers.find((s) => s.id === id);
if (target) setRemoveTarget(target);
Expand All @@ -2425,6 +2463,8 @@ function App() {
});
});
}}
highlightedServerIds={highlightedServerIds}
onClearHighlight={clearHighlight}
serverSupportsTaskToolCalls={
!!capabilities?.tasks?.requests?.tools?.call
}
Expand Down Expand Up @@ -2490,6 +2530,20 @@ function App() {
onClose={() => setConfigModal(null)}
onSubmit={onConfigSubmit}
/>
<ServerImportConfigModal
opened={importConfigOpen}
existingIds={existingIds}
onClose={() => setImportConfigOpen(false)}
onFetchSource={importSource}
onAddServer={addServerHighlighted}
onUpdateServer={updateServer}
/>
<ServerImportJsonModal
opened={importJsonOpen}
existingIds={existingIds}
onClose={() => setImportJsonOpen(false)}
onAddServer={addServerHighlighted}
/>
<ServerSettingsModal
// Remount per open (and per target server) so the accordion resets to
// its initial "options" section — the body-dropped toast deep-links
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,10 @@ const meta: Meta<typeof ImportServerJsonPanel> = {
component: ImportServerJsonPanel,
args: {
onJsonChange: fn(),
onValidate: fn(),
onSelectPackage: fn(),
onEnvVarChange: fn(),
onServerNameChange: fn(),
onAddServer: fn(),
onCancel: fn(),
envVars: [],
validation: [],
},
Expand Down
Loading
Loading