- status.ts + api-client.ts now dispatch on URL scheme so an https
mcpd URL no longer crashes with "Protocol https: not supported".
Caught by fulldeploy smoke runs — status.ts had `import http` only
and was synchronously throwing against https://mcpctl.ad.itaz.eu.
Each http.get call is wrapped so future scheme-mismatch errors also
degrade to "unreachable" instead of a stack trace.
- .dockerignore no longer excludes src/mcplocal/ (the new
Dockerfile.mcplocal needs those files).
- scripts/demo-mcp-call.py: standalone, stdlib-only Python demo that
makes an MCP request (initialize + tools/list, optional tools/call)
using an mcpctl_pat_ bearer. Counterpart to `mcpctl test mcp` for
showing external (e.g. vLLM) clients how the bearer flow works.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Delivers the final piece of the mcptoken stack: a containerized,
network-accessible mcplocal that serves Streamable-HTTP MCP to off-host
clients (the vLLM use case), authenticated by project-scoped McpTokens.
New binary (same package, new entry):
- src/mcplocal/src/serve.ts — HTTP-only entry. Reads MCPLOCAL_MCPD_URL,
MCPLOCAL_MCPD_TOKEN, MCPLOCAL_HTTP_HOST/PORT, MCPLOCAL_CACHE_DIR from
env. No StdioProxyServer, no --upstream.
- src/mcplocal/src/http/token-auth.ts — Fastify preHandler that
validates mcpctl_pat_ bearers via mcpd's /api/v1/mcptokens/introspect.
30s positive / 5s negative TTL. Rejects wrong-project with 403.
Shared HTTP MCP client:
- src/shared/src/mcp-http/ — reusable McpHttpSession with initialize,
listTools, callTool, close. Handles http+https, SSE, id correlation,
distinct McpProtocolError / McpTransportError. Plus mcpHealthCheck
and deriveBaseUrl helpers.
New CLI verb `mcpctl test mcp <url>`:
- Flags: --token (also $MCPCTL_TOKEN), --tool, --args (JSON),
--expect-tools, --timeout, -o text|json, --no-health.
- Exit codes: 0 PASS, 1 TRANSPORT/AUTH FAIL, 2 CONTRACT FAIL.
Container + deploy:
- deploy/Dockerfile.mcplocal (Node 20 alpine, multi-stage, pnpm
workspace, CMD node src/mcplocal/dist/serve.js, VOLUME
/var/lib/mcplocal/cache, HEALTHCHECK on :3200/healthz).
- scripts/build-mcplocal.sh mirrors build-mcpd.sh.
- fulldeploy.sh is now a 4-step pipeline that also builds + rolls out
mcplocal (gated on `kubectl get deployment/mcplocal` so the script
stays green before the Pulumi stack lands).
Audit + cache:
- project-mcp-endpoint.ts passes MCPLOCAL_CACHE_DIR into FileCache at
both construction sites and, when request.mcpToken is present, calls
collector.setSessionMcpToken(id, ...) so audit events carry the
tokenName/tokenSha.
Tests:
- 9 unit cases on `mcpctl test mcp` (happy path, health miss,
expect-tools hit/miss, transport throw, tool isError, json report,
$MCPCTL_TOKEN env fallback, invalid --args).
- Smoke test src/mcplocal/tests/smoke/mcptoken.smoke.test.ts —
gated on healthz($MCPGW_URL), skipped cleanly when unreachable.
Covers happy path, wrong-project 403, --expect-tools contract
failure, and revocation 401 within the negative-cache window.
1773/1773 workspace tests pass. Pulumi resources (Deployment, Service,
Ingress, PVC, Secret, NetworkPolicy) still need to land in
../kubernetes-deployment before the smoke gate flips on.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a per-server tools/list cache in McpRouter (positive + negative TTL)
so a slow or dead upstream only stalls the first discovery call, not every
subsequent client request. Invalidated on upstream add/remove.
Health probes now apply a default liveness spec (tools/list via the real
production path) to any RUNNING instance without an explicit healthCheck,
so synthetic and real failures converge on the same signal.
Includes supporting updates in mcpd-client, discovery, upstream/mcpd,
seeder, and fulldeploy/release scripts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The publish-rpm step was deleting the existing package by version
before uploading, but Gitea RPM registry keys by version (not
version+arch). When building both amd64 and arm64 in a matrix,
the second job would delete the first job's upload.
Remove the delete-before-upload pattern. Gitea supports multiple
architectures under the same version. Handle 409 (already exists)
gracefully instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- nfpm.yaml: use ${NFPM_ARCH} (Go's ExpandEnv doesn't support :-default)
- arch-helper.sh: export RPM_ARCH (x86_64/aarch64) alongside NFPM_ARCH
- build-rpm/deb.sh: build TypeScript before running tests (tests need
built @mcpctl/shared), generate Prisma client on fresh checkout
- Fix RPM filename matching to use aarch64 not arm64
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Build scripts now check for required tools before building and install
them automatically if missing. Handles both amd64 and arm64 host systems.
- pnpm: installed via corepack or npm
- bun: installed via official install script
- nfpm: downloaded from GitHub for the correct host architecture
- node_modules: runs pnpm install if missing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add cross-architecture build support so the project can be developed on
ARM64 (Fedora aarch64 laptop) while still producing amd64 packages for
production. All build, package, publish, and install scripts are now
architecture-aware via shared arch-helper.sh detection.
- Add scripts/arch-helper.sh for shared architecture detection
- CI builds both amd64 and arm64 in matrix strategy
- nfpm.yaml uses NFPM_ARCH env var instead of hardcoded amd64
- Build scripts support MCPCTL_TARGET_ARCH for cross-compilation
- installlocal.sh auto-detects RPM/DEB and filters by architecture
- release.sh gains --both-arches flag for dual-arch releases
- Package cleanup is arch-scoped (won't clobber other arch's packages)
- build-mcpd.sh supports --platform and --multi-arch flags
- Add pnpm scripts: rpm:build:amd64, deb:build:arm64, release:both
- Conditional rpm/dpkg-deb checks for cross-distro compatibility
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Internal API calls still use 10.0.0.194:3012, but all user-facing
install instructions now use the public GITEA_PUBLIC_URL.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Support DEB packaging alongside RPM for Debian trixie (13/stable),
forky (14/testing), Ubuntu noble (24.04 LTS), and plucky (25.04).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the minimal CI workflow with a complete build/release pipeline:
- lint, typecheck, test (parallel, every push/PR)
- build: TS + completions + bun binaries + RPM packaging
- docker: build & push all 4 images (mcpd, node-runner, python-runner, docmost-mcp)
- publish-rpm: upload RPM to Gitea packages
- deploy: update Portainer stack
Also adds scripts/link-package.sh shared helper to auto-link packages
to the repository (Gitea 1.24+ API with graceful fallback), called from
all build/publish scripts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check via Gitea API whether the uploaded package is linked to the
repository and warn with manual linking URL if not (Gitea 1.22 has
no API for automated linking).
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>
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>
Scripts now auto-load .env for GITEA_TOKEN, handle re-publishing
by deleting existing versions first, and release.sh does build +
publish + install in one command.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bundles the CLI into a standalone binary via bun compile, packages
as RPM with nfpm, and publishes to Gitea's built-in package registry.
Users install with: dnf config-manager --add-repo <gitea>/rpm.repo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>