Skip to content

feat: add Docker dev support for the MCP server via ./dev.sh - #4132

Merged
akira28 merged 7 commits into
mainfrom
feature/add-mcp-to-docker
Sep 18, 2026
Merged

akira28 merged 7 commits into
mainfrom
feature/add-mcp-to-docker

Conversation

@akira28

@akira28 akira28 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Extends the dev Docker setup from #4076 to cover the MCP server. Running ./dev.sh serve-mcp starts the MCP HTTP server at http://localhost:8080/docs/_mcp without a local .NET SDK install. ./dev.sh build-mcp builds only the MCP image; extra flags (e.g. --progress=plain, --no-cache) pass through to bake.

Affects: Dev tooling

Why

The MCP server is deployed as a separate service but had no dev workflow equivalent to ./dev.sh serve. Developers needed a local .NET SDK to run and test it.

What

MCP bake target and compose service

A new mcp target in docker-bake.hcl points at the production Dockerfile so there is no duplicated build logic. A matching mcp service and mcp-artifacts volume are added to docker-compose.yml.

dev.sh commands

build-mcp, serve-mcp, serve-mcp-detached, and stop-mcp follow the same conventions as the existing build/serve/stop commands. Extra arguments to build-mcp pass through to bake.

MCP Dockerfile — single AL2023 base image

The Dockerfile previously used a node:22-bookworm-slim stage to copy Node binaries into the AL2023 build image. AL2023 ships nodejs22 and nodejs22-npm packages, so the extra stage is gone and Node is now installed via dnf alongside the other build dependencies.

MCP Dockerfile — build fixes

Three bugs in the production Dockerfile were fixed. DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 was set in the build stage, causing the regex source generator to reject "en-US" culture names (SYSLIB1042) — it now lives in the runtime stage only. The RID detection now uses the same TARGETOS/TARGETARCHRID="${TARGETOS}-${arch}" pattern as Elastic.Documentation.Api/Dockerfile instead of a custom PUBLISH_RID build arg. The publish output is read from the standard .artifacts/publish/… path rather than a custom --output directory.

Verify

./dev.sh build-mcp
./dev.sh serve-mcp
# connect an MCP client to http://localhost:8080/docs/_mcp

🤖 Generated with Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

Docs preview (local build)

Handbook preview: https://docs-v3-preview.elastic.dev/elastic/docs-builder/pull/4132/

@akira28 akira28 changed the title feat: add MCP service support to local Docker setup feat: add Docker dev support for the MCP server via ./dev.sh Sep 17, 2026
@akira28
akira28 marked this pull request as ready for review September 17, 2026 15:43
@akira28
akira28 requested a review from a team as a code owner September 17, 2026 15:43
@akira28
akira28 requested a review from technige September 17, 2026 15:43

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: MCP_SERVER_PROFILE is documented as configurable for serve-mcp, but the current compose service does not pass it into the container, so profile switching does not work.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

Comment thread build/dev/docker-compose.yml
@akira28
akira28 enabled auto-merge (squash) September 17, 2026 16:00

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.


What is this? | From workflow: PR Review

Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

@akira28
akira28 disabled the stack merge September 18, 2026 07:32
@akira28
akira28 added this pull request to stack #4139 September 18, 2026 07:32
akira28 and others added 7 commits September 18, 2026 09:34
Extends the dev Docker setup introduced in c7c3ace to cover the MCP
server. Running `./dev.sh serve-mcp` starts the MCP HTTP server at
`http://localhost:8080/docs/_mcp` without requiring a local .NET SDK
install. `./dev.sh build-mcp` builds only the MCP image; extra flags
(e.g. `--progress=plain`, `--no-cache`) pass through to bake.

The MCP server uses its own production Dockerfile at
`src/api/Elastic.Documentation.Mcp.Remote/Dockerfile`. Three bugs in
that file were fixed along the way: `npm` installed Node 18 from dnf
while Playwright requires Node 20+, `DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1`
was set in the build stage (causing the regex source generator to reject
the `"en-US"` culture name), and the publish command hardcoded
`linux-x64` regardless of the builder's native architecture. Node is now
copied from `node:22-bookworm-slim`, the invariant flag moved to the
runtime stage only, and a `PUBLISH_RID` build arg controls the target
RID — defaulting to `linux-x64` for CI, falling back to `TARGETARCH`
when empty (so Apple Silicon builds `linux-arm64` natively via bake).

The OTel package bump in `Directory.Packages.props` is a forward-port of
the fix from the `fix/otel-resources-host-vulnerability` branch: the
MCP server's restore fails without it due to `NU1903` on
`OpenTelemetry.Resources.Host` 1.15.1-beta.1.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on.Api

Replace the custom PUBLISH_RID build arg and case-statement with the same
arch=$TARGETARCH / if amd64 → x64 / RID="${TARGETOS}-${arch}" pattern used
in src/api/Elastic.Documentation.Api/Dockerfile. Docker buildx always
populates TARGETARCH and TARGETOS correctly for both CI and local builds,
so the override arg was not needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…file

Match Elastic.Documentation.Api/Dockerfile exactly: drop the --output flag,
introduce a RID variable, and cp from the standard artifacts path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…de image

AL2023 ships nodejs22 and nodejs22-npm packages, so there is no need for
a separate node:22-bookworm-slim stage and the COPY/symlink dance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without explicit propagation, host env vars are ignored by Compose and
the app always uses its default profile. The variable now defaults to
"public" so the service behaves the same as before when the var is unset.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@akira28
akira28 force-pushed the feature/add-mcp-to-docker branch from 40452b9 to 51d2f1c Compare September 18, 2026 07:34
RUN curl -L https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
RUN chmod +x ./dotnet-install.sh
RUN ./dotnet-install.sh
RUN dnf update -y \

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for reducing the layers 😺

@akira28
akira28 merged commit 0afc288 into main Sep 18, 2026
32 checks passed
@akira28
akira28 deleted the feature/add-mcp-to-docker branch September 18, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants