Tool servers and the Model Context Protocol (MCP)

Last updated on 2026-11-10 | Edit this page

Overview

Questions

  • What is MCP, and what problem does it solve?
  • What can the EIC tool servers do?
  • How do you connect an assistant to them?

Objectives

  • Start the servers, check them, and read a server log when something fails (eic-mcp up/status/logs).
  • Generate the connection file for your own client with eic-mcp config.
  • Discover a real DIS dataset by prompting, without hard-coding names or paths.
  • Judge which returned quantities are worth verifying, and against what.

One interface for tools


Tools are the only way an assistant can act (Episode 1). The Model Context Protocol (MCP) defines a standard interface for them: write a tool once as a server, and any client (assistant) that supports MCP can use it.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart LR
    accTitle: {EIC MCP data tools}
    accDescr: {EIC MCP data tools}
    A["AI assistant<br/>opencode · Copilot · Cursor"]:::core <-->|"MCP"| S["uproot tool server<br/>(MCP, in eic-shell)"]:::tool
    S <-->|"uproot"| F["EDM4eic ROOT file"]:::data
    classDef core fill:#e7efff,stroke:#4c6ef5,stroke-width:1.5px,color:#10204a;
    classDef tool fill:#e6f7ed,stroke:#2f9e44,stroke-width:1.5px,color:#0b3d1f;
    classDef data fill:#fff4e0,stroke:#f08c00,stroke-width:1.5px,color:#5c3b00;

This lesson uses three servers. They run inside eic-shell, and the assistant talks to them over a local web address.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart LR
    accTitle: {EIC MCP data tools}
    accDescr: {EIC MCP data tools}
    R["rucio-mcp<br/>find the dataset"]:::tool -->|"file locations"| X["xrootd-mcp<br/>check the files"]:::tool
    X -->|"checked files"| U["uproot-mcp<br/>analyze in place"]:::core
    classDef tool fill:#e6f7ed,stroke:#2f9e44,stroke-width:1.5px,color:#0b3d1f;
    classDef core fill:#e7efff,stroke:#4c6ef5,stroke-width:1.5px,color:#10204a;
  • rucio-mcp searches the data catalog: it finds a dataset by name, lists its files, and gives their root:// locations.
  • xrootd-mcp browses the data store and checks that the files exist.
  • uproot-mcp reads a root:// file in place. It can list a file’s contents, compute statistics and histograms, and run short NumPy calculations over one file or a whole dataset. It returns small summaries (counts, bin edges, statistics) that you can check, not raw data. The calculations run in a sandbox that cannot install software or write files.

Start the servers and connect


You did this once on the Setup page. Inside eic-shell, in your analysis directory:

BASH

$ eic-mcp up                # start uproot, xrootd, rucio
$ eic-mcp status            # which are running; `eic-mcp logs uproot` shows a server's log
$ eic-mcp config opencode   # write opencode.jsonc in this directory
$ opencode

In the session, /mcp lists the connected servers and their tools. Other clients use the same URLs: eic-mcp config claude (or copilot, vscode, cursor, codex) writes the file where that client reads it.

List the available campaigns


ePIC data is organized by production campaign, a version such as 26.06.0. The campaign, the beam/target, and the physics process are all part of the rucio DID (e.g. epic:/RECO/26.06.0/epic_craterlake/DIS/pythia8.316-1.0/NC/noRad/ep/18x275/...). Before locating a specific dataset, check which campaigns exist so you use a current one:

Using the rucio tools, find which production campaigns are available (the version field in the DIDs, e.g. 26.06.0) and show the most recent few.

Watch how the assistant does this: the catalog holds thousands of datasets in no particular order, so looking at only the first page of results can miss the newest campaigns.

Challenge

Exercise: locate a dataset (≈ 10 min)

Ask your assistant:

Use the rucio tools to find the ePIC reconstructed-DIS dataset for the BeAGLE eCu ep 10x115 GeV sample in campaign 26.04.1. Report its DID and its number of files, give me the root:// URLs of its first three files, and confirm with the xrootd tools that those three exist.

The dataset is epic:/RECO/26.04.1/epic_craterlake/DIS/BeAGLE1.03.02-1.0/eCu/ep/10x115/q2_1to1000, with 374 files. A file’s URL is root://epicxrd1.sdcc.bnl.gov:1095//eic/EPIC followed by its rucio name (/RECO/…); list_file_replicas returns the same URL.

Inspect the dataset


Take one of the root:// URLs from the previous exercise (written below as root://epicxrd1.sdcc.bnl.gov:1095//…) and analyze it in place.

Challenge

Exercise: check the schema (≈ 5 min)

Issue the request:

Using the uproot tools, report the schema of the events in dataset in root://epicxrd1.sdcc.bnl.gov:1095//<your-discovered-file>.root and list the members of the ReconstructedChargedParticles collection.

The assistant reads the structure of the events tree and reports something like:

OUTPUT

File:  root://epicxrd1.sdcc.bnl.gov:1095//…/<dataset-file>.root
Tree:  events   — branches grouped by collection

ReconstructedChargedParticles collection:
  ReconstructedChargedParticles.PDG          int32[]   PDG particle-ID code
  ReconstructedChargedParticles.momentum.x   float[]   p_x [GeV]
  ReconstructedChargedParticles.momentum.y   float[]   p_y [GeV]
  ReconstructedChargedParticles.momentum.z   float[]   p_z [GeV]
  … energy, charge, mass, type, referencePoint.*, covMatrix.*

The names are read from the file, not guessed, so the assistant cannot invent branch names.

Challenge

Exercise: identify the species present (≈ 5 min)

Issue the request:

Using the uproot tool execute_kernel on the same file, count the ReconstructedChargedParticles per PDG code (np.unique with return_counts; no import, np and ak are predefined) and show a table of species, count and fraction.

The assistant makes a histogram with one bin per PDG code. A reconstructed-DIS file gives, for example:

OUTPUT

   PDG  species   count
  -211   pi-      11447
   211   pi+       9885
    11   e-        4489
     0   unID      2971      <- tracks with no PID hypothesis
  -321   K-        1662
   321   K+        1588
   -11   e+         967
 -2212   pbar       693
  2212   p          684      <- protons are rare
Bar histogram of reconstructed charged-particle PDG codes, with pions dominating and protons rare
Reconstructed charged-particle species

Pions dominate; protons are rare (≈ 2%), so the Λ⁰ signal will be small. A sizeable fraction of tracks have no PID (code 0) or a wrong one. This misidentification adds to the combinatorial background, which is why we fit the peak instead of counting it.

Challenge

Exercise: a bare question (≈ 5 min)

The prompts above name the tool and the branches. Now ask without any of that, in a new session, and count the tool calls:

How many events are in the first file of the campaign 26.04.1 BeAGLE eCu ep 10x115 reconstructed-DIS dataset?

1,223. In a test run with a small model this took 16 tool calls and 102 s: it never asked the catalog, walked the store directory by directory, and two calls failed. Note your own count; Episode 4 repeats the question.

Callout

Verify the returned quantities

Look at the returned numbers (bin edges, counts, statistics). Do the PDG peaks fall at physical codes, and are the proton and pion yields plausible? Episode 4 turns this into explicit success criteria.

The same Λ⁰ peak can be obtained without MCP, with ROOT RDataFrame, TTreeReader, plain uproot, or the PODIO Frame API; scripts are in extras/.

The assistant can now query the data through tools whose output you can check, but it has to be told how each time. The next episode writes that down once, in AGENTS.md and a skill.

If something fails


Shared cluster node: ports there are open to every user. Skip eic-mcp up and eic-mcp config; inside eic-shell, let the client start its own servers:

BASH

$ R=https://raw.githubusercontent.com/eic/tutorial-mcp/main/files/mcp-config/cluster
$ curl -fsSL -o opencode.jsonc $R/opencode.jsonc && opencode    # opencode
$ curl -fsSL -o .mcp.json $R/claude.json && claude              # Claude Code
Key Points
  • MCP servers give an assistant tools; any MCP assistant can use them.
  • eic-mcp up starts the servers in eic-shell and eic-mcp config opencode connects opencode.