All in One View

Content from Generative AI as an agentic research tool


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

Overview

Questions

  • How does an agentic assistant differ from a chat model?
  • What are the parts of an LLM “harness”?
  • How do we get trustworthy results from a stochastic model?

Objectives

  • Trace one pass of the agent loop for a physics task, naming what enters the context at each step.
  • Draft acceptance criteria precise enough that a grader — human or agent — can apply them.
  • Tell a tool from a skill.

The bottleneck is rarely the physics


In a typical analysis the physics steps are few: select a final state, build an observable, fit a signal. Most of the time goes into the software around them: finding datasets, learning the data model, getting branch names and units right, and iterating on plotting and analysis code. LLMs can reduce this work, but only if their results can be checked. Later episodes apply this to the decay \(\Lambda^0 \to p\,\pi^-\).

Two modes of use


A chat completion is a single request: a prompt goes in, text comes out. If the text is code, you run it, read the error, and paste it back by hand. The model never observes your data or the result of running anything.

An agentic loop runs the same model inside a control structure that lets it act. The model proposes an action, an external tool carries it out, the result is added to the context, and the model is called again, until a stopping condition is met. The model then works from the actual state of your files and the output of real computations, not only from its training.

In this lesson “generative AI” means an LLM-based coding assistant used in this agentic mode. Machine learning for reconstruction or particle identification is not covered.

The agent loop


The model calls a tool, looks at what came back, and decides whether it is done.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart TD
    accTitle: {The agent loop}
    accDescr: {The agent loop}
    Q["task"]:::user --> M["model"]:::core
    M -->|"tool call"| T["tools<br/>read files · run code · query "]:::tool
    T -->|"result appended to context"| D{"task<br/>complete?"}:::core
    D -->|"no"| M
    D -->|"yes"| R["result + provenance"]:::out
    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 out fill:#f3e8ff,stroke:#7048e8,stroke-width:1.5px,color:#2e1065;
    classDef user fill:#f1f3f5,stroke:#868e96,stroke-width:1.5px,color:#212529;

The model together with the software that runs this cycle is called a harness. It has four parts:

  • Model: reasoning and code generation. The provider and model can be swapped; they are not part of the method.
  • Context: everything the model sees in a step: instructions, files, earlier turns, tool outputs. Its size is limited (the context window).
  • Tools: operations the model may call. Tools are the only way the model can change anything outside itself.
  • Control loop: repeats propose → execute → observe until the task is done. This is what separates an agent from a chatbot.

Extending the harness

Current assistants add a few standard extension points to this core.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart TB
    accTitle: {AI agent harness}
    accDescr: {AI agent harness}
    MCP["MCP servers<br/>external tools & data"]:::tool --> H
    SUB["subagents<br/>specialized, isolated context"]:::tool --> H
    HOOKS["hooks<br/>lifecycle automation"]:::tool --> H
    MON["monitors<br/>watch & react"]:::tool --> H
    H(["harness core<br/>model + context + tools + loop"]):::core
    classDef core fill:#e7efff,stroke:#4c6ef5,stroke-width:1.5px,color:#10204a;
    classDef tool fill:#e6f7ed,stroke:#2f9e44,stroke-width:1.5px,color:#0b3d1f;

This lesson uses two ways to extend a harness: * MCP servers: tools in a standard form that works with any assistant. This is how the assistant gets physics tools (Episode 3). * Skills: versioned procedures (a SKILL.md) loaded when a request matches (Episode 4). A tool is a capability; a skill is a recipe.

Assistants also offer subagents (a separate assistant for a sub-task), hooks (scripts run at fixed moments, e.g. after every edit), and monitors (watch a background job).

The verification loop


An agent stops when it decides the task is complete. An LLM is stochastic: the same prompt can give different outputs, and a confident answer is not evidence of a correct one. A second loop around the first handles this. A grader checks the agent’s result against explicit acceptance criteria, and a failure goes back to the agent as feedback for another attempt.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart TD
    accTitle: {The verification loop}
    accDescr: {The verification loop}
    A["agent loop"]:::core -->|"draft result"| G["grader<br/>explicit success criteria"]:::tool
    G --> P{"pass?"}:::core
    P -->|"no — feedback into context"| A
    P -->|"yes"| R["accepted result"]:::out
    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 out fill:#f3e8ff,stroke:#7048e8,stroke-width:1.5px,color:#2e1065;

