MCP wrapper around CarSim domain: parsfile parsing/editing, headless solver execution, simfile generation, co-simulation scaffolding, result parsing, and database navigation for vehicle dynamics simulation.
CarSim MCP demonstrates solid domain expertise with 23 well-named, action-verb tools covering simulation workflows. Tool descriptions are detailed and contextual (averaging ~180 chars), exceeding the 10 - 1024 char baseline. All parameters have type declarations and descriptions. However, output schemas are not explicitly documented in the source, only inferred from return statements. Error handling is minimal; tools return raw dicts without recovery guidance. Security considerations (path traversal, injection) are not visible. Composition is strong: tools chain logically (e.g., find_dataset → get_dataset → set_dataset). No tool combines unrelated concerns. Naming follows verb_noun convention consistently (read_*, write_*, list_*, get_*, set_*, run_*, etc.), aiding LLM intent inference.
List datasets inside a library (optionally a category subfolder), each with its GUI identity {dataset, category, library} and file path. e.g. browse_library('Powertrain', 'Motor') or browse_library('Vehicles'). Feed a returned 'file' to get_dataset to read/edit it. Returns {library, category, n, datasets}.
Build the local keyword dictionary (keywords.json) by harvesting CarSim's own echo files (DATA\Results\**\*_echo.par), mapping each keyword to its unit + description. Run once per install (and after upgrading CarSim). refresh=False just reports the existing dictionary; max_files>0 does a quick partial build. The dictionary is a LOCAL artifact (CarSim's proprietary text), not redistributed. Returns {path, n_keywords, n_echo_files, refreshed}.
Resolve and existence-check every path the CarSim MCP relies on (CarSim root, GUI exe, CLI solver, Simulink solver dir, vs_sf mex, MATLAB exe, working database). Use this first to confirm the toolchain is wired up. Returns a dict with per-path {path, exists} plus a top-level 'ok' flag.
Create a copy of a dataset with a fresh #FileID and optional new GUI identity (library/dataset/category). Useful for creating variants without polluting the shipped library. Returns {file, identity, file_id}.
Post-process a CarSim run output directory: collect results, echo files, and metadata into a single .zip archive for easy transport/archival. Returns {archive, size_mb, n_files}.
Output schemas not explicitly documented. Tool descriptions state return structure (e.g., '{returncode, stdout_tail, stderr_tail}') but no formal JSON Schema definitions visible in source. LLMs cannot reliably parse undocumented outputs.
No error handling guidance. Tools return raw dicts on success but no documented error responses, recovery paths, or categorization (retryable vs. fatal). E.g., run_solver may timeout or fail to find simfile, but no error schema or guidance provided.
No input validation or injection protection visible. Tools accept file paths (read_parsfile, write_parsfile, set_dataset) without documented sanitization. Path traversal attacks possible if LLM is tricked into passing '../../../etc/passwd'.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 56 | 2026-07-28+ | v2 |
Look up a parsfile keyword's meaning + unit in the local keyword dictionary (e.g. describe_keyword('M_SU') -> mass of sprung mass, kg). If unknown, returns similar keyword names. Build the dictionary once with build_keyword_dictionary. Returns {keyword, known, desc?, unit?, similar?}.
Fuzzy-search the whole database by GUI identity text (library/dataset/category names). Answers 'where do I set X', e.g. find_dataset('motor'), find_dataset('sprung mass'), find_dataset('jounce'). Returns {query, n, results:[{file, library, dataset, category}]}.
Create/refresh a co-sim simfile at out_path by templating from a GUI-generated simfile.sim (template_simfile), optionally swapping the referenced parsfile to run_parsfile. Returns {simfile, parsfile, templated_from}.
Read a dataset parsfile into structured form: GUI identity + scalar 'params' (each {value, desc?, unit?}) + 'tables' (each {method, rows, desc?, unit?}). With annotate=True, keywords are tagged with meaning + unit from the local keyword dictionary (build it once via build_keyword_dictionary). This is the high-level read for editing vehicle/suspension/powertrain/motor parameters. Returns {path, identity, n_params, params, n_tables, tables}.
Parse the link/assembly layer of a dataset (e.g. a Vehicle Assembly that references a Powertrain, Suspension, Tire, Steering, etc.). Returns {path, n, links:[{keyword, target_file, target_identity}]}. Use resolve_assembly to recursively expand the tree.
Launch the CarSim GUI (CarSim_64.exe), non-blocking. Optionally pass a dataset/cpar path to open. Returns the new process pid.
List shipped CPAR archives (Resources\CPAR_Archives) and Simulink example models (DATA\Extensions\Simulink). Returns {cpar_archives, simulink_models, n_cpar, n_models}.
List the CarSim GUI libraries (DATA subfolders that hold datasets, e.g. Suspensions, Powertrain, Vehicles, Steering, Tires, IO_Channels). Each entry has {name, n_datasets, categories}. Start here to discover where a parameter lives. Returns {db, n, libraries}.
Parse a CarSim parsfile into {path, keywords, raw_lines}. 'keywords' maps each KEYWORD to a list of its value-strings (occurrences preserved in order).
Parse a results file (.mat, or a CarSim ERD .vs/.vsb) into per-channel summaries {len, min, max, final, unit}. A full CarSim run can have 1000+ channels, so use pattern= to keep only matching names (case-insensitive substring or regex, e.g. pattern='Vx' or 'Ay|AVz|Yaw'); use names_only=True to list channel names+units without reading the binary (fast discovery). Returns {source, n, n_returned, truncated, channels|names}.
Recursively expand a dataset's link tree (e.g. Vehicle -> Powertrain -> Motor, Transmission; Suspension -> Spring, Damper). Returns a tree {path, identity, children:[{keyword, target_identity, children:[...]}]}. max_depth limits recursion (default 3). Useful for understanding the full parameter space of a vehicle configuration.
OPTIONAL FALLBACK. Run a generated co-sim driver via `matlab -batch` (cold, unattended). The primary path is to run runner_m through the official MATLAB MCP instead (warm, visible session). Returns {returncode, cosim_ok, stdout_tail, stderr_tail, results_path}.
Run the headless CarSim solver (no MATLAB) on a simfile via the CLI solver wrapper. Returns {returncode, stdout_tail, stderr_tail, output_dir, declared_outputs}.
Generate a ready-to-run co-sim package in out_dir. kind='slx' wires a Simulink model containing the CarSim S-Function (defaults to a copy of the shipped example.mdl). kind='mfile' wraps a MATLAB .m controller call (must assign a variable 'results'). Writes run_cosim.m. Hand the returned 'runner_m' to the MATLAB MCP (run_matlab_file) to execute. Returns {runner_m, model, simfile, results_path, solver_dir}.
Edit scalar keyword values in a dataset (edits = {KEYWORD: new_value}) and refresh its #Modified stamp so the change shows in the GUI list. Writes a .bak backup. e.g. set_dataset(massfile, {'M_SU': '1500', 'H_CG_SU': '530'}). Returns {changed, added, backup, modified_stamp}.
Rewire a link in a dataset (e.g. swap the Powertrain in a Vehicle Assembly). 'keyword' is the link keyword (e.g. POWERTRAIN_FILE); 'target_file' is the new target dataset path. Returns {keyword, old_target, new_target, backup}.
Replace the rows of a CarSim TABLE block (KEYWORD [method] ... ENDTABLE). Most chassis/powertrain/battery PHYSICS is tables: motor torque maps, tire Pacejka carpets, battery OCV-vs-SOC, spring/damper curves. 'rows' is a list of rows, each a string ("0, 1") or a list ([0, 1]). method overrides the interpolation token (LINEAR/STEP/SPLINE...) if given. occurrence picks the Nth table block (1-based) if the keyword repeats. Re-reads to verify. Returns {keyword, n_rows, method, backup, verified}. (set_dataset only edits scalars; this owns tables.)
Edit scalar keyword values in a parsfile. 'edits' = {KEYWORD: new_value}. Replaces the chosen occurrence of each keyword (appends if absent), writing a .bak backup. occurrence: 1-based (default 1 = first); occurrence=0 replaces ALL occurrences -- use this for keywords that legitimately repeat (IDIFF, per-axle PARSFILE links). For TABLE blocks use set_table; for links use set_link. Returns {changed, added, backup}.
No pagination or result limits enforced. read_results accepts max_channels=50 default, but browse_library defaults to limit=300 and find_dataset to limit=50. Inconsistent limits; no guidance on when to paginate or how to handle large result sets.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint). Destructive tools like write_parsfile, set_dataset, delete operations lack explicit markers. LLMs cannot distinguish safe reads from dangerous writes without parsing descriptions.