wavekit-mcp
English | 中文
An MCP server that gives AI assistants a persistent, sandboxed Python environment for waveform analysis using wavekit.
The AI can open VCD/FST/FSDB files, load and manipulate waveforms, run temporal pattern matching, and iterate across multiple tool calls — all within a shared execution context that persists state between calls.
Why wavekit-mcp?
The problem: Digital waveforms are huge. A single simulation can produce millions of transitions across thousands of signals. Sending this data to an LLM directly is both inefficient and ineffective — the AI sees noise, not insight.
Our approach: Give the AI tools, not data. wavekit-mcp exposes wavekit's full waveform analysis capabilities through a persistent Python session. The AI writes code to:
- Load signals from VCD/FST/FSDB files
- Apply temporal pattern matching
- Compute statistics, detect anomalies, extract events
The AI gets only the answers it asks for — a mean, a timing violation, a filtered subset — never the raw waveform. Output limits ensure the AI must think in terms of signal semantics, not value sequences.
Installation
pip install wavekit-mcp
Start the server:
wavekit-mcp
wavekit-mcp --config /path/to/wavekit_mcp.toml
MCP client example:
{
"mcpServers": {
"wavekit": {
"command": "wavekit-mcp",
"args": ["--config", "/path/to/wavekit_mcp.toml"]
}
}
}
Configuration
Copy wavekit_mcp.toml.example and edit as needed. All fields are optional.
[limits]
max_sessions = 8
run_timeout_sec = 120
output_max_chars = 4000
[file_access]
read_enabled = false
write_enabled = false
read_allowed_paths = ["/tmp/**"]
write_allowed_paths = ["/tmp/**"]
[log]
file = "" # empty = stderr only
level = "INFO"
[sandbox]
# Defaults already allow wavekit, wavekit.*, numpy, numpy.*
# allowed_imports = ["plotly", "matplotlib.*"]
Scalar fields can be overridden by environment variables:
WAVEKIT_MCP_RUN_TIMEOUT_SEC=300 wavekit-mcp
Tools
| Tool | Description |
|---|---|
open_session(description?) |
Create a persistent Python execution session. |
close_session(session_id) |
Close a session and release worker resources. |
list_sessions() |
List active sessions. |
run(session_id, code) |
Execute Python and return {result, output, error, duration_ms}. |
get_history(session_id, last_n) |
Return recent execution records. |
get_api_docs(topic) |
Inspect wavekit Reader/Waveform/pattern API docs. |
Each session pre-injects only:
wavekit— usewavekit.VcdReader,wavekit.Waveform, etc.Viewer— optional waveform visualization helper.
Import everything else explicitly:
import numpy as np
import wavekit
from wavekit.pattern import Pattern, match, collect, Channel, MatchStatus
run() returns the last expression in a REPL-like form: the value is displayed as truncated repr(...) text; the real Python objects remain in the session namespace.
Basic usage
# call 1
import numpy as np
import wavekit
r = wavekit.VcdReader("/data/sim.vcd")
data = r.load_waveform("tb.dut.data[7:0]", clock="tb.clk")
# call 2 — state persists
print(f"samples={len(data.value)} mean={np.mean(data.value):.2f}")
Reader examples
Matched loading
waves = r.load_matched_waveforms(
signal_path="tb.dut.fifo_{0..3}.w_ptr[2:0]",
clock_path="tb.clk",
)
for key, wave in waves.items():
print(f"{key}: mean={np.mean(wave.value):.2f}")
Matched APIs return dict[CaptureKey, ...]. CaptureKey is a tuple of typed captures such as BraceCapture, RegexCapture, or WildcardCapture.
Unknown/X/Z masks
value = r.load_waveform("tb.bus[7:0]", clock="tb.clk", xz_value=0)
unknown = r.load_unknown_mask("tb.bus[7:0]", clock="tb.clk")
known_value = value.mask(unknown == 0)
unknowns = r.load_matched_unknown_masks(
signal_path="tb.dut.fifo_{0..3}.data[7:0]",
clock_path="tb.clk",
)
Expressions
occupancy = r.eval(
"tb.dut.w_ptr[3:0] - tb.dut.r_ptr[3:0]",
clock="tb.clk",
)
occupancies = r.eval(
"tb.fifo_{0..3}.w_ptr[2:0] - tb.fifo_{0..3}.r_ptr[2:0]",
clock="tb.clk",
mode="zip",
)
Query syntax
| Syntax | Example | Meaning |
|---|---|---|
| Plain path | tb.dut.valid |
Exact signal/scope path. |
| Brace | fifo_{0..3}.ptr |
Alternatives or integer ranges. |
/regex/ |
tb./lane_(\d+)/.valid |
Canonical regex with captures. |
@regex |
`tb.@(req | ack)` |
* / ** |
tb.*.valid, tb.**.valid |
One-level / recursive wildcard. |
$ / $$ |
tb.$fifo.data, tb.$$fifo.data |
FSDB module-definition match. |
Use r.top_scopes, r.get_matched_signals(path), and r.get_matched_scopes(path) to explore hierarchy.
Pattern matching
Pattern builds declarative timing checks. Execute with module-level match(...).
from wavekit.pattern import Pattern, match, MatchStatus
ar_fire = arvalid & arready
r_fire = rvalid & rready
records = match(
Pattern()
.wait(ar_fire)
.wait(r_fire)
.capture("rdata", rdata),
timeout=256,
)
ok = records.filter_ok()
print(f"transactions={len(ok)}")
print(f"latencies={ok.end.clock - ok.start.clock}")
print(ok.captures["rdata"].value[:8])
timeouts = records.filter_status(MatchStatus.Timeout)
require_failures = records.filter_status(MatchStatus.RequireViolated)
Use consume(..., channel=...) when a match must claim an event exclusively. Successful blocking steps continue in the same cycle; use .delay(1) for next-cycle behavior.
For value-dependent flows, use programmable collect(...):
from wavekit.pattern import collect
cmd_fire = cmd_valid & cmd_ready
rsp_fire = rsp_valid & rsp_ready
def read_cmd(ctx):
if not ctx.value(cmd_fire):
return None
addr = int(ctx.value(cmd_addr))
ctx.consume(rsp_fire, channel="rsp")
return {"addr": addr, "status": int(ctx.value(rsp_status))}
commands = collect(read_cmd, timeout=128)
print(f"commands={len(commands)}")
Viewer
viewer = Viewer()
viewer.waveforms.append(data)
viewer.markers.append(time=int(data.time[0]), name="start")
viewer.zoom_to_fit()
viewer.push_state()
print(viewer.url)
Keep the session open while the user is viewing; closing the session closes the viewer.
Security
User code runs under RestrictedPython in a worker process. Imports are restricted by sandbox.allowed_imports; file I/O is disabled unless explicitly enabled in [file_access].
This prevents accidental operations and isolates crashes, but it is not a complete sandbox for hostile code.
AI assistant skill
This repository includes a wavekit-mcp skill:
Metadata
Release files for wavekit-mcp 0.5.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| wavekit_mcp-0.5.0.tar.gz | 28.2 kB | Without provenance | Details for wavekit_mcp-0.5.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wavekit_mcp-0.5.0-py3-none-any.whl | Python 3 | none | any | Details for wavekit_mcp-0.5.0-py3-none-any.whl |
Total release size: 59.9 kB
Release files / wavekit_mcp-0.5.0.tar.gz
| Download URL | wavekit_mcp-0.5.0.tar.gz |
|---|---|
| Size | 28.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a132148ac48dbbb6486c39d253046d6a0f1a06a2f01e717e0f274a904a3b93eb
|
|
BLAKE2b-256 checksum How to use checksums |
91ce35a19bc9d146db60f2890dbea331541963c48a31aae6bb7c9ad81363fc37
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Release files / wavekit_mcp-0.5.0-py3-none-any.whl
| Download URL | wavekit_mcp-0.5.0-py3-none-any.whl |
|---|---|
| Size | 31.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b7c31ddfd62f3faa6ba3ded7028aab41ff85d74d8c613c03e08cc7e2e7dad34f
|
|
BLAKE2b-256 checksum How to use checksums |
e8013a1f36e1a4f2351ce2ab9264ce6db24a2422309428a0d5d96cb06cd9069f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Security for wavekit-mcp 0.5.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.