Treat every model output as a hypothesis and accept it only after checking it against something external: the data, a fit statistic, a known physical value, or an independent implementation. Prefer tools that return small quantities you can inspect (counts, bin edges, fit parameters), and keep a record of what was run.

Challenge

Exercise: acceptance criteria (≈ 5 min)

An assistant reports “the Λ⁰ peak was fitted successfully”. Write three or four checks the fit must pass before you accept it. Each must be a comparison a script could evaluate.

  • \(|\mu - 1.115683\,\mathrm{GeV}| < 5\,\mathrm{MeV}\).
  • \(\sigma\) of a few MeV, the detector resolution.
  • \(\chi^2/\mathrm{ndf}\) of order 1.
  • Enough entries in the fit window for the fit to mean anything.

“The fit looks good” is not a criterion. In Episode 4 these checks go into the lambda-fit skill; an agent that fails them must report the failure instead of a result.

Outer loops


Two more loops can wrap these. In an operation loop an event (a schedule, a finished production job, a new pull request) starts the agent and its verified output updates a report or a documentation page; Episode 6 shows ePIC examples. In an improvement loop you read what the agent actually did and change the harness: clarify AGENTS.md, tighten a skill’s success criteria, add a missing tool.

A person decides at each level: approving a sensitive tool call, signing off a graded result, reviewing what runs unattended, and choosing which harness changes to keep.

Key Points
  • An agentic assistant runs tools and reads their output; a chat model only returns text.
  • Treat every result as a hypothesis and check it against explicit criteria before you accept it.
  • A tool is a capability; a skill is a recipe.

The loop schematics are adapted from The Art of Loop Engineering.

Content from The measurement: Λ⁰ → p π⁻


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

Overview

Questions

  • What is the p π⁻ invariant-mass observable, and its peak and background?
  • Which EDM4eic collections and units does it need?

Objectives

  • Write m(p, π) in terms of the four branches and the PDG masses.
  • State the expected peak position, width scale, and background shape before looking at data.

The observable


The Λ⁰ decays weakly to p π⁻ (branching fraction ≈ 63.9%, cτ ≈ 7.9 cm). It is neutral, so we reconstruct it from its charged daughters. Assign the proton mass to one track and the pion mass to the other, using the reconstructed particle ID:

\[E_i = \sqrt{|\vec{p}_i|^2 + m_i^2}, \qquad m(p, \pi) = \sqrt{(E_1 + E_2)^2 - |\vec{p}_1 + \vec{p}_2|^2}\]

True Λ⁰ decays accumulate in a peak at 1.115683 GeV. Its width, a few MeV, is the detector resolution; the natural width is negligible. Random proton–pion pairs form a smooth combinatorial background under the peak, so the yield comes from a fit: a Gaussian on a second-order polynomial (Episode 5). The charge-conjugate mode Λ̄ → p̄ π⁺ is reconstructed the same way.

Quantity Value
m(Λ⁰) 1.115683 GeV
m(p) 0.9382720813 GeV
m(π±) 0.13957061 GeV

The data model


ePIC reconstruction output uses EDM4eic. A file contains an events tree; each entry is one event, each branch a collection. We need one collection and four members:

events  (tree; one entry per event)
    ReconstructedChargedParticles.PDG          reconstructed particle-ID hypothesis
    ReconstructedChargedParticles.momentum.x   p_x  [GeV]
    ReconstructedChargedParticles.momentum.y   p_y  [GeV]
    ReconstructedChargedParticles.momentum.z   p_z  [GeV]

Select protons (2212) and π⁻ (-211) for Λ⁰, antiprotons (-2212) and π⁺ (211) for Λ̄.

