Skip to main content
Glama

ie-mcp

License: MIT Python 3.8+ Platform: Windows MCP Status: alpha Version: 0.1.0

Give an LLM the ability to drive a legacy IE-only web application.

⚠️ Status: alpha / work in progress. This project is under active development. The tool surface and configuration may change between versions, and breaking changes can land without notice until 1.0.0. Issues and PRs welcome — see Development and Roadmap.

ie-mcp is a Model Context Protocol server that automates Microsoft Edge in IE mode (the Trident / legacy document-mode engine) via the Selenium IEDriver, and exposes it over MCP stdio so clients like Claude Code, Claude Desktop, or Codex can read, click, fill, and scrape any IE-only intranet app that nothing else can touch anymore.

The server is application-agnostic: it knows nothing about any specific site. You point it at a target through environment variables and the Edge IE-mode site list, and it gives the model a Playwright-style toolbox (auto-waiting locators, frame navigation, grid extraction, readiness probing) tuned for the quirks of the Trident engine.


Installation

No clone, no separate install. Requires uv on PATH (pip install uv, or winget install astral-sh.uv). uvx fetches the repo, installs selenium into an isolated env, and runs the server on each launch:

claude mcp add ie-mcp -- uvx --from git+https://github.com/thomfilg/ie-mcp.git ie-mcp
codex  mcp add ie-mcp -- uvx --from git+https://github.com/thomfilg/ie-mcp.git ie-mcp
gemini mcp add ie-mcp    uvx --from git+https://github.com/thomfilg/ie-mcp.git ie-mcp

claude mcp add takes <name> <command…> — here the name is ie-mcp and the command is the uvx … part. A bare repo URL won't work (it'd be treated as a remote HTTP server).

Option B — install once, then a short command

Install the ie-mcp command on PATH, then let it register itself with every MCP client:

# 1. install (pipx/uv keep it isolated)
pipx install git+https://github.com/thomfilg/ie-mcp.git
#   or:  uv tool install git+https://github.com/thomfilg/ie-mcp.git
#   or from a clone:  pipx install .

# 2. register with every MCP client found on PATH (Claude Code, Codex, Gemini CLI)
ie-mcp --install

ie-mcp --install runs claude mcp add / codex mcp add / gemini mcp add for you with the launch path already solved. Remove it again with ie-mcp --uninstall.

Register a client by hand

ie-mcp --install (Option B) does this for you. To wire one up manually, use the ie-mcp command (after pipx install). If you skipped the install (Option A), replace ie-mcp in the command column with uvx --from git+https://github.com/thomfilg/ie-mcp.git ie-mcp.

Client

Command

Claude Code

claude mcp add ie-mcp -- ie-mcp

Codex CLI

codex mcp add ie-mcp -- ie-mcp

Gemini CLI

gemini mcp add ie-mcp ie-mcp

Or via a client's config file (all equivalent — { "command": "ie-mcp" }):

  • Claude Desktop — claude_desktop_config.json (Windows: %APPDATA%\Claude\claude_desktop_config.json)

  • Gemini CLI — ~/.gemini/settings.json (Windows: %USERPROFILE%\.gemini\settings.json)

{
  "mcpServers": {
    "ie-mcp": { "command": "ie-mcp" }
  }
}
  • Codex CLI — ~/.codex/config.toml (Windows: %USERPROFILE%\.codex\config.toml)

[mcp_servers.ie-mcp]
command = "ie-mcp"

Any other stdio MCP client works too: set the server command to ie-mcp. To override a default, add the env var to that client's env block — see Configuration.

IEDriver attach can be slow on the first call. If a client reports the server as unresponsive on startup, give it a longer init timeout and confirm ie-mcp --selftest passes standalone first.

Download IEDriverServer.exe (matching your Edge / selenium version) from the Selenium downloads page if it isn't already in the default per-user selenium cache.

Verify the setup

Before (or after) wiring up a client, run the built-in self-test. It checks dependencies and paths, then opens and closes a real IE-mode session and confirms the Trident engine is actually in use:

ie-mcp --selftest

A passing run prints RESULT: PASS and an IE/Trident userAgent.


