- name
- stata-code
- description
- 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 22 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).
# 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 mpgg` fail?"
- "Fix this do-file and rerun it until it passes."
- "Open `analysis.ipynb` cell 3 and replace it with a robust SE specification."
- "Pull `e(b)` and the residual variance after my last `regress`."
- "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 `.do` file**: start with `version 18`, load the data, `set seed` before any randomness, comment each block, and bake in defensive guards (see `references/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 23 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. |
| `set_value_labels(path, value_labels?, attach?, data_label?, dry_run?)` | The user wants value labels defined, changed, dropped or attached in a `.dta` file on disk, or its dataset label changed (e.g. "label the codes of this survey file"). Stata-free. Call it with only `path` first: that returns every variable's label, attached value label and notes, every value-label set and the dataset label. `value_labels` is `{name: {code: text}}` and replaces the whole set (`null` drops it and detaches it); a code is an integer or `.a`-`.z`. `attach` is `{variable: name}` (`""` detaches; numeric variables only; the name must exist after this call). The observations are never re-encoded; nothing is written if any part is invalid. For a dataset in a session's memory use `label define` / `label values` via `stata_run` instead. |
| `set_variable_labels(path, labels?, dry_run?)` | The user wants variable labels added or fixed in a `.dta` file on disk (e.g. "label this unlabeled dataset"). Stata-free and in place: only the label fields change, so notes, value labels and formats survive. Omit `labels` to read the current ones first; `changed[].before` is the undo. 80 characters max; formats older than 118 take ASCII only. For a dataset in a session's memory use `label variable` via `stata_run` instead. |
| `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):
```jsonc
{
"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, …},
Ver en GitHub