Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

tmodloader-mcp

PYPI · TMODLOADER-MCP · SCANNED SEP 20

Drive a running tModLoader (Terraria) instance from an agent: launch, query, screenshot, read state.

Available components

54 Trust /100
Trust breakdown (7 categories)

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →

Supply Chain Security50
  • Malware scan not yet available for this package.Unverified
  • No known CVEs affecting this package version or its production dependencies.Pass
  • Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
  • 1 of 29 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency45
Schema Quality & AI Usability77
  • 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).Pass
  • AI-judged instruction clarity (excellent).Pass
  • Context-footprint check failed: tool/resource definitions use about 6400 tokens (~266/item across 24 items; 24 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
  • Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management0
  • Stability not yet verified: not enough scan history yet (needs a 30-day window).Unverified
Tool Coverage71
  • 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
  • 0% of tool parameters carry a description.Fail
  • Structured output schemas are declared (96% of tools); any adoption earns full credit.Pass
Tool Safety100
  • No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
  • All 3 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation.Pass
  • An AI judge read all 25 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
  • Implements a current MCP spec version (2026-07-28).Pass

Unverified: 1 category

A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

Install

How do I install the tmodloader-mcp server?

tmodloader-mcp runs locally as a PyPI package, launched with uvx tmodloader-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

pypi · tmodloader-mcp

# add to Claude Code
claude mcp add musharna-tmodloader-mcp -- uvx tmodloader-mcp
// .cursor/mcp.json
{
  "mcpServers": {
    "musharna-tmodloader-mcp": {
      "command": "uvx",
      "args": [
        "tmodloader-mcp"
      ]
    }
  }
}
// .vscode/mcp.json
{
  "servers": {
    "musharna-tmodloader-mcp": {
      "command": "uvx",
      "args": [
        "tmodloader-mcp"
      ]
    }
  }
}
# add to Codex CLI
codex mcp add musharna-tmodloader-mcp -- uvx tmodloader-mcp
// opencode.json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "musharna-tmodloader-mcp": {
      "type": "local",
      "command": [
        "uvx",
        "tmodloader-mcp"
      ],
      "enabled": true
    }
  }
}
# add to OpenClaw
openclaw mcp add musharna-tmodloader-mcp --command uvx --arg tmodloader-mcp
# ~/.hermes/config.yaml
mcp_servers:
  musharna-tmodloader-mcp:
    command: "uvx"
    args: ["tmodloader-mcp"]
// ~/.netclaw/config/netclaw.json
{
  "McpServers": {
    "musharna-tmodloader-mcp": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "tmodloader-mcp"
      ]
    }
  }
}
# add to Vellum
assistant mcp add musharna-tmodloader-mcp -t stdio -c uvx -a tmodloader-mcp
// mcp.json
{
  "mcpServers": {
    "musharna-tmodloader-mcp": {
      "command": "uvx",
      "args": [
        "tmodloader-mcp"
      ]
    }
  }
}
Changelog

Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.

  • 17 Sept 26 54

    First indexed and scored.

Diagnostics

Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.

Captured 20 Sept 2026 · Analysed pypi/tmodloader-mcp@0.6.3

Provenance No attestation

The registry publishes no build provenance for this version, so there is nothing to verify.

Result No attestation
Ecosystem pypi

Background: How many MCP packages publish verified provenance →

Install scripts 1 script
Hook Tier Command
build_backend allowlisted hatchling.build

Background: Why install scripts are a supply-chain risk →

Dependencies 29 packages
Packages resolved 29
No linked repository 1
Tree resolution Complete

Background: SBOMs and build attestations, explained →

MCP tools · 24 exposed · ~6,270 tokens

The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →

Tool Tokens
api_search ~321

Find a type, field, property or method in the INSTALLED tModLoader. Args: query: Part of a name, or a type. `cloudAlpha`, `rain`, `QuickSpawnItem`, `IEntitySource`. Case-insensitive. kind: Narrow to one of type, field, property, method. limit: How many matches to return, best first. ANSWERS THE QUESTION YOU HAVE BEFORE YOU WRITE ANYTHING. A compile tells you exactly whether the call you already wrote is right; it cannot tell you what is there. `Main.maxRaining` is only findable if you already suspect the name. READ FROM THE ASSEMBLY'S OWN METADATA, so it cannot drift from the version installed — which is the failure mode of every wiki page and every model's recollection of an API. It carries no prose, because it is not documentation: it is the public surface, with signatures. The index is built once per tModLoader version and cached against the DLL it came from, so a game update invalidates it by construction rather than by anybody remembering to. The first call after an update pays a few seconds. Needs a .NET SDK, because the indexer is a small C# tool — it reads metadata without loading or running the game assembly. Without one this refuses and says so, rather than answering from a stale or absent index.

NameTypeReqDescription
kind
limitinteger
querystringyes
NameTypeReqDescription
indexedintegeryes
matchesarrayyes
truncatedbooleanyes

No examples provided.

build_mod ~164

Compile the configured mod source into a .tmod. Args: timeout: Seconds to wait for the compile. A large mod on a slow machine can outlast the default, and a build that runs out of time says so rather than reporting a compile failure with no errors in it. tModLoader REFUSES to build while the game is open, and says so with an error that otherwise reads like a compile failure. That case is reported as itself, with the fix, rather than as a broken build — which is the difference between closing the game and hunting a syntax error that is not there. Success is read from the output, not the exit code, which is not reliable here.

NameTypeReqDescription
timeoutnumber
NameTypeReqDescription
errorsintegeryes
game_was_openbooleanyes
okbooleanyes
summarystringyes
warningsintegeryes

No examples provided.

captures ~46

Every capture in the save directory, newest last. Names, not paths — a path handed out is a path that can come back changed, and `read_capture` deliberately accepts only a name.

Input schema present but exposes no named parameters.

Structured output declared, but exposes no named fields.

No examples provided.

commands ~246

What this side's mod says it serves, read from the mod itself. The list is published by the responder when it loads, not assembled here. This harness used to carry its own copy of one mod's twelve commands and its own belief about which read an argument — facts that belonged to running C# and drifted the moment either side changed alone. `responder` IS THE USEFUL FIELD when something is wrong. False means no list was published: the mod is not loaded, or is a build with the dev bridge compiled out. That is a different answer from a game still starting, and it used to arrive as a readiness timeout, which names the wrong thing entirely — it reads as slow rather than as never going to answer. A list that exists but cannot be read is an ERROR rather than `responder: false`, because it means a responder IS running and this side cannot understand it — a version mismatch, which needs a human rather than a wait. Args: server: Ask the dedicated server rather than the client. Each side publishes its own list, and they are not always the same.

NameTypeReqDescription
serverboolean
NameTypeReqDescription
commandsarrayyes
noteyes
responderbooleanyes

No examples provided.

diag ~234

Ask one side of the session what it currently sees, parsed. Args: server: Read the dedicated server's view instead of the client's. target: Address a specific client by name. timeout: Seconds for the WHOLE call — the reply and then the dump it promises, out of one budget rather than one each. A large world takes longer. Returns counters as integers and the mod's absence markers as null, so a reading of 0 — a real measurement — cannot be confused with "no data". Asking both sides at the same moment is the only way to answer "the client reports no NPC", which one side alone cannot. `records` carries the indented list bodies the scalars only summarise: `fields["npcs"]` says `active=6 mutated=1`, and `records["npcs"]` says which six. Those lines were parsed and discarded until now, so a caller could see that something was there and never what it was.

NameTypeReqDescription
serverboolean
target
timeoutnumber
NameTypeReqDescription
fieldsobjectyes
recordsobjectyes
sidestringyes

No examples provided.

heartbeat ~422

Why the game is not answering, for both sides at once. `launch` already reads this file to decide readiness and keeps one bit of it. When a launch SUCCEEDS that is all anyone needs. When it fails, the discarded detail is the entire answer, and what comes back instead is `no live heartbeat within 300s` — which names the symptom and none of the four causes: - **absent** — nothing ever wrote one. The mod is not loaded, is not enabled in this install, or was built without the dev bridge. For clients this is an EMPTY `clients` list, not an entry saying so. - **stale** — a game ran and is no longer running. The file outlives the process, so this is indistinguishable from live to anything that only checks whether it exists. - **live, no world** — still loading. Nothing is wrong; wait longer. - **live, world, not armed** — loaded and ticking, bridge not listening. `clients` is a LIST because two clients can share one save directory and the old single-client shape reported whichever wrote last. Expect an entry per client — and one more: an entry with `player: null` is the untokened heartbeat every client writes before its character loads, which nothing deletes and each new client overwrites. Treat it as a slot rather than as a client. A real client is the one carrying a token and an advancing `polls`. Reads OFF DISK and needs no session, deliberately: a failed `launch` raises without storing one, so a tool that required a session could never answer the question it exists for. It is also the only tool here that is useful when nothing else is. Both sides are returned together because "the client is silent and the server is fine" is a different diagnosis from both being silent, and asking one at a time cannot see the difference.

Input schema present but exposes no named parameters.

NameTypeReqDescription
clientsarrayyes
serveryes

No examples provided.

inventory ~276

The worlds, characters and mods on this machine. `launch` states two preconditions and could check neither. `player` must already exist — it does not create one, and a duplicate is kicked — and `world` wants a WINDOWS path the caller had to know in advance. Both are facts about directories sitting right there, and until now the only way to learn either was to launch and read the failure: a kick for the wrong character, and a readiness timeout blaming the heartbeat for the wrong world. Each world's `path_win` is the exact string `launch(world=...)` wants. The mods answer something else. `commands` reports `responder: false` for three different situations — the mod is not built, or it is built and switched off, or it is on and was compiled without the dev bridge — and `enabled` plus `built_here` separate the first two. THOSE TWO ARE NOT ONE FACT. A mod can be enabled and have no `.tmod` here, because a workshop mod is installed from somewhere else entirely; on the install this was written against, `CheatSheet` is exactly that. Collapsing them into `installed` would report it missing and send someone rebuilding a mod that was never the problem.

Input schema present but exposes no named parameters.

NameTypeReqDescription
modsarrayyes
playersarrayyes
worldsarrayyes

No examples provided.

join ~284

Bring another character into the session that is already running. Args: player: Character name. Must already exist — `-player` does not create one — and must not be one this session already has, in any casing. timeout: Seconds to wait for that client to report a live, world-ready heartbeat of its own. The protocol has supported several clients since answers became per-player; the LIFECYCLE supported one, so the arrangement that work exists to make safe could only be reached by spawning a game by hand. This is that, with the waiting done properly. It waits for THIS client, not for a process. A new pid says something started — not that a character loaded, that the join was accepted, or that a world is under it. And it watches only that player's own tokened heartbeat: the unsuffixed `<mod>-hooks.txt` is a shared slot holding whichever client booted last, so accepting it would return against the heartbeat of the game that was already here. Address the new client by name — `diag(target=...)`, `shot(target=...)` — which already works, because addressing was never the half that was missing. `stop` takes it down with everything else the session started.

NameTypeReqDescription
playerstringyes
timeoutnumber
NameTypeReqDescription
joinedarrayyes
playerstringyes
started_pidsarrayyes

No examples provided.

launch ~334

Start tModLoader and wait until it can actually answer. Args: mode: "server_client" — a server plus one joined client. It is the only mode there is, and the only way to observe what a CLIENT sees, which is where most sync bugs live. The other two are refused because the engine cannot satisfy them: "singleplayer" has no headless entry point, and "server" alone never ticks, so the mod never polls and never answers. port: Server port. Change it only if something else holds the default. player: Character name for the client. Must already exist — `-player` does not create one, and a duplicate name is kicked. world: WINDOWS path to a `.wld`, overriding TMODLOADER_WORLD_WIN. A WSL path is refused rather than tried: tModLoader runs as a Windows process, cannot resolve /mnt/c, and the only symptom is a readiness timeout blaming the heartbeat. timeout: Seconds to wait for readiness. Raise it on a slow machine or a large world — the default assumes neither. Waits for a heartbeat that is BOTH recent and reporting a live world. Those fail differently — a stale-but-ready heartbeat means the process died, a fresh-but-not-ready one means it is still loading — and checking only existence conflates them.

