MCP Galaxy / Directory / Meraki Read-Only
Cloud Networking Read-Only Docker / Podman

Meraki Read-Only

A production-grade, read-only Model Context Protocol server for the Cisco Meraki Dashboard API v1. Launches directly from your MCP client over stdio, in a Docker or Podman container.

Runtime
Docker / Podman
Transport
stdio
API
Meraki Dashboard v1
Overview

What This Server Does

Meraki Read-Only exposes a curated set of GET-only tools that wrap the Cisco Meraki Dashboard API v1. It's designed to be safe in production environments: no tool will ever issue a POST, PUT, PATCH, or DELETE against the Meraki cloud.

The server is pinned to a single organization at startup via the MERAKI_ORG_ID environment variable. Org-scoped tools take no org_id parameter at all, there is no surface for the model to target an unrelated org the API key can also see.

Ships as both a Docker and a Podman container from the same server.py, pick whichever runtime you already run.

Try Asking

What to Ask, in Plain English

A handful of real questions you can put to your AI client once this server is wired in. The model figures out which tools to call, you describe the outcome.

  • "List Meraki networks that have any APs reporting offline."
  • "Which clients failed to associate to wireless today, and where?"
  • "Show channel utilization across all APs in network L_123456, flag any above 70%."
  • "What IDS events fired on the security appliance in the last 24 hours?"
  • "Pull the AutoVPN reachability matrix and call out any tunnels that are down."
  • "What configuration changes happened in the last 24 hours, and who made them?"
Capabilities

63 Read-Only Tools Across the Meraki Stack

A
Organizations
Org detail, networks, inventory, devices, statuses, admins, licenses, alerts, API usage, configuration changes.
B
Networks
Network detail, devices, clients, events, traffic, alert history, topology.
C
Devices
Device detail, uplink settings, clients, LLDP/CDP neighbors, loss & latency.
D
Wireless (MR)
SSIDs, channel utilization, signal quality, failed connections, per-AP radio status & settings.
E
Switch (MS)
Port config, real-time port status, stacks, VLANs, access policies.
F
Security Appliance (MX)
VLANs, uplink usage, IDS/IPS events, L3/L7/inbound firewall rules, content filtering, AutoVPN config and status.
G
Camera (MV)
Video and Sense (analytics) settings, metadata only, no video streams.
Installation

Build & Configure

Two supported runtimes, pick the one you already run. The docker/ and podman/ backends ship an identical server.py; only the container file and the way the client is wired differ. This server reads its two credentials as plain environment variables only, there is no file-mounted secret mode. Expand your runtime below.

New to the Docker MCP Gateway? The Docker pane below uses a catalog → registry → gateway flow with three bind-mounts. If any of that terminology is unfamiliar, skim Docker MCP Gateway under Good to Know first, it walks through the architecture these steps build on. The Podman pane runs the container directly and doesn't depend on the gateway.
Docker Docker Desktop · MCP Gateway
Step 1, Get the project files

Clone the repository. The Docker backend lives in docker/ and ships a custom-catalog.yaml alongside the Dockerfile.

Clone
$ git clone https://github.com/rosarion97/meraki-readonly-mcp-server-public.git
$ cd meraki-readonly-mcp-server-public/docker
Step 2, Build the image

The image tag must match the image: field in custom-catalog.yaml (meraki-readonly-mcp:latest).

Build
$ docker build -t meraki-readonly-mcp:latest .
Step 3, Provide credentials

You need two values, both validated at startup: MERAKI_API_KEY and MERAKI_ORG_ID. Grab the org ID with your key:

Find your org ID
$ curl -H "Authorization: Bearer $MERAKI_API_KEY" \
    https://api.meraki.com/api/v1/organizations

Option A, MCP Toolkit secret (recommended). Store each value in Docker Desktop's encrypted store; the gateway injects them as env vars at launch. With Option A, continue through Steps 4–7 below.

