- 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>
- tests.sh: run all tests with `bash tests.sh`, summary with `--short`
- tests.sh --filter mcpd/cli: run specific package
- project-routes.test.ts: 17 new route-level tests covering CRUD,
attach/detach, and the ownerId filtering bug fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The list endpoint was filtering by ownerId before RBAC could include
projects the user has view access to via name-scoped bindings.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs fixed:
- GET /api/v1/servers/:cuid now resolves CUID→name before RBAC check,
so name-scoped bindings match correctly
- List endpoints now filter responses via preSerialization hook using
getAllowedScope(), so name-scoped users only see their resources
Also adds fulldeploy.sh orchestrator script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix resourceName assignment in mapUrlToPermission for strictness
- Use RbacRoleBinding type in restore-service instead of loose cast
- Remove stale ProjectMemberInput export from validation index
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>
SSE-transport MCP servers (like ha-mcp) use a different protocol flow:
GET /sse to establish event stream, read endpoint event, then POST
JSON-RPC messages to /messages?session_id=... URL. Previously was
POSTing to root which returned 404.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mcpd and MCP containers share the mcp-servers Docker network. HTTP probes
must use the container's internal IP + containerPort instead of localhost
+ host-mapped port. Also extracts container IP from Docker inspect.
Updated home-assistant template to use ghcr.io/homeassistant-ai/ha-mcp
Docker image (SSE transport) instead of broken npm package.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements Kubernetes-style liveness probes that call MCP tools defined
in server healthCheck configs. For STDIO servers, uses docker exec to
spawn a disposable MCP client that sends initialize + tool call. For
HTTP/SSE servers, sends JSON-RPC directly.
- HealthProbeRunner service with configurable interval/threshold/timeout
- execInContainer added to orchestrator interface + Docker implementation
- Instance findById now includes server relation (fixes describe showing IDs)
- Events appended to instance (last 50), healthStatus tracked as
healthy/degraded/unhealthy
- 12 unit tests covering probing, thresholds, intervals, cleanup
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>
- Add syncStatus() to InstanceService: detects crashed/stopped containers,
marks them ERROR with last log line as context
- Reconcile now syncs container status first (detect dead before counting)
- Add 30s periodic sync loop in main.ts
- Switch node-runner from alpine to slim (Debian) for npm compatibility
(fixes home-assistant-mcp-server binary not found on Alpine)
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>
STDIO servers with packageName (e.g. @leval/mcp-grafana) need a Node.js
container that runs `npx -y <package>`. Previously, packageName was used
as a Docker image reference causing "invalid reference format" errors.
- Add Dockerfile.node-runner: minimal node:20-alpine with npx entrypoint
- Update instance.service.ts: detect npm-based servers and use node-runner
image with npx command instead of treating packageName as image name
- Fix NanoCPUs: only set when explicitly provided (kernel CFS not available
on all hosts)
- Add mcp-servers network with explicit name for container isolation
- Configure MCPD_NODE_RUNNER_IMAGE and MCPD_MCP_NETWORK env vars
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>