NameTypeReqDescription
modestring
playerstring
portinteger
timeoutnumber
world
NameTypeReqDescription
modestringyes
playerstringyes
portintegeryes
started_pidsarrayyes
worldyes

No examples provided.

log_files ~56

Which logs exist right now, and how many earlier runs are archived. Read off disk rather than listed as a constant: which logs exist depends on what was run, and a server-only session writes no `client.log` at all.

Input schema present but exposes no named parameters.

Structured output declared, but exposes no named fields.

No examples provided.

log_since ~372

Only what a log has gained since you last looked. Args: name: A log filename from `log_files`. offset: The `next_offset` from your previous call, or 0 to start at the beginning. BYTES, not lines — a line count is not a resume point, because the number of lines you have read is not where the file continues. contains: Case-insensitive filter, applied to the new lines only. fingerprint: The previous call's `fingerprint`, or omit on the first. It is how a rotation is detected when the NEW log has already outgrown your offset — without it that case reads as a quiet continuation, silently skipping the head of the new run. NOT A LIVE TAIL, and it cannot be one. Tools here are synchronous and a game session is process-global state, so a `launch` blocking for five minutes is not something another call watches from the side. What this buys is the read between calls: `logs` re-reads a file that grows all run, and this returns the new part. `restarted` is the field to check. tModLoader ZIPS the previous run's logs and starts fresh, so an offset from a run that has since rotated points past the end of a now-shorter file. Reading there would report an empty log forever, which looks exactly like a quiet game rather than like a log that restarted underneath you. When that happens the read begins again at zero and says so, because handing back the whole file is only correct if the caller is told why.

NameTypeReqDescription
contains
fingerprint
namestringyes
offsetinteger
NameTypeReqDescription
fingerprintstringyes
linesarrayyes
next_offsetintegeryes
restartedbooleanyes
truncatedbooleanyes

No examples provided.

log_watch ~444

Block until a log line matches, instead of polling `log_since` by hand. Args: name: A log filename from `log_files`. contains: Case-insensitive text to wait for. REQUIRED — without one this matches the first line written and is `log_since` wearing a longer name. offset: Where to start reading. 0 includes the log's HISTORY, which is usually what you want ("did the mod load" is a question about a line that is already there). Pass a previous call's `next_offset` to watch only what comes after it. fingerprint: The `fingerprint` from an earlier `log_since`/`log_watch`, when resuming — see `log_since` for the rotation it catches. Within one call the polls carry it themselves. timeout: Seconds for the WHOLE call, spent across every poll. poll: Seconds between reads. THE OFFSET IS THE MECHANISM. Each poll resumes where the last stopped, so a line is matched exactly once — never missed in the gap between two polls, and never re-reported on the next. A watch that re-read the file from the top would match a line written before the wait began and call it news, which is how "wait for the crash" passes on the crash from the PREVIOUS run. Not matching is an ANSWER, not an error: it returns `matched: false` with the resume point, so "nothing was logged for 30s" is as expressible as waiting for something. A MISSING log still raises, because that is nobody having been asked rather than a line failing to arrive. `restarted` means the log rotated during the wait — tModLoader zips the previous run's logs and starts fresh, so your offset stopped meaning anything and the lines you are holding came out of a different file.

NameTypeReqDescription
containsstringyes
fingerprint
namestringyes
offsetinteger
pollnumber
timeoutnumber
NameTypeReqDescription
elapsednumberyes
fingerprintstringyes
linesarrayyes
matchedbooleanyes
next_offsetintegeryes
pollsintegeryes
restartedbooleanyes

No examples provided.

logs ~314

