juanqui

JouleScope JS220 MCP Server

Community juanqui
Updated

JouleScope JS220 MCP Server

joulescope-mcp is a Model Context Protocol (MCP) server for the JouleScope JS220 precision energy analyzer. It exposes agent-friendly tools for measuring current, voltage, power, charge, and energy, plus lower-level access to the JouleScope driver PubSub topic tree.

The primary tool is measure_energy: provide a duration and accumulation interval, and it returns total charge and energy plus one sample per interval. For example, duration_s=15 and interval_s=0.5 returns 30 interval samples along with totals such as total_charge_mAh.

Quick Start

Install directly from GitHub with uvx:

{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

If you prefer SSH, use the same Git install shape with the SSH URL:

{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+ssh://[email protected]/juanqui/joulescope-mcp.git",
        "joulescope-mcp"
      ]
    }
  }
}

Then ask your MCP client:

Measure JouleScope power for 15 seconds with 500 ms intervals. Include voltage.

Expected result shape, with compact sample arrays shortened for display:

{
  "total_charge_mAh": 0.0051,
  "total_energy_mWh": 0.019,
  "average_current_mA": 1.23,
  "average_voltage_v": 3.70,
  "interval_count": 30,
  "sample_charge_mAh": [0.00015, 0.00015, 0.00015]
}

Requirements

  • Python 3.11 or newer
  • JouleScope JS220 connected over USB
  • uv for the recommended uvx install path
  • An MCP client that can run stdio servers

The Python package installs pyjoulescope_driver>=2.1.0 and pyjls>=0.17. On Linux, configure JouleScope udev rules as documented by JouleScope before running the server.

uvx is an alias for uv tool run; it runs Python command-line tools in an isolated environment without a permanent install.

Install Options

Option 1: GitHub with uvx

uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp

MCP JSON:

{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Use this for normal installs.

Option 2: GitHub over SSH with uvx

Use this if you prefer SSH or need GitHub SSH authentication:

uvx --from git+ssh://[email protected]/juanqui/joulescope-mcp.git joulescope-mcp

Option 3: Local Checkout

Use this while developing or when you want to pin the MCP server to a local clone:

git clone [email protected]:juanqui/joulescope-mcp.git
cd joulescope-mcp
uv sync --extra dev
uv run joulescope-mcp

Verify that the driver can see the JS220:

uv run python -m pyjoulescope_driver scan
uv run python -m pyjoulescope_driver statistics --frequency 2 --duration 1

Local checkout MCP JSON:

{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/joulescope-mcp",
        "run",
        "joulescope-mcp"
      ]
    }
  }
}

Use an absolute path. Several MCP clients launch servers with a limited PATH; if uv or uvx is not found, replace "command": "uvx" or "command": "uv" with the full executable path from which uvx or which uv.

Client Configuration

Most MCP clients use one of two JSON shapes:

  • mcpServers: Claude Desktop, Claude Code project config, Cursor, Windsurf, Cline, and many other clients
  • servers: VS Code / GitHub Copilot MCP config

Choose one install command:

Current situation Use this command in client configs
Normal GitHub install uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp
GitHub over SSH uvx --from git+ssh://[email protected]/juanqui/joulescope-mcp.git joulescope-mcp
Local development checkout uv --directory /absolute/path/to/joulescope-mcp run joulescope-mcp

The snippets below show the normal GitHub install path.

GitHub replacement:

{
  "command": "uvx",
  "args": [
    "--from",
    "git+https://github.com/juanqui/joulescope-mcp",
    "joulescope-mcp"
  ]
}

Local checkout replacement:

{
  "command": "uv",
  "args": [
    "--directory",
    "/absolute/path/to/joulescope-mcp",
    "run",
    "joulescope-mcp"
  ]
}

Configure only one always-on client for a physical JS220. Many desktop clients auto-start configured MCP servers, and the JS220 should not be shared between multiple MCP server processes.

Timeout Configuration

measure_energy is a blocking hardware measurement. A 15 second measurement takes at least 15 seconds, plus JS220 startup and cleanup time. Configure MCP clients for a 5 minute tool timeout when they expose a timeout setting.

Recommended values:

  • Tool call timeout: 300 seconds / 300,000 ms
  • Server startup timeout: 60 seconds / 60,000 ms

If a client does not document a timeout setting, keep synchronous measurements short enough for that client or use a client with configurable MCP tool timeouts. A future async measurement API can avoid long single tool calls, but the current measure_energy call is synchronous by design.

Claude Code

Recommended global install:

claude mcp add joulescope-js220 -- uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp
claude mcp list

Launch Claude Code with 5 minute MCP tool calls and a 60 second server startup timeout:

MCP_TIMEOUT=60000 MCP_TOOL_TIMEOUT=300000 claude

Project .mcp.json:

{
  "mcpServers": {
    "joulescope-js220": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Inside Claude Code, run /mcp to inspect server status. MCP_TIMEOUT and MCP_TOOL_TIMEOUT are Claude Code process environment variables, not per-server env values.

Claude Desktop

Edit:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json
{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Restart Claude Desktop after editing the file. Claude Desktop does not document a portable per-server timeout field in claude_desktop_config.json; if your launch environment supports MCP timeout environment variables, set MCP_TOOL_TIMEOUT=300000 before starting Claude Desktop.

Codex

Recommended global install:

codex mcp add joulescope-js220 -- uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp
codex mcp list

Direct ~/.codex/config.toml entry:

[mcp_servers.joulescope-js220]
command = "uvx"
args = ["--from", "git+https://github.com/juanqui/joulescope-mcp", "joulescope-mcp"]
startup_timeout_sec = 60
tool_timeout_sec = 300

The codex mcp add command creates the server entry. Edit ~/.codex/config.toml afterward to add startup_timeout_sec and tool_timeout_sec.

Cursor

Global config:

  • macOS/Linux: ~/.cursor/mcp.json
  • Windows: %USERPROFILE%\.cursor\mcp.json

Project config:

  • .cursor/mcp.json
{
  "mcpServers": {
    "joulescope-js220": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Cursor also supports adding MCP servers from Settings. After changing the config, restart Cursor or refresh MCP tools from the MCP settings panel. Cursor does not currently document a portable mcp.json timeout field; if your Cursor build exposes a request/tool timeout in Settings, set it to 300 seconds.

VS Code / GitHub Copilot Agent Mode

Workspace config:

  • .vscode/mcp.json

User config:

  • Run MCP: Open User Configuration from the Command Palette.
{
  "servers": {
    "joulescope-js220": {
      "type": "stdio",
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Command-line install:

code --add-mcp '{"name":"joulescope-js220","type":"stdio","command":"uvx","args":["--from","git+https://github.com/juanqui/joulescope-mcp","joulescope-mcp"]}'

VS Code's MCP configuration reference does not document a per-server tool timeout field. Do not add unsupported timeout keys to .vscode/mcp.json; use shorter synchronous measurements if your Copilot host times out long calls.

Windsurf

Edit:

  • macOS/Linux: ~/.codeium/windsurf/mcp_config.json
  • Windows: %USERPROFILE%\.codeium\windsurf\mcp_config.json
{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ]
    }
  }
}

Windsurf Cascade has a total enabled-tool limit. If you use many MCP servers, disable tools you do not need. Windsurf's public MCP docs do not document a portable timeout field in mcp_config.json; if your Windsurf build exposes a request/tool timeout setting, set it to 300 seconds.

Cline

CLI install:

cline mcp add joulescope-js220 -- uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp

Manual config:

  • CLI default: ~/.cline/data/settings/cline_mcp_settings.json
  • VS Code extension: open the MCP Servers panel, then choose Configure MCP Servers
{
  "mcpServers": {
    "joulescope-js220": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/juanqui/joulescope-mcp",
        "joulescope-mcp"
      ],
      "timeout": 300,
      "disabled": false,
      "alwaysAllow": []
    }
  }
}

Run as HTTP

Most local MCP clients should use stdio. If you need streamable HTTP:

uvx --from git+https://github.com/juanqui/joulescope-mcp joulescope-mcp --transport streamable-http --mount-path /mcp

Troubleshooting

  • Client cannot find uvx: use the absolute path from which uvx.
  • No JouleScope found: run your configured server command directly in a terminal to check startup errors, then run uvx --from pyjoulescope-driver pyjoulescope_driver scan to verify the official driver can see the JS220.
  • Permission denied on Linux: install JouleScope udev rules and reconnect the JS220.
  • Multiple clients fight over the device: run only one MCP client/server process against a JS220 at a time.
  • GitHub install fails: verify git clone https://github.com/juanqui/joulescope-mcp works first.
  • Tools changed but client still shows old tools: restart the client or reset/reload MCP tools.

Quick local health check:

uv run python scripts/hardware_smoke.py --duration-s 2 --interval-s 0.5

Agent Workflow

For firmware or application power optimization, keep the measurement setup stable:

  1. Run a baseline measurement with measure_energy.
  2. Apply one firmware or software change.
  3. Run the same measure_energy duration and interval again.
  4. Compare total_charge_mAh, total_energy_mWh, average_current_mA, and the interval samples.
  5. Repeat the measurement when differences are close to normal run-to-run variance.

Example request:

{
  "duration_s": 15,
  "interval_s": 0.5,
  "compact": true
}

The response includes:

  • total_charge_mAh: total charge over the measurement window
  • total_energy_mWh: total energy over the measurement window
  • average_current_mA: average current over the actual captured duration
  • average_power_mW: average power over the actual captured duration
  • actual_interval_s: average actual interval captured by the JS220
  • sample_charge_mAh: compact per-interval charge list when compact=true
  • sample_energy_mWh: compact per-interval energy list when compact=true
  • sample_voltage_avg_v, sample_voltage_min_v, sample_voltage_max_v: compact per-interval voltage lists when compact=true and include_voltage=true
  • samples: full per-interval statistics when compact=false

If duration_s is not an exact multiple of interval_s, the server rounds up to the next full interval and reports both requested_duration_s and actual_duration_s.

Tools

list_devices

Lists connected JouleScope devices. For JS220 devices, it attempts to include hardware, firmware, and FPGA versions.

device_info

Returns retained driver topic values for a selected device. Set include_metadata=true to include topic metadata returned by the driver.

measure_energy

Measures charge and energy using JS220 sensor-side statistics.

Parameters:

  • duration_s: requested measurement duration in seconds
  • interval_s: accumulation interval in seconds
  • device_path: optional explicit device path, such as u/js220/005920
  • configure_auto_range: defaults to true; configures current and voltage range modes to auto
  • compact: returns compact charge and energy arrays and omits full samples
  • include_voltage: when used with compact, also returns per-interval voltage arrays

Implementation detail: the JS220 publishes per-interval current.integral in coulombs and power.integral in joules. The server sums those integrals, then also converts charge to mAh and energy to mWh.

capture_statistics

Frequency-based wrapper around measure_energy. Use when you want frequency_hz instead of interval_s.

configure_frontend

Sets current and voltage range modes and optional range selections. Use auto for normal measurements.

target_power_status

Reports whether the JS220 target/DUT power path is connected. For JS220, DUT power is controlled through s/i/range/mode: off disconnects Current+ from Current-, while auto or manual connects the target path for measurement.

set_target_power

Connects or disconnects power to the DUT through the JS220 current path.

Parameters:

  • power_on: true to connect target power, false to disconnect it
  • on_mode: auto by default, or manual
  • settle_ms: optional wait after changing state

cycle_target_power

Power-cycles the DUT by setting target power off, waiting, then restoring target power.

Parameters:

  • off_ms: hold-off time in milliseconds
  • on_mode: auto by default, or manual
  • settle_ms: optional wait after restoring power

record_jls

Records raw samples to a JLS v2 file using the JouleScope driver's Record API. This is useful for later waveform analysis in the JouleScope UI or JLS tooling. Existing files are rejected unless overwrite=true.

read_gpi

Reads JS220 general-purpose input state and returns a 32-bit value plus decoded pins.

list_topics

Lists retained driver topics, values, and optional metadata. This is the discovery tool for advanced JS220 capabilities.

query_topic

Queries one driver topic. Relative topics are resolved under the selected device, so c/fw/version becomes u/js220/<serial>/c/fw/version.

publish_topic

Publishes a value to a driver topic. This exposes advanced JS220 features and can change device behavior. Prefer typed tools when available.

Resources and Prompts

Resources:

  • joulescope://devices: JSON device list
  • joulescope://driver: server and driver version information

Prompt:

  • power_optimization_session: template for measurement-driven power optimization loops

Safety and Limits

The server opens a short-lived JouleScope driver connection per tool call and serializes device access inside one server process. Blocking measurements have guardrails:

  • Minimum interval: 0.5 ms
  • Maximum duration: 3600 seconds
  • Maximum returned intervals: 10,000
  • Statistics collection times out if the JS220 does not publish the expected samples

set_target_power, cycle_target_power, record_jls, and publish_topic are marked as write/destructive-capable MCP tools. cycle_target_power intentionally interrupts the DUT. Agents should use it only when an interruption is part of the requested test.

Development

Set up a local checkout:

uv sync --extra dev

Run tests:

uv run python -m pytest

Run linting:

uv run python -m ruff check .

Build the package:

uv run python -m build

Hardware smoke test:

uv run python -m pyjoulescope_driver scan
uv run python -m pyjoulescope_driver statistics --frequency 2 --duration 1
uv run python - <<'PY'
from joulescope_mcp.service import Js220Service
r = Js220Service().measure_energy(duration_s=2, interval_s=0.5)
print(r["total_charge_mAh"], r["average_current_mA"], [s["charge_mAh"] for s in r["samples"]])
PY

Repeatable hardware smoke script:

uv run python scripts/hardware_smoke.py --duration-s 2 --interval-s 0.5

Design

See docs/design.md for the MCP design, measurement semantics, tool rationale, and verification strategy.See docs/testing.md for repeatable software, MCP, and hardware checks.See docs/adversarial-reviews.md for the implementation and README review logs.

References

Client configuration references used for the examples above:

Popular MCP setup examples reviewed:

License

Apache License 2.0. See LICENSE.

MCP Server ยท Populars

MCP Server ยท New

    uarlouski

    ๐Ÿš€ TestRail MCP Server

    AI-native MCP server connecting Claude, Cursor, Windsurf, and other AI assistants to TestRail โ€” manage test cases, runs, and results through natural-language conversation, with typed schemas built for LLMs.

    Community uarlouski
    metabase

    Metabase MCP Server

    The easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:

    Community metabase
    mindsdb

    USE CASES

    Platform dedicated to building an open foundation for applied Artificial Intelligence, designed for people seeking production-ready AI systems they can truly control, extend and deploy anywhere.

    Community mindsdb
    reflex-search

    Reflex

    Reflex - The instant, code-aware local search engine.

    Community reflex-search
    Licinexus

    @licinexusbr/mcp

    MCP server for Brazilian public procurement data (PNCP + Receita Federal). Maintained by Licinexus.

    Community Licinexus