ZennoPoster MCP

Edit ZennoPoster automation projects, run and monitor tasks, drive browser instances and Android devices from AI agents. Windows only, requires ZennoPoster 7.9.2+.

Documentation

English | Русский

Connecting the ZennoPoster/ProjectMaker MCP servers to your own LLM client

The MCP servers are published as self-contained win-x64 binaries via GitHub Releases in the shared repository https://github.com/ZennoLab/zennoposter-mcp/releases. Each server has its own release line (its own tag prefix):

  • ProjectMaker (editor) — tags mcp-projectmaker-v*, archive MCP.ProjectMaker-v*-win-x64.zip
  • Instance (browser control, dual-mount) — tags mcp-instance-v*, archive MCP.Instance-v*-win-x64.zip
  • ZennoPoster (task runner) — tags mcp-zennoposter-v*, archive MCP.ZennoPoster-v*-win-x64.zip
  • Android (ZennoDroid device) — tags mcp-android-v*, archive MCP.Android-v*-win-x64.zip

PublicApi documentation (the OpenAPI contract rendered with Redoc, the integrator guide, error codes, versioning policy) lives at https://zennolab.github.io/zennoposter-mcp/ (the pages are published from the docs/ folder of this repository).

These servers talk to ZennoPoster 7.9.2 and newer and to ZennoDroid 2.6.1 and newer. MCP.Instance applies to ZennoPoster only and MCP.Android to ZennoDroid only; MCP.ProjectMaker and MCP.ZennoPoster apply to both. Which server version goes with which product and contract version: https://zennolab.github.io/zennoposter-mcp/compatibility.html.

Install buttons for Cursor and VS Code and the Claude Code commands for every server: https://zennolab.github.io/zennoposter-mcp/install.html.

The model: your own MCP instance with your own key

The product itself starts internal MCP servers for its built-in AI chat — they live on the internal port band 6107–6113 (on ZennoDroid everything is shifted by +10), receive a least-privilege service key from the host, and ignore the Authorization header of incoming requests. This is internal infrastructure: connecting to it from outside is not supported (permissions there are defined by the service key, not yours), and its ports must not be occupied — a foreign process on a port from this band prevents the built-in server from starting (the product logs an error, but its AI stack is left without that server).

For your own LLM client you run a separate copy of the MCP server from the public package: it listens on the public port band 6207–6211 and talks to the same product PublicApi (:5299 ProjectMaker / :5300 ZennoPoster), but with your ApiKey — with the scopes/tier you chose when issuing the key. Both copies run side by side without interfering with each other.

On ZennoDroid the two servers that serve both products listen +10 higher — MCP.ProjectMaker on 6217, MCP.ZennoPoster on 6220 — the same shift the product applies to its internal band. That way a machine with both products installed can run both sets at once. MCP.Android exists only on ZennoDroid and keeps 6211: nothing on ZennoPoster occupies it.

Default ports (set in the appsettings.json next to the exe):

ServerPortTalks toKey in config
MCP.ProjectMaker6207PM PublicApi :5299ProjectMaker:ApiKey
MCP.Instance (Target=projectmaker)6208PM PublicApi :5299Instance:ApiKey
MCP.Instance (Target=zennoposter)6209 (convention, set explicitly)ZP PublicApi :5300Instance:ApiKey
MCP.ZennoPoster6210ZP PublicApi :5300ZennoPoster:ApiKey
MCP.Android (ZennoDroid, editor device)6211ZDroid PM PublicApi :5309Android:ApiKey
MCP.Android (ZennoDroid, task devices)6212 (convention, set explicitly)ZDroid ZP PublicApi :5310Android:ApiKey

The product API those servers call listens on different ports in the two products:

ApplicationZennoPosterZennoDroid
ProjectMaker52995309
ZennoPoster53005310

The defaults above and the examples below are for ZennoPoster. On ZennoDroid each server needs both its listen port and its BaseUrl set explicitly:

ServerPortTalks toKey in config
MCP.ProjectMaker6217 (set explicitly)ZDroid PM PublicApi :5309ProjectMaker:ApiKey
MCP.ZennoPoster6220 (set explicitly)ZDroid ZP PublicApi :5310ZennoPoster:ApiKey
MCP.Android (editor device)6211ZDroid PM PublicApi :5309Android:ApiKey
MCP.Android (task devices)6212 (set explicitly)ZDroid ZP PublicApi :5310Android:ApiKey

Everything can be overridden through standard ASP.NET Core configuration: the appsettings.json next to the exe, environment variables (ASPNETCORE_URLS, ProjectMaker__ApiKey, …) or command-line arguments (--urls, --ProjectMaker:ApiKey=…, …) — arguments override environment variables, environment variables override appsettings.json.

1. Download the server you need

On the releases page find the latest release of the server you need (by tag prefix), download its *-win-x64.zip and unpack it into any folder. Each archive is a single self-contained .exe + appsettings.json; no additional .NET runtime is required.

2. Issue an ApiKey

In ProjectMaker: Settings → API KeysAdd — the "Add API key" dialog opens:

  1. Set a Label (an arbitrary key name, to tell keys apart in the list).
  2. Choose the Max tier (the minimum sufficient for your tasks — T0 for read-only, higher for mutating operations).
  3. Tick the Scopes you need (only *:read are ticked by default; add others as needed).
  4. Confirm — the raw key is shown once; copy it to a safe place immediately.

More on scopes/tiers — security-model.md.

3. Run the server with your key

The key is set in the MCP server's own configuration (not in the MCP client's headers — the server accepts connections from loopback only and does not read the Authorization header of incoming requests). The simplest way is to put the key into ApiKey in the appsettings.json next to the exe and start with no arguments; or pass it via environment variables / arguments:

# ProjectMaker (editor): 6207 -> :5299
.\ZennoLab.AI.MCP.ProjectMaker.exe --ProjectMaker:ApiKey=zp_xxx

# Instance for the editor (PM browser): 6208 -> :5299
.\ZennoLab.AI.MCP.Instance.exe --Instance:ApiKey=zp_xxx

# Instance for the runner — a SECOND copy of the same exe: the port and the Target/BaseUrl pair are set explicitly
.\ZennoLab.AI.MCP.Instance.exe --urls http://localhost:6209 `
  --Instance:Target=zennoposter --Instance:BaseUrl=http://localhost:5300/api/v1 `
  --Instance:ApiKey=zp_xxx

# ZennoPoster (runner tasks/sessions): 6210 -> :5300
.\ZennoLab.AI.MCP.ZennoPoster.exe --ZennoPoster:ApiKey=zp_xxx

# Android (the device attached to ProjectMaker): 6211 -> :5309
.\ZennoLab.AI.MCP.Android.exe --Android:ApiKey=zp_xxx

# Android for the runner — a SECOND copy of the same exe, for the devices of running tasks
.\ZennoLab.AI.MCP.Android.exe --urls http://localhost:6212 `
  --Android:BaseUrl=http://localhost:5310/api/v1 --Android:ApiKey=zp_xxx

MCP.Android mounts twice for the same reason MCP.Instance does on ZennoPoster: the Android domain is served both by ProjectMaker's PublicApi (the device you see in the editor) and by the runner's (the devices its tasks are driving). Unlike MCP.Instance it has no Target, so the two copies differ only by --urls and BaseUrl.

On ZennoDroid, the same two executables are started on the shifted ports and pointed at the ZennoDroid API:

# ProjectMaker on ZennoDroid: 6217 -> :5309
.\ZennoLab.AI.MCP.ProjectMaker.exe --urls http://localhost:6217 `
  --ProjectMaker:BaseUrl=http://localhost:5309/api/v1 --ProjectMaker:ApiKey=zp_xxx

# ZennoPoster tasks on ZennoDroid: 6220 -> :5310
.\ZennoLab.AI.MCP.ZennoPoster.exe --urls http://localhost:6220 `
  --ZennoPoster:BaseUrl=http://localhost:5310/api/v1 --ZennoPoster:ApiKey=zp_xxx