Tail one of tModLoader's logs, optionally filtered. Args: name: Which log — see `log_files` for what this install actually has. `client.log` and `server.log` are the game; `Launch.log` and the `environment-*.log` pair are written by the launcher, which is where a run that died BEFORE the game started says why. previous: Read the run BEFORE this one. tModLoader zips the previous run's logs into `Old/` when a new run starts, so after a failed launch and a retry the failure is in an archive and the live log belongs to the retry. contains: Keep only lines containing this substring, case-insensitively. Applied to the WHOLE log before the tail, so this returns the last N MATCHING lines rather than the matches among the last N. lines: How many trailing lines to return. Zero returns none; a negative count is refused rather than guessed at. Useful when a launch fails: the reason is usually in a log and not in anything the trigger protocol can reach, because the game never got far enough to poll. Which is also why `previous` exists — the obvious thing to do after a failed launch is launch again, and that rotates the evidence.

NameTypeReqDescription
contains
linesinteger
namestring
previousboolean

Structured output declared, but exposes no named fields.

No examples provided.

prune_captures ~295

Delete all but the newest `keep` captures, and say which went. Captures accumulated forever. `shot` writes one per call and nothing ever removed them, so an agent photographing in a loop grew the SAVE DIRECTORY without bound — the folder holding the worlds and characters, which is not a cache and not somewhere to leave litter. Args: keep: How many of the newest captures to keep. REQUIRED and deliberately without a default, the same way `shot` requires a region: this deletes files, and a destructive tool that runs with no arguments is one that gets called by accident. `keep=0` removes all of them, which is a real request and has to be spelled out. Only files matching THIS mod's capture pattern are touched, and each is re-checked to resolve to a direct child of the save directory — the same containment `read_capture` uses, because a delete that listed and removed through different rules would be looser than the read beside it. The whole set is validated before anything is unlinked, so a refusal costs nothing rather than leaving a half-finished prune. Newest is decided by MTIME, not by the index in the filename: the index is this harness's counter and the timestamp is the disk's account of what happened.

NameTypeReqDescription
keepintegeryes
NameTypeReqDescription
remainingarrayyes
removedarrayyes

No examples provided.

read_capture ~158

Return one capture's PNG as image content. `shot` answers with a filesystem path, which is worth nothing to an agent that is not running on this machine. This is how the picture itself gets back, and it is a SEPARATE call on purpose: a full-frame PNG is tens of kilobytes before base64, so a caller that only wanted to know the capture succeeded should not be made to pay for the pixels. Args: name: A capture filename from `captures`, e.g. `<mod>-shot-<token>-001-topleft.png`. A NAME, never a path — see `captures.read` for why the containment is structural.

NameTypeReqDescription
namestringyes

No output schema declared.

No examples provided.

restart ~269

Stop, rebuild, and start again with the session's own settings. Args: build: Compile the mod between stopping and starting. On by default, because picking up a code change is the reason this loop exists. timeout: Seconds to wait for readiness on the relaunch. build_timeout: Seconds to allow the compile. THE ORDER IS THE POINT. tModLoader REFUSES to build while the game is open and reports it with an error that reads like a compile failure, so stop-then-build-then-launch is not a preference — building first sends you hunting a syntax error that is not there. Three separate calls let a caller get that order wrong; this one cannot. The mode, port, player and WORLD come from the running session rather than from arguments or defaults. That last one is why `Session` had to start recording the world it resolved: a relaunch that fell back to the configured default would quietly load a different world than the one being tested, and report success. Needs a running session, because a session is where those settings live. With nothing running there is nothing to reuse — call `launch`.

NameTypeReqDescription
buildboolean
build_timeoutnumber
timeoutnumber
NameTypeReqDescription
build_summaryyes
builtyes
killed_pidsarrayyes
modestringyes
playerstringyes
portintegeryes
started_pidsarrayyes
worldyes

No examples provided.

save_restore ~113

Overwrite the world and characters with a snapshot. This DESTROYS what is on disk now, so it saves that first: the state being overwritten is copied to `auto-before-restore` and returned as `undo`, which `save_restore` accepts like any other label. A restore aimed at the wrong snapshot is therefore recoverable rather than final. Refuses while the game is running, and refuses a label that does not exist by listing the ones that do.

NameTypeReqDescription
labelstringyes
NameTypeReqDescription
filesarrayyes
labelstringyes
removedarrayyes
sizeintegeryes
undoyes

No examples provided.

save_snapshot ~251

Copy this world and its characters aside, so a run can be undone. WHAT THIS IS FOR. The mutating verbs write to a real install and none of that FAILS — it accumulates. Enemy NPCs do not survive a reload, so `spawn` looks harmless, but `give` writes the character file, `time` and `weather` live in the world, and `settile` changes it for good. The damage is invisible when it is done and shows up later as a measurement nobody doubts. Take one before a run that mutates, and `save_restore` after it. REFUSES WHILE THE GAME IS RUNNING, naming the pids. A running tModLoader owns these files and writes them out on its own schedule, so a copy taken now is mid-write. Stop the session first. Copies the configured world's `.wld` and `.twld` and every `.plr`/`.tplr` — not the whole Worlds directory, which measured 41MB against 3MB for one world, and not `.bak` files, which are the game's own safety net.

NameTypeReqDescription
labelstringyes
NameTypeReqDescription
filesarrayyes
labelstringyes
sizeintegeryes
takennumberyes

No examples provided.

save_snapshots ~65

Every snapshot on this machine, newest first, with its age in seconds. A snapshot whose manifest cannot be read is omitted rather than listed as empty, because `save_restore` refuses it for the same reason: putting back nothing and reporting success is the worst available outcome.

Input schema present but exposes no named parameters.

NameTypeReqDescription
rootstringyes
snapshotsarrayyes

No examples provided.

shot ~247

Capture a region of the game's own back buffer and return the PNG path. Args: region: topleft, topright, bottomleft, bottomright, or full. REQUIRED and deliberately without a default — the frame holds the player's character name, world name and any chat, so a request says which corner it wants. target: Address a specific client. timeout: Seconds for the WHOLE call — the reply and then the PNG, out of one budget rather than one each. This reads what the game rendered, not the screen, so no other window can appear in it — by construction rather than by luck. It also sees things the in-game capture camera cannot: dust and the interface layer. The path comes back only once the bytes behind it are a WHOLE PNG. A file exists from the moment it is created rather than the moment it is finished, so waiting on the name alone would hand back half a picture as readily as a whole one — and anything else that landed on that name as readily as either.

NameTypeReqDescription
regionstringyes
target
timeoutnumber
NameTypeReqDescription
pathstringyes
regionstringyes

No examples provided.

status ~123

Whether a session is running, and what it is. The only read-only way to ask. Without it an agent that lost track had to provoke an error to find out — `launch` fails when one exists, `diag` fails when one does not — so the cheapest question on the surface was the one that had to be asked by breaking something. Reports what this server BELIEVES it started. It does not re-query the process table, so it cannot tell you a game was closed from outside; `stop` is what verifies against reality.

Input schema present but exposes no named parameters.

NameTypeReqDescription
joinedyes
modeyes
playeryes
portyes
runningbooleanyes
started_pidsyes
worldyes

No examples provided.

stop ~222

Kill only the processes this session started, and confirm they are gone. Args: settle: Seconds a killed process may take to leave the process table before it counts as a survivor. `/F` returns before Windows has caught up, so verifying too eagerly reports a successful teardown as a refused one — raise this on a loaded machine rather than lower it. Surgical on purpose: a developer usually has their own game open, and a teardown that killed every tModLoader it could find would take it with them. `killed_pids` are pids VERIFIED to have left the process table, not pids a kill was aimed at. If any survive, this FAILS rather than answering with a shorter list — and the session is deliberately kept, so calling `stop` again retries exactly those pids. Releasing it would leave a running game that nothing owns, which is how the next `launch` ends up refusing to start over a process nobody remembers starting.

NameTypeReqDescription
settlenumber
NameTypeReqDescription
killed_pidsarrayyes
noteyes

No examples provided.

trigger ~520

Ask the running game to do something, and return what it said. Args: command: One of the dev commands THIS mod serves — call `commands` to see them, since they are the running mod's rather than a list kept here. A word it does not serve is refused before anything is written to disk: a game that does not recognise one does nothing, and from outside that is indistinguishable from a hang. target: Address the request to one player by name. Every request is already addressed to this session's own player by default, so pass this only to ask a DIFFERENT client — one another session drives on the same machine. argument: Only some commands read one — `commands` says which. Passing one to a command that takes none is refused here, because the mod would refuse it too and that costs a round trip. server: Send to the dedicated server rather than the client. Some commands are server-authoritative and refuse on a client, and each side publishes its own list. timeout: Seconds to wait for the game's reply. A command that does real work on a large world can outlast the default, and for `capture` that is now safe at any value: the capture lock records the deadline this argument implies, so another session waits it out instead of guessing from the lock's age. Raising it no longer trades a slow capture against a collision. `refused` is reported separately from `ok`: a refusal is the mod deliberately saying no, and treating it as success is how a rejected action reads as a completed one. `note` is usually null and is the one field with no other way to reach you: something that happened on the way to this reply which the reply itself cannot show. Currently it says a capture lock was broken to take this picture — captures are serialised across sessions sharing a save directory, and this one's holder was judged gone. It names WHICH rule judg…

NameTypeReqDescription
argument
commandstringyes
serverboolean
target
timeoutnumber
NameTypeReqDescription
commandstringyes
noteyes
okbooleanyes
refusedbooleanyes
textstringyes

No examples provided.

wait_until ~494

Poll `diag` until one of its fields satisfies a comparison. Args: field: A TOP-LEVEL diag field, exactly as `diag` reports it - `vats`, `items`, `world-ready`. Not a path into one: `diag` splits `key: value` and stops, so `npcs` is the whole string `active=4 mutated=0` and there is no `npcs.active`. op: One of `==`, `!=`, `<`, `<=`, `>`, `>=`, `contains`, `changed`. value: What to compare against, as text - it is converted to whatever type the field actually reads as. Omitted for `changed`, which baselines on its first reading. server: Watch the dedicated server's view instead of the client's. target: Watch a specific client by name. timeout: Seconds for the WHOLE call, spent across every poll rather than granted to each. poll: Seconds between polls. Use this instead of sleeping and taking a diag. A guessed sleep is wrong in both directions, and the short one is dangerous: the check reads the state BEFORE the thing happened, which looks exactly like the feature being broken. THE COMPARISON IS TYPED. `diag` returns counters as ints and the heartbeat's flags as bools; `world-ready == true` compares as a boolean and `items >= 10` as a number, so neither `"10" < "9"` nor the truthiness of `"False"` can come back here. IT REFUSES WHAT CAN NEVER COME TRUE rather than waiting it out. An unknown field names the fields that do exist; ordering a composite string says what the value actually is. Both used to be spellable and would have reported a timeout - blaming a game that was answering perfectly. Not matching is an ANSWER, not an error: it returns `matched: false` with the last reading it took, so a wait that expected nothing to happen is as expressible as one that expected something to.

NameTypeReqDescription
fieldstringyes
opstringyes
pollnumber
serverboolean
target
timeoutnumber
value
NameTypeReqDescription
elapsednumberyes
lastyes
matchedbooleanyes
noteyes
pollsintegeryes

No examples provided.

Common questions

What is the tmodloader-mcp server?

tmodloader-mcp is listed in the public MCP registry as io.github.musharna/tmodloader-mcp. Drive a running tModLoader (Terraria) instance from an agent: launch, query, screenshot, read state. This page covers its PyPI package (tmodloader-mcp).

Is the tmodloader-mcp server safe to use?

tmodloader-mcp scores 54 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 20 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

What tools does the tmodloader-mcp server expose?

tmodloader-mcp exposes 24 tools: build_mod, launch, join, trigger, commands, and 19 more. Their descriptions and schemas cost roughly 6,270 tokens of context every time the server is loaded.

Is the tmodloader-mcp server still maintained?

tmodloader-mcp is still listed as active in the MCP registry. We last reached this channel on 20 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

What licence is the tmodloader-mcp server under?

tmodloader-mcp declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.