Related MCP server: mcp-selenium-python

Why this exists

Modern browser-automation tools (Playwright, Puppeteer, Selenium-on-Chromium) cannot render IE-only apps — the ones that depend on ActiveX, document.all, framesets, VBScript-era behaviours, or an enforced legacy document mode. Edge's IE mode is the last supported way to run them, but it is notoriously awkward to automate:

  • IEDriver attach is flaky (Protected-Mode boundary crossings, "could not find IE window").

  • The legacy engine rejects Selenium's modern JS atoms (el.text, Select, get_attribute).

  • Old document modes have no window.JSON, so naive scripts throw 'JSON' is undefined.

  • Frame-based apps lose state if the session is recreated.

ie-mcp works around all of these so the model gets a stable, high-level interface.


Features

  • One long-lived session kept alive across tool calls, so frame-based apps keep state.

  • Auto-waiting locators (id / css / xpath / name / link_text / tag / class).

  • Nested frame navigation — address frames-inside-frames with a path like "3/0".

  • Trident-safe primitives — click, fill, select, hover, key-press, scroll, upload, dialogs, all implemented to survive the legacy JS engine.

  • Readiness probing (ie_wait_ready) that waits for every same-origin frame's readyState to complete and the DOM to stop changing — handles slow AJAX/frameset grids.

  • Grid extraction (ie_grid) that heuristically finds the real data table and returns header-keyed rows.

  • Screenshots (full window or single element), JS execution, history navigation, resize.

  • One browser per MCP process, with parallel agents isolated by session identity.

  • Atomic per-session leases prevent two processes from claiming the same explicit identity.

  • Session-scoped logs and ownership records under %TEMP%\ie-mcp\sessions\<session-id>.

  • Safe orphan cleanup kills only browsers proven to belong to a crashed session; live agents and untracked Edge processes are preserved.

  • No required pip install — selenium can be loaded from a vendored deps folder.

  • Built-in --selftest to diagnose your setup before wiring up a client.


Requirements

Component

Notes

Windows

IE mode only exists on Windows; the process-tracking and policy code use tasklist, taskkill, PowerShell/CIM and winreg.

Microsoft Edge

With IE mode available (Edge ships it on Windows 10/11).

IEDriverServer.exe

The Selenium IE driver (e.g. 4.14.0).

Python 3.8+

Standard library only, plus selenium.

selenium

>=4.14.0 — installed via requirements.txt or vendored (see below).


Configuration

All configuration is via environment variables — all optional:

Variable

Default

Purpose

IE_EDGE_PATH

C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe

Path to msedge.exe.

IE_DRIVER_PATH

(per-user cache path)

Path to IEDriverServer.exe.

IE_SITE_LIST

(unset)

Path to an Edge IE-mode site-list XML. If set, the server verifies the HKCU Edge policy points at it so listed sites render in IE mode. If unset, no policy is managed and Edge is assumed already configured.

IE_DEFAULT_URL

about:blank

URL ie_open uses when none is given.

IE_ATTACH_RETRIES

3

IEDriver attach attempts.

IE_PAGE_TIMEOUT

30

Per-page load timeout (seconds).

IE_PYDEPS

../.pydeps

Extra sys.path dir for vendored selenium.

IE_INTERACTION_MODE

native

Input strategy: background avoids forced focus and physical mouse input; native uses focused OS events for maximum legacy compatibility. Selected when the MCP process starts.

IE_LOG_FILE

<session-dir>\ie-mcp.log

Optional override for this session's log file.

IE_SESSION_ID

pid-<MCP process PID>

Stable agent/session name. Different IDs can run concurrently; the same ID is exclusive.

IE_SESSION_ROOT

%TEMP%\ie-mcp\sessions

Root for session directories containing logs, leases, and ownership records.

IE_LOCK_FILE

<session-dir>\session.lock

Optional override for this session's atomic lease file.

IE_NO_LOCK

(unset)

Development escape hatch that disables the per-session lease. Do not use for normal agents.


Tools

Tool

Description

ie_open

Start (or reuse) an Edge IE-mode session and navigate to a URL.