Important note on the two MCP.Instance copies: for the Instance server, Target (which instructions it serves to the AI — about ProjectMaker or about ZennoPoster) and BaseUrl (where HTTP requests actually go) are configured only together, as a pair, and are not linked in code (Target=projectmakerBaseUrl at the PM PublicApi :5299, Target=zennoposterBaseUrl at the ZP PublicApi :5300). On startup the server makes a best-effort check via the target host's /capabilities and logs a warning on mismatch, but if the target host is unreachable at startup the check is silently skipped — a mismatch is then not detected, and the AI gets instructions about one host while requests go to another.

4. Configure your LLM client

A ready-made configuration fragment (the format matches Claude Code's .mcp.json; use the equivalent for your MCP client if needed):

{
  "servers": {
    "projectmaker": {
      "type": "http",
      "url": "http://localhost:6207"
    },
    "instance-pm": {
      "type": "http",
      "url": "http://localhost:6208"
    },
    "instance-zp": {
      "type": "http",
      "url": "http://localhost:6209"
    },
    "zennoposter": {
      "type": "http",
      "url": "http://localhost:6210"
    }
  }
}

On ZennoDroid the entries are these — different names, so both products can be configured in one client, and no instance-* mount, which ZennoDroid does not have:

{
  "servers": {
    "projectmaker-droid": {
      "type": "http",
      "url": "http://localhost:6217"
    },
    "zennodroid": {
      "type": "http",
      "url": "http://localhost:6220"
    },
    "android-pm": {
      "type": "http",
      "url": "http://localhost:6211"
    },
    "android-zd": {
      "type": "http",
      "url": "http://localhost:6212"
    }
  }
}

No authorization is needed on this leg: the MCP servers listen on loopback only, and the permissions are defined by the key the server itself was started with (step 3).

5. Verify the connection

With any MCP client (or plain HTTP) call a safe read-only method and make sure it responds 200 OK with the expected data — e.g. get_product_version/ping on the server you need. If the key is invalid or lacks a scope/tier, the server returns a structured error (401 unauthorized / 403 forbidden with required/current fields), not a silent failure.

Changing ports

Each server has two ports: the one it listens on for your LLM client, and the product API port it calls.

Listen port. Any of the three, arguments winning over environment, environment over the file:

.\ZennoLab.AI.MCP.ProjectMaker.exe --urls http://localhost:7207

$env:ASPNETCORE_URLS = "http://localhost:7207"
.\ZennoLab.AI.MCP.ProjectMaker.exe

or "Urls": "http://localhost:7207" in the appsettings.json next to the exe. After moving a listen port, update the matching URL in the client configuration from step 4.

Stay off 6107–6113 (6117–6123 on ZennoDroid): those belong to the product's built-in servers, and a foreign process on one of them prevents the built-in server from starting.

Product API port. Set the BaseUrl of that server's own section — this is what you change on ZennoDroid:

# ProjectMaker API on 5309 instead of 5299
.\ZennoLab.AI.MCP.ProjectMaker.exe --ProjectMaker:BaseUrl=http://localhost:5309/api/v1 --ProjectMaker:ApiKey=zp_xxx

# ZennoPoster API on 5310 instead of 5300
.\ZennoLab.AI.MCP.ZennoPoster.exe --ZennoPoster:BaseUrl=http://localhost:5310/api/v1 --ZennoPoster:ApiKey=zp_xxx

The section is named after the server: ProjectMaker, Instance, ZennoPoster, Android. Versions up to 0.3.0 of MCP.ProjectMaker and MCP.ZennoPoster used NeuroBot and ZennoPosterApi; those names still work in later versions and the server logs a warning at startup.

License

The files in this repository (documentation and the OpenAPI specification) are licensed under the MIT License. The MCP server binaries on the Releases page are proprietary; their terms are in TERMS.md.