You do not download a file. In Episode 3 the assistant uses the rucio tools to find a DIS dataset and xrootd to verify its files, then reads one of the dataset’s root:// URLs (e.g. root://epicxrd1.sdcc.bnl.gov:1095//...) in place with the uproot tools. It reads these branches without you writing any I/O code.

Key Points
  • The Λ⁰ shows up as a peak at 1.1157 GeV in the proton–pion invariant mass.
  • The peak width comes from detector resolution.

Content from 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.

Content from Persisting instructions: AGENTS.md and SKILL.md


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

Overview

Questions

  • How do you give an assistant durable project context?
  • AGENTS.md or SKILL.md: when to use each?
  • What’s in a usable SKILL.md for the Λ⁰ fit?

Objectives

  • Say what belongs in AGENTS.md and what in a SKILL.md.
  • Install the lesson’s AGENTS.md and lambda-fit skill.
  • Write a small SKILL.md with success criteria.

Two ways to make instructions persistent


Typed requests (Episode 3) have to repeat the data model, conventions, and procedure every session, and two runs can differ. Two kinds of file solve this.

  • AGENTS.md: context read at the start of every session: environment, data model, conventions, and what “done” means.
  • SKILL.md: a procedure in a named skill directory, loaded only when a request matches its description. It describes one repeatable workflow.
%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart TD
    accTitle: {Skills and AGENTS.md}
    accDescr: {Skills and AGENTS.md}
    R["your project"] --> AG["AGENTS.md<br/>whole file always in context"]:::always
    R --> SK[".opencode/skills/lambda-fit/SKILL.md<br/>only its description is indexed"]:::ondemand
    AG --> M(["model context"]):::core
    SK -. "body loaded only when a<br/>request matches its description" .-> M
    classDef always fill:#e6f7ed,stroke:#2f9e44,stroke-width:1.5px,color:#0b3d1f;
    classDef ondemand fill:#fff4e0,stroke:#f08c00,stroke-width:1.5px,color:#5c3b00;
    classDef core fill:#e7efff,stroke:#4c6ef5,stroke-width:1.5px,color:#10204a;

AGENTS.md answers “what is this project and how do we work here?”; a SKILL.md answers “how do I carry out this task?”.

AGENTS.md: project context


AGENTS.md is plain Markdown at your project root. opencode, Codex, Copilot CLI, Cursor, Antigravity, and others read it automatically. The lesson’s file:

MARKDOWN

# AGENTS.md — Lambda analysis in ePIC data

## Data access
- Find datasets with the `rucio` MCP tools; keep `limit` at 50 or less.
- A file's URL is root://epicxrd1.sdcc.bnl.gov:1095//eic/EPIC followed by its
  rucio name (/RECO/...). The `xrootd` tools take the path /eic/EPIC/RECO/...
- Read files only through the `uproot` MCP tools. No downloads, no local
  ROOT/uproot, no software installs.
- Never call get_tree_info on an EDM4eic file (it returns 1.5 MB). Check
  branches with validate_dataset_schema.

## Data model
- Tree `events`, collection ReconstructedChargedParticles:
  .PDG, .momentum.x, .momentum.y, .momentum.z (GeV).
- m(p) = 0.9382720813, m(pi) = 0.13957061, m(Lambda) = 1.115683 GeV.

## Kernels (uproot sandbox)
- np and ak are predefined. No `import`, no tuple unpacking in for-loops,
  np.sum(x) not x.sum().
- Test a kernel on one file with execute_kernel before a dataset call: the
  dataset tools do not show the error.

## When a tool fails
- A timeout means the server is busy. Wait, retry once, then stop and report.

## Definition of done
- Every number comes from a tool call in this session and is reported with the
  dataset DID and the file list.

Every line is something the model cannot find out by itself and got wrong in Episode 3: where the files are served from, which call floods the context, what the sandbox rejects. Two rules:

  • Keep it short. It is loaded on every turn, so every line costs tokens.
  • Write what went wrong. When the assistant makes a mistake twice, add one line.

Claude Code reads CLAUDE.md instead; make that file one line, @AGENTS.md. Do not copy the rules into a second file: the copies will diverge.

SKILL.md — a named procedure


A skill is a directory containing a SKILL.md that describes a repeatable procedure:

BASH

skills/
  lambda-fit/
    SKILL.md              specification: applicability, inputs, steps, success criteria

Its YAML frontmatter has a name and a description; only these stay in context, and the body is read when a request matches the description. The lesson’s skill:

MARKDOWN

---
name: lambda-fit
description: >
  Reconstruct and fit the Lambda0 -> p pi- invariant-mass peak in ePIC EDM4eic
  data. Use when asked to measure the Lambda yield, mass, or width, or to
  reproduce the Lambda peak from a .root file or a file list.
---

# Lambda invariant-mass fit

## Steps
1. Check the input files with validate_dataset_schema.
2. Histogram m(p pi-) and m(pbar pi+) over [1.08, 1.16] GeV in 80 bins, and
   return the counts and their sum. One file: execute_kernel. Many files:
   execute_kernel_dataset with reduce_code, at most 10 files per call.
3. Fit each histogram in one more execute_kernel call: Gaussian + 2nd-order
   polynomial. Scan mu and sigma on a grid and solve the other four parameters
   by weighted linear least squares.
4. Report mu, sigma, the yield S = A sqrt(2 pi) sigma / bin width, and
   chi2/ndf, with uncertainties.

## Success criteria (check before reporting success)
- The entries in the fitted histogram equal the sum returned in step 2. If
  not, counts were lost in copying: redo the fit.
- At least 50 entries. With fewer, report insufficient statistics and stop.
- |mu - 1.115683 GeV| < 0.005 GeV.
- sigma in [0.001, 0.005] GeV (detector resolution).
- chi2/ndf between 0.5 and 1.5.
If any check fails, report the failure and the fit numbers, not a result.

## Provenance
List the dataset DID, the files used, and the tool calls with their arguments.

The skill says what to do and how to judge the result, not how to code it; the assistant writes the kernels.

Install both files


opencode reads skills from .opencode/skills/<name>/SKILL.md in the project directory; Claude Code uses .claude/skills/. In your analysis directory:

BASH

R=https://raw.githubusercontent.com/eic/tutorial-mcp/main/files/skills
curl -fsSLO $R/AGENTS.md
curl -fsSL --create-dirs -o .opencode/skills/lambda-fit/SKILL.md $R/lambda-fit/SKILL.md

Restart opencode so it reads them. Loading a skill is the model’s decision, based on the skill’s description. A small model may answer without loading it, so every prompt in this lesson names the skill: “Using the lambda-fit skill”.

Challenge

Exercise: the bare question again (≈ 5 min)

In a new session, with AGENTS.md in place, repeat the question from Episode 3 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?

Same answer, 1,223. In the test run: 3 tool calls (list_dids, list_files, validate_dataset_schema) and 20 s, against 16 calls and 102 s without the file.

Your project layout


A project that behaves the same under any assistant:

BASH

lambda/
├── AGENTS.md                        
├── opencode.jsonc                   
└── .opencode/
    └── skills/
        └── lambda-fit/              
            └── SKILL.md            
Challenge

Exercise: a species-count skill (≈ 10 min)

Write a minimal SKILL.md for “count the reconstructed charged particles per PDG code in an EDM4eic file”, place it where your client loads skills, restart the client, and try it on a file from Episode 3.

MARKDOWN

---
name: species-count
description: >
  Count reconstructed charged particles per PDG code in an EDM4eic .root file.
  Use when asked which particle species a reconstruction file contains.
---

# Species count

## Steps
1. validate_dataset_schema on the file: tree 'events', branch
   ReconstructedChargedParticles.PDG. Note the event count.
2. execute_kernel with that branch and this kernel, unchanged:

       def kernel(events):
           pdg = ak.to_numpy(ak.flatten(events["ReconstructedChargedParticles.PDG"]))
           unique = np.unique(pdg, return_counts=True)
           return {"pdg": unique[0].tolist(), "count": unique[1].tolist()}

