eviews-mcp
Drive EViews from Python, and expose it to LLM clients over the ModelContext Protocol.
Documentation site · Researcher guide · PyPI
Two things in one package:
- A library. An
EViewsclass for scripts and notebooks — build workfiles,estimate models, read results back as text or pandas DataFrames. - An MCP server. The same capabilities as tools, so an assistant can doeconometrics in a real EViews session.
Built and tested against EViews 13 on Windows; EViews 10–14 resolvecorrectly through the same COM interface.
New to this? The EViews Researcher Guidetakes you from a clean machine to a finished ARDL study, with every command andevery output verified against a real EViews session. No Python knowledge assumed.
Install
pip install eviews-mcp
With pandas support:
pip install "eviews-mcp[pandas]"
Or from a clone, for development:
git clone https://github.com/merwanroudane/MCP_EVIEWS.git
cd MCP_EVIEWS
pip install -e .[dev]
Requires Windows and a local EViews installation, since it drives EViewsthrough COM automation.
Library use
from eviews_mcp import EViews
with EViews() as ev:
ev.create_workfile("q", "1990q1", "2020q4")
ev.run("""
series k = 100 + @trend + 3*@nrnd
series l = 50 + 0.5*@trend + 2*@nrnd
series gdp = 10 + 0.6*k + 0.3*l + 2*@nrnd
equation eq1.ls gdp c k l
""")
print(ev.show("eq1"))
print(ev.value("eq1.@r2"))
Dependent Variable: GDP
Method: Least Squares
Included observations: 124
Variable Coefficient Std. Error t-Statistic Prob.
C 9.58073 0.829252 11.5535 2.90e-21
K 0.591693 0.0333819 17.7250 1.99e-35
L 0.322394 0.0673315 4.78816 4.81e-06
R-squared 0.994702 Mean dependent var 131.080
Any EViews view, as text
show takes a view, so diagnostics need no extra API:
ev.show("eq1", "wald c(2)=c(3)") # coefficient restriction test
ev.show("eq1", "resids(t)") # residual table
ev.show("eq1", "coefcov") # coefficient covariance
ev.show("eq1", "auto(2)") # Breusch-Godfrey serial correlation
ev.show("eq1", "white") # White heteroskedasticity test
ev.show("gdp", "uroot") # unit root test
ev.show("gdp", "correl") # correlogram
ev.show("ardl1", "cointrel") # ARDL long-run relationship
ev.show("var1", "impulse(t)") # impulse response table
ev.show("var1", "testexog") # Granger causality
resids and impulse draw graphs by default; the (t) variants ask EViewsfor the table form. Views that freeze into a spool rather than a table -- theARDL cointegrating relationship among them -- cannot be read over COM at all,so show falls back to a text dump for those.
For the numbers rather than the layout, table() returns raw rows at fulldouble precision, and value() returns one number:
rows = ev.table("eq1") # tuple of row tuples
r2 = ev.value("eq1.@r2") # 0.9947015...
beta = ev.value("eq1.@coefs(2)")
Results as data, not just text
show formats a table for reading. These return the numbers instead, fortesting, tabulating, or passing to something else:
ev.coefficients("eq1")
# [{'variable': 'LNK', 'coefficient': 0.549198724914677,
# 'std_error': 0.023936158605687322, 't_stat': 22.94431341143375,
# 'p_value': 5.677925593821611e-41}, ...]
ev.fit("eq1")["R-squared"] # 0.9858630667863459
Order of integration, tested down through differences until stationary:
ev.unit_root("lngdp")
# {'series': 'LNGDP', 'order_of_integration': 1, 'conclusion': 'I(1)',
# 'steps': [{'difference': 0, 'statistic': -0.5001, 'p_value': 0.8856, ...},
# {'difference': 1, 'statistic': -12.1778, 'p_value': 0.0001, ...}]}
Pass options to choose the test: "pp" for Phillips-Perron, "kpss" forKPSS, "adf, trend" to add a trend. KPSS reverses the null, so the reportedorder does not apply to it.
The standard post-estimation battery in one call:
report = ev.diagnose("eq1")
report["summary"]
# 'All 3 diagnostics pass at the 0.05 level.'
Breusch-Godfrey, White and Jarque-Bera, each with its statistic, p-value andwhether the null is rejected. A test that cannot run is listed underreport["skipped"] with the reason EViews gave, so the summary never overstateshow much was actually checked.
These verdicts read p-values against a level you choose. They do not establishthat a specification is sound -- structural breaks, seasonality and shortsamples all mislead these tests.
pandas both ways
frame = ev.to_dataframe(["gdp", "k", "l"]) # indexed 1990Q1, 1990Q2, ...
frame.corr()
Writing back, a DatetimeIndex or PeriodIndex decides the page frequency andspan, so dates stay aligned:
import numpy as np
import pandas as pd
rng = np.random.default_rng(7)
index = pd.period_range("2005Q1", periods=40, freq="Q")
unemployment = 7.0 - 0.05 * np.arange(40) + rng.normal(0, 0.4, 40)
df = pd.DataFrame(
{"unemployment": unemployment,
"inflation": 9.0 - 0.9 * unemployment + rng.normal(0, 0.5, 40)},
index=index,
)
ev.from_dataframe(df) # creates a quarterly 2005Q1-2014Q4 page
ev.run("equation phillips.ls inflation c unemployment")
Non-numeric columns are skipped rather than failing the whole frame.
Graphs
Graphs cannot be rendered as text, so write them to a file:
ev.export_object("phillips", "residuals.png", view="resids")
Graph formats: png, jpg, pdf, emf, wmf, bmp, gif, eps, tex.Table formats: csv, rtf, txt, html.
Errors
EViews reports failures precisely, including the line number inside a program,and those messages are passed through unchanged:
ev.run("""series ok = 1
broken_command
""")
EViewsError: BROKEN_COMMAND is not defined or is an illegal command in "BROKEN_COMMAND"
in MCP_77B699157F3B.PRG on line 2.
The generated program is given a random name each run, so only that part varies.
MCP server use
Register the eviews-mcp command with your MCP client:
{
"mcpServers": {
"eviews": {
"command": "eviews-mcp"
}
}
}
For Claude Code:
claude mcp add eviews -- eviews-mcp
Tools
| Tool | Purpose |
|---|---|
eviews_status |
Connection, version, active workfile. Start here when debugging. |
reset_eviews |
Discard the instance and start clean. |
set_eviews_visible |
Show or hide the EViews window. |
create_workfile |
New page by frequency and range. |
open_workfile / save_workfile |
Open and save .wf1 / .wf2. |
close_workfile |
Close one or all open workfiles. |
workfile_info |
Name, page, frequency, range, sample. |
list_objects |
Inventory, filterable by EViews type. |
set_sample |
Set the estimation sample. |
run_eviews_code |
Main tool. Run a block of EViews program code. |
run_program_file |
Run an existing .prg, with arguments. |
command |
A single command line. |
show |
Render any object as a text table. |
evaluate |
One value from an expression. |
describe_object |
Type, plus statistics for a series. |
equation_coefficients |
Coefficients as a clean table of numbers. |
unit_root |
Order of integration, tested down through differences. |
diagnose_equation |
Serial correlation, heteroskedasticity and normality. |
read_data |
Series as an aligned table or full-precision CSV. |
write_series |
Write values into a series. |
import_data |
Read .xlsx, .csv, .dta, .sav, and more. |
export_data |
Write series to a file. |
export_object |
Save an object — the way to retrieve graphs. |
Results are not echoed by run_eviews_code, because EViews sends programoutput to its own log window where COM cannot reach it. Estimate into a namedobject and call show on it.
Behaviour worth knowing
These are EViews characteristics that the library handles for you, documentedbecause they surprise people writing COM code directly.
- Writes respect the active sample. Under
smpl 2000m3 2000m6, writing 12values lands 4 and silently leaves the rest NA. Writes therefore default tothe whole page; passsample=""to opt into the current sample instead. saveignores the file extension.graph.save "out.png"writes EMFdata. The format is passed explicitly, and a save that produces no fileraises rather than reporting success.- Relative paths resolve against EViews, not the calling process, so pathsare made absolute before they are handed over.
- A dated frame governs the page. Writing a 12-row quarterly frame into anopen 80-row page would land the values on the wrong dates, so a page matchingthe frame is created instead.
- No log redirection. The
outputcommand requires a frozen object nameand otherwise writes nothing at all, so it cannot capture a log. Results comefrom freezing an object into a table and reading that. - No
GetScalar/PutScalar/GetString. These are not on the EViewsCOM interface at all.Getcovers them and infers the type. - One COM thread. MCP dispatches synchronous tools across a thread pool,and a COM pointer is not valid across apartments, so every EViews call isfunnelled onto a single apartment-initialised thread.
- Importing into an open workfile truncates the file to that page's length,silently. Imports therefore create a new workfile by default; merging into thecurrent page is opt-in.
- EViews limits how many workfiles may be open and then refuses to createanother, so
close_workfileexists to keep long sessions healthy.
Tests
python tests/test_offline.py # 27 tests, no EViews needed
python tests/test_live.py # 25 tests, drives the MCP tool layer
python tests/test_live_client.py # 58 tests, drives the library API
pytest runs the offline suite by default; the live suites are opt-in becausethey need an EViews licence.
Licence
MIT. Copyright (c) 2026 Merwan Roudane.