ie_goto

Navigate the current session to a new URL.

ie_status

Session state as JSON (active/alive, title, url, owned PIDs).

ie_browsers

List all Edge IE-mode windows + IEDriver processes, flagging the one this session owns.

ie_kill_orphans

Terminate processes recorded for crashed sessions only; never kills live-agent or unknown browsers.

ie_frames

List the frames/iframes of the current page (index, name, id, src).

ie_text

Get visible text of the page or a specific frame.

ie_html

Get HTML source of the page or a frame.

ie_click

Click an element (auto-waits); click / double / right.

ie_fill

Type text into an input/textarea (auto-waits).

ie_upload

Set a <input type=file> by sending a local path.

ie_dialog

Handle a JS dialog (accept / dismiss / text / sendkeys).

ie_js

Execute JavaScript in the page or a frame and return the result.

ie_screenshot

PNG screenshot of the window, or of a single element.

ie_wait_text

Poll until a substring appears (or disappears) in the page text.

ie_wait_ready

Wait until the page (and same-origin frames) finish loading and settle.

ie_select

Select an <option> by label / value / index (Trident-safe).

ie_press_key

Press a key (Enter/Tab/Escape/arrows/… or a literal char).

ie_back / ie_forward

Navigate browser history.

ie_hover

Hover over an element (menus/tooltips).

ie_resize

Resize the browser window.

ie_get

Read an element's text, value, or a named attribute (Trident-safe).

ie_wait_element

Wait for an element to appear — or, with gone=true, disappear.

ie_scroll

Scroll an element into view, to top/bottom, or by pixels.

ie_grid

Extract a tabular grid as structured (header-keyed) rows.

ie_sleep

Sleep N seconds (last resort; prefer ie_wait_ready / ie_wait_text).

ie_close

Close the session and quit the browser.

Working with frames

Many legacy apps are framesets. Use ie_frames to enumerate them, then pass frame to most tools. A nested path reaches frames-inside-frames — e.g. "3/0" is the first frame inside the fourth frame.


No-pip / vendored selenium

On locked-down or offline machines you can run without a global pip install. Drop the selenium package (and its deps) into a folder and point IE_PYDEPS at it:

pip install --target ../.pydeps "selenium>=4.14.0,<5"

By default the server looks for ../.pydeps relative to ie_mcp.py, so the MCP protocol itself has zero pip dependencies (newline-delimited JSON-RPC 2.0 is implemented inline).


How IE mode is reached

When IE_SITE_LIST is set, the server self-heals the HKCU Edge policy (SOFTWARE\Policies\Microsoft\Edge) so the listed sites open in IE mode:

  • InternetExplorerIntegrationLevel = 1

  • InternetExplorerIntegrationSiteList = file:///…/your-site-list.xml

  • InternetExplorerIntegrationReloadInIEModeAllowed = 1

These are written under HKCU (no admin needed). If the key isn't writable, the server logs the exact values to set manually. When IE_SITE_LIST is unset, the server manages no policy and assumes Edge is already configured for IE mode.

Session creation navigates to the target URL during session start — the only reliable way to cross the Protected-Mode boundary into IE mode without the driver losing the browser.


Troubleshooting

Symptom

Fix

could not start Edge IE mode after N tries

Confirm IE mode renders the site manually in Edge; check IE_SITE_LIST; try bumping IE_ATTACH_RETRIES.

IE session '<id>' is already owned

Two clients used the same IE_SESSION_ID. Give each agent a unique ID, or omit it to use the MCP process PID automatically.

IE engine: NO (Chromium?) in selftest

The site isn't matching the IE-mode site list; the page rendered in Chromium. Fix the site list / policy.

Leftover Edge windows after a crash

Use ie_browsers to inspect and ie_kill_orphans to clean up.

'JSON' is undefined from your own ie_js

The page is in IE7/quirks mode with no window.JSON. Return delimited strings instead of JSON.stringify.


Development

This is a single-file server (ie_mcp.py). Install it editable so the ie-mcp command points at your working copy:

git clone https://github.com/thomfilg/ie-mcp.git
cd ie-mcp
pip install -e .          # or: pipx install --editable .