3. Return a table: PDG code, species name, count, fraction.

## Success criteria
The fractions sum to 1. Code 0 is reported as "no PID", not dropped.

Save it as .opencode/skills/species-count/SKILL.md and name it in the prompt (“Using the species-count skill, …”).

Challenge

Optional exercise: richer provenance

Extend the provenance section of lambda-fit so a run also records the number of input files and the total number of candidate pairs.

Replace the skill’s Provenance section with:

MARKDOWN

## Provenance
List the dataset DID, the files used, the number of input files processed,
the total number of proton-pion candidate pairs entering the histogram, and
the tool calls with their arguments, so the run can be reproduced.

The kernel can return the number of pairs before histogramming as one more dictionary entry.

The next episode runs this skill end to end

Key Points
  • AGENTS.md holds project context; a SKILL.md holds a procedure the assistant loads when needed.
  • Put success criteria in the skill so you can check the result.

Content from An end-to-end, reproducible Λ⁰ analysis


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

Overview

Questions

  • How do assistant + server + skill compose into one analysis?
  • How does the kernel scale from one file to many?
  • How is the yield extracted and made reproducible?

Objectives

  • Set up any MCP client for the end-to-end run in three steps.
  • Scale the analysis from one file to a full dataset.
  • Accept or reject an agent-produced yield using the audit checklist.

Continue in the directory from Episodes 3 and 4: servers running (eic-mcp status), AGENTS.md and the lambda-fit skill installed, and /mcp in opencode listing uproot, xrootd, and rucio.

The composed pipeline


The skill (Episode 4) gives the steps, the uproot server (Episode 3) reads the data, and the agent loop (Episode 1) runs and checks it.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart LR
    accTitle: {End-to-end agent run}
    accDescr: {End-to-end agent run}
    A["resolve input<br/>root:// file or file list"]:::data --> B["build m(p,π) histogram<br/>uproot MCP"]:::tool
    B --> C["fit Gaussian + poly-2<br/>uproot MCP"]:::tool
    C --> D["report μ, σ, S, χ²/ndf<br/>+ provenance"]:::out
    classDef data fill:#fff4e0,stroke:#f08c00,stroke-width:1.5px,color:#5c3b00;
    classDef tool fill:#e6f7ed,stroke:#2f9e44,stroke-width:1.5px,color:#0b3d1f;
    classDef out fill:#f3e8ff,stroke:#7048e8,stroke-width:1.5px,color:#2e1065;

One file, end to end


One request runs the whole chain on the first file of the dataset:

Using the lambda-fit skill, measure the Lambda0 peak in this file:
root://epicxrd1.sdcc.bnl.gov:1095//eic/EPIC/RECO/26.04.1/epic_craterlake/DIS/BeAGLE1.03.02-1.0/eCu/ep/10x115/q2_1to1000/BeAGLE1.03.02-1.0_DIS_eCu_ep_10x115_q2_1to1000_ab.0000.eicrecon.edm4eic.root

One file gives only about 10 candidates in the fit window, so the correct answer is insufficient statistics: the skill says to stop, and a model that quotes \(\mu\) anyway has ignored it.

Callout

Smaller models take shortcuts: check the result, not the route

A smaller model may call different tools than the skill says and still get the same histogram. The audit checklist below judges the result, not the route.

Scaling up


The same kernel can run over many files and return one merged histogram:

Using the lambda-fit skill, measure the Lambda0 and anti-Lambda0 peaks in the first 10 files of
the dataset epic:/RECO/26.04.1/epic_craterlake/DIS/BeAGLE1.03.02-1.0/eCu/ep/10x115/q2_1to1000.

Ten files take about a minute and a half. For 30 files the skill makes three such calls:

Using the lambda-fit skill, repeat the measurement with the first 30 files of the same dataset.

Thirty files take about ten minutes. A reference fit:

OUTPUT

