2026-02-21 13:34:18 +00:00
|
|
|
# Stage 1: Build TypeScript
|
|
|
|
|
FROM node:20-alpine AS builder
|
|
|
|
|
|
|
|
|
|
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
|
|
|
|
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
|
|
|
|
|
# Copy workspace config and package manifests
|
|
|
|
|
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json tsconfig.base.json ./
|
|
|
|
|
COPY src/mcpd/package.json src/mcpd/tsconfig.json src/mcpd/
|
|
|
|
|
COPY src/db/package.json src/db/tsconfig.json src/db/
|
|
|
|
|
COPY src/shared/package.json src/shared/tsconfig.json src/shared/
|
feat(mcpd+deploy): serve web UI at /ui + smoke tests + docs (Stage 6)
The closing stage. mcpd now hosts the Stage 5 SPA, the Docker image
bundles the build artifact, a smoke test exercises the personality
HTTP surface end-to-end, and the user-facing docs spell out the
mental model.
mcpd:
- Add @fastify/static dep.
- New routes/web-ui.ts: registers /ui/* against a static bundle. Looks
for the bundle at $MCPD_WEB_ROOT, then /usr/share/mcpd/web (the
Docker image path), then a dev-tree fallback. Logs and skips
cleanly if missing — API-only deploys keep working.
- SPA fallback: any /ui/<path> that doesn't match a file falls through
to index.html so direct hits to react-router URLs work.
- /ui/* falls through to `kind: skip` in mapUrlToPermission, so the
static assets are served unauthenticated. Each API call from the
SPA still carries the bearer token.
Deploy:
- Dockerfile.mcpd builds the @mcpctl/web bundle in the same builder
stage and copies dist/ to /usr/share/mcpd/web in the runtime image.
Smoke (personality.smoke.test.ts):
- Live mcpd flow: create secret/llm/agent/personality, attach an
agent-direct prompt, verify the binding listing, reject double-
attach (409) + foreign-agent prompt (400), set defaultPersonality
by name, detach + delete cleanup.
Docs:
- New docs/personalities.md: VLAN-on-ethernet model, system-block
ordering table, three prompt scopes, CLI walkthrough, web UI
walkthrough, full API surface, RBAC notes.
- agents.md and chat.md cross-link.
- README's Agents section gains a Personalities subsection.
Test count after Stage 6:
mcpd: 801/801 cli: 430/430
web: 7/7 db: 58/62 (4 pre-existing)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:48:43 +01:00
|
|
|
COPY src/web/package.json src/web/tsconfig.json src/web/
|
2026-02-21 13:34:18 +00:00
|
|
|
|
|
|
|
|
# Install all dependencies
|
|
|
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
|
|
|
|
|
|
# Copy source code
|
|
|
|
|
COPY src/mcpd/src/ src/mcpd/src/
|
|
|
|
|
COPY src/db/src/ src/db/src/
|
|
|
|
|
COPY src/db/prisma/ src/db/prisma/
|
|
|
|
|
COPY src/shared/src/ src/shared/src/
|
feat(mcpd+deploy): serve web UI at /ui + smoke tests + docs (Stage 6)
The closing stage. mcpd now hosts the Stage 5 SPA, the Docker image
bundles the build artifact, a smoke test exercises the personality
HTTP surface end-to-end, and the user-facing docs spell out the
mental model.
mcpd:
- Add @fastify/static dep.
- New routes/web-ui.ts: registers /ui/* against a static bundle. Looks
for the bundle at $MCPD_WEB_ROOT, then /usr/share/mcpd/web (the
Docker image path), then a dev-tree fallback. Logs and skips
cleanly if missing — API-only deploys keep working.
- SPA fallback: any /ui/<path> that doesn't match a file falls through
to index.html so direct hits to react-router URLs work.
- /ui/* falls through to `kind: skip` in mapUrlToPermission, so the
static assets are served unauthenticated. Each API call from the
SPA still carries the bearer token.
Deploy:
- Dockerfile.mcpd builds the @mcpctl/web bundle in the same builder
stage and copies dist/ to /usr/share/mcpd/web in the runtime image.
Smoke (personality.smoke.test.ts):
- Live mcpd flow: create secret/llm/agent/personality, attach an
agent-direct prompt, verify the binding listing, reject double-
attach (409) + foreign-agent prompt (400), set defaultPersonality
by name, detach + delete cleanup.
Docs:
- New docs/personalities.md: VLAN-on-ethernet model, system-block
ordering table, three prompt scopes, CLI walkthrough, web UI
walkthrough, full API surface, RBAC notes.
- agents.md and chat.md cross-link.
- README's Agents section gains a Personalities subsection.
Test count after Stage 6:
mcpd: 801/801 cli: 430/430
web: 7/7 db: 58/62 (4 pre-existing)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:48:43 +01:00
|
|
|
COPY src/web/src/ src/web/src/
|
|
|
|
|
COPY src/web/index.html src/web/vite.config.ts src/web/
|
2026-02-21 13:34:18 +00:00
|
|
|
|
feat(mcpd+deploy): serve web UI at /ui + smoke tests + docs (Stage 6)
The closing stage. mcpd now hosts the Stage 5 SPA, the Docker image
bundles the build artifact, a smoke test exercises the personality
HTTP surface end-to-end, and the user-facing docs spell out the
mental model.
mcpd:
- Add @fastify/static dep.
- New routes/web-ui.ts: registers /ui/* against a static bundle. Looks
for the bundle at $MCPD_WEB_ROOT, then /usr/share/mcpd/web (the
Docker image path), then a dev-tree fallback. Logs and skips
cleanly if missing — API-only deploys keep working.
- SPA fallback: any /ui/<path> that doesn't match a file falls through
to index.html so direct hits to react-router URLs work.
- /ui/* falls through to `kind: skip` in mapUrlToPermission, so the
static assets are served unauthenticated. Each API call from the
SPA still carries the bearer token.
Deploy:
- Dockerfile.mcpd builds the @mcpctl/web bundle in the same builder
stage and copies dist/ to /usr/share/mcpd/web in the runtime image.
Smoke (personality.smoke.test.ts):
- Live mcpd flow: create secret/llm/agent/personality, attach an
agent-direct prompt, verify the binding listing, reject double-
attach (409) + foreign-agent prompt (400), set defaultPersonality
by name, detach + delete cleanup.
Docs:
- New docs/personalities.md: VLAN-on-ethernet model, system-block
ordering table, three prompt scopes, CLI walkthrough, web UI
walkthrough, full API surface, RBAC notes.
- agents.md and chat.md cross-link.
- README's Agents section gains a Personalities subsection.
Test count after Stage 6:
mcpd: 801/801 cli: 430/430
web: 7/7 db: 58/62 (4 pre-existing)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:48:43 +01:00
|
|
|
# Generate Prisma client and build TypeScript + web SPA
|
2026-02-21 13:34:18 +00:00
|
|
|
RUN pnpm -F @mcpctl/db db:generate
|
|
|
|
|
RUN pnpm -F @mcpctl/shared build && pnpm -F @mcpctl/db build && pnpm -F @mcpctl/mcpd build
|
feat(mcpd+deploy): serve web UI at /ui + smoke tests + docs (Stage 6)
The closing stage. mcpd now hosts the Stage 5 SPA, the Docker image
bundles the build artifact, a smoke test exercises the personality
HTTP surface end-to-end, and the user-facing docs spell out the
mental model.
mcpd:
- Add @fastify/static dep.
- New routes/web-ui.ts: registers /ui/* against a static bundle. Looks
for the bundle at $MCPD_WEB_ROOT, then /usr/share/mcpd/web (the
Docker image path), then a dev-tree fallback. Logs and skips
cleanly if missing — API-only deploys keep working.
- SPA fallback: any /ui/<path> that doesn't match a file falls through
to index.html so direct hits to react-router URLs work.
- /ui/* falls through to `kind: skip` in mapUrlToPermission, so the
static assets are served unauthenticated. Each API call from the
SPA still carries the bearer token.
Deploy:
- Dockerfile.mcpd builds the @mcpctl/web bundle in the same builder
stage and copies dist/ to /usr/share/mcpd/web in the runtime image.
Smoke (personality.smoke.test.ts):
- Live mcpd flow: create secret/llm/agent/personality, attach an
agent-direct prompt, verify the binding listing, reject double-
attach (409) + foreign-agent prompt (400), set defaultPersonality
by name, detach + delete cleanup.
Docs:
- New docs/personalities.md: VLAN-on-ethernet model, system-block
ordering table, three prompt scopes, CLI walkthrough, web UI
walkthrough, full API surface, RBAC notes.
- agents.md and chat.md cross-link.
- README's Agents section gains a Personalities subsection.
Test count after Stage 6:
mcpd: 801/801 cli: 430/430
web: 7/7 db: 58/62 (4 pre-existing)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:48:43 +01:00
|
|
|
RUN pnpm -F @mcpctl/web build
|
2026-02-21 13:34:18 +00:00
|
|
|
|
|
|
|
|
# Stage 2: Production runtime
|
|
|
|
|
FROM node:20-alpine
|
|
|
|
|
|
2026-03-08 01:14:28 +00:00
|
|
|
RUN apk add --no-cache git openssh-client \
|
|
|
|
|
&& corepack enable && corepack prepare pnpm@9.15.0 --activate
|
2026-02-21 13:34:18 +00:00
|
|
|
|
|
|
|
|
WORKDIR /app
|
|
|
|
|
|
|
|
|
|
# Copy workspace config, manifests, and lockfile
|
|
|
|
|
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
|
|
|
|
COPY src/mcpd/package.json src/mcpd/
|
|
|
|
|
COPY src/db/package.json src/db/
|
|
|
|
|
COPY src/shared/package.json src/shared/
|
|
|
|
|
|
|
|
|
|
# Install all deps (prisma CLI needed at runtime for db push)
|
|
|
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
|
|
|
|
|
|
# Copy prisma schema and generate client
|
|
|
|
|
COPY src/db/prisma/ src/db/prisma/
|
|
|
|
|
RUN pnpm -F @mcpctl/db db:generate
|
|
|
|
|
|
|
|
|
|
# Copy built output from builder
|
|
|
|
|
COPY --from=builder /app/src/shared/dist/ src/shared/dist/
|
|
|
|
|
COPY --from=builder /app/src/db/dist/ src/db/dist/
|
|
|
|
|
COPY --from=builder /app/src/mcpd/dist/ src/mcpd/dist/
|
|
|
|
|
|
feat(mcpd+deploy): serve web UI at /ui + smoke tests + docs (Stage 6)
The closing stage. mcpd now hosts the Stage 5 SPA, the Docker image
bundles the build artifact, a smoke test exercises the personality
HTTP surface end-to-end, and the user-facing docs spell out the
mental model.
mcpd:
- Add @fastify/static dep.
- New routes/web-ui.ts: registers /ui/* against a static bundle. Looks
for the bundle at $MCPD_WEB_ROOT, then /usr/share/mcpd/web (the
Docker image path), then a dev-tree fallback. Logs and skips
cleanly if missing — API-only deploys keep working.
- SPA fallback: any /ui/<path> that doesn't match a file falls through
to index.html so direct hits to react-router URLs work.
- /ui/* falls through to `kind: skip` in mapUrlToPermission, so the
static assets are served unauthenticated. Each API call from the
SPA still carries the bearer token.
Deploy:
- Dockerfile.mcpd builds the @mcpctl/web bundle in the same builder
stage and copies dist/ to /usr/share/mcpd/web in the runtime image.
Smoke (personality.smoke.test.ts):
- Live mcpd flow: create secret/llm/agent/personality, attach an
agent-direct prompt, verify the binding listing, reject double-
attach (409) + foreign-agent prompt (400), set defaultPersonality
by name, detach + delete cleanup.
Docs:
- New docs/personalities.md: VLAN-on-ethernet model, system-block
ordering table, three prompt scopes, CLI walkthrough, web UI
walkthrough, full API surface, RBAC notes.
- agents.md and chat.md cross-link.
- README's Agents section gains a Personalities subsection.
Test count after Stage 6:
mcpd: 801/801 cli: 430/430
web: 7/7 db: 58/62 (4 pre-existing)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:48:43 +01:00
|
|
|
# Copy the web SPA bundle. registerWebUi() looks for it at this path
|
|
|
|
|
# (or wherever MCPD_WEB_ROOT is set).
|
|
|
|
|
COPY --from=builder /app/src/web/dist/ /usr/share/mcpd/web/
|
|
|
|
|
|
2026-02-22 22:24:35 +00:00
|
|
|
# Copy templates for seeding
|
|
|
|
|
COPY templates/ templates/
|
|
|
|
|
|
2026-02-21 13:34:18 +00:00
|
|
|
# Copy entrypoint
|
|
|
|
|
COPY deploy/entrypoint.sh /entrypoint.sh
|
|
|
|
|
RUN chmod +x /entrypoint.sh
|
|
|
|
|
|
|
|
|
|
EXPOSE 3100
|
|
|
|
|
|
|
|
|
|
HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=10s \
|
|
|
|
|
CMD wget -q --spider http://localhost:3100/healthz || exit 1
|
|
|
|
|
|
|
|
|
|
ENTRYPOINT ["/entrypoint.sh"]
|