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>
103 lines
3.4 KiB
Bash
Executable File
103 lines
3.4 KiB
Bash
Executable File
#!/bin/bash
|
|
# Full deployment: mcpd image → k8s rollout → RPM build/publish/install
|
|
#
|
|
# Production runtime is Kubernetes (context: worker0-k8s0, namespace: mcpctl).
|
|
# The docker-compose stack under stack/ + deploy/ is kept for local/VM testing
|
|
# only and is no longer invoked from here.
|
|
#
|
|
# Infra (Deployment shape, env, RBAC, NetworkPolicies) is managed by Pulumi
|
|
# in ../kubernetes-deployment. This script runs `pulumi preview` before the
|
|
# rollout; if there is infra drift it halts so you can `pulumi up` first.
|
|
set -e
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
cd "$SCRIPT_DIR"
|
|
|
|
# Load .env
|
|
if [ -f .env ]; then
|
|
set -a; source .env; set +a
|
|
fi
|
|
|
|
KUBE_CONTEXT="${KUBE_CONTEXT:-worker0-k8s0}"
|
|
KUBE_NAMESPACE="${KUBE_NAMESPACE:-mcpctl}"
|
|
KUBE_DEPLOYMENT="${KUBE_DEPLOYMENT:-mcpd}"
|
|
PULUMI_DIR="${PULUMI_DIR:-$SCRIPT_DIR/../kubernetes-deployment}"
|
|
PULUMI_STACK="${PULUMI_STACK:-homelab}"
|
|
|
|
echo "========================================"
|
|
echo " mcpctl Full Deploy"
|
|
echo "========================================"
|
|
|
|
# --- Pre-flight: Pulumi drift check ---
|
|
echo ""
|
|
echo ">>> Pre-flight: checking for Pulumi infra drift"
|
|
echo ""
|
|
if [ -d "$PULUMI_DIR" ]; then
|
|
if [ -z "$PULUMI_CONFIG_PASSPHRASE" ]; then
|
|
echo " WARNING: PULUMI_CONFIG_PASSPHRASE not set — skipping drift check."
|
|
echo " Set it in .env or export it to enable."
|
|
else
|
|
preview_output=$(cd "$PULUMI_DIR" && pulumi preview --stack "$PULUMI_STACK" --non-interactive --diff 2>&1) || true
|
|
if echo "$preview_output" | grep -qE '^\s+[-+~]'; then
|
|
echo "$preview_output"
|
|
echo ""
|
|
echo "ERROR: Pulumi detected infra changes that have not been applied."
|
|
echo " Run: cd $PULUMI_DIR && pulumi up -s $PULUMI_STACK"
|
|
echo " Then re-run this script."
|
|
exit 1
|
|
fi
|
|
echo " No drift — infra is in sync."
|
|
fi # passphrase check
|
|
else
|
|
echo " WARNING: Pulumi repo not found at $PULUMI_DIR — skipping drift check."
|
|
fi
|
|
|
|
echo ""
|
|
echo ">>> Step 1/4: Build & push mcpd Docker image"
|
|
echo ""
|
|
bash scripts/build-mcpd.sh "$@"
|
|
|
|
echo ""
|
|
echo ">>> Step 2/4: Build & push mcplocal (HTTP-mode) Docker image"
|
|
echo ""
|
|
bash scripts/build-mcplocal.sh "$@"
|
|
|
|
echo ""
|
|
echo ">>> Step 3/4: Roll out mcpd + mcplocal on k8s ($KUBE_CONTEXT / $KUBE_NAMESPACE)"
|
|
echo ""
|
|
kubectl --context "$KUBE_CONTEXT" -n "$KUBE_NAMESPACE" rollout restart "deployment/$KUBE_DEPLOYMENT"
|
|
kubectl --context "$KUBE_CONTEXT" -n "$KUBE_NAMESPACE" rollout status "deployment/$KUBE_DEPLOYMENT" --timeout=3m
|
|
if kubectl --context "$KUBE_CONTEXT" -n "$KUBE_NAMESPACE" get deployment/mcplocal >/dev/null 2>&1; then
|
|
kubectl --context "$KUBE_CONTEXT" -n "$KUBE_NAMESPACE" rollout restart deployment/mcplocal
|
|
kubectl --context "$KUBE_CONTEXT" -n "$KUBE_NAMESPACE" rollout status deployment/mcplocal --timeout=3m
|
|
else
|
|
echo " NOTE: deployment/mcplocal does not exist in the cluster yet — skipping rollout."
|
|
echo " Apply the Pulumi stack in ../kubernetes-deployment to create it."
|
|
fi
|
|
|
|
echo ""
|
|
echo ">>> Step 4/4: Build, publish & install RPM"
|
|
echo ""
|
|
bash scripts/release.sh
|
|
|
|
echo ""
|
|
echo ">>> Post-deploy: Restart mcplocal"
|
|
echo ""
|
|
systemctl --user restart mcplocal
|
|
sleep 2
|
|
|
|
echo ""
|
|
echo ">>> Post-deploy: Smoke tests"
|
|
echo ""
|
|
export PATH="$HOME/.npm-global/bin:$PATH"
|
|
if pnpm test:smoke; then
|
|
echo " Smoke tests passed!"
|
|
else
|
|
echo " WARNING: Smoke tests failed! Verify mcplocal + mcpd are healthy."
|
|
fi
|
|
|
|
echo ""
|
|
echo "========================================"
|
|
echo " Full deploy complete!"
|
|
echo "========================================"
|