feat(mcpi): experimental session CLI client (#1432) - #1783
BobDickinson wants to merge 25 commits into
Conversation
Add servers/list and servers/show, --relogin / --stored-auth-only, browser OAuth navigation with OSC 8 links, and extract method handlers under clients/cli/src/handlers. Tracks #1781. Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce clients/mcpi (session front-end + local daemon), wire it into monorepo validate/build/coverage/install, and document the design. Not shipped in the published inspector tarball. Closes #1432. Co-authored-by: Cursor <cursoragent@cursor.com>
Take the merged #1782 CLI sources from v2/main; keep mcpi client wiring in root validate/format/coverage; register mcpi in format-coverage verify. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
# Conflicts: # AGENTS.md # README.md # clients/launcher/package-lock.json # package.json
Two interface-drift breaks surfaced only at build/runtime (the git merge itself was clean): - clients/cli/handlers/method-types.ts dropped metaValueToString when _meta was widened to RequestMetadata (JsonValue, not string) in #1910. mcpi's stringifyMeta was flattening to strings for a type that no longer exists; pass the already-JSON metadata through directly instead. - core/ gained proper-lockfile (auth/node/file-lock.ts) and yaml (mcp/skillFile.ts) as new dependencies. Both are bundled into mcpi's daemon via the noExternal @inspector/core reach-in, and both do a dynamic require() of a Node builtin in their CJS internals, which esbuild's ESM bundle output can't satisfy — the daemon threw on startup with 'Dynamic require of "process"/"path" is not supported' and every session-CLI test that needed a live daemon timed out waiting for it. Externalize both packages (already root dependencies, resolvable from node_modules at runtime). Verified: npm run build:mcpi succeeds, npm run validate:mcpi (lint + typecheck + 117 tests, coverage gate) passes clean.
Ad-hoc connect targets (bare URL/stdio, no catalog entry) had no way to request auto/modern era negotiation - loadServerEntries/ headersToServerSettings never populate protocolEra, so an ad-hoc connect always defaulted to legacy with no override, unlike a catalog entry with a protocolEra field. Add --era <legacy|auto|modern> to mcpi connect, applied via a new withEraOverride() helper (mirrors the existing withConnectTimeout() pattern) that overrides protocolEra on the resolved settings, or synthesizes a bare-defaults settings object carrying just the override when the target had none.
mcpi initialize never sent a live initialize request - it replayed InspectorClient's cached connect-time state, which is populated the same way regardless of era (legacy initialize response vs. modern server/discover). The name was misleading, and it omitted the two fields that do differ by era: protocolEra and (when probed) supportedVersions. Remove "initialize" from SESSION_RPC_METHODS (kept in ONE_SHOT_METHODS - the one-shot CLI's --method initialize still matches the literal wire method name on purpose). Add mcpi sessions/show [session], combining daemon session bookkeeping (name, serverIdentity, timestamps, isMru) with live connection state (serverInfo, protocolVersion, protocolEra, capabilities, instructions, supportedVersions). New daemon op sessions/show plus SessionRegistry.sessionFor() backing both it and the existing clientFor(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI (npm run validate) failed on verify:typecheck-coverage: mcpi had no typecheck script wired into validate, unlike cli/tui/launcher. Fixing that surfaced further gaps once mcpi's own toolchain was actually exercised end-to-end: - clients/mcpi: add typecheck/check scripts (validate -> check -> typecheck), tsconfig.test.json (mirrors cli's pattern), and the same compilerOptions overrides cli/tui use so core/'s noUncheckedIndexedAccess strictness doesn't spuriously fail here. - clients/mcpi/package.json: bump @modelcontextprotocol/client, core, server, and server-legacy from a stale 2.0.0-beta.5 to 2.0.0 (real SDK version skew vs. the rest of the monorepo, caught by verify:dep-lockstep). Per AGENTS.md, the shared toolchain (eslint, vitest, typescript, etc.) is declared once at the repo root and in no client manifest, so drop mcpi's own copies of all of it instead of keeping them in sync by hand - matching cli/tui/launcher exactly, it now resolves the root copies via npm/Node's directory walk-up. Keep only what's genuinely client-specific: tsup (its bundler) and @types/express (needed transitively by the test-server barrel import, same reason cli has it). That transitive import also drags in a hoisted @types/node that drifts from root's, so pin it via overrides the same way clients/cli does. - Fix real bugs typecheck caught: an unsound cast in format-session.ts, wrong-arity expectCliFailure() calls and an invalid AuthChallengeReason literal in two test files, and two throws missing an Error cause (preserve-caught-error) once the root's current eslint actually ran against this client for the first time. - Register mcpi with the repo's cross-cutting guards that assumed a fixed client list: verify-test-timeouts.mjs (CONFIG_ROOTS / EXPECTED_PROJECTS plus its own test fixtures), verify-bundle- externals.mjs (BUNDLED_CLIENTS, since mcpi ships a tsup bundle), workflow-gate.test.mjs (local:validate must reach mcpi the same way it reaches its siblings), and root package.json's local:validate script. Verified: npm run validate passes clean end-to-end (guards, core, and all six clients). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d them The previous commit fixed verify:typecheck-coverage, which unblocked two CI jobs that had never run to completion on this branch before: the per-file branch-coverage gate and the externalized-dependency check. Coverage (clients/mcpi, >=90% branches per file): - server.ts, format-human.ts, and mcp.ts had branch coverage below the 90% gate. The gaps traced to two features added this session that had thin or subprocess-only test coverage: sessions/show's enrichment fields (era, serverInfo, capabilities, supportedVersions, instructions) and the --era flag (previously untested at all). - Added in-process tests (server.handle()/callDaemon() against a daemon constructed directly in the test file) instead of runMcp() subprocess calls, since subprocess code isn't visible to coverage instrumentation. Covers sessions/show's absent-field fallback paths and full success path, the --era auto/invalid-value paths, and the mcp.ts positional sessionArg fallback for sessions/show. Build gate (verify:bundle-externals): - mcpi's tsup entry is named mcp-bin.js (multi-entry config), not index.js like web/cli/tui, so the script's hardcoded index.js existence check reported "build/index.js is missing" for mcpi even after a real build. Added a per-client `entry` override (defaults to index.js) so mcpi's real entry file is checked. - Once buildable, the scan found @modelcontextprotocol/ext-apps (a root runtime dependency reachable through cli's noExternal reach-in) had been inlined into mcpi's bundle instead of staying external. cli and tui already list it as external; added the same entry to mcpi's tsup config. npm run validate and npm run coverage both pass cleanly at the repo root after these changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously both fell into registerRpcCommands()'s argument-less default case (a side effect of the generic loop over SESSION_RPC_METHODS), so skills/get could parse and run but could never succeed: there was no way to pass --uri. Add dedicated Commander cases for skills/list and skills/get (positional/--uri for the latter), both with --verify. --verify's NDJSON report, stderr summary, and non-zero exit code were also being silently dropped crossing the daemon socket (RpcResult only carried lines) and, in text mode, forced through the tools/list --app-info human formatter (wrong shape). Thread summary/exitCode through the daemon protocol and dispatch layer, add a dedicated formatSkillVerifyListHuman, and apply the verify exit code the same way NO_APP/TOOL_ERROR already are. Verified end-to-end against a live skills-enabled server: skills/list, skills/list --verify (exit 7 nonconformant / 0 clean), skills/get <uri>, skills/get --uri --verify, in both --format text and json. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
sessions/list, connect, and sessions/use all returned SessionInfo with no era field, so seeing which era a session negotiated required a separate sessions/show <name> lookup per session - awkward with several open sessions against different servers, and the exact visibility dual-era support is supposed to give. Move protocolEra onto the base SessionInfo type (SessionShowResult already had it; it is now inherited rather than redeclared) and populate it from client.getProtocolEra() - already connected, so free - at all three SessionInfo construction sites in SessionRegistry (list/use/connect). formatSessionsListHuman renders it inline (@name (MRU) - server [era]); formatSessionInfoHuman already handled protocolEra being present without a protocolVersion, so connect/ sessions/use output picks it up with no further changes. Verified end-to-end: connect, sessions/list (text + json), and sessions/use against a live server all show [legacy]/Era: legacy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the one remaining concrete gap flagged in the era-support review:
tasks/update was absent from SESSION_RPC_METHODS entirely, so a modern
(SEP-2663) task paused on input_required could be observed (tasks/get)
or killed (tasks/cancel) from mcpi but never resumed by hand.
- method-types.ts: add "tasks/update" to SESSION_RPC_METHODS; add
inputResponsesJson to MethodArgs (mirrors roots/set's rootsJson
JSON-blob convention).
- run-method.ts: add a tasks/update case validating --task-id and
parsing --input-responses as a JSON object, then calling
InspectorClient.updateRequestorTask; echoes {updated, taskId} since
the ack is empty and status only advances on a later tasks/get poll.
- mcp.ts: dedicated Commander case (positional [taskId] / --task-id,
--input-responses <json>), consistent with the existing tasks/get,
tasks/cancel, tasks/result case.
- mcp-coverage.test.ts: cover the missing-input-responses error,
invalid-JSON error, and the call-through path.
Verified against a live tasks-modern-http.json server: a task-augmented
tools/call paused on input_required, tasks/update with the confirm
input resumed it successfully; missing/invalid --input-responses both
fail with clear errors. Full mcpi suite (120 tests) and coverage gate
pass; cli suite (384 tests) and typecheck pass in both packages.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
run-method.ts's tasks/update case (added in 22d5c9f) wasn't exercised by the cli package's own test suite — only through mcpi's build, which doesn't count toward this package's coverage gate. That dropped run-method.ts branch coverage to 85.81%, below the 90% per-file threshold, and failed CI's coverage job. Add updateRequestorTask to the mocked client and cover: the success path (result shape, args forwarded to updateRequestorTask), missing taskId, missing --input-responses, invalid JSON, and non-object JSON (array). run-method.ts is back to 93.24% branch coverage; full cli suite (384 tests) passes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s/update) Closes the last open item from the mcpi era-support review: add a 'Protocol era support' section to the mcpi README covering the --era connect flag (legacy/auto/modern, overrides catalog/config, only way to set it for an ad-hoc target), the [era] annotation now shown inline in sessions/list, sessions/use, and connect output, sessions/show's fuller era/version/capabilities/supported-versions detail, and how to resume a paused modern task with tasks/update. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds duplex daemon-IPC framing so a pending elicitation on the
InspectorClient can pause an in-flight tools/call over the daemon
socket, prompt the user in mcpi, and resume the call with the answer.
- protocol.ts: new ElicitationRequestFrame/ElicitationResponseFrame
- ipc-glue.ts: ElicitationChannel + ConnectionElicitationChannel for
per-connection pause/resume duplex exchange
- elicitation-bridge.ts (new): wires InspectorClient's
newPendingElicitation events to the daemon's ElicitationChannel,
skipping task-input-required origin (out of scope for Phase 1)
- server.ts: threads the channel through handle/handleOutcome/
dispatch/runRpc, wrapping runMethod() with the bridge
- sessions.ts: advertise elicit: { url: true } capability
- client.ts (callDaemon): duplex line handling for elicitation
request/response frames, with an onElicitation callback
- elicitation-prompt.ts (new): terminal UI for URL-mode accept/
cancel; form-mode auto-declines (not yet implemented) and
non-interactive callers auto-cancel
- dispatch.ts: wires prompting in for interactive text-format
sessions only
Covers legacy elicitInput({mode:"url"}) and modern non-task MRTR
elicitation. Task-augmented MRTR elicitation and form-mode rendering
are deferred (see work/mcpi-era-support.md).
Manually verified end-to-end against test-servers/configs/
url-elicitation-form.json: accept, cancel, and non-interactive
auto-cancel paths all round-trip correctly through the daemon.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds terminal rendering for form-mode elicitation, building on the
Phase 1 duplex daemon-IPC framing.
- session/form-schema.ts (new): parses a requestedSchema into an
ordered list of typed fields, per the MRTR elicitation spec's
restricted primitive-field shape (string/number/integer/boolean,
single-select enum via enum or titled oneOf, multi-select enum via
array + items.enum/items.anyOf). Returns null for anything outside
that shape so callers can fall back to a clear decline.
- session/form-prompt.ts (new): prompts once per field (text/numeric/
y-n/numbered single-select/numbered comma-separated multi-select),
pre-fills defaults, validates required/length/range/min-max-items
with a retry loop, then a review step (submit / re-edit a field by
name / cancel) before returning the answer.
- session/elicitation-prompt.ts: form-mode branch now renders via
form-schema/form-prompt instead of always declining; still declines
when non-interactive or when the schema doesn't parse.
- daemon/sessions.ts: advertise elicit: { url: true, form: true }.
Also fixes prettier formatting on the Phase 1 files that hadn't been
run through npm run format:check before pushing (client.ts,
ipc-glue.ts, and a few test files) - this is what broke the build
check on ac7e4bf.
Manually verified end-to-end against test-servers/configs/
modern-mrtr-http.json (boolean field, mrtr_confirm) and
mrtr-showcase-http.json (string field, multi-round MRTR,
mrtr_two_step): accept, cancel-at-review, edit-a-field-at-review, and
non-interactive auto-decline all round-trip correctly through the
daemon.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a per-connection override for the elicitation capability mcpi
advertises to a server, mirroring the existing --era mechanism:
- InspectorServerSettings.elicitCapability ("off"|"url"|"form"|"both",
default "both") persists on disk as elicitCapability, omitted when it
equals the default, and round-trips through serverList.ts the same
way protocolEra does.
- mcpi connect gains --elicit <mode>, validated the same way as --era,
with a withElicitOverride() helper mirroring withEraOverride() (incl.
synthesizing bare-defaults settings for ad-hoc targets).
- createSessionClient() now derives the InspectorClient elicit option
from serverSettings.elicitCapability via elicitCapabilityToClientOption()
instead of the old Phase-1 hardcoded { url: true, form: true }.
This lets a caller that cannot handle an interactive elicitation prompt
(a script, an agent) opt out entirely so the server sees no elicitation
capability and can fall back to its own alternative, instead of every
elicitation request being auto-declined.
Also updates clients/mcpi/README.md with an "Elicitation support"
section (previously undocumented, despite already-shipped URL/form
prompt rendering) and the --elicit flag, and refreshes the stale
"Sampling / elicitation CLI: Still TUI/web" to-do row in
specification/v2_cli_v2.md.
Manually verified end-to-end against the modern-mrtr-http test server:
--elicit off makes the server itself reject the mid-round input request
("capabilities do not declare the required capability"); --elicit both
(default) succeeds and reaches the interactive/auto-decline prompt path
as before.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add mcpi's build output to the root package's `bin`/`files` so that `npm install -g @modelcontextprotocol/inspector` also installs the `mcpi` binary alongside `mcp-inspector`. mcpi's tsup build already bundles all monorepo-internal code, and root already declares every third-party dependency mcpi needs, so no new dependencies or CI risk are introduced. - package.json: add "mcpi" bin entry and "clients/mcpi/build" to files - README.md / AGENTS.md: update project-layout notes to reflect that mcpi is now bundled into the published package - clients/mcpi/README.md: add an "Install" section documenting `npm install -g @modelcontextprotocol/inspector` as the real-world install path, and rename the old install section to "Build / run from this repo (development)" to distinguish it from end-user install Verified with npm pack + an isolated global install from the packed tarball: both mcp-inspector and mcpi bins resolve and run correctly with no missing dependencies. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a repo-root skills/mcpi/SKILL.md — a concise, usage-only guide to mcpi for coding agents/LLMs, installable via `npx skills add modelcontextprotocol/inspector --skill mcpi`. Ship it in the published npm package (added to root package.json's `files`) so it's also reachable from a plain `npm install -g @modelcontextprotocol/inspector` with no git/GitHub access needed. - clients/mcpi/src/session/mcp.ts: add `mcpi agent-help` (prints the SKILL.md content) and `mcpi agent-help --path` (prints its resolved file path); add a short "Agent skill for mcpi" pointer between the root command's description and its Options list. - package.json: add "skills/mcpi" to files. - skills/mcpi/SKILL.md: new — connect/run/target-session/disconnect usage, --format, catalog vs. ad-hoc targets, auth, elicitation, and session persistence across invocations. Deliberately excludes install instructions and daemon implementation details — assumes mcpi is already installed and documents only what's needed to use it. Verified: mcpi `npm run check` + `npm test` (184/184), and an isolated global install from a packed tarball resolving `mcpi agent-help` / `mcpi agent-help --path` correctly with no monorepo context present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mcpi runs as a front-end for whatever invokes it — a human at a terminal, or an agent without a real TTY on stdin/stderr — but it inherited the one-shot CLI's TTY refuse-gate, which exists to protect unattended CI from hanging up to 15 minutes. That assumption doesn't hold for mcpi: an agent without a TTY is still expected to relay the printed URL to an attended human, not run unattended. mcpi now always admits interactive OAuth (`isTTY: true`), unless `--stored-auth-only` opts out entirely. Since the printed line can no longer assume a human is reading the terminal directly, mcpi wires a custom `promptMessage` into `createCliOAuthNavigation` (new option, additive, default preserved for the one-shot CLI): real TTY still gets "Please navigate to: <url>"; no TTY gets "The user needs to navigate to this link to authenticate: <url>" so an agent knows it must relay the link rather than treat it as its own. Live-testing this surfaced a real, pre-existing bug: mcpi never wired a shared `autoOpenControl` between the navigation and the OAuth call, so the printed line's `armed` gate was always false and nothing was ever printed, in either mode. Fixed by wiring the same disarmed-until-armed control the one-shot CLI uses. Also live-tested: interactive OAuth taking longer than the daemon's 60s idle timeout (very plausible for a real human login — SSO, MFA) left the pre-auth daemon having self-exited by the time mcpi retried the connect, producing a confusing `daemon_unreachable` error right after a successful login. Fixed by re-calling `ensureDaemon()` before the retry instead of reusing the pre-OAuth socket path; `ensureDaemon()` is already idempotent (pings first, only respawns if unreachable), so this is a no-op when the daemon is still alive. Finally, add a SIGINT/SIGTERM handler around the OAuth callback wait (`core/auth/node/runner-interactive-oauth.ts`, shared with the one-shot CLI and mirrors the existing streaming-command pattern in mcpi's dispatch.ts): Ctrl-C or a kill now rejects cleanly with "OAuth authorization cancelled (SIGINT/SIGTERM)." instead of an abrupt process death, classified as the existing `auth_required` exit code. All changes verified live end-to-end against a local OAuth test server (URL print, consent, loopback callback, token exchange, tools/call), not just via unit tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CI's coverage gate (per-file, >=90% on all four dimensions) was failing for src/session/mcp.ts (88% functions, 88.1% branches). This predates the OAuth work in the previous commit -- it was already failing on the prior "Add mcpi agent-help command" commit too, since that added the agent-help command (and its resolveAgentSkillPath helper) with zero test coverage. It went unnoticed locally because validate:mcpi runs vitest run without --coverage, and only coverage:mcpi (which CI's coverage job runs separately) enforces the per-file threshold. Add the missing coverage: - New __tests__/agent-help.test.ts: exercises "mcpi agent-help" (prints SKILL.md content) and "mcpi agent-help --path" (prints resolved path). - __tests__/mcp-coverage.test.ts: exercise the previously-unregistered servers/show, skills/list (with/without --verify), and skills/get (positional and --uri forms) RPC command actions over a live connected session. Verified locally: "cd clients/mcpi && npx vitest run --coverage" now exits 0 with mcp.ts at 96.66/92.43/100/96.92 (stmts/branch/funcs/lines), all above the 90% gate. Also re-checked coverage:cli and coverage:web (cover the other files touched by the previous commit) -- both clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Elicitation (both URL mode and form mode) was gated on a real TTY the
same way OAuth used to be: `interactive` required
`stdin.isTTY && stdout.isTTY`, so a piped/non-TTY stdin (an agent
relaying prompts to a human, or answering on their behalf) always got
an immediate decline/cancel instead of a chance to answer.
That gate wasn't technically necessary. promptForm()/promptField() and
the URL-mode confirm all just use readline's question() over
process.stdin/stderr -- plain line-based I/O, no raw-mode or TTY
features involved. An agent piping answers back on stdin after reading
the prompts on stderr works exactly like a human typing at a terminal.
Changes:
- dispatch.ts: `interactive` is now `format === "text"` (drop the
isTTY checks). Only `--format json` still auto-declines/cancels,
since its stdout is a single machine-readable payload with no room
to interleave a prompt.
- elicitation-prompt.ts: reworded the two "requires an interactive
terminal" messages to reflect the real reason (`--format json`), and
raced the URL-mode confirm's rl.question() against a new
watchForClose() signal (see below).
- form-prompt.ts: added watchForClose(rl)/ask() -- every rl.question()
in promptField() and the review step now races against the
readline interface's "close" event. Previously, if stdin closed
before an answer arrived (e.g. `mcpi ... </dev/null`, or any
genuinely non-interactive invocation), rl.question() would hang
forever waiting for a line that would never come. Now it rejects
promptly, and the caller's existing catch-all in
promptElicitation() converts that into a clean decline/cancel.
Verified live end-to-end with a real MCP test server (the
`collect_elicitation` preset, which sends a real form-mode elicitation
over a stdio connection):
- `mcpi tools/call ... </dev/null` (stdin already closed): declines
immediately, no hang.
- Same call with stdin as a live FIFO, writing an answer after the
prompt appears (simulating an agent relaying/answering): went
through the field prompt, the review step, and returned
`{"action":"accept","content":{"name":"Ada Lovelace"}}`, exactly
like a human at a terminal would.
Also re-ran `validate:mcpi` (191 tests, format/lint/typecheck clean)
and `coverage:mcpi` (per-file gate, all green) after these changes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously SIGINT/SIGTERM handling only existed for the two
STREAM_METHODS (logging/tail, resources/subscribe). Every other
session RPC -- tools/call, connect, an elicitation wait, etc -- had no
signal handler at all, so Ctrl-C during one of those just killed the
process abruptly instead of cancelling cleanly (the same class of
problem the OAuth SIGINT fix addressed earlier this session, just for
a different code path).
- daemon/client.ts: callDaemon() now accepts a `signal?: AbortSignal`
option (mirroring streamDaemon's existing one). On abort it fails the
in-flight call with a clear `'<op>' cancelled.` error
(`code: "cancelled"`, exit code USAGE) instead of leaving the socket
to time out or the process to die uncleanly.
- session/dispatch.ts: wires an AbortController + SIGINT/SIGTERM
handlers around the general `callDaemon("rpc", ...)` call the same
way STREAM_METHODS already does, listeners removed in `finally`.
Also fixed a stale doc comment: daemon/protocol.ts's
`SessionNameParams.requireExplicit` said the front-end sets it from
`!process.stdout.isTTY`, but dispatch.ts actually keys off `stdin` (so
piping output, e.g. `mcpi tools/list | jq`, still uses MRU when a human
is at the keyboard) -- the comment just hadn't been updated to match.
New tests:
- __tests__/dispatch.test.ts: SIGINT/SIGTERM now aborts the general rpc
call too (not just streams), and listeners are removed once the call
settles (no leak across calls).
- __tests__/elicitation-client.test.ts: a real socket that never
responds is aborted mid-call via signal.abort(), rejecting promptly
with the cancellation error rather than hanging until timeoutMs.
Verified: validate:mcpi (194 tests, format/lint/typecheck clean) and
coverage:mcpi (per-file gate, all green).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mcpi work summary — PR #1783 (2026-09-13 → 2026-09-14)Branch: Organized by what actually changed functionally, not commit order. 1. Modern protocol-era support (era + skills primitives)Gave mcpi first-class awareness of MCP's protocol eras (legacy vs. modern/
2. Elicitation features (legacy URL-mode and modern/MRTR form-mode)Built out MCP's elicitation flow, covering both eras' mechanisms:
3. Making mcpi agent-friendlyEverything else — reframing and hardening mcpi so an AI agent driving it
Key design note (MRU): the daemon is a single shared process, so MRU Non-functional maintenance (excluded from the above as "not changes")These kept the branch buildable/green but didn't change behavior:
|
Closes #1432
Depends on #1782 (CLI improvements — handlers and shared helpers live there).
Summary
clients/mcpisession CLI (connect once, many commands) with an implicit local Unix-socket daemonclients/clihandlers / OAuth helpers via a temporary build-time@inspector/clialiasvalidate/build/coverage/install-clientsand document in AGENTS +specification/v2_cli_v2.mdPackaging
@modelcontextprotocol/inspectortarball (filesallowlist unchanged)bin.mcpi— install vianpm linkfromclients/mcpifor local use (seeclients/mcpi/README.md)Test plan
cd clients/mcpi && npm run validate(117 tests)v2/mainafter CLI: catalog listing, auth flags, and OAuth browser UX #1782 mergesnpm run cifrom repo root on the stacked branchMade with Cursor