# diagnose deps/paths and open a real IE-mode session
ie-mcp --selftest

Running straight from the clone without installing also works — python ie_mcp.py --selftest / --install fall back to launching the file by absolute path.

Project layout

  • ie_mcp.py — the entire server: config, the IEDriver wrapper (IeSession), one t_* function per tool, the TOOLS registry, the --install/--selftest CLI, and the stdio JSON-RPC loop.

  • pyproject.toml — packaging + the ie-mcp console-script entry point.

  • requirements.txt — runtime deps (just selenium; mirrors pyproject.toml).

Adding a tool

  1. Write a t_<name>(args) function that returns text_result(...) (or an image result).

  2. Append an entry to the TOOLS list with name, description, inputSchema, and fn.

  3. Keep it Trident-safe: prefer direct arguments[0].innerText / getAttribute over Selenium's JS atoms, and never rely on window.JSON inside injected scripts (IE7/quirks document modes don't have it — return delimited strings and parse them in Python).

  4. Re-run --selftest, then exercise the tool from an MCP client against a real IE-mode app.

Debugging

  • Set IE_LOG_FILE to capture the [ie-mcp] stderr log to a file.

  • Use ie_browsers / ie_kill_orphans to inspect sessions and clean up browsers left by crashed owners. Cleanup deliberately preserves unknown processes and every live agent.

  • Set a descriptive IE_SESSION_ID when logs need to remain easy to correlate with an agent.

  • Set IE_NO_LOCK=1 only while debugging the lease implementation itself.

Parallel agents

Every MCP process still owns exactly one long-lived Selenium session and therefore one browser. Multiple MCP processes can run simultaneously because their leases, logs, owner records, and IEDriver-created Edge profiles are session-scoped. With no configuration, the MCP process PID is used as the session ID, which makes separate Codex/Claude agent processes independent automatically.

[mcp_servers.ie-mcp.env]
IE_SESSION_ID = "qa-agent-country"

Choosing interaction mode (LLM guidance)

LLM agents should choose background when the MCP runs on a person's active desktop. It avoids forcing the IE-mode Edge window to the foreground and uses synthetic events instead of physical mouse/keyboard input. Prefer it for navigation, page inspection, ordinary clicks, and form entry:

[mcp_servers.ie-mcp.env]
IE_INTERACTION_MODE = "background"

Use native only as a compatibility fallback after a required interaction fails in background mode—for example, a legacy control, hover menu, file upload, OS modal, or ActiveX component. Native mode uses focused OS input and may take over the user's mouse:

[mcp_servers.ie-mcp.env]
IE_INTERACTION_MODE = "native"

The mode is fixed for the lifetime of the MCP process, so restart the MCP after changing it. Neither mode is headless: Edge still creates a visible IE-mode window. When multiple native-mode agents run on the same Windows desktop, their focus-sensitive interactions can still conflict. The server also returns this selection guidance in its MCP initialize response so clients can present it to LLMs.

Tests

The ownership suite uses mocked process tables and never opens Edge:

python -m unittest discover -s tests -p "test_*.py" -v

Contributing

Bug reports and PRs are welcome. Please:

  • Keep the server single-file and dependency-light (selenium is the only runtime dep).

  • Match the existing tool conventions (auto-waiting find, frame arg, text_result).

  • Note any IE-mode / Trident quirk you worked around in a code comment — they're rarely obvious.


Roadmap

Planned / in progress (subject to change while in alpha):

  • Multi-tab / multiple window-handle support.

  • Cookie and localStorage inspection tools.

  • Richer ie_grid (pagination + virtualized-grid scrolling helpers).

  • Optional structured (JSON) tool results alongside text.

  • Configurable per-tool default waits.

  • Packaged release + versioned tags once the tool surface stabilizes (1.0.0).


License

MIT © 2026 thomfilg

Available Tools

28 tools
ie_backC

Navigate back in browser history.

Inputs: wait.

ie_browsersA

List ALL Edge IE-mode windows and IEDriver processes open on the machine, flagging which one THIS session owns (mine=true) and which are orphans. Use to see leftover browsers from prior/other runs.