Option A, MCP Toolkit secret
$ docker mcp secret set MERAKI_API_KEY
$ docker mcp secret set MERAKI_ORG_ID
$ docker mcp secret ls   # values are masked
Option B writes your key to disk in clear text. Use it only when you don't have Docker Desktop, chmod 600 .env, never commit it (it's gitignored and excluded from the image). This path skips the gateway: skip Steps 4–6, point your client straight at docker run --env-file (below), then jump to Step 7.
Option B, .env file (fallback)
$ cp .env.example .env
$ chmod 600 .env   # then set MERAKI_API_KEY and MERAKI_ORG_ID
claude_desktop_config.json (Option B)
{
  "mcpServers": {
    "meraki": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/absolute/path/to/docker/.env",
        "meraki-readonly-mcp:latest"
      ]
    }
  }
}
Step 4, Install the custom catalog

Steps 4–6 are the Option A (secret store) path. Used Option B? Skip to Step 7.

Catalog
$ mkdir -p ~/.docker/mcp/catalogs
$ cp custom-catalog.yaml ~/.docker/mcp/catalogs/custom.yaml
Step 5, Enable the server in the registry

Add the entry under the single top-level registry: key in ~/.docker/mcp/registry.yaml. Don't overwrite the file if it already exists.

~/.docker/mcp/registry.yaml
registry:
  meraki-readonly:
    catalog: custom
    enabled: true
Step 6, Point Claude Desktop at the gateway

Add the gateway block to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json). The gateway spawns the Meraki image on demand, reads your catalog and registry, and resolves your MERAKI_* secrets at request time. Replace <your-username> with your macOS username (run whoami to check):

claude_desktop_config.json, gateway block
{
  "mcpServers": {
    "mcp-toolkit-gateway": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "-v", "/var/run/docker.sock:/var/run/docker.sock",
        "-v", "/Users/<your-username>/.docker/mcp:/mcp",
        "-v", "/Users/<your-username>/Library/Caches/docker-secrets-engine/engine.sock:/root/.cache/docker-secrets-engine/engine.sock",
        "docker/mcp-gateway:latest",
        "--catalog=/mcp/catalogs/custom.yaml",
        "--registry=/mcp/registry.yaml",
        "--transport=stdio"
      ]
    }
  }
}

All three bind-mounts are required: the Docker socket lets the gateway spawn the server container; ~/.docker/mcp is where it reads your catalog and registry; the docker-secrets-engine socket is the resolver Docker Desktop exposes for the secret store, without it your MERAKI_* URLs resolve to empty strings and the server never starts. On Linux Docker Desktop the path is ~/.docker/desktop/secrets-engine/engine.sock; find it with find ~ -name engine.sock.

Works the same for other clients. Claude Code takes the same args via claude mcp add -s user mcp-toolkit-gateway -- docker run …; Codex reads the same shape from ~/.codex/config.toml as [mcp_servers.mcp-toolkit-gateway]. Any MCP-capable client, Google's Gemini included, reads an mcpServers block from its own settings file; only the location changes, the entry is identical.

Then quit and reopen the client. claude_desktop_config.json never contains the API key, the gateway resolves it from Docker's secret store at request time. As a shortcut, docker mcp client connect claude-desktop (or MCP Toolkit → Clients in Docker Desktop) writes a similar block automatically; the explicit JSON above survives Docker Desktop updates that may rewrite the auto-managed entry.

Step 7, Verify
Verify
$ docker mcp server list   # meraki-readonly → enabled
$ docker mcp tools list

Or run the image directly, it starts and waits silently on stdin (correct for an MCP stdio server). Press Ctrl+C to exit; a missing value fails fast on stderr.

Smoke test (Option B)
$ docker run --rm -i --env-file .env meraki-readonly-mcp:latest
Podman Rootless · stdio
Step 1, Get the project files

Clone the repository. The Podman backend lives in podman/.

Clone
$ git clone https://github.com/rosarion97/meraki-readonly-mcp-server-public.git
$ cd meraki-readonly-mcp-server-public/podman
Step 2, Build the image