Lambda      -> p pi-:    mu = 1116.25 +/- 0.37 MeV   sigma = 3.1 +/- 0.5 MeV   S =  97 +/- 19   chi2/ndf = 1.06
anti-Lambda -> pbar pi+: mu = 1115.50 +/- 0.25 MeV   sigma = 2.2 +/- 0.2 MeV   S = 102 +/- 15   chi2/ndf = 0.71

The histogram is the same in every run (321 and 294 entries in the window). The assistant writes its own fit, so \(\mu\), \(\sigma\), and \(S\) vary between runs within these uncertainties.

Proton–pion invariant-mass spectrum with Gaussian-plus-polynomial fits showing clear Lambda and anti-Lambda peaks near 1.1157 GeV
Fitted Λ⁰ and Λ̄ invariant-mass spectra (reference fit)

Extracting the yield


The fit model is a Gaussian signal on a second-order polynomial background over \([1.08, 1.16]\) GeV:

\[ f(m) = A \exp\!\left[ -\tfrac{1}{2} (m - \mu)^2 / \sigma^2 \right] + \left( c_0 + c_1 (m - m_\Lambda) + c_2 (m - m_\Lambda)^2 \right) \]

The polynomial absorbs the combinatorial background (Episode 2); the integrated signal is \(S = A\sqrt{2\pi}\,\sigma / (\text{bin width})\). Report \(S\) with its uncertainty alongside \(\mu\), \(\sigma\), and \(\chi^2/\text{ndf}\); a bare bin count mixes signal with background.

Audit


Callout

Audit checklist

  • Signal. \(\mu\) within a few MeV of 1.115683 GeV; \(\sigma\) consistent with detector resolution; \(\chi^2/\text{ndf}\) of order unity; \(S\) reported with an uncertainty.
  • Inputs pinned. Dataset (campaign and file list), particle masses, mass window, binning, and fit range all fixed and recorded.
  • Provenance. Tool calls and their arguments logged, so the run can be reconstructed.
  • Cost bounded. A few files during development before scaling up.
  • Oversight. A human inspected the fit before the result was reported.

The whole analysis in one prompt


Everything in Episodes 3 to 5 fits in one request, starting from nothing but the sample name:

Using the lambda-fit skill, measure the Lambda0 and anti-Lambda0 peaks in the first 30 files of
the campaign 26.04.1 BeAGLE eCu ep 10x115 reconstructed-DIS dataset.

The tools, the skill, and the criteria make this short prompt reproducible: the same request gives the same histogram on any MCP client.

Callout

Try a different beam

The same prompt works with any reconstructed-DIS DID. Nuclear beams (eCu, eAu) give the most Λ per event; ep samples need a few times more files.

The final episode lists the other MCP servers the EIC provides.

Key Points
  • One prompt runs the whole Λ⁰ analysis, from one file to many.
  • Check the fit against the audit checklist before you trust it.

Content from Catalog: MCP servers and AI infrastructure in the EIC ecosystem


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

Overview

Questions

  • Which other MCP servers does EIC provide?
  • Where are shared skills kept?
  • How can you use the tools without any setup?

Objectives

  • Find a shared skill in eic/skills before writing your own.
  • Ask the DISpatcher bot a question about data, software, or production.

More EIC servers


The three servers you used are part of a larger set, built mostly in BNL’s NPPS group (this episode is based on Torre Wenaus’s June 2026 talk to the ePIC user-learning WG). The eic GitHub organization has the current list.

