A modern MCP (Model Context Protocol) and HTTP API explorer — browse tools, prompts, resources, invoke HTTP APIs, and chat with LLMs over live MCP connections. Built with a Vue 3 / Vite / PrimeVue frontend + ASP.NET Core 10 backend, with a companion .NET CLI for scripted HTTP API and YAML runbook workflows.
Note
🔄 This is a ground-up rewrite of the original MCP Explorer (Blazor Server UI), migrated to Vue 3 + Vite + PrimeVue with a clean-architecture ASP.NET Core 10 backend, Docker-first deployment, and Azure Key Vault / Entra ID integration.
📚 Documentation → — full user guide and feature reference built with Hugo
- 🔌 MCP Connections — Streamable HTTP transport; duplicate saved configurations; custom headers including raw/Bearer/Basic Authorization values; per-connection auth modes (Custom Headers, Azure Client Credentials, OAuth)
- 🔐 Azure Key Vault Integration — resolve connection secrets (client secrets, API keys) directly from Key Vault references; no plaintext secrets stored on disk
- 🏢 Azure Entra App Registrations — browse and select app registrations from your tenant via Microsoft Graph; auto-populates client ID and tenant fields
- 📜 Certificate Authentication — generate self-signed client certificates inline (BCL crypto, no OpenSSL) and upload the public key to an App Registration in one click; token requests signed locally with
ClientCertificateCredential— the private key (0600, git-ignored, audit-logged) never leaves the machine; CSR flow for CA-issued certs and Key Vault import/export for team sharing - ♻️ Certificate Renew & Rotate — expiry toasts + topbar badge 30 days out; one-click rotation generates a successor, re-uploads it everywhere, repoints referencing connections, and flags stale key credentials in Azure for cleanup;
mcp-http certsCLI for automation - 🛠️ Tools — browse and invoke tools with dynamic parameter forms; inspect JSON responses inline
- 🌐 HTTP API Explorer — manage HTTP API connections under Infrastructure, invoke from HTTP API Explorer with runtime
{placeholder}inputs (supports defaults via{name:default}), inspect tabbed request/response history, and auto-redact sensitive values in persisted history details - ⌨️ CLI (
mcp-http) — script HTTP API invoke/compare/history, YAML runbook validate/execute (MCP + HTTP), and API definition import/export from terminal automation; list MCP connections offline, and connect to MCP servers to list tools/resources/prompts/templates or invoke tools directly - 🚇 Dev Tunnels — create public DevTunnels for webhook callbacks, inspect live payloads over SSE, scrub event history, and replay captured requests
- 💬 Prompts — list, execute, and evaluate prompts; pipe results directly to an LLM
- 📄 Resources & Templates — browse MCP resources; expand templates with runtime parameters
- 🤖 Chat — SSE-streamed AI chat with automatic MCP tool calling; shows token usage and active tool badges
- ⚡ Workflows — multi-step tool chain builder with execution history and built-in load testing
- 🙋 Elicitations — server-initiated input requests rendered as dynamic forms (text, bool, number, radio, checkbox, dropdown); real-time SSE delivery
- 🛡️ Sensitive Data Protection — regex + heuristic detection of secrets in tool parameters and chat messages; AES-256-GCM encryption at rest
- 🎨 10 Themes — Command Dark/Light, Nord, Dracula, Catppuccin Mocha, Solarized Light, GitHub Dark/Light, Material Dark/Light — persisted per user
- ⌨️ Command Palette — Ctrl+K / ⌘+K for keyboard-first navigation
- 📦 Import / Export — password-protected AES-256-GCM connection export; plain JSON workflow export
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart LR
Browser["🌐 Browser\nVue 3 · Vite · PrimeVue"] --> Deployment["🐳 Deployment\n(single container or compose)"]
Deployment --> Backend["🏗️ ASP.NET Core 10\nClean Architecture"]
CLI["⌨️ .NET CLI\nmcp-http"] --> HTTPAPIs["🌐 External HTTP APIs"]
CLI --> MCPServers
CLI --> Storage
Backend --> MCPServers["🖥️ MCP Servers"]
Backend --> HTTPAPIs["🌐 External HTTP APIs"]
Backend --> LLMAPIs["☁️ LLM APIs"]
Backend --> Azure["🔐 Azure\nKey Vault · Entra ID"]
Backend --> Storage[("💾 Persistent data (/data)\nsettings.json + HttpApis/http-apis.json + HttpApis/*/*.jsonl")]
Backend -->|"spawns devtunnel CLI"| DevTunnel["🚇 Dev Tunnel\nPublic webhook endpoint"]
MCPServers -->|"webhook POST"| DevTunnel
DevTunnel -->|"forwards payload"| Backend
Backend -->|"SSE stream"| Browser
classDef browser fill:#312e81,stroke:#6366f1,color:#e0e7ff
classDef deploy fill:#164e63,stroke:#22d3ee,color:#cffafe
classDef backend fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef external fill:#1c1917,stroke:#f59e0b,color:#fef3c7
classDef cli fill:#334155,stroke:#94a3b8,color:#e2e8f0
classDef azure fill:#1e1b4b,stroke:#818cf8,color:#e0e7ff
classDef data fill:#292524,stroke:#a78bfa,color:#ede9fe
classDef tunnel fill:#064e3b,stroke:#10b981,color:#d1fae5
class Browser browser
class Deployment deploy
class Backend backend
class MCPServers,HTTPAPIs,LLMAPIs external
class CLI cli
class Azure azure
class Storage data
class DevTunnel tunnel
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart TB
subgraph SINGLE["Single Container — docker run"]
direction TB
S_SUPER["supervisord\n(process manager)"]
S_GW["🔀 YARP Gateway :8080\nServes Vue SPA static files\n/api/** → API"]
S_API["⚙️ ASP.NET Core API\n:5000 (internal)"]
S_SUPER --> S_GW
S_SUPER --> S_API
S_GW -->|"reverse proxy"| S_API
end
subgraph COMPOSE["Docker Compose — docker compose up"]
direction TB
C_NGINX["🌐 nginx :8080 (external)\nServes Vue SPA static files\n/api/** → API"]
C_API["⚙️ ASP.NET Core API\n:5000 (internal, Docker network)"]
C_NGINX -->|"proxy_pass"| C_API
end
classDef gateway fill:#164e63,stroke:#22d3ee,color:#cffafe
classDef api fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef proc fill:#292524,stroke:#a78bfa,color:#ede9fe
class S_GW,C_NGINX gateway
class S_API,C_API api
class S_SUPER proc
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart TB
subgraph CTRL["Controllers /api/v1/"]
direction LR
C1["🔌 connections"] --- C2["🛠️ tools"] --- C3["💬 prompts"]
C4["📄 resources"] --- C5["🤖 chat · SSE"] --- C6["⚡ workflows"]
C7["🙋 elicitations · SSE"] --- C8["🧠 llmmodels"] --- C9["⚙️ preferences"]
C10["🌐 http-apis"] --- C11["🧪 http-api-collections"] --- C12["📜 http-api-history"]
end
subgraph CORE["Core (no framework deps)"]
direction LR
DOMAIN["📦 Domain Models\nConnectionDefinition · WorkflowDefinition\nLlmModelDefinition · HttpApiDefinition · HttpApiCollection"]
IFACES["🔗 Interfaces\nIConnectionService · IUserPreferencesStore\nIWorkflowEngine · IConnectionExportService\nIHttpApiStore · IHttpApiInvoker · IHttpApiSnapshotStore"]
end
subgraph INFRA["Infrastructure"]
direction LR
MCPSDK["🔌 MCP SDK\nActiveConnection · Tool\nPrompt · Resource · Elicitation"]
HTTPAPI["🌐 HTTP API Services\nHttpApiInvoker · SnapshotStore · TemplateResolver"]
LLMPROV["🧠 LLM Providers\nOpenAI · AzureOpenAI · AzureAIFoundry\nOllama · Custom"]
PERSIST["💾 JSON Persistence\nUserPreferencesStore"]
SECURITY["🔐 Security\nAES-256-GCM · PBKDF2-SHA256\nSensitiveField Detection"]
WFENG["⚡ Workflow Engine\nSerial · Parallel · Load Test"]
AZURESVC["🔐 Azure Services\nKeyVaultService · GraphService"]
end
CTRL --> CORE
INFRA --> CORE
classDef ctrl fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef core fill:#1e1b4b,stroke:#818cf8,color:#e0e7ff
classDef infra fill:#134e4a,stroke:#14b8a6,color:#ccfbf1
class C1,C2,C3,C4,C5,C6,C7,C8,C9,C10,C11,C12 ctrl
class DOMAIN,IFACES core
class MCPSDK,HTTPAPI,LLMPROV,PERSIST,SECURITY,WFENG,AZURESVC infra
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart LR
subgraph HOST["Host Machine"]
DOTAZURE["📁 ~/.azure\n(az login session)"]
end
subgraph CONTAINER["Container"]
MOUNT["📁 /root/.azure\n(volume mount, writable)"]
CRED["🔑 AzureCliCredential"]
MOUNT --> CRED
end
KV["🔐 Azure Key Vault\nRead secret by reference\nfor connection auth"]
GRAPH["🏢 Microsoft Graph API\nList / search App Registrations\nauto-populate connection fields"]
DOTAZURE -->|"-v ~/.azure:/root/.azure"| MOUNT
CRED --> KV
CRED --> GRAPH
classDef host fill:#292524,stroke:#a78bfa,color:#ede9fe
classDef ctr fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef azure fill:#1e1b4b,stroke:#818cf8,color:#e0e7ff
class DOTAZURE host
class MOUNT,CRED ctr
class KV,GRAPH azure
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart LR
EL_SERVER["🖥️ MCP Server\nsends elicitation request"]
EL_BUFFER["🏗️ API\nbuffers request"]
EL_SSE["📡 SSE Stream\npushes to browser"]
EL_FORM["🙋 Dynamic Form\n(text · bool · number\nradio · checkbox · dropdown)"]
EL_POST["📨 POST\n/api/v1/elicitations/{id}/respond"]
EL_FWD["🖥️ MCP Server\nreceives answer"]
EL_SERVER --> EL_BUFFER --> EL_SSE --> EL_FORM --> EL_POST --> EL_FWD
classDef server fill:#1c1917,stroke:#f59e0b,color:#fef3c7
classDef api fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef sse fill:#0c4a6e,stroke:#38bdf8,color:#e0f2fe
classDef form fill:#312e81,stroke:#6366f1,color:#e0e7ff
class EL_SERVER,EL_FWD server
class EL_BUFFER,EL_POST api
class EL_SSE sse
class EL_FORM form
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart LR
DT_BROWSER["🌐 Browser\nDev Tunnels dashboard"]
DT_API["🏗️ API\n/api/v1/devtunnels"]
DT_CLI["⚙️ devtunnel CLI\n(managed child process)"]
DT_CLOUD["🚇 DevTunnel Cloud\nPublic HTTPS endpoint"]
DT_MCPTOOL["🖥️ MCP Tool\nsends webhook POST"]
DT_WEBHOOK["📨 POST\n/api/v1/devtunnels/{name}/webhook"]
DT_BUFFER["💾 Event Buffer\nin-memory ring (per tunnel)"]
DT_SSE["📡 SSE Stream\n/api/v1/devtunnels/{name}/events"]
DT_INSPECTOR["🔍 Inspector\nTraffic · Archive tabs\nScrub · Replay · Time-travel"]
DT_BROWSER -->|"create / start / stop"| DT_API
DT_API -->|"spawn & manage"| DT_CLI
DT_CLI -->|"public URL"| DT_CLOUD
DT_CLOUD -->|"public webhook URL →\nshare with MCP tool"| DT_MCPTOOL
DT_MCPTOOL -->|"JSON payload POST"| DT_CLOUD
DT_CLOUD -->|"tunnel forward"| DT_WEBHOOK
DT_WEBHOOK --> DT_BUFFER
DT_BUFFER -->|"push event"| DT_SSE
DT_SSE -->|"live SSE update"| DT_INSPECTOR
classDef browser fill:#312e81,stroke:#6366f1,color:#e0e7ff
classDef api fill:#1e3a5f,stroke:#3b82f6,color:#bfdbfe
classDef cli fill:#292524,stroke:#a78bfa,color:#ede9fe
classDef tunnel fill:#064e3b,stroke:#10b981,color:#d1fae5
classDef server fill:#1c1917,stroke:#f59e0b,color:#fef3c7
classDef sse fill:#0c4a6e,stroke:#38bdf8,color:#e0f2fe
classDef ui fill:#312e81,stroke:#6366f1,color:#e0e7ff
class DT_BROWSER,DT_INSPECTOR browser
class DT_API,DT_WEBHOOK,DT_BUFFER api
class DT_CLI cli
class DT_CLOUD tunnel
class DT_MCPTOOL server
class DT_SSE sse
%%{ init: { "theme": "base", "themeVariables": {
"primaryColor": "#6366f1",
"primaryTextColor": "#ffffff",
"primaryBorderColor": "#4f46e5",
"lineColor": "#94a3b8",
"secondaryColor": "#0f172a",
"tertiaryColor": "#1e293b",
"background": "#0f172a",
"mainBkg": "#1e293b",
"nodeBorder": "#334155",
"clusterBkg": "#1e293b",
"titleColor": "#f8fafc",
"edgeLabelBackground": "#1e293b",
"fontFamily": "ui-monospace, monospace"
} } }%%
flowchart TB
INPUT["🔑 Input Data\nconnection secrets · API keys"]
DETECT["🔍 Sensitive Field Detector\nregex + heuristic analysis"]
CIPHER["🔐 AES-256-GCM Cipher\nPBKDF2-SHA256 key derivation"]
STORE[("💾 settings.json\nencrypted at rest\n/data volume")]
INPUT --> DETECT --> CIPHER --> STORE
EXPORT["📦 Export Path"]
EXPBUNDLE["🔒 Password-Protected\n.json bundle\n(AES-256-GCM)"]
EXPORT --> EXPBUNDLE
classDef input fill:#312e81,stroke:#6366f1,color:#e0e7ff
classDef detect fill:#134e4a,stroke:#14b8a6,color:#ccfbf1
classDef cipher fill:#1e1b4b,stroke:#818cf8,color:#e0e7ff
classDef data fill:#292524,stroke:#a78bfa,color:#ede9fe
classDef export fill:#3b0764,stroke:#c026d3,color:#fae8ff
class INPUT input
class DETECT detect
class CIPHER cipher
class STORE data
class EXPORT,EXPBUNDLE export
src/
├── Garrard.Mcp.Explorer.Core/ # Domain models + interfaces (no framework deps)
├── Garrard.Mcp.Explorer.Infrastructure/ # MCP SDK, HTTP API services, LLM providers, persistence, security
│ ├── Azure/KeyVaultService.cs # Key Vault secret resolution
│ └── Azure/GraphService.cs # Microsoft Graph App Registration browser
├── Garrard.Mcp.Explorer.Api/ # ASP.NET Core Web API (versioned MCP + HTTP API Explorer controllers)
├── Garrard.Mcp.Explorer.Cli/ # .NET CLI (mcp-http) — HTTP API scripting (invoke/compare/history/export) + MCP connection/tool/resource/prompt/template listing and tool invocation
├── Garrard.Mcp.Explorer.Gateway/ # YARP reverse proxy + Vue SPA host
├── Garrard.Mcp.MessageContentProtection/# Sensitive data detection library
└── frontend/ # Vue 3 + Vite + PrimeVue 4 SPA
├── src/api/ # Typed API client layer
├── src/stores/ # Pinia stores (chat, connections, preferences, themes, workflows)
├── src/views/ # Feature views (MCP, HTTP API, Dev Tunnels, chat, workflows)
├── src/components/ # Shared components (CommandPalette, ThemeSwitcher, JsonViewer)
└── src/themes/ # CSS custom property themes
tests/
├── Garrard.Tests.Mcp.Explorer.Core/ # 66 unit tests
├── Garrard.Tests.Mcp.Explorer.Infrastructure/ # 47 unit tests
└── Garrard.Tests.Mcp.Explorer.Api/ # 34 unit tests
docs/
└── learn/ # Hugo documentation site (serve with: hugo server -s docs/learn)
- .NET 10 SDK
- Node.js 22+ and npm
- Docker (optional, for containerised deployment)
# 1. Clone and restore
git clone <repo-url>
cd mcp-explorer-v2
dotnet restore
# 2. Copy env config
cp .env.example .env
# Edit .env with your LLM API keys
# 3. Start the API (terminal 1)
dotnet run --project src/Garrard.Mcp.Explorer.Api
# 4. Start the frontend dev server (terminal 2)
cd src/frontend
npm install
npm run dev
# Opens http://localhost:5173 (proxied to API on :5000)
# 5. Run tests
dotnet test# Build frontend
cd src/frontend && npm run build && cd ../..
# Build and run the gateway (hosts built Vue SPA + proxies API)
dotnet publish src/Garrard.Mcp.Explorer.Gateway -c Release -o out/gateway
dotnet run --project src/Garrard.Mcp.Explorer.Api &
dotnet out/gateway/Garrard.Mcp.Explorer.Gateway.dllThe CLI ships as a .NET global tool and runs on Windows, Ubuntu (x64), and macOS (Apple Silicon / x64). Requires the .NET 10 SDK.
dotnet tool install -g Garrard.Mcp.Explorer.Cli# 1 – pack
dotnet pack src/Garrard.Mcp.Explorer.Cli -c Release -o ./nupkg
# 2 – install
dotnet tool install -g Garrard.Mcp.Explorer.Cli --add-source ./nupkg
# update an existing installation
dotnet tool update -g Garrard.Mcp.Explorer.Cli --add-source ./nupkg# Show all commands and branches
mcp-http --helpThese commands operate directly on the MCP connections stored in your settings file.
# List all saved MCP connections (offline – no network required)
mcp-http mcp connections
# List all tools exposed by a named MCP server
mcp-http mcp tools --name "My Server"
# List all resources
mcp-http mcp resources --name "My Server"
# List all prompts (required arguments shown in bold)
mcp-http mcp prompts --name "My Server"
# List all resource templates
mcp-http mcp templates --name "My Server"
# Invoke a tool – parameters as key=value pairs (auto-typed: numbers, booleans, JSON objects)
mcp-http mcp invoke --name "My Server" --tool echo --param message=hello
# Invoke a tool – parameters as a JSON object
mcp-http mcp invoke --name "My Server" --tool search --params '{"query":"dotnet","limit":10}'
# Mix both: --params sets defaults, --param overrides individual keys
mcp-http mcp invoke --name "My Server" --tool search --params '{"query":"dotnet"}' --param limit=5
# Use a custom data directory
mcp-http --data-path "/home/user/McpExplorerData" mcp connections
# Run and validate an MCP YAML runbook
mcp-http mcp runbook --file ./mcp-runbook.yaml
mcp-http mcp runbook --file ./mcp-runbook.yaml --validate-onlyName matching —
--nameperforms an exact match first, then falls back to a single case-insensitive partial match. If multiple connections match the partial name, the CLI lists the candidates and exits without connecting.OAuth connections — connections that use OAuth authentication require a running API callback endpoint. A warning is printed; the connect attempt still proceeds.
Client certificates for Azure client-credential auth — shares the same certs/ store as the web app.
# List certificates with expiry and upload state
mcp-http certs list
# Generate a self-signed certificate (BCL crypto — no OpenSSL required)
mcp-http certs create --name finance-api-cert --key-size 4096 --validity 24
# Upload the public key to an Entra App Registration (requires az login)
mcp-http certs upload --name finance-api-cert --app-id 00000000-0000-0000-0000-000000000000
# Rotate: generate successor, re-upload, repoint referencing connections, optional cleanup
mcp-http certs renew --name finance-api-cert --remove-old
# Delete (blocked while referenced — prints the referencing connections)
mcp-http certs delete --name finance-api-cert --yes# Invoke by API name
mcp-http http api invoke --name "My API"
# Invoke from a different data directory (e.g. running outside Docker)
mcp-http --data-path "/home/user/McpExplorerData" http api invoke --name "My API"
# Invoke against localhost when the API was saved with a host.docker.internal base URL
mcp-http http api invoke --name "My API" --use-localhost
# Run a collection and fail CI on schema breaks
mcp-http http collection run --name "Regression Suite" --fail-on-breaking
# Run a collection using localhost URLs from a custom data directory
mcp-http --data-path "/home/user/McpExplorerData" http collection run --name "Regression Suite" --use-localhost --fail-on-breaking
# Run and validate an HTTP YAML runbook
mcp-http http runbook --file ./http-runbook.yaml
mcp-http http runbook --file ./http-runbook.yaml --validate-onlyHTTP runbooks can use saved API definitions (endpoint/endpointId) or inline YAML connections with an optional defaultConnection.
Both mcp runbook and http runbook support per-step assertions:
assert:
path: isSuccess
operator: equals
value: trueUse continueOnAssertFailure: true at runbook or step level to continue after failed assertions; otherwise execution stops on first assertion failure.
Commands exit with code 1 if any assertion fails.
| Option | Scope | Description |
|---|---|---|
--data-path <dir> |
All commands | Override the data directory. Pass the folder that contains settings.json (e.g. the host path you mount into Docker). |
--use-localhost |
http api invoke, http collection run |
Replace host.docker.internal with localhost in the API base URL — useful when running the CLI directly on the host against a locally running service. |
| Platform | Default data directory |
|---|---|
| Windows | %LOCALAPPDATA%\McpExplorer\ |
| Linux / macOS | ~/.local/share/McpExplorer/ |
Override with --data-path <dir> or the PREFERENCES__StoragePath environment variable.
The runtime image now installs the devtunnel CLI alongside azure-cli, so Dev Tunnel creation and device-code login work inside the container without extra manual setup.
# Build
docker build --build-arg APP_VERSION=2.0.0 -t mcp-explorer:2.0.0 .
# Run
dataRoot="$HOME/Library/Application Support/McpExplorerv2"
mkdir -p "${dataRoot}"
docker run --rm -it \
-p 8091:8080 \
-v "${dataRoot}:/data" \
-v ~/.azure:/root/.azure \
-e AZURE_CONFIG_DIR=/root/.azure \
-e HOST_AZURE_CONFIG_DIR=${HOME}/.azure \
-e PREFERENCES__StoragePath=/data/settings.json \
-e ASPNETCORE_ENVIRONMENT=Production \
garrardkitchen/mcp-explorer-x:latestcp .env.example .env
# Edit .env
docker compose up --buildServices: api on :5000 (internal), gateway on :8080 (external).
The api image includes the devtunnel CLI by default. If you need a custom location, set DEVTUNNELS__ExecutablePath.
MCP Explorer can resolve connection secrets from Azure Key Vault and browse App Registrations via Microsoft Graph. This requires an az login session on the host machine and the ~/.azure directory mounted into the container.
az login
az account set --subscription "<your-subscription-id>"| Resource | Role | Purpose |
|---|---|---|
| Azure Key Vault | Key Vault Secrets User | Read secret values by name or reference |
| Azure Key Vault | Key Vault Reader (optional) | List available secrets in the picker UI |
Assign with:
az role assignment create \
--assignee $(az ad signed-in-user show --query id -o tsv) \
--role "Key Vault Secrets User" \
--scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>The signed-in user needs one of:
| Permission | Type | Purpose |
|---|---|---|
Application.Read.All |
Delegated | Browse and search App Registrations |
This is a delegated permission using your az login session — no app registration or client secret is required for the explorer itself.
If Application.Read.All is not pre-consented in your tenant, a Global Administrator must grant admin consent or the user must have the Application Administrator or Cloud Application Administrator Entra role.
If your Key Vault has a firewall enabled, you must allow access from the machine running the container:
- Trusted Microsoft services — not applicable (this is not an Azure service)
- IP allowlist — add your machine's public IP:
az keyvault network-rule add \ --name <vault-name> \ --ip-address <your-public-ip>
- Private endpoint — if using a private endpoint, the container must be on the same VNet or use a VPN/tunnel.
Tip: For local development, temporarily set the Key Vault access to "Allow all networks" or add your IP. Re-enable the firewall for production.
Mount your host ~/.azure directory without :ro — Azure CLI must be able to write session files:
-v ~/.azure:/root/.azure # writable — required for token refresh
-e AZURE_CONFIG_DIR=/root/.azureCopy .env.example to .env and fill in your values. Key variables:
| Variable | Description | Default |
|---|---|---|
ASPNETCORE_ENVIRONMENT |
Runtime env — controls logging/error detail | Production |
AppMetadata__Version |
Version string shown in UI footer | 0.5.0 |
MCP_CLIENT_NAME |
Client name sent to MCP servers in handshake and User-Agent | mcp-explorer |
MCP_DATA_PATH |
Host directory mounted as /data — persists settings, workflows, models, and HTTP API definitions/history |
(anonymous volume) |
PREFERENCES__StoragePath |
Absolute path to settings.json inside the container |
/data/settings.json |
HOST_AZURE_CONFIG_DIR |
Absolute path to host ~/.azure — mounted into container for AzureCliCredential |
(empty) |
AZURE_CONFIG_DIR |
Path to .azure directory inside the container |
/root/.azure |
LlmService__OpenAiBaseUrl |
Base URL for OpenAI-compatible APIs | https://api.openai.com/v1 |
LlmService__AzureApiVersion |
Azure OpenAI API version query param | 2024-02-15-preview |
LlmService__MaxRetryAttempts |
Max LLM request retries | 3 |
LlmService__TimeoutSeconds |
LLM response timeout | 30 |
ToolInvoke__TimeoutSeconds |
MCP tool call timeout (0 = no limit) | 300 |
ToolInvoke__MaxRetryAttempts |
Tool call retry attempts | 2 |
Elicitation__TimeoutSeconds |
Elicitation dialog timeout (0 = wait forever) | 0 |
GATEWAY_PORT |
Host port the app is exposed on | 8090 |
ReverseProxy__Clusters__api-cluster__Destinations__api__Address |
API address for YARP (docker-compose only) | http://api:5000/ |
VITE_API_BASE_URL |
Browser API base URL (empty = same origin via gateway) | (empty) |
VITE_APP_VERSION |
App version baked into the frontend bundle | 0.5.0 |
Note:
VITE_*variables are baked into the static JS bundle at build time and cannot be changed at runtime.
See .env.example for the full annotated list.
All endpoints are versioned at /api/v1/. Swagger UI available at /swagger in development.
| Prefix | Description |
|---|---|
/api/v1/connections |
CRUD + connect/disconnect MCP servers |
/api/v1/tools |
List and invoke tools per connection |
/api/v1/prompts |
List and execute prompts |
/api/v1/resources |
List and read resources + templates |
/api/v1/chat |
SSE streaming chat with tool calling |
/api/v1/workflows |
Workflow CRUD, execution, load test |
/api/v1/elicitations |
SSE stream + respond to server-initiated requests |
/api/v1/llmmodels |
LLM model definitions CRUD |
/api/v1/preferences |
User preferences + theme |
/api/v1/http-apis |
HTTP API definitions CRUD, invoke/bookmark/compare/history |
/api/v1/http-api-collections |
HTTP API collection CRUD + collection run |
/api/v1/http-api-history |
Global HTTP API invocation history |
/api/v1/azure |
Azure subscription list, Key Vault secret resolution, App Registration search |
Ten built-in themes, selectable via the top-bar theme switcher or Command Palette:
| ID | Name | Mode |
|---|---|---|
command-dark |
Command Dark | Dark |
command-light |
Command Light | Light |
nord |
Nord | Dark |
dracula |
Dracula | Dark |
catppuccin |
Catppuccin Mocha | Dark |
solarized |
Solarized Light | Light |
material |
Material Dark | Dark |
material-light |
Material Light | Light |
github |
GitHub Dark | Dark |
github-light |
GitHub Light | Light |
Theme is persisted to POST /api/v1/preferences/theme and cached in localStorage.
| 📖 Documentation | mcp-explorer-x-docs.garrardkitchen.com |
| 🐛 Report a bug | Open an issue |
| 💡 Request a feature | Open an issue |
| 🔒 Report a vulnerability | Security policy |
| 🤝 Contributing guide | CONTRIBUTING.md |
| 📋 Pull request template | PR template |
| 📜 Code of conduct | CODE_OF_CONDUCT.md |
| 📚 Cite this project | CITATION.cff |
- Read CONTRIBUTING.md for the full guide.
- Fork and create a feature branch using Conventional Commits.
- Run
dotnet testanddotnet test tests/Garrard.Tests.Mcp.Explorer.Cli/Garrard.Tests.Mcp.Explorer.Cli.csproj— all tests must pass. - Run
npm run buildinsrc/frontend— zero errors. - Open a PR using the PR template.
MIT — see LICENSE.md