Stata code
Use this skill whenever the user asks to run Stata code, debug a `.do` file, work with a Stata-backed Jupyter notebook, repair a Stata error, interpret `r()` / `e()` results, or write/plan a Stata analysis — and the `stata-code` MCP server is available (or, when it is not, to generate self-contained do-files). The skill teaches Claude the v1.0 RunResult schema, the 21 MCP tools, token-economy defaults, the typed-error repair loop, and a routing table into an on-demand Stata reference library (syntax, data management, econometrics, causal inference, panel/time series, graphics, tables, error codes, defensive coding, and key packages).From its SKILL.md
npx -y skills add brycewang-stanford/stata-code --skill stata-codeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
21.9 KB, as published. Nobody here has run it
stata-code Skill
stata-code is an agent-native Stata bridge. This skill briefs Claude on how to drive Stata efficiently through it. Do not regress to log-grepping; the schema is the contract.
1. When this skill applies
Activate this skill whenever the user mentions Stata in a way that implies execution, inspection, repair, or authoring, e.g.:
- "Run this regression in Stata."
- "Why does
summarize mpggfail?" - "Fix this do-file and rerun it until it passes."
- "Open
analysis.ipynbcell 3 and replace it with a robust SE specification." - "Pull
e(b)and the residual variance after my lastregress." - "Write a staggered DiD / event-study in Stata."
It also applies on context carry-over: if earlier turns were about Stata, keep the skill active even when a later message doesn't say "Stata".
Confirm the MCP server is wired up with stata_info() once per session — this also selects your execution mode (§2).
2. Execution mode: live vs offline
Live (MCP) mode — default. stata_info() returns available: true. Execute code with stata_run, read the structured result, and repair using typed errors (§7–8). This is the normal path; everything below assumes it.
Offline (code-gen) mode. No stata-code server, or stata_info() returns available: false. You cannot execute Stata. Do not pretend to. Instead:
- Surface the install hint once (
pip install "stata-code[mcp]") if the user expected execution. - Produce a complete, self-contained
.dofile: start withversion 18, load the data,set seedbefore any randomness, comment each block, and bake in defensive guards (seereferences/defensive-coding.md). - Present the code plus expected outputs, clearly labeled as not-yet-run.
3. Reference library — read on demand
The skill ships a references/ library of dense Stata domain knowledge. Progressive disclosure: read at most 1–3 files relevant to the current task — never preload them all. The schema and tool sections below are always in effect regardless of which references you open.
| If the task is about… | Read |
|---|---|
| Core syntax, macros, missing values, loops, factor/time-series operators | references/syntax-core.md |
| Loading, cleaning, merging, reshaping, labeling data | references/data-management.md |
| Regression, GLM, IV mechanics, fixed effects, postestimation, margins | references/econometrics.md |
| Causal designs — DiD, event study, RDD, matching/weighting, synthetic control | references/causal-inference.md |
Panel data / time series — xtset, tsset, xtreg, dynamic panels, arima | references/panel-timeseries.md |
| Plots and visualization | references/graphics.md |
| Regression/summary tables, LaTeX/Word/Excel/Markdown export | references/tables-export.md |
| Choosing public data sources or documenting source provenance | references/data-sources.md |
| Data fetched by external MCPs should become Stata inputs | references/data-mcp-handoff.md |
| Cross-package or cross-language robustness / parity checks | references/parity-audit.md |
| Turnkey empirical recipes — DiD/event study, IV/2SLS, RDD, publication tables, cross-validation | references/recipes/<recipe>.md |
Interpreting results.estimation (coefficient table, diagnostics), error.recovery, reproducible-do / submission bundles, verify_dataset | references/structured-results.md |
Diagnosing a failed run, Stata rc codes, the self-repair loop | references/error-codes.md |
| Writing correct, reproducible Stata that fails loudly (not silently) | references/defensive-coding.md |
| A specific community package | references/packages/<pkg>.md — reghdfe, csdid, drdid, did_imputation, eventstudyinteract, did_multiplegt_dyn, rdrobust, ivreg2, ivreghdfe, boottest, estout, outreg2, coefplot, gtools |
Routing examples: "panel regression with clustered SEs" → econometrics.md (+ panel-timeseries.md); "my merge gives wrong N" → defensive-coding.md; "command not found: reghdfe" → error-codes.md + packages/reghdfe.md; "make a publication table" → tables-export.md + recipes/publication-tables.md; "compare Stata csdid against R did" → parity-audit.md + packages/csdid.md; "OECD MCP pulled a CSV; now analyze it in Stata" → data-mcp-handoff.md; "run the full DiD/event-study workflow" → recipes/did-event-study.md.
4. The 21 MCP tools (cheat sheet)
| Tool | Use it when… |
|---|---|
stata_run(code, session_id?, …) | The user wants Stata code executed. Default to session_id="main". See §4.1 for the payload/timeout knobs. |
stata_run_status(job_id, wait_ms?) | Poll a run you submitted with run_in_background: true. wait_ms blocks up to 60 s — far cheaper than a tight poll loop. |
list_background_runs() | See which background runs are still going. Summaries only; fetch a result with stata_run_status. |
stata_info() | At session start (also picks live vs offline mode), or when capabilities / Stata edition matter. |
get_log(ref) | A prior stata_run returned log.truncated: true and you need the full log. |
search_log(ref, pattern, is_regex?, ignore_case?, context?, max_matches?) | You need only specific lines from a truncated log:// ref — grep it instead of pulling the whole log back with get_log. |
get_graph(ref, format?) | The user wants graph bytes (export, display, embed). |
get_matrix(ref) | A matrix in results.r.matrices / results.e.matrices came back with values: null. By default every matrix is a stub, so this is the normal way to get raw e(V) / e(b) numbers. |
inspect_data(varlist?, detail?, session_id?) | "What's in this dataset?" Runs describe + codebook; returns the structured dataset block plus the codebook log. |
lint_do(code? / path?) | Before running a long or generated do-file, statically check it (unbalanced braces, missing end, dangling ///). Cheap, Stata-free; catches structural mistakes without spending a run. Advisory — a clean result is not a guarantee. |
install_package(name, source?, url?, replace?, session_id?) | A run failed with command_not_found (rc 199) for a community package, or the user asks to install one. Builds ssc/net install, then verifies with which. |
list_sessions() | The user mentions multiple parallel Stata "tabs", or you need to find a session by id. |
cancel_session(session_id) | A run is hung or the user said "stop". Subprocess workers terminate; in-flight code is killed. |
reset_session(session_id?) | The user wants clear all-style fresh state for a session. |
notebook_outline(path) | The user references a .ipynb and you need to know which cells exist. |
notebook_get_cell(path, cell_id) | Read one cell's source plus a compact outputs summary. |
notebook_locate(path, snippet/regex/error_text) | Find which cell contains a snippet or produced an error message. |
notebook_edit_cell(path, cell_id, new_source, expected_source?) | Atomic cell replace. Pass expected_source for optimistic concurrency. |
notebook_insert_cell(path, source, after_cell_id?, before_cell_id?, at_start?, at_end?, cell_type?) | Insert a new cell with a fresh nbformat 4.5 uuid. |
notebook_delete_cell(path, cell_id, expected_source?) | Remove a cell. Pass expected_source when guarding against drift. |
list_runs(log_dir or origin_path, …) | Search the on-disk run-bundle index — "show me my last failed run on this file". |
4.1 stata_run options worth knowing
Defaults are already tuned for you; reach for these only when the situation calls for it.
| Option | Default | Reach for it when… |
|---|---|---|
include_results | "scalars" | You genuinely need raw matrix numbers inline: "full". Under the default, scalars and macros are inline and every matrix is a matrix:// stub with its shape — because one estimation otherwise ships the same numbers four times (e(b), e(V), e(beta), r(table)) on top of results.estimation. "none" drops r()/e() entirely and still gives you estimation. |
include_estimation | "full" | The model is dominated by i.year i.firm nuisance terms and you only want the model-level block: "summary". |
max_coefficients | unset | You want the first N coefficient rows. estimation.n_coefficients still reports the true count and coefficients_truncated flags the cut, so you can always tell. |
timeout_ms | 600000 | A run legitimately needs longer, or you want to fail fast. The budget covers queueing, so a call waiting on a busy session returns rc: -5 rather than hanging. |
run_in_background | false | Bootstraps, permutation tests, prodest loops — anything multi-minute. Returns a job_id immediately; poll stata_run_status. Give the job its own session_id if you want to keep working, since one Stata process serves one session. |
track_output_files | true | Leave it on: result.outputs tells you which esttab tables / exports / .dta files the run wrote, so you don't have to go hunting with shell tools. |
auto_close_logs | true | Leave it on. It closes log handles a failed run leaked; without it an aborted log using makes every later run in that session die with r(604). |
There are also MCP resources (stata://schema/run-result, log://..., graph://..., matrix://...) and prompts (run_do_file_and_report, debug_stata_error, fix_and_rerun_until_passes, replication_audit, summarize_estimation_results, run_notebook_cell_and_report, fix_and_rerun_notebook_cell, plan_cross_stack_parity_audit, data_mcp_to_stata_handoff, did_event_study, iv_2sls, rdd, publication_table, cross_validate_did).
5. The v1.0 RunResult schema (read this once)
Every stata_run reply has this shape (full spec: stata://schema/run-result or SCHEMA.md in the repo):
{
"ok": true, // ← branch on this first
"rc": 0, // Stata _rc; -1 adapter crash, -2 timeout, -3 cancelled,
// -4 policy blocked, -5 session busy
"session_id": "main",
"request_id": "01HX…",
"started_at": "2026-…Z",
"elapsed_ms": 234,
"stata_elapsed_ms": 198,
"stata": {"version": "18.0", "edition": "MP", "backend": "pystata"},
"log": {
"head": "...", // first 20 lines by default
"tail": "...", // last 20 lines (empty when not truncated)
"lines_total": 42,
"bytes_total": 2380,
"truncated": true,
"error_window": null, // ~10 lines around the failure on errors
"ref": "log://run-7f3a9b" // fetch full via get_log(ref) / grep via search_log(ref)
},
"results": {
"r": {"scalars": {…}, "macros": {…}, "matrices": {…}},
// By default every matrix is a STUB: no values, no labels, just shape + ref.
"e": {"scalars": {…}, "macros": {…},
"matrices": {"b": {"rows": [], "cols": [], "values": null,
"ref": "matrix://…", "n_rows": 1, "n_cols": 2}, …}},
"last_estimation_cmd": "regress",
// ← THE typed view. Read this instead of the raw matrices.
"estimation": {
"command": "regress", "command_family": "ols", "depvar": "price",
"n_obs": 74, "statistic_kind": "t", "source": "r_table",
"coefficients": [{"term": "mpg", "b": …, "se": …, "statistic": …,
"p_value": …, "ci_low": …, "ci_high": …}, …],
"n_coefficients": 2, // true term count, even if rows were trimmed
"coefficients_truncated": false,
"model_stats": {"N": 74, "r2": 0.219, …},
"diagnostics": {"weak_id_F": …, "hansen_p": …} // command-aware, never fabricated
}
},
"dataset": {"frame": "default", "n_obs": 74, "n_vars": 12, "changed": false, …},
"graphs": [{"ref": "graph://…", "format": "png", "source_command": "scatter …", "source_line": 5}],
"outputs": [{"path": "/w/tables/t1.tex", "bytes": 4552, "created": true}], // files the run wrote
"warnings": [{"kind": "convergence", "message": "…"}],
"error": null, // populated iff ok=false; see §7
"origin": null, // echoes origin_* request fields
"schema_version": "1.0",
"capabilities": ["log_truncation", "graph_ref", "matrix_ref", "multi_session",
"result_budget", "background_runs", "output_tracking", "log_hygiene", …]
}
Key invariants:
- Branch on
okfirst; never greplog.headto decide success. - Scalars are native numbers. Missing is JSON
null, not"."and never8.988e+307. The same holds for every matrix cell. results.estimationis the coefficient table to read. It is always derived from the complete values, so it carries realse/p_value/ CI even when the matrices are stubs. Do not reconstruct inference frome(b)ande(V)yourself.- Matrices arrive as
values: null+ amatrix://ref by default — callget_matrixonly when you need the raw numbers. result.outputslists the files the run wrote. Read it instead of shelling out to find theesttabtable you just produced.- Graphs default to refs, not base64 bytes.
include_graphs: "inline"now returns real image content blocks (viewable), capped at 4 per response.
6. Token-economy defaults — keep responses small
stata-code is already aggressive about this; do not undo its work:
- Do not pass
include_full_log: trueunless the user asked for the full log or the head/tail clearly miss the relevant content. - Do not pass
include_graphs: "inline"unless the agent needs the bytes (rare; usually surface theref). - Do not read
get_log(ref)proactively; prefersearch_log(ref, pattern)to pull just the lines you need, and only fall back toget_logfor the full transcript. - Do not pass
include_results: "full"reflexively.results.estimationalready has the typed table with real standard errors;"full"roughly doubles the envelope for a wide model and hands you the same numbers three more times. - Do reach for
include_estimation: "summary"ormax_coefficientswhen a model has dozens of fixed-effect terms you are not going to report. - Do quote specific numbers from
results.estimation/results.e.scalarsrather than dumping JSON. - Do send genuinely long jobs to
run_in_backgroundon their ownsession_idinstead of blocking a foreground call for minutes.
7. The typed-error taxonomy (34 kinds)
On failure, the error block looks like:
{
"kind": "varname_not_found", // ← branch on this, not on rc or message
"rc": 111,
"rc_label": "variable not found",
"message": "variable mpgg not found",
"command": "summarize mpgg",
"line": 3, // line within source_file, else within submitted code
"source_file": null, // set when the failure was inside a `do`/`run` script
"context": {"before": ["use auto"], "failing": "summarize mpgg", "after": []},
"commands_executed": 1,
"varname": "mpgg", // populated for varname_* / file_* / name_* kinds
"suggestions": [{"action": "Did you mean `mpg`?", "command": "describe"}],
"recovery": {"category": "user_code", "retriable": false,
"needs_code_change": true, "needs_user_input": false}
}
When you submit do "analysis.do" and it fails, line and context point inside
analysis.do and source_file names it — you do not need to re-read the script to
find the offending line. A failed run also still carries a full log, so
search_log(ref, …) works on failures.
Kinds you will see most often:
varname_not_found(rc 111) —varnameis filled; checkdataset.variablesfor the right name.syntax(rc 100–103/121–127/130/132/197/198) — usually a typo; inspectcontext.failing.command_not_found(rc 199) — often a community package:install_package(name=...).file_not_found/file_exists/file_corrupt(rc 601/602/610/688) —pathis filled.not_sorted(rc 5) — prependsort <var>.name_conflict(rc 110) — usereplaceor pick a fresh name.convergence/infeasible(rc 430/491) — model issue, not a typo; do not loop on it.no_estimation_results(rc 301) — likelypredict/marginsbefore anyregress.log_state(rc 604/606) — a log handle is in the wrong state, almost always left by an earlier aborted run. Fix iscapture log close _all, not a code change;recovery.retriableis true.auto_close_logsprevents most of these.session_busy(−5) — that session's Stata process is still running an earlier request. Nothing ran; the code is fine. Wait, raisetimeout_ms, use a differentsession_id, or send the long job to the background.timeout(−2) /cancelled(−3) /adapter_crash(−1) — system-level; do not retry blindly.
The full rc → kind → fix table and the self-repair algorithm live in references/error-codes.md. Read it whenever you hit a non-trivial failure. Use error.suggestions as hints, not directives — apply a fix automatically only if the user asked you to repair and rerun.
8. The two big workflows
8.1 Diagnose-only (default)
1. stata_run(code)
2. If ok: report scalars/warnings. Done.
3. If not ok:
- State error.kind, error.line, error.context.failing.
- List error.suggestions verbatim.
- Ask the user how to proceed (do not edit source files).
8.2 Fix-and-rerun-until-passes (only when the user said so)
Drive the loop from error.kind (full version in references/error-codes.md):
loop (cap ~5 iterations):
result = stata_run(current_code)
if result.ok: break
switch result.error.kind:
command_not_found → if community pkg: install_package(name); else fix spelling
varname_not_found → closest match from error.varname / dataset.variables
syntax → fix the line at error.line
not_sorted → prepend `sort <var>`
name_conflict → add `replace` or drop the conflicting object first
file_not_found → fix error.path or generate the missing file
convergence/infeasible/estimation_failure → MODEL issue: respecify, do NOT loop
adapter_crash/timeout/cancelled → STOP and surface to the user
policy_blocked → the code used a blocked OS-escape command (shell/erase/rmdir/!);
do the task with a native Stata command instead, do NOT retry as-is
rewrite the .do file or notebook cell; re-run
if the same kind+line repeats unchanged twice → STOP with a summary
For notebook repair, use notebook_edit_cell(path, cell_id, new_source, expected_source=<old>) with optimistic concurrency so a user-side edit aborts your write rather than silently overwriting it.
9. Multi-session etiquette
- Default session is
"main". Long analyses with conflicting state belong in named sessions (session_id="model_a"). Valid ids match[A-Za-z0-9_-]+; ids that are not legal Stata frame names are mapped to private frames and still echo the public id. - The VS Code extension calls sessions "tabs". A new session lazily spawns or maps to a Stata frame; data does not cross.
- After heavy state changes, prefer
reset_session(session_id)over rerunning withclear all— it is cheaper and clears refs.
10. Origin metadata (helpful for run-bundle audit)
When the user supplies a source file or notebook cell, pass:
origin_path: absolute path of the.do/.ipynborigin_kind:"file","selection","line","cell","section","code"origin_label:"analysis/main.do:42"or similarorigin_cell_id: nbformat 4.5 cell uuid when it's a notebook cell
The runner echoes these into result.origin and writes them to the run-bundle manifest. list_runs then finds prior runs by cell or by file.
11. Things to NOT do
- Do not shell out to
stata/do-file editor/pystatadirectly. Usestata_run. - Do not use OS-escape / file-deletion commands (
shell,winexec,erase,rm,rmdir,!) insidestata_run. They are blocked by the command-safety policy and returnpolicy_blocked. Stay within native Stata data commands (save/use/copy); if a file genuinely must be deleted, ask the user to do it or to relax the policy. - Do not parse English from
log.headto detect success — useok/rc/error.kind. - Do not retry a failing command unchanged. The taxonomy tells you why it failed; act on it or report it.
- Do not loop the fix-and-rerun routine on a model problem (
convergence,infeasible). - Do not assume
e()is populated after a non-estimation command. Checkresults.last_estimation_cmdfirst. - Do not rewrite a
.dofile or.ipynbcell unless the user asked for repair. Diagnostics first. - Do not paste graph base64 into chat unless the user explicitly asked for bytes; graphs go through
get_graph(ref). - Do not preload the whole
references/library — open only the 1–3 files the task needs.
12. Reference
- Domain knowledge: the
references/library (routing table in §3). - Full schema:
SCHEMA.mdin the repo or the MCP resourcestata://schema/run-result. - Server capabilities + tool list: MCP resource
stata://server/capabilities. - Examples (DiD, IV, graphs, multi-session, large matrices, parity audits, data-MCP handoff):
examples/in the repo. - License: MIT (
stata-codeitself); Stata is a registered trademark of StataCorp LLC.