desmume_webassembly_harness
Click on "Install 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., "@desmume_webassembly_harnessStart analyze with state path C:\dq9\states\battle-a.dst"
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.
desmume_webassembly_harness
desmume_webassembly/webassembly/API_CURRENT.md の現行仕様専用の stdio MCP ハーネスです。Codex から DeSmuME WebAssembly を操作するために使います。人間が Node.js ライブラリとして手動オーケストレーションすることは主目的ではありません。
Requirements
Node.js 22+
Google Chrome
Codex CLI / Codex IDE extension / ChatGPT desktop app のローカル MCP 対応環境
harness.tomlに指定する ROM とスクリーンショット保存先、start_analyze呼び出し時に指定する State 追加 npm パッケージは使用しません。
Related MCP server: dosbox-mcp
設定
harness.example.toml を harness.toml にコピーし、ROM とスクリーンショット保存先を設定します。State は設定ファイルには置かず、start_analyze の呼び出しごとに外部指定します。Windows パスは TOML literal string としてシングルクォートで囲めます。
rom_path = 'C:\dq9\rom.nds'
screenshot_path = 'harness/screenshots'
export_path = 'harness/exports'
baseline_name = 'battle-start'harness.toml と Chrome の分離プロファイル .harness/ は Git 対象外です。
Codex へ追加する
このハーネスは src/mcpMain.js を stdio MCP サーバーとして起動します。Codex は STDIO MCP をローカルプロセスとして起動でき、command、args、cwd を config.toml に設定できます。
Codex CLIから追加
このリポジトリのパスが C:\Users\owner\Documents\desmume_webassembly_harness の場合:
codex mcp add desmume_harness -- node C:\Users\owner\Documents\desmume_webassembly_harness\src\mcpMain.js C:\Users\owner\Documents\desmume_webassembly_harness\harness.toml登録後は次で確認できます。
codex mcp listCodex TUI 内では /mcp で接続状態を確認できます。
config.tomlへ直接追加
ユーザー全体では ~/.codex/config.toml、trusted project 単位では .codex/config.toml に追加できます。
[mcp_servers.desmume_harness]
command = "node"
args = ["C:\\Users\\owner\\Documents\\desmume_webassembly_harness\\src\\mcpMain.js", "C:\\Users\\owner\\Documents\\desmume_webassembly_harness\\harness.toml"]
cwd = "C:\\Users\\owner\\Documents\\desmume_webassembly_harness"
startup_timeout_sec = 30
tool_timeout_sec = 600
tool_output_token_limit = 1000000Codexから見えるMCP tools
start_analyze: 新しいlane専用。state_pathまたはsave_pathのどちらか一方を受け、Chrome起動、ROM読込、State/Save読込、analysis baseline作成まで行う。既存laneには使用せずrestart_analyzeを使う。初回起動失敗だけはfresh Chromeから最大3回まで再試行する。restart_analyze: 既存laneのChromeウインドウを使い回し、State/Saveを差し替えてanalysis baselineを作り直す。isolation_id省略は既存laneが1個だけの場合に限る。閉じられた/クラッシュしたChromeは復活させない。list_instances: 現在MCPプロセスが保持しているlaneとalive/dead状態を最大64件返す。Chromeを新規作成しない。list_commands: 実行中ページのDesmumeMCP.list()を読む。既定は名前だけ64件、最大64件でページングし、description要求時も各160文字までに制限して巨大なAPI一覧を一度に返さない。load_state_file: 既存laneへローカルStateを投入する。新しいChromeは作らない。load_save_file: 既存laneへローカルSaveを投入する。新しいChromeは作らない。export_state_file: 現在Stateをexport_pathへ保存する。State本文はMCP出力へ返さない。export_save_file: 現在Saveをexport_pathへ保存する。Save本文はMCP出力へ返さない。status: 現在のDeSmuME状態を取得する。direct_status: WebMCP transportを挟まずdocumented browser APIから現在のDeSmuME statusを直接取得する。analysis_context: チャットを跨いで作業を再開するため、ブラウザ/エミュレータからその場で取得した小さい状況要約を返す。Harness内部のState pathやscript source pathには依存せず、配列も上限付きにする。pause: エミュレータを停止する。resume: エミュレータを再開する。call: 任意のDeSmuME commandを呼ぶ。micro_macro_list: このharnessプロセス内に登録済みのマイクロマクロID、step数、総wait時間を返す。micro_macro_get: 指定IDのマイクロマクロJSONを返す。micro_macro_exec: AIが決めたIDとsteps JSONを渡して登録/上書きし、その場で実行する。steps省略時は登録済みIDを再実行する。各stepはwait_ms待機後、toolで指定したharnessのトップレベルMCP toolを通常の単独呼び出しと同じhandler経路で呼ぶ。eval:desmume.eval相当のisolated JavaScriptを実行する。rerun_script: UTF-8 JavaScriptファイルを読み、desmume.runScript相当で実行する。rerun_pscript: Persistent Scriptのsourceをeditorへ読み込み、直接runLoadedPersistentScriptを実行する。同名更新の停止・script-only trap解放はページ本体のupdate経路へ任せる。rerun_pscript_console: Persistent Scriptを読み込み・起動し、そのscriptの最新print(...)出力まで1回のMCP callで返す。script_console: 起動中Persistent Scriptのscript_idを指定して最新print(...)/printf(...)出力だけをdirectlistScriptPrintで取得する。stop_pscript: Persistent Scriptを停止する。restart_pscript: Persistent Scriptを再起動する。snapshot_elements: 現在の操作要素と位置を取得する。screenshot: DeSmuMEの256x384フレームバッファだけをPNG化し、screenshot_pathディレクトリへ新しいPNGとして保存する。ブラウザウインドウ全体は撮らない。nameは任意。save_baseline: analysis baselineを保存する。restore_baseline: analysis baselineを復元する。list_pscript_mcp: Persistent Scriptが公開したMCP一覧を取得する。call_pscript_mcp: Persistent Script MCPを呼ぶ。inject_bytes_file: 絶対パスのローカルファイルを最大1MiBまで読み、指定開始アドレスへそのままバイト注入する。close_instance: isolation idに対応するChrome/DeSmuMEを閉じる。close_all_sessions: このstdio MCPプロセスが管理している全Chrome/DeSmuME laneを閉じる。通常のBrowser.close後も子Chromeが残っていればkill fallbackを行う。start_analyzeだけが新しいChrome laneを作成します。status、call、analysis_context、script操作、screenshotなど他のtoolへ存在しないisolation_idを渡してもChromeは作られずエラーになります。 同じisolation_idのstart_analyzeがすでに進行中の場合も、二重起動せず直ちにエラーにします。start_analyze以外はisolation_idを省略したとき既存laneが1個ならそれを使い、複数laneがある場合は明示指定を要求します。 既存laneでNATIVE_FAULTのrunFrameが観測された場合、そのlaneはharness側で使用不能として固定され、それ以降の通常lane操作は直ちにエラーになります。例外はstart_analyze、close_instance、close_all_sessionsです。start_analyzeを同じisolation_idへ呼ぶとfault済みChromeを閉じてfresh laneを作成し、close系はfault済みlaneをそのまま破棄できます。macro実行中にfaultした場合も次stepへ進まず、UI interaction lockの解除だけは内部cleanupとして実行します。
マイクロマクロ
micro_macro_execはDeSmuMEページ内command専用のbatchではなく、call、resume、rerun_pscript、call_pscript_mcp、load_state_fileなどharnessが公開しているトップレベルMCP toolそのものを短い待機付き手順へまとめます。マクロはstdio MCPプロセス内メモリに保持され、AIが任意のIDを決めます。
{
"id": "battle-turn-flow",
"isolation_id": "lane-a",
"steps": [
{
"wait_ms": 0,
"tool": "call_pscript_mcp",
"arguments": { "name": "queue_defend", "params": {} }
},
{
"wait_ms": 120,
"tool": "resume",
"arguments": {}
}
]
}wait_msはそのstepを呼ぶ直前の待機です。toolは内部名のresume/call_pscript_mcpだけでなく、AIから実際に見えるdesmume_harness__resume/desmume_harness__call_pscript_mcp形式も受け付けます。実行対象の既存laneでは、最初のstep前にページAPI setUiInteractionLock を実行ごとに一意なowner付きで有効化し、macro終了時にfinallyでその実行ownerだけ解除します。同じmacro IDの並列実行も互いのロックを解除しません。これは人間のGUI操作だけを止めるため、macro自身のpause、永続スクリプト由来のpause、DesmumeMCP.call()などのプログラム操作は妨げません。解決時はharnessのTOOLS一覧を総当たりし、完全一致を優先したうえで__<tool name>の後方一致を一意に解決して内部名へ正規化します。micro_macro_exec側へroot isolation_idを指定すると、isolation_idを受け付ける全stepへその値を継承します。ただしstep自身のarguments.isolation_idが明示されている場合はAI側の指定を優先し、root値で上書きしません。isolation_id非対応toolへはroot値を注入しません。初回はsteps付きで登録・実行し、以後は{ "id": "battle-turn-flow", "isolation_id": "lane-a" }だけで同じ手順を再実行できます。micro_macro_listとmicro_macro_getは実行せず保存内容だけを確認します。マイクロマクロから別のmicro_macro_*を呼ぶ再帰実行は行いません。
start_analyze
新しい isolation_id では最初に start_analyze を呼びます。state_path と save_path は排他的で、必ずどちらか一方を指定します。既存laneの再利用はrestart_analyzeで行い、start_analyzeへ既存isolation_idを渡すとエラーにします。
start_analyze { isolation_id: "lane-a", state_path: "C:\\dq9\\states\\battle-a.dst" }isolation id 専用Chrome profileと自動割当DevTools portでChromeを起動する。
Chromeを
--enable-features=WebMCPTesting,DevToolsWebMCPSupport付きで起動し、DeSmuMEのWebMCP登録完了を待つ。harness.tomlのROMをfile inputへローカル投入し、file transaction完了まで待つ。ROM投入後に
romLoaded=trueだけでなくrunning=true/paused=falseまで待つ。Service Workerやruntime初期化失敗などでrunningへ到達しない場合、そのlaneを閉じ、500ms後に新しいChromeから開始処理全体をやり直す。state_pathの場合はStateをState Inへ、save_pathの場合はSaveをSave Inへ投入し、対応するfile transaction完了まで待つ。読込直後の状態を
saveAnalysisBaselineで保存する。baseline作成のために余計なframeは進めない。{ status: "ok", paused, running }だけを返す。UI snapshot、full status、snapshotContextは返さず、必要な場合は専用toolを別途呼ぶ。
既存laneを別State/Saveからやり直す場合は、まずlist_instancesでlaneを確認してrestart_analyzeを呼びます。既存laneが1個だけならisolation_idは省略できます。ユーザーがChromeを閉じた、またはChromeがクラッシュしたlaneはdeadとして扱い、自動的に新しいChromeへ差し替えません。
チャットを跨いで作業を再開する
analysis_context は再開に必要な情報だけをブラウザAPIから毎回取得して小さく返します。通常はbreakpoint一覧も省略し、必要な場合だけ include_breakpoints: true を指定します。call stack、disassembly、script source、ローカルState path、console全文は自動では含めません。baselineとrunning scriptは各16件、明示要求したbreakpointも128件までです。
返す主な情報はROM load状態、paused / running / frame、現在CPUと主要register、trace/IRQ方針、最新break、ブラウザ側recent file一覧、analysis baseline一覧、起動中Persistent Scriptです。Harnessが過去に覚えたpathを正本にはしません。
direct_status はDeSmuMEのdocumented status commandをbrowser API bridgeから直接呼びます。WebMCP transport wrapperを経由した status が不要な内部・機械処理向けです。
Persistent Scriptの最短console取得
overlay_jp.js のように print(...) を使うPersistent Scriptでは、rerun_pscript_console を使うとローカルsource読込、direct runPersistentScript、direct listScriptPrint を1回のstdio MCP callにまとめられます。Persistent Scripts editorや巨大なUI snapshotは経由しません。
以後のconsole取得は analysis_context に出る script_id を script_console に渡せば、scriptを再実行せずdirect listScriptPrint 1回だけで取得できます。
複数エミュレータ
すべての主要toolは isolation_id を受け取ります。異なる isolation_id は別Chrome profileと別DevTools portを使用するため、同じstdio MCPプロセスから複数DeSmuMEを同時に保持できます。
start_analyze { isolation_id: "lane-a", state_path: "C:\\dq9\\states\\a.dst" }
start_analyze { isolation_id: "lane-b", state_path: "C:\\dq9\\states\\b.dst" }harness.toml の [instances.<id>] でROM、スクリーンショット保存先、baseline名をlane単位に上書きできます。Stateは常に start_analyze 引数です。
[instances.lane-a]
baseline_name = 'battle-a'
screenshot_path = 'harness/screenshots/lane-a'
[instances.lane-b]
baseline_name = 'battle-b'
screenshot_path = 'harness/screenshots/lane-b'フレームスクリーンショット
screenshot はブラウザやChromeウインドウのキャプチャではなく、DeSmuME本体の takeScreenshot が ui.screen.toDataURL() で生成する256x384のDSキャンバスPNGだけを保存します。screenshot_path は保存ディレクトリで、デフォルトはCodex sandboxから参照しやすい harness/screenshots です。呼び出し時に name を省略すると frame-000001.png, frame-000002.png, ... とlaneごとに自動採番し、name: "battle-start" のように指定すると battle-start.png として保存します。PNG本体のdata URLはMCP出力へ返しません。MCPの返り値 path は保存したPNGの絶対パスです。
State読込直後はAPI_CURRENTの仕様上、1つのcomplete emulator frameが進むまで画面capture APIが SCREEN_INVALID になります。start_analyze は読み込んだStateを厳密に保つため勝手に1フレーム進めません。そのため必要なタイミングでフレームを進めた後に screenshot を呼びます。
State / Save export
export_state_fileとexport_save_fileはブラウザAPIの通常のdownloadをCDPで専用一時ディレクトリへ受け、完了後にharness.tomlのexport_pathへ移します。本文byte列やdata URLはstdio MCPへ返さず、pathとbytesだけを返します。nameを省略するとstate-000001.dstまたはsave-000001.savのようにlaneごとに採番します。
WebMCP transport
Chrome内部ではDeSmuMEページが登録したWebMCPを使用しますが、生の desmume.call、desmume.eval、desmume.runScript tool objectをCodex側へ再公開しません。stdio MCP側では用途別のtoolとして隠蔽します。
call/pause/resume/statusは内部でdesmume.callを使う。evalは内部でdesmume.evalを使う。rerun_scriptは内部でdesmume.runScriptを使う。screenshotはPNG bytesを保存する必要があるため、内部のdocumentedtakeScreenshot({download:false,includeDataUrl:true})を直接使い、data URLはstdio MCPへ漏らさない。Persistent Script管理、file transaction serial監視、baseline管理など、structured resultを機械判定する内部処理はDeSmuMEのdocumented browser APIを直接使う。 WebMCP実行系が返す
status: "Completed"のようなtransport wrapperはstdio MCPの外へ出しません。実際のDeSmuME出力だけを返します。
Chromeウインドウ
Chrome起動時は最小化起動を使いません。AI操作前にはCDPのwindow stateを確認し、最小化されていればnormalへ戻してから操作します。Page.bringToFrontは呼ばないため、長時間の自動操作がOSのキーボードフォーカスを継続的に奪う動作にはしません。またChromeはbackground timer/renderer/occluded-window throttlingを無効化して起動し、背面化によるエミュレータ停止を避けます。
ARMv5T assembly preprocessor
src/armv5t-assembly-preprocessor.js は従来の armv5tAssemblyPlayground/assembly.php の変換規則をNode.jsへ移植したものです。#! origin、label、.addr、.addr4、BL/bl FUN_xxxxxxxx、.ltorg のアドレス計数規則を維持し、preprocessAssemblySource(source, initialBaseAddress) を公開します。local-mcp-chatgpt-tunnel 側のsandboxed buildv5tassembly MCPから、このモジュールを絶対パスで固定指定して利用できます。
MCP output
DeSmuMEのobject結果をJSON文字列へ変換してさらにJSON-RPCへ詰める二重JSONは避けます。stdio MCPの structuredContent には実際のobjectをそのまま置き、content はDeSmuME本体の compactOutputText と同じflatten方式の短いtextを返します。内側WebMCPがすでに content / structuredContent を返した場合も不要な再ラップをしません。
例として { ok: true, paused: true, frame: 42 } はtext側では次の形になります。
ok=true
paused=true
frame=42Persistent Scripts
rerun_pscript は固定UIDや Run / Update のclickを使用しません。
Load sourcefile inputを現在DOMから直接解決してローカルUTF-8 sourceを投入する。UI snapshotは取得も返却もしない。textareaの改行正規化(CRLF/CR→LF)を考慮して、editorへsourceが反映されたことを確認する。
runLoadedPersistentScriptを直接呼ぶ。同名scriptの置換はページ本体のupdate経路へ任せ、script-only breakpoint処理中でも通常のstopScriptでpaused状態を固定しない。wait_for_registration=falseならAPI_CURRENTのstarted:true時点で返し、registration完了を待ちません。startup_timeout_msはWorker/parser/compile/started handshakeだけに適用され、script bodyのtimeoutにはしません。
UTF-8
harness.toml とローカルJavaScript sourceはUTF-8のみ受け付けます。UTF-16/UTF-32 BOMまたは不正UTF-8は実行前にエラーにします。
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
A paid remote MCP for OpenAI Codex agent coordination MCP, built to return verdicts, receipts, usage
Generate, edit, and deploy immersive 3D/WebGL web projects from any MCP assistant.
Remote MCP server for supportsheep: run AI interviews and manage support content for your blog.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables programmatic control of the mGBA emulator for Game Boy, Game Boy Color, and Game Boy Advance games, including screenshot capture, memory reading, sprite data dumping, and custom Lua script execution for automated testing and game analysis.63-
- AlicenseNot gradedqualityCmaintenanceBridges AI agents to a DOSBox emulator, enabling control of DOS programs via MCP tools for typing, screen reading, video capture, Lua scripting, and memory access.2GPL 2.0
- AlicenseNot gradedqualityDmaintenanceMCP server for the mGBA Game Boy/GBA emulator, enabling full emulator control, memory access, debugging, and screenshot capture through natural language.4MIT
- AlicenseBqualityAmaintenanceControls a real Windows 98 VM through 47 MCP tools, enabling screenshots, mouse/keyboard input, command execution, and file transfers.85342MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/DaisukeDaisuke/desmume_webassembly_harness'
If you have feedback or need assistance with the MCP directory API, please join our Discord server