OriginLab-MCP
Provides tools for interacting with OriginLab Origin, enabling worksheet reads/writes, plotting data, building and styling multi-panel graphs, configuring axes and legends, and exporting figures.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OriginLab-MCP Plot columns A and B from Sheet1 and export the figure as PNG."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OriginLab-MCP
An MCP server for OriginLab Origin. It reads and writes worksheets, builds and styles multi-panel graphs, and exports figures — and it is careful about the difference between what Origin accepted and what Origin did.
1.0.0. Every tool here has been run against a real Origin 2024, the Origin App installs and starts the bridge, and
examples/five_panel_figure.pybuilds a five-panel figure end to end.Known gaps, so they are not a surprise: a legend placed in a corner is checked for being inside its panel, not for sitting on top of the data; a log axis gets no minor ticks unless you ask; and the 213-entry option table records what Origin documents, not what each option was measured to do — sixteen of them have been watched change a picture.
Requirements
Origin or OriginPro 2021b or newer (embedded Python 3.9+)
Python 3.10+ for the server
Windows, either natively or in a VM
Related MCP server: origin-mcp-kimi
Install
pip install originlab-mcp
originlab-mcp --build-app .That prints the three steps Origin needs, which are worth stating here because
the obvious one does nothing: dragging the folder onto the Apps gallery is
accepted in silence and adds no button. Copy the generated OriginLab MCP Bridge folder into %LOCALAPPDATA%\OriginLab\Apps, pack it from Origin's
Command Window with
mkOPX app:="OriginLab MCP Bridge" opx:="C:\path\to\OriginLab MCP Bridge.opx";— backslashes, because forward slashes hang mkOPX — and drag the resulting
.opx into Origin. A button appears; clicking it brings the Script Window
forward, where the bridge reports starting, loaded, serving.
Point your MCP client at originlab-mcp.
originlab-mcp --status says whether a bridge is listening, and exits non-zero
when one is not. originlab-mcp --stop ends it.
Stopping is a command rather than a second button because a button must not do
the work itself: while the bridge serves it holds Origin's UI thread, so
anything the button ran would run from inside the message pump. A button that
only spawns an external process is possible -- that is how the upstream project
does it -- but the instruction still has to reach the bridge over the socket,
and --stop is that instruction without the indirection.
What's different
Origin accepts a great deal that it then ignores, without ever saying so.
set <plot> -zzzz 9— an option Origin has never heard of — returns true, reads back as 9.0, and leaves the graph pixel-for-pixel identical.plot:=2003is not a plot type. Origin makes the window, draws the axes, writes the legend, and plots nothing.A style written to a plot inside a group reads back exactly as requested, while Origin goes on drawing the group's style.
None of these is catchable from the reply, so the checking happens before the write and after the figure:
Before. Options are checked against Origin's own documented list, and an
invented one is refused with the options it was probably meant to be. This
matters because the real names are unguessable — line width is -w, bar fill
pattern is -pfp — so a plausible guess is the likely failure, not a typo.
Writing to a grouped plot member is refused outright. Plot types come from
oPlotIDs.h in the Origin installation rather than from documentation, and
every name in the table was drawn and looked at.
After. A per-call check cannot reach the question that matters, which is whether the figure is right. So the operations that build one report the thing that could contradict them:
operation | what it reports back |
| the layer used, and how many plots Origin says are in it |
| every layer's real geometry, and the box they all occupy |
| where the legend is, and whether that is inside its layer |
| which side of the frame the title landed on |
That list exists because of a measured failure: a five-panel figure where all
twenty-three steps returned OK and the result was unusable. One legend had been
placed at left = 24953 on a page 6432 wide — legend.x is in data
coordinates, not the percentages it was being given — and Origin's export grew
the canvas to contain it, squeezing the figure into a corner. Nothing in any
reply said anything was wrong.
A read-back is reported as a read-back, never as proof:
{
"requested": 60,
"readback": {"readable": true, "value": 60.0, "matches_request": true},
"readback_means": "Origin stored this value where 'get' can find it. It is not
proof that the graph changed: Origin also stores options it
does not recognise."
}Exporting the graph is the only thing that settles it, which is why
origin_graph_export is a first-class tool rather than a way to save pictures.
Arguments this server does not read are refused, with the nearest name it
does. The same failure it guards against in Origin turned up here first:
worksheet_read takes max_rows, and a call passing rows=3 used to get every
row in the sheet and no indication its argument had been dropped.
The tool surface is small on purpose. One plot tool with a plot_type
parameter rather than one per chart type; one plot_option_get/set pair
rather than a wrapper per property.
Tools
| Is the bridge alive — answers even when originpro is broken |
| Where the bridge is, and whether it is answering |
| Size, column names, formats |
| Columns as lists, refusing rather than truncating |
| Columns in, with the format stated not guessed |
| Plot columns into a new page or a new layer of an existing one |
| Layers, plots, addresses, group membership |
| PNG out — and the only witness that a style took |
| Add a panel, and say what its index is |
| Grid the panels, and report where they really went |
| Rebuild a legend, place it, check it is inside its panel |
| Scale, range and title, with the title's side reported |
| Read an axis back |
| Read one LabTalk |
| Write one, and report what that established |
| Search Origin's 213 documented |
| The escape hatch, with Origin's output captured |
examples/five_panel_figure.py builds a
five-panel figure end to end and prints, at every step, the number that could
contradict it.
Setups
Windows, everything local — the usual case. Server and Origin on the same machine, talking over loopback TCP.
Origin in a Windows VM, working from macOS or Linux — also supported. The server runs inside the VM over ssh; only the MCP client is on the host. This works because the server reaches Origin through a loopback socket rather than COM, which cannot cross a Windows session boundary.
MCP client ──ssh──▶ server (Windows) ──TCP 127.0.0.1──▶ Origindocs/remote-origin.md walks through that one:
the ssh server, the key (Windows puts an administrator's in a different file
and ignores the usual one without saying so), and pointing an MCP client at it.
Notes on Origin's behaviour
Things measured here that no documentation mentions:
originpro is single-threaded, absolutely. Called from any thread but Origin's UI thread it blocks while holding the GIL and deadlocks the whole embedded interpreter. Origin's window stays responsive; its Python is gone. The bridge therefore serves on that thread and pumps Origin's message queue between requests.
A modal dialog stops the bridge, because the dialog holds the thread the bridge serves on — while the handshake file goes on saying it is up. Exporting into a directory that does not exist opens one, so that is checked before the export is sent.
A legend has two pairs of coordinates in different spaces.
legend.x/legend.yare the layer's data coordinates;legend.left/.topare page units, the same onespage.widthreports.plotxy ogl:=<new>makes a new layer, not a new page, and does not make it active.[<new>]is the new page.Origin exchanges the axes for bar charts and reports it through no layer property. It moves the title objects to match and keeps their rotation, so the value-axis title ends up under the frame reading bottom to top.
-wis points × 500;-wptakes points. Passing a point value to-wgives a hairline rather than an error.A fill pattern set with
-pfpalone is invisible: Origin draws it in the fill colour, so-pfcis needed too, and neither changes anything on its own.LabTalk stops at the first statement it rejects, so a script that reports failure has run an unknown prefix of itself.
Origin refuses to start Python while a command is in flight, and every bridge request is one. Scripts containing
run -pyforpy.execare refused up front rather than failing without explanation.
References
Ge-Shun/origin-mcp — the embedded-bridge transport approach this project's architecture follows.
garethbeaumo/originlab-mcp — a COM-attached server; simpler to run when Origin is on the same desktop session.
Origin and OriginPro are products of OriginLab Corporation. This project is not affiliated with OriginLab.
Licence
MIT.
Available Tools
17 toolsorigin_axis_getA
Read an axis's scale and range as Origin currently holds them.
Worth doing before setting a range: Origin's own rescaling may already have moved it, and the number a caller remembers setting is not necessarily the one in the graph.
Inputs: graph (required), axis, layer. Returns: object
origin_axis_setA
Set an axis's scale, range or title, and read back what Origin holds.
scale is a name (linear, log10, ln, log2, probability,
reciprocal) or an Origin scale number. Omitted arguments are left alone.
Unlike plot styles, these read back from the same properties Origin draws
from, so matches_request is a real check. Origin clamps and rounds axis
ranges for its own reasons, and a mismatch usually means it did.
Origin swaps the axes of bar charts internally: the value axis of a
horizontal bar chart is the one LabTalk calls y. Setting what looks like
the value axis on one will retitle the categories instead.
Inputs: graph (required), end, axis, layer, +4 more. Returns: object
origin_bridge_statusA
Report where the bridge is, and whether it is actually answering.
These are different questions, and the difference is not academic. The
bridge publishes a handshake file when it starts and withdraws it when it
stops -- but a modal dialog inside Origin takes over the UI thread the
bridge serves on, so it can stop answering for hours while the handshake
goes on saying it is up. Measured: set <dataset> -dc opens such a dialog,
and nothing was served until someone clicked it.
So announced is what the file says and answering is what a request
found. Only the second means the bridge can do anything. Pass
check_liveness=False to skip the request when the answer does not matter.
Inputs: check_liveness. Returns: object
origin_graph_exportA
Export a graph to a PNG.
Also the only way to find out whether a style change did anything: Origin
stores values it never acts on, so the picture is the only witness. The
result reports where Origin was told to write, not that it succeeded --
expGraph says almost nothing about failure.
Inputs: graph (required), filename (required), directory (required), width. Returns: object
origin_graph_infoA
List a graph's layers and plots, with the addresses styling needs.
Also reports each plot's group_mode. A plot in a group is drawn with the
group's style, so writing to a member is refused -- check here before
trying to style one.
Inputs: graph (required). Returns: object
origin_labtalkA
Run a LabTalk script inside Origin and return what it printed.
The escape hatch: anything Origin can do from its command window. Prefer a dedicated tool where one exists, because this one cannot check its own work.
returned says the script parsed and dispatched, not that it did anything.
A LabTalk script stops at its first bad expression, so when returned is
false the last captured line is the last line that ran. Origin's own error
text goes to its console and cannot be captured from here.
Scripts that start Python (run -pyf, py.exec) are refused: Origin will
not run Python while a command is in flight, and every request here is one.
Inputs: script (required), capture_output. Returns: object
origin_layer_addC
Add a layer (a panel) to a graph, and report which index it got.
Returns the new layer's index because every other operation takes one, and assuming the new layer is 1 is wrong the moment the graph already had two.
Inputs: graph (required). Returns: object
origin_layers_arrangeC
Lay a graph's layers out as a grid of panels.
A grid too small for the layers is refused: Origin would leave one off the page, and nothing about the result would say so.
Inputs: rows (required), graph (required), columns (required). Returns: object
origin_legend_setA
Rebuild a layer's legend, and optionally place or hide it.
Origin generates its legend from the plots present at the moment it is refreshed rather than storing it, so a legend showing the wrong series is almost always one that has not been refreshed since the plots changed. Call this after adding plots.
position is one of top_left, top_right, bottom_left, bottom_right.
Inputs: graph (required), show, layer, position. Returns: object
origin_pingA
Check the bridge is reachable and report what is on the far end.
Answers even when originpro is broken, which is when it matters most.
Inputs: no named parameters. Returns: object
origin_plotA
Plot worksheet columns into a graph layer, and return the graph and layer.
Without graph this makes a new graph page. Give graph to add another
panel to one that already exists -- that is how a multi-panel figure is
built, one call per panel, then origin_layers_arrange.
plot_type is a name (line, scatter, column, bar, area, box,
histogram, stack_column, ...) or any Origin plot-type id as a number,
so an unusual chart is never blocked by the name list.
Columns are given as zero-based indexes or Origin short names. Several y
columns go into the same layer, grouped unless group is false.
Use the returned graph and layer for everything afterwards. Origin
chooses the name, and the new layer is not the active one, so neither can
be assumed. plots_in_layer is read back from Origin rather than counted
from the request.
Inputs: x (required), y (required), book (required), graph, +3 more. Returns: object
origin_plot_option_getA
Read one LabTalk set option off a data plot.
option is a LabTalk option token such as -w (line width, in points x
500) or -pfp (bar fill pattern). Use origin_set_options to find one --
the names are not guessable, and an invented one would be accepted by
Origin and do nothing.
Inputs: graph (required), option (required), plot, layer. Returns: object
origin_plot_option_setA
Set one LabTalk set option on a data plot, and read it back.
The result's readback says whether Origin stored the value. Read
readback_means before treating that as success: Origin stores options it
does not recognise, so a matching read-back is necessary evidence, not
sufficient. Render the graph if you need to know it changed.
Writing to a plot inside a group is refused, because Origin would draw the group's style regardless while the write read back as if it had worked.
Inputs: graph (required), value (required), option (required), plot, +1 more. Returns: object
origin_set_optionsA
Look up LabTalk set options by what they control.
Origin's option names are short and unguessable -- line width is -w, bar
fill pattern is -pfp -- so guessing produces plausible names that Origin
accepts and ignores. Search here instead.
Each entry's verified says whether this option has been exercised against
a real Origin: null means documented but untested, which is most of them.
Inputs: limit, search. Returns: object
origin_worksheet_infoA
Describe a worksheet -- size, column names, formats -- without reading it.
Omit both arguments for the active worksheet.
Inputs: book, sheet. Returns: object
origin_worksheet_readA
Read worksheet columns as lists, with the format Origin holds them in.
A read that could not fit is refused rather than shortened, because a
silently truncated answer looks exactly like a short worksheet. Use
start_row and max_rows to take a window deliberately, and read
rows_in_sheet and rows_after_window -- named separately because a
single "rows available" count reads exactly like a short worksheet.
Date and time columns come back as Julian day numbers, not text. Converting them involves choices -- timezone, calendar, precision -- that belong to whoever knows what the data means.
Inputs: book, sheet, columns, max_rows, +1 more. Returns: object
origin_worksheet_writeA
Write columns into a worksheet.
Each column is {"name": ..., "format": ..., "values": [...]}. The format
is stated rather than inferred: double, int, text, mixed, date,
time, complex. Guessing from the values is how columns get quietly
corrupted -- whole numbers are not necessarily an integer column, and one
empty cell can turn a numeric column into text.
Dates go in as Julian day numbers with format: "date".
Inputs: columns (required), book, sheet, start_col. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 17 tools
Most tools target a distinct resource (worksheet, axis, plot option, layer, legend) with clear boundaries, and the descriptions actively warn where confusion could arise (e.g., plot_option vs axis vs set_options). The only mild overlap is origin_ping vs origin_bridge_status, and origin_worksheet_info vs origin_worksheet_read, both of which are carefully delineated but require reading the details.
The dominant pattern is origin_<noun>_<verb> (axis_get, layer_add, worksheet_read), but there are deviations: origin_set_options puts the verb first, origin_graph_info and origin_worksheet_info use 'info' where 'get' would match axis_get, and several tools (ping, labtalk, plot, bridge_status) are bare nouns. The get/set pairs are consistent where they exist, but the mixed conventions prevent a higher score.
Seventeen tools is at the boundary where the set starts to feel heavy, but for a bridge to a full desktop application with worksheet, plotting, and styling concerns, each tool has a discernible role and the count is justified. The escape-hatch design (origin_labtalk) keeps the surface from being even larger.
The surface covers the full lifecycle from data reading/writing to plotting, styling, arranging, and exporting, with a clear read/modify/verify pattern throughout. The only notable gap is the lack of any delete/clear operation (no way to remove a plot, layer, or worksheet), though origin_labtalk serves as a viable fallback for these rarer operations.
Maintenance
Related MCP Connectors
Market Data App MCP — wraps the Market Data App API (marketdata.app)
Read and write Mission Control state via MCP — projects, tasks, subtasks, templates, status updates.
UMLS MCP — wraps the NLM UMLS Terminology Services REST API (uts-ws.nlm.nih.gov/rest)
Auto.dev MCP — wraps the Auto.dev automotive data API (auto.dev)
Related MCP Servers
- AlicenseAqualityBmaintenanceEnables AI assistants to control Origin/OriginPro on Windows, including data import, worksheet editing, graphing, analysis, and figure export.25169 PyPI153MIT
- AlicenseBqualityDmaintenanceA local MCP server that enables AI assistants to control Origin/OriginPro on Windows via Python automation, with adaptive path detection to resolve import errors.632MIT
- AlicenseAqualityDmaintenanceEnables AI assistants to control OriginLab Origin Pro via COM automation for data analysis, graphing, and styling, with real-time GUI updates.256145 PyPI47MIT
- FlicenseNot gradedqualityBmaintenanceEnables AI clients to drive a running OriginLab Origin instance, allowing LabTalk scripting, worksheet read/write, and project management.-