Ship eic-mcp: launcher for the EIC MCP servers#328
Closed
aprozo wants to merge 1 commit into
Closed
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new eic-mcp launcher to the EIC container image, intended to orchestrate running multiple EIC MCP servers locally over SSE for LLM clients (including cloning/building servers into $HOME, starting/stopping them, and exposing /sse endpoints).
Changes:
- Introduces
containers/eic/eic-mcp, a registry-driven Bash launcher withsetup/up/down/status/logssubcommands for MCP servers. - Adds the launcher to the image as
/opt/local/bin/eic-mcpvia a newCOPY --chmod=0755line in the EIC Dockerfile.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| containers/eic/eic-mcp | New Bash launcher that manages cloning/building and running MCP servers as local SSE services. |
| containers/eic/Dockerfile | Installs eic-mcp into the final image under /opt/local/bin. |
Comment on lines
+94
to
+100
| run_in_env() { | ||
| if in_container; then bash -lc "$1"; else | ||
| local sif; sif="$(find_sif)" | ||
| [ -z "$sif" ] && { echo "ERROR: not inside eic-shell and no eic_xl .sif found (set EIC_MCP_SIF)." >&2; exit 1; } | ||
| apptainer exec "$sif" bash -lc "$1" | ||
| fi | ||
| } |
Comment on lines
+101
to
+109
| run_in_env_bg() { # cmd pidfile logfile — setsid → own process group for reliable stop | ||
| local cmd="$1" pidfile="$2" logfile="$3" sif | ||
| if in_container; then | ||
| setsid bash -lc "$cmd" >"$logfile" 2>&1 </dev/null & echo $! >"$pidfile" | ||
| else | ||
| sif="$(find_sif)"; [ -z "$sif" ] && { echo "ERROR: no eic_xl .sif found (set EIC_MCP_SIF)." >&2; return 1; } | ||
| setsid apptainer exec "$sif" bash -lc "$cmd" >"$logfile" 2>&1 </dev/null & echo $! >"$pidfile" | ||
| fi | ||
| } |
| || echo "Some servers failed — check 'eic-mcp logs <name>'." >&2 | ||
| return $rc | ||
| } | ||
| pid_on_port() { command -v ss >/dev/null 2>&1 || return 0; ss -ltnpH "sport = :$1" 2>/dev/null | grep -oP 'pid=\K[0-9]+' | head -1; } |
| printf "%-8s %-30s %s\n" "$n" "http://$BIND_HOST:${SRV_PORT[$n]}/sse" "$st" | ||
| done | ||
| } | ||
| cmd_logs() { local n="${1:-}"; [ -z "$n" ] && { echo "usage: eic-mcp logs <name>" >&2; exit 2; }; exec tail -n 100 -f "$LOG_DIR/$n.log"; } |
Comment on lines
+212
to
+230
| stop_one() { | ||
| local name="$1" pidfile="$RUN_DIR/$1.pid" port="${SRV_PORT[$1]:-}" pid i | ||
| [ -z "$port" ] && { echo " (unknown server $name)"; return; } | ||
| if [ -f "$pidfile" ]; then | ||
| pid="$(cat "$pidfile" 2>/dev/null)" | ||
| if [[ "$pid" =~ ^[0-9]+$ ]]; then | ||
| kill -TERM -"$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true | ||
| for ((i = 0; i < 6; i++)); do port_open "$port" || break; sleep 1; done | ||
| if port_open "$port"; then kill -KILL -"$pid" 2>/dev/null || kill -KILL "$pid" 2>/dev/null || true; fi | ||
| fi | ||
| rm -f "$pidfile" | ||
| fi | ||
| if port_open "$port"; then pid="$(pid_on_port "$port")"; [[ "$pid" =~ ^[0-9]+$ ]] && kill -9 "$pid" 2>/dev/null || true; fi | ||
| if port_open "$port"; then | ||
| echo " ✗ $name: still listening on $port — could not stop (check 'eic-mcp logs $name' or kill it manually)" >&2 | ||
| else | ||
| echo " stopped $name" | ||
| fi | ||
| } |
Add /opt/local/bin/eic-mcp, a small registry-driven launcher that sets up
(clone + build into $HOME) and runs the EIC MCP servers (uproot, xrootd, rucio;
extensible) as local SSE services for an LLM client such as opencode:
eic-mcp setup # one-time: clone + build the enabled servers
eic-mcp up # start them (SSE on 127.0.0.1:9101-9103)
eic-mcp list | status | logs <name> | down
New servers are added with a drop-in ~/.config/eic-mcp/servers.d/*.conf (no edit
to the script). stdio-only servers are exposed over SSE via a supergateway bridge.
Depends on the servers' SSE-transport support (PRs to eic/{uproot,xrootd,rucio}
-mcp-server); until those land, 'eic-mcp setup' clones their feat/sse-transport
branches and falls back to the default branch otherwise.
Author
|
Thanks for the review — addressed all five in the latest push (force-updated `feat/eic-mcp`):
Verified in `eic_xl:nightly`: `up` → live MCP calls (xrootd `list_directory`, rucio `list_scopes`) → `down`, plus the traversal guard. |
wdconinc
reviewed
Jul 1, 2026
Contributor
There was a problem hiding this comment.
This tool should be managed and CI-tested outside of the container repository.
aprozo
added a commit
to aprozo/containers
that referenced
this pull request
Jul 17, 2026
Supersedes the bundled eic-mcp script (and eic#328): the launcher and every MCP server now come from eic-spack recipes and land in /opt/local/bin via the environment view. - xl/spack.yaml: add eic-mcp, opencode, rucio-eic-mcp-server, supergateway, uproot-mcp-server (xrootd-/zenodo-mcp-server were already in) - packages.yaml: version/variant requires for the new packages; py-mcp +cli - drop containers/eic/eic-mcp and its Dockerfile COPY - eic-spack.sh: TESTING pin to aprozo/eic-spack feat/mcp-servers (contains the recipes); to be reverted to eic/eic-spack at the merge SHA
aprozo
added a commit
to aprozo/containers
that referenced
this pull request
Jul 17, 2026
The launcher and every MCP server now come from eic-spack recipes and land in /opt/local/bin via the environment view (supersedes eic#328: no bundled script, no runtime clone/build). - xl/spack.yaml: add eic-mcp, opencode, rucio-eic-mcp-server, supergateway, uproot-mcp-server (xrootd-/zenodo-mcp-server were already in) - packages.yaml: version/variant requires for the new packages; py-mcp +cli - eic-spack.sh: TESTING pin to aprozo/eic-spack (develop@dd8a73e + the MCP recipes); to be reverted to eic/eic-spack at the merge SHA
aprozo
added a commit
to aprozo/containers
that referenced
this pull request
Jul 17, 2026
The eic-mcp launcher and every MCP server now come from eic-spack recipes and land in /opt/local/bin via the environment view (supersedes eic#328: no bundled script, no runtime clone/build). - xl/spack.yaml: add eic-mcp, rucio-eic-mcp-server, supergateway, uproot-mcp-server (xrootd-/zenodo-mcp-server were already in) - packages.yaml: version requires for the servers and bridge; py-mcp +cli
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
/opt/local/bin/eic-mcp, a small registry-driven launcher that sets up and runs the EIC MCPservers as local SSE services for an LLM client (opencode, Claude Code, Copilot, …) inside eic-shell:
Why
The MCP servers need a little orchestration — build, run over SSE, per-server auth/config — that
isn't shipped today.
eic-mcpprovides it as one command, so a user only needseic-shell. It isregistry-driven: new EIC servers are added with a drop-in
~/.config/eic-mcp/servers.d/*.conf(noedit to the script), and stdio-only servers are exposed over SSE via a
supergatewaybridge.Details
containers/eic/eic-mcp(bash),COPY'd to/opt/local/bin/eic-mcp— same pattern aseic-shell/eic-info/eic-news.rucioauthenticates automatically with the shared read-onlyeicreadaccount from/opt/rucio/etc/rucio.cfg— no secrets in the image.$HOME/eic-mcp/servers(not the image), so the image change is just onescript + one
COPYline.Dependency
Full SSE functionality depends on SSE-transport support in the servers (PRs to
eic/uproot-mcp-server,eic/xrootd-mcp-server,eic/rucio-eic-mcp-server). Until those merge,eic-mcp setupclones theirfeat/sse-transportbranches and falls back to the default branchotherwise. (Once
xrootd-mcp-server/zenodo-mcp-serverare packaged and built in the image,eic-mcpcan also run those directly instead of cloning.)Test
Verified end-to-end inside
eic_xl:nightly:eic-mcp setup(fresh venv/npm build) →up(uproot/xrootd/rucio all ready) → live MCP tool calls (rucio
list_scopes, xrootdlist_directoryagainst JLab, uproot
get_file_structure) →down. Lifecycle edge cases (double-upidempotency,port-in-use, unknown server) covered by a test harness.