On macOS / Windows, start a Podman machine first (podman machine init && podman machine start); Linux runs natively.

Build
$ podman build -t meraki-readonly-mcp:latest .
Step 3, Provide credentials

Two required values: MERAKI_API_KEY and MERAKI_ORG_ID (one container serves one org). Option 1 keeps them in Podman's encrypted secret store and is recommended; Option 2 writes a plaintext .env.

Option 1, podman secret (recommended)
$ printf '%s' 'YOUR_MERAKI_API_KEY' | podman secret create meraki_api_key -
$ printf '%s' 'YOUR_ORG_ID'         | podman secret create meraki_org_id -
$ podman secret ls   # confirm both (values not shown)
Option 2 writes your key to disk in clear text. Use it only for local testing, chmod 600 .env, never commit it (it's gitignored).
Option 2, .env file (fallback)
$ cp .env.example .env
$ chmod 600 .env   # then set MERAKI_API_KEY and MERAKI_ORG_ID
Step 4, Configure your AI client

Add the entry matching your Step 3 choice. Option 1 injects each secret with type=env,target=NAME; Option 2 reads them from your .env.

claude_desktop_config.json, Option 1 (secret store)
{
  "mcpServers": {
    "meraki": {
      "command": "podman",
      "args": [
        "run", "--rm", "-i",
        "--secret", "meraki_api_key,type=env,target=MERAKI_API_KEY",
        "--secret", "meraki_org_id,type=env,target=MERAKI_ORG_ID",
        "meraki-readonly-mcp:latest"
      ]
    }
  }
}
claude_desktop_config.json, Option 2 (.env)
{
  "mcpServers": {
    "meraki": {
      "command": "podman",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/absolute/path/to/podman/.env",
        "meraki-readonly-mcp:latest"
      ]
    }
  }
}
Works the same for other assistants. Any MCP-capable client reads an mcpServers block from its own settings.json, Google's Gemini included. Drop this same entry into that client's settings.json and the Meraki tools appear there too. Only the file's name and location change; the server entry is identical.

Claude Code uses the same shape in .claude/settings.json (project) or ~/.claude/settings.json (global). -i is required; paths must be absolute.

Step 5, Verify

Run the image manually, it starts and waits on stdin. Press Ctrl+C to exit.

Smoke test
$ podman run --rm -i \
    --secret meraki_api_key,type=env,target=MERAKI_API_KEY \
    --secret meraki_org_id,type=env,target=MERAKI_ORG_ID \
    meraki-readonly-mcp:latest

One container serves one organization, run a second container with its own credentials to serve another org. --rm cleans up the container after each session.

Configuration

Three Variables

  • MERAKI_API_KEY, required. Bearer token used for every API call. Generate it from the Meraki Dashboard under My Profile → API access.
  • MERAKI_ORG_ID, required. Pins this instance to a single organization. Run a second container with a different .env to serve another org.
  • MERAKI_MAX_RESPONSE_BYTES, optional. Caps the JSON size of any one tool response. Default 120000 (~30k tokens). Raise for big-window models, lower for small ones.
Requirements

Before You Start

  • Docker Engine 24+ / Docker Desktop 4.27+ (MCP Toolkit for the secret path), or Podman 4.x+ on $PATH. Pick one; the step-by-step for each is above.
  • A Cisco Meraki Dashboard API key, generated from My Profile → API access → Generate new API key.
  • A read-only admin bound to that API key. The server enforces read-only at the protocol layer, but a read-only admin role is defense-in-depth.
  • An MCP-capable client, Claude Desktop, Claude Code, Gemini CLI, or any client speaking MCP over stdio.
License & Policy

Read-Only by Construction

Provided as-is for internal use. Every data query maps to an HTTP GET against api.meraki.com/api/v1. The server contains no code path that issues a write verb, so it is incapable of modifying any Meraki configuration regardless of what an LLM or user asks for. Pull requests that add additional read-only endpoints are welcome; any change that introduces a write-capable verb against the Dashboard API will be rejected.