Inputs: no named parameters.

ie_clickB

Click an element (auto-waits). 'by' is id/css/xpath/name/link_text/tag/class. action: click (default) | double | right.

Inputs: by (required), value (required), wait, frame, +2 more.

ie_closeA

Close the IE-mode session and quit the browser.

Inputs: no named parameters.

ie_dialogA

Handle a JS dialog (alert/confirm/prompt). action: accept (default) | dismiss | text | sendkeys (with text). Note: OS-level IE modals may be unreachable.

Inputs: text, action.

ie_fillC

Type text into an input/textarea element (auto-waits).

Inputs: by (required), text (required), value (required), clear, +1 more.

ie_forwardC

Navigate forward in browser history.

Inputs: wait.

ie_framesA

List the frames/iframes of the current page (index, name, id, src). Use the index or name with the 'frame' arg of other tools.

Inputs: no named parameters.

ie_getC

Read an element's text (default), its 'value', or a named attribute/property. Set attr=text|value|.

Inputs: by (required), value (required), attr, frame, +1 more.

ie_gotoC

Navigate the current IE-mode session to a new URL.

Inputs: url (required), wait.

ie_gridA

Extract a tabular grid as structured rows. Auto-picks the table with the most multi-cell rows (or pass table_id). Returns headers + rows (header-keyed dicts when the header row is detected/given). App-agnostic.

Inputs: frame, limit, headers, table_id, +1 more.

ie_hoverC

Hover the mouse over an element (for menus/tooltips).

Inputs: by (required), value (required), wait, frame.

ie_htmlC

Get the HTML source of the page or of a specific frame.

Inputs: frame, limit.

ie_jsC

Execute JavaScript in the page or a frame and return the result.

Inputs: script (required), frame, limit.

ie_kill_orphansA

Terminate only IE-mode Edge / IEDriver processes recorded for crashed ie-mcp sessions. Never kills browsers owned by live agents or unknown processes.

Inputs: no named parameters.

ie_openB

Start (or reuse) an Edge IE-mode session and navigate to a URL (default IE_DEFAULT_URL). Keeps the session alive for later calls.

Inputs: url, wait.

ie_press_keyA

Press a key on an element (if by/value given) or the active element. Key names: Enter, Tab, Escape, Space, Backspace, Delete, Up/Down/Left/Right, Home, End, PageUp, PageDown, F5 — or a literal character.

Inputs: key (required), by, wait, frame, +1 more.

ie_resizeB

Resize the browser window (useful for consistent screenshots).

Inputs: width (required), height (required).

ie_screenshotA

Capture a PNG screenshot of the IE-mode window, or of a single element if by/value are given (returned as an image; optionally saved to save_path).

Inputs: by, frame, value, no_inline, +1 more.

ie_scrollA

Scroll: bring an element into view (by/value), scroll page/container to top|bottom (to=...), or by pixels (dx/dy). Needed for virtualized grids.

Inputs: by, dx, dy, to, +2 more.

ie_selectC

Select an in a dropdown, by label (visible text), option_value, or index. Provide exactly one of label/option_value/index.

Inputs: by (required), value (required), frame, index, +2 more.

ie_sleepA

Sleep for N seconds (last resort; prefer ie_wait_ready / ie_wait_text).

Inputs: seconds.

ie_statusA

Session state as JSON: active/alive, title, url, window_handles, and the interaction mode, session identity/directory, and owned browser PIDs/profile.

Inputs: no named parameters.

ie_textC

Get visible text of the page or of a specific frame.

Inputs: frame, limit.

ie_uploadC

Set a file by sending a local file path.

Inputs: by (required), path (required), value (required), frame.

ie_wait_elementA

Wait for an element (by/value) to appear — or, with gone=true, to disappear. Use before acting on slow-rendering content.

Inputs: by (required), value (required), gone, frame, +2 more.

ie_wait_readyA

Wait until the page finishes loading: every same-origin frame's document.readyState is 'complete' AND the DOM stops changing for a few polls (content settled). Handles slow async/frameset content. Use THIS to know content is ready, then search the data separately. Do NOT wait for a specific value to detect load — if it's absent you can't tell 'still loading' from 'not present'. Returns loaded=true/false with frame count, text length, element count and a content sample.

