Move backup SSH keys and repo URL from MCPD_BACKUP_REPO env var to a
"backup-ssh" secret in the database. Keys are auto-generated on first
init and stored back into the secret. Also fix ERR_HTTP_HEADERS_SENT
crash caused by reply.send() without return in routes when onSend hook
is registered.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DB is source of truth with git as downstream replica. SSH key generated
on first start, all resource mutations committed as apply-compatible YAML.
Supports manual commit import, conflict resolution (DB wins), disaster
recovery (empty DB restores from git), and timeline branches on restore.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Persistent file cache in ~/.mcpctl/cache/proxymodel/ with LRU eviction
- Pause queue for temporarily holding MCP traffic
- Hot-reload watcher for custom stages and proxymodel definitions
- CLI: mcpctl cache list/clear/stats commands
- HTTP endpoints for cache and pause management
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
proxyMode "direct" was a security hole (leaked secrets as plaintext env
vars in .mcp.json) and bypassed all mcplocal features (gating, audit,
RBAC, content pipeline, namespacing). Removed from schema, API, CLI,
and all tests. Old configs with proxyMode are accepted but silently
stripped via Zod .transform() for backward compatibility.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite README Content Pipeline section as Plugin System section
documenting built-in plugins (default, gate, content-pipeline),
plugin hooks, and the relationship between gating and proxyModel
- Update all README examples to use --proxy-model instead of --gated
- Add unit tests: proxyModel normalization in JSON/YAML output (4 tests),
Plugin Config section in describe output (2 tests)
- Add smoke tests: yaml/json output shows resolved proxyModel without
gated field, round-trip compatibility (4 tests)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolves proxyModel from gated boolean when the DB value is empty
(pre-migration projects). The gated field is no longer included in
get -o yaml/json output, making it apply-compatible with the new schema.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- proxyModel field now determines both YAML pipeline stages AND plugin
gating behavior ('default'/'gate' = gated, 'content-pipeline' = not)
- Deprecate --gated/--no-gated CLI flags (backward compat preserved:
--no-gated maps to --proxy-model content-pipeline)
- Replace GATED column with PLUGIN in `get projects` output
- Update `describe project` to show "Plugin Config" section
- Unify proxymodel discovery: GET /proxymodels now returns both YAML
pipeline models and TypeScript plugins with type field
- `describe proxymodel gate` shows plugin hooks and extends info
- Update CLI apply schema: gated is now optional (not required)
- Regenerate shell completions
- Tests: proxymodel endpoint (5), smoke tests (8)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add userName column to AuditEvent schema with index and migration
- Add GET /api/v1/auth/me endpoint returning current user identity
- AuditCollector auto-fills userName from session→user map, resolved
lazily via /auth/me on first session creation
- Support userName and date range (from/to) filtering on audit events
and sessions endpoints
- Audit console sidebar groups sessions by project → user
- Add date filter presets (d key: all/today/1h/24h/7d) to console
- Add scrolling and page up/down to sidebar navigation
- Tests: auth-me (4), audit-username collector (4), route filters (2),
smoke tests (2)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Audit Console Phase 1: tool_call_trace emission from mcplocal router,
session_bind/rbac_decision event kinds, GET /audit/sessions endpoint,
full Ink TUI with session sidebar, event timeline, and detail view
(mcpctl console --audit).
System prompts: move 6 hardcoded LLM prompts to mcpctl-system project
with extensible ResourceRuleRegistry validation framework, template
variable enforcement ({{maxTokens}}, {{pageCount}}), and delete-resets-
to-default behavior. All consumers fetch via SystemPromptFetcher with
hardcoded fallbacks.
CLI: -p shorthand for --project across get/create/delete/config commands,
console auto-scroll improvements, shell completions regenerated.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add warmup() to LlmProvider interface for eager subprocess startup
- ManagedVllmProvider.warmup() starts vLLM in background on project load
- ProviderRegistry.warmupAll() triggers all managed providers
- NamedProvider proxies warmup() to inner provider
- paginate stage generates LLM-powered descriptive page titles when
available, cached by content hash, falls back to generic "Page N"
- project-mcp-endpoint calls warmupAll() on router creation so vLLM
is loading while the session initializes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comprehensive MCP server management with kubectl-style CLI.
Key features in this release:
- Declarative YAML apply/get round-trip with project cloning support
- Gated sessions with prompt intelligence for Claude
- Interactive MCP console with traffic inspector
- Persistent STDIO connections for containerized servers
- RBAC with name-scoped bindings
- Shell completions (fish + bash) auto-generated
- Rate-limit retry with exponential backoff in apply
- Project-scoped prompt management
- Credential scrubbing from git history
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ink statically imports react-devtools-core (only used when DEV=true).
With --external, bun compile leaves a runtime require that fails in the
standalone binary. Instead, provide a no-op stub that bun bundles inline.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ink-based TUI that shows exactly what an LLM sees through MCP.
Browse tools/resources/prompts, execute them, and see raw JSON-RPC
traffic in a protocol log. Supports gated session flow with
begin_session, raw JSON-RPC input, and session reconnect.
- McpSession class wrapping HTTP transport with typed methods
- 12 React/Ink components (header, protocol-log, menu, tool/resource/prompt views, etc.)
- 21 unit tests for McpSession against a mock MCP server
- Fish + Bash completions with project name argument
- bun compile with --external react-devtools-core
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The /llm/providers endpoint now runs isAvailable() on each provider in
parallel and returns health status per provider. The status command shows
✓/✗ per provider based on actual availability, not just the fast tier.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds tier-based LLM routing so fast local models (vLLM, Ollama) handle
structured tasks while cloud models (Gemini, Anthropic) are reserved for
heavy reasoning. Single-provider configs continue to work via fallback.
- Tier type + ProviderRegistry with assignTier/getProvider/fallback chain
- Multi-provider config format: { providers: [{ name, type, tier, ... }] }
- NamedProvider wrapper for multiple instances of same provider type
- Setup wizard: Simple (legacy) / Advanced (fast+heavy tiers) modes
- Status display: tiered view with /llm/providers endpoint
- Call sites use getProvider('fast') instead of getActive()
- Full backward compatibility with existing single-provider configs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pool refactor made ACP client creation lazy, causing the first
/llm/health call to spawn + initialize + prompt Gemini in one request
(30s+). Now warmup() eagerly starts the subprocess on mcplocal boot.
Also fetch models in parallel with LLM health check.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ACP session pool with per-model subprocesses and 8h idle eviction
- Per-project LLM config: local override → mcpd recommendation → global default
- Model override support in ResponsePaginator
- /llm/models endpoint + available models in mcpctl status
- Remove --llm-provider/--llm-model from create project (use edit/apply)
- 8 new smart pagination integration tests (e2e flow)
- 260 mcplocal tests, 330 CLI tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Status command now queries mcplocal's /llm/health endpoint instead of
spawning the gemini binary. This uses the persistent ACP connection
(fast) and works for any configured provider, not just gemini-cli.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace per-call gemini CLI spawning (~10s cold start each time) with
persistent ACP (Agent Client Protocol) subprocess. First call absorbs
the cold start, subsequent calls are near-instant over JSON-RPC stdio.
- Add AcpClient: manages persistent gemini --experimental-acp subprocess
with lazy init, auto-restart on crash/timeout, NDJSON framing
- Add GeminiAcpProvider: LlmProvider wrapper with serial queue for
concurrent calls, same interface as GeminiCliProvider
- Add dispose() to LlmProvider interface + disposeAll() to registry
- Wire provider disposal into mcplocal shutdown handler
- Add status command spinner with progressive output and color-coded
LLM health check results (green checkmark/red cross)
- 25 new tests (17 ACP client + 8 provider)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Setup wizard auto-detects gemini binary via `which`, saves full path
so systemd service can find it without user PATH
- `mcpctl status` tests LLM provider health (gemini: quick prompt test,
ollama: health check, API providers: key stored confirmation)
- Shows error details inline: "gemini-cli / gemini-2.5-flash (not authenticated)"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wait for stdout.write callback before process.exit in STDIO transport
to prevent truncation of large responses (e.g. grafana tools/list)
- Handle MCP notification methods (notifications/initialized, etc.) in
router instead of returning "Method not found" error
- Use -p shorthand in config claude output
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix MCP proxy to support SSE and STDIO transports (not just HTTP POST)
- Enrich tool descriptions with server context for LLM clarity
- Add Prompt and PromptRequest resources with two-resource RBAC model
- Add propose_prompt MCP tool for LLM to create pending prompt requests
- Add prompt resources visible in MCP resources/list (approved + session's pending)
- Add project-level prompt/instructions in MCP initialize response
- Add ServiceAccount subject type for RBAC (SA identity from X-Service-Account header)
- Add CLI commands: create prompt, get prompts/promptrequests, approve promptrequest
- Add prompts to apply config schema
- 956 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The bridge now parses SSE text/event-stream responses (extracting data:
lines) in addition to plain JSON. Also sends correct Accept header
per MCP streamable HTTP spec. Added tests for SSE handling and
command option parsing (-p/--project).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The mcp subcommand now has its own -p/--project option with
passThroughOptions(), so `mcpctl mcp --project NAME` works when Claude
spawns the process. Updated config claude to generate
args: ['mcp', '--project', project] and added Commander-level tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- New `mcpctl mcp -p PROJECT` command: STDIO-to-StreamableHTTP bridge
that reads JSON-RPC from stdin and forwards to mcplocal project endpoint
- Rework `config claude` to write mcpctl mcp entry instead of fetching
server configs from API (no secrets in .mcp.json)
- Keep `config claude-generate` as backward-compat alias
- Fix discovery.ts auth token not being forwarded to mcpd (RBAC bypass)
- Update fish/bash completions for new commands
- 10 new MCP bridge tests, updated claude tests, fixed project-discovery test
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Only set Content-Type: application/json when request body is present (fixes
Fastify rejecting empty DELETE with "Body cannot be empty" 400 error)
- Changed PROJECT_INCLUDE to return full server objects instead of just {id, name}
so project server listings show transport, package, image columns
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Added __mcpctl_needs_server_arg guard in fish and position check in
bash so completions stop after one server name is selected.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Instances have no name field — use server.name for completions
- attach-server: show only servers NOT in the project
- detach-server: show only servers IN the project
- Add helper functions for project-aware server completion
- 5 new tests covering all three fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
API returns { "resources": [...] } not bare arrays, so .[].name
produced no output. Use .[][].name to unwrap the outer object first.
Also auto-load .env in pr.sh.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The regex "name":\s*"..." on JSON matched nested server names inside
project objects, mixing resource types in completions. Switch to
jq -r '.[].name' for proper top-level extraction. Add jq as RPM
dependency. Add pr.sh for PR creation via Gitea API.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fish completions are additive — sourcing a new file doesn't remove old
rules. Add `complete -c mcpctl -e` at the top to clear stale entries.
Also add 12 structural tests to prevent completion regressions.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Hide attach-server/detach-server from --help (only relevant with --project)
- --project shows only project-scoped commands in tab completion
- Tab after resource type fetches live resource names from API
- --project value auto-completes from existing project names
- Stop offering resource types after one is already selected
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mcpctl --project NAME get servers — shows only servers attached to the project
mcpctl --project NAME get instances — shows only instances of project servers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
STDIO MCP servers read from stdin and exit on EOF. Docker containers close
stdin by default, causing all STDIO servers to crash immediately. Added
OpenStdin: true to container creation.
Describe instance now resolves server names (like logs command), preferring
RUNNING instances. Added 7 new describe tests covering server name resolution,
healthcheck display, events section, and template detail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- `mcpctl logs <server-name>` resolves to first RUNNING instance
- `mcpctl logs <server-name> -i <N>` selects specific replica
- Shows "instance N/M" hint when server has multiple replicas
- Added 5 proper tests: server name resolution, RUNNING preference,
replica selection, out-of-range error, no instances error
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Instance list now shows server NAME instead of cryptic server ID
- Include server relation in findAll query (Prisma include)
- Logs command accepts server name, server ID, or instance ID
(resolves server name → first RUNNING instance)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add global error handler: clean messages instead of stack traces
- Add --force flag to create server/secret/project: updates on 409 conflict
- Strip null values and template-only fields from --from-template payload
- Add tests: 409 handling, --force update, null-stripping from templates
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add healthCheck spec to templates and servers (tool, arguments, interval, timeout, failureThreshold)
- Add healthStatus, lastHealthCheck, events fields to instances
- Create grafana, home-assistant, node-red templates with healthcheck probes
- Add healthcheck probes to existing templates (github, slack, postgres, jira)
- Show HEALTH column in `get instances` and Events section in `describe instance`
- Display healthCheck details in `describe server` and `describe template`
- Schema + storage + display only; actual probe runner is future work
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Introduce a Helm-chart-like template system for MCP servers. Templates are
YAML files in templates/ that get seeded into the DB on startup. Users can
browse them with `mcpctl get templates`, inspect with `mcpctl describe
template`, and instantiate with `mcpctl create server --from-template=`.
Also adds Portainer deployment scripts, mcplocal systemd service,
Streamable HTTP MCP endpoint, and RPM packaging for mcpctl-local.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>