%%{init: {'theme':'base', 'themeVariables': {'fontSize':'15px','lineColor':'#94a3b8','edgeLabelBackground':'#e2e8f0','clusterBkg':'#1f293720','clusterBorder':'#94a3b8','titleColor':'#94a3b8'}}}%%
flowchart TB
    accTitle: {EIC MCP server catalog}
    accDescr: {EIC MCP server catalog}
    A(["your AI assistant"]):::core
    A --> DATA
    A --> REC
    A --> CODE
    A --> PROD
    subgraph DATA["analysis & data"]
        direction LR
        UP["uproot-mcp"]:::tool
        XR["xrootd-mcp"]:::tool
        RU["rucio-mcp"]:::tool
    end
    subgraph REC["records"]
        direction LR
        ZE["zenodo-mcp"]:::rec
    end
    subgraph CODE["code knowledge"]
        direction LR
        DO["eic-docs-mcp"]:::code
        LX["LXR-mcp · BNL-hosted"]:::code
        GH["GitHub-mcp · standard"]:::code
    end
    subgraph PROD["production · via the bot"]
        direction LR
        PB["PanDA · PCS · streaming"]:::pkg
    end
    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 rec fill:#f3e8ff,stroke:#7048e8,stroke-width:1.5px,color:#2e1065;
    classDef code fill:#fff4e0,stroke:#f08c00,stroke-width:1.5px,color:#5c3b00;
    classDef pkg fill:#ffe3e3,stroke:#e03131,stroke-width:1.5px,color:#5c0a0a;
    click UP "https://github.com/eic/uproot-mcp-server" _blank
    click XR "https://github.com/eic/xrootd-mcp-server" _blank
    click RU "https://github.com/eic/rucio-eic-mcp-server" _blank
    click ZE "https://github.com/eic/zenodo-mcp-server" _blank
    click DO "https://github.com/eic/eic-docs-mcp" _blank
    click LX "https://eic-code-browser.sdcc.bnl.gov/lxr/source" _blank
    click GH "https://github.com/github/github-mcp-server" _blank
    click PB "https://chat.epic-eic.org/main/channels/dispatcher" _blank

Click a box to open the server’s repository or page.

Analysis and data


Callout

uproot-mcp: read ROOT/EDM4eic files · available · used in this lesson

uproot logo

eic/uproot-mcp-server reads ROOT files. Used in Episodes 3 and 5.

Callout

xrootd-mcp: find files on the data store · available · used in this lesson

XRootD logo

eic/xrootd-mcp-server browses the ePIC data stores.

Callout

rucio-mcp: query the data-management system · available · used in this lesson

Rucio logo

eic/rucio-eic-mcp-server searches the Rucio data catalog (read-only).

Records


Callout

zenodo-mcp: search the open-data repository · available

Zenodo logo

eic/zenodo-mcp-server searches Zenodo, including ePIC documents.

Code knowledge


Callout

eic-docs-mcp: search the documentation · available

eic/eic-docs-mcp gives full-text search over the eic.github.io pages and the software tutorials, including this one.

Callout

LXR-mcp: source cross-reference · available (BNL-hosted)

Searches the ePIC source code through the LXR browser, which is updated nightly. It runs only on the BNL-hosted services.

Shared skills: eic/skills


The AGENTS.md and SKILL.md you used in Episode 4 live in one analysis directory. eic/skills (site) is the collaboration’s shared repository of such definitions: skills, agents, and four packs that bundle them (onboarding, simulation, reconstruction, analysis), with setup guides for Copilot CLI, Claude, Cursor, Codex, and VS Code. Look there before writing a skill, and contribute the ones that work: a skill that encodes how to find a dataset or check a campaign saves every user the tool calls you counted in Episode 3.

No setup: the DISpatcher bot


DISpatcher is a Mattermost bot (chat.epic-eic.org → dispatcher) that anyone in ePIC can use, in the channel or by DM. It has the data tools from this lesson plus tools for production jobs, physics samples, software, and documents.

Post this in the dispatcher channel or DM the bot:

Which production campaigns contain the BeAGLE eCu ep 10x115 DIS sample, and is it in the newest campaign? For campaign 26.04.1, which generator version and settings produced it, and how many events were produced? Say which tool each part of the answer came from.

Compare its event count with yours from Episode 3

The EIC software portal also has an AI search box (“Ask anything about EIC…”).

corun-ai


BNLNPPS/corun-ai runs longer jobs with larger models and keeps the results. Its first use, codoc-ai, writes software documentation and reviews ePIC pull requests. Ask Torre for an account.

Key Points
  • EIC provides more MCP servers than the three used here.
  • Shared skills and agent definitions are in eic/skills.
  • The DISpatcher bot in Mattermost gives you these tools without any setup.