Inputs: settle, timeout, interval, min_chars, +1 more.

ie_wait_textA

Poll until a specific substring appears in the page text (for slow async content). Scans all frames unless 'frame' is given; returns which frame matched + a preview. For 'is the page loaded?' use ie_wait_ready instead.

Inputs: contains (required), gone, frame, timeout, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 28 tools

    Disambiguation5/5

    Each tool targets a distinct action or state in browser automation, with clear separation between navigation, waiting, interaction, and data extraction. No two tools have overlapping purposes that would cause confusion for an agent.

    Naming Consistency5/5

    All tools follow the 'ie_' prefix with a consistent verb_noun pattern (e.g., ie_press_key, ie_wait_element). No mixing of naming conventions, making the set predictable.

    Tool Count4/5

    28 tools is on the high side, but each tool addresses a specific need in IE-mode automation (e.g., waiting, scrolling, grids). The count feels justified given the complexity of the domain.

    Completeness5/5

    The tool set covers the full lifecycle of browser automation: navigation, page state, element interaction, waiting, dialogs, JavaScript, screenshots, and structured data extraction. No obvious gaps for typical web automation tasks.

    Related MCP Connectors

    • Mode Analytics MCP server for ChatGPT, Claude and any AI agent: list reports, run report, read run results, edit query, create schedule, share report to Slack and archive report in your own Mode Analytics account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to Mode Analytics yourself; Cotlet does not store your password and you need no Mode Analytics API key. About the official Mode Analytics API: Mode has a REST API, but only for Workspaces on a paid plan, and it signs in with a token: a Workspace token an Admin creates, or a member token that expires after 90 days once an Admin turns member tokens on; Mode offers no official MCP server (checked 5 Nov 2026). It covers reports, queries, report runs, exports, schedules and subscriptions, and it has no one-off share to Slack or email. Setup and FAQ: https://cotlet.dev/integrations/mode-analytics-api-mcp-server

    • LinkedIn MCP server for ChatGPT, Claude and any AI agent: create post, comment on post, send message, send connection invitation, search people, search companies, list connections and read conversations in your own LinkedIn account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to LinkedIn yourself; Cotlet does not store your password and you need no LinkedIn API key. About the official LinkedIn API: LinkedIn has an official API, but most of it needs an approved application (checked 6 Nov 2026). The open Share on LinkedIn permission lets an app post, comment and like on behalf of a member. Setup and FAQ: https://cotlet.dev/integrations/linkedin-api-mcp-server

    • Buildium MCP server for ChatGPT, Claude and any AI agent: record lease payment, list outstanding balances, send text message, create task, add work order and send announcement in your own Buildium account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to Buildium yourself; Cotlet does not store your password and you need no Buildium API key. About the official Buildium API: Yes, Buildium has an official REST API, the Open API, but only on the Premium plan (checked 27 Oct 2026). Buildium's developer guide says "To take advantage of the Buildium Open API you must have a Premium Subscription", and an administrator has to switch it on under Settings > Application settings > Api settings before anyone can create a key. Setup and FAQ: https://cotlet.dev/integrations/buildium-api-mcp-server

    • Absorb LMS MCP server for ChatGPT, Claude and any AI agent: list users, list course enrollments, list overdue learners, approve enrollment request, enroll users in a course, message learners, create user and deactivate user in your own Absorb LMS account. Cotlet works in the account through your Cloud Browser, a real Chrome where you sign in to Absorb LMS yourself; Cotlet does not store your password and you need no Absorb LMS API key. About the official Absorb LMS API: Absorb LMS has an Integration API, a REST API with JSON for learners, courses, departments and grades, but it needs a private key tied to your portal that your Absorb account team provides, and your own code (checked 20 Oct 2026). Absorb's Aura MCP for Claude and ChatGPT is planned for 27 Oct 2026 and is read and answer only. Setup and FAQ: https://cotlet.dev/integrations/absorb-lms-api-mcp-server

    Related MCP Servers