Skip to main content
Glama

meraki-mcp

A read-only, production-shaped Model Context Protocol server for the Cisco Meraki Dashboard, built for infra/delivery teams (MR / MS / MX). It exposes a small, high-signal set of tools an LLM can select reliably — the tool design rationale lives in ../docs/; the code architecture is in ARCHITECTURE.md.

Built on the MCP Python SDK (FastMCP) and the official async meraki library.

What makes it enterprise-shaped

  • One response contract. Every tool returns ToolResult{summary, data, meta, error}. Nobody hand-rolls output; tools project Meraki payloads to declared fields (no raw blobs) and cap arrays. meta carries scope, count, truncated, elapsed_ms, partial/warnings (a partially-failed call declares what's missing), and note.

  • Response-size budget. The registrar runs enforce_budget on every result: if it serializes past MERAKI_MCP_MAX_RESPONSE_CHARS (~15k tokens) the largest arrays are shrunk to fit and the result is flagged (meta.truncated + meta.note) — so a response can never overflow the Copilot/agent context.

  • One lifecycle owner. register_tool wraps every tool with timeout, exception→ToolResult normalization, elapsed_ms, and structured logging. A raw exception never crosses the MCP boundary.

  • One SDK seam. All Meraki calls flow through MerakiGateway (concurrency limit, per-call timeout, error normalization, logging) — applied once, not per tool.

  • Names in, IDs never invented. Tools accept "Rome DC"; the cached Resolver turns it into an ID (ambiguity returns the candidate list for the model to pick).

  • Testable by construction. Tools depend on the gateway interface, so tests/fakes.FakeGateway exercises real tool logic offline, no API key.

  • Read-only by default. The sole non-GET tool (live_diagnostics) is behind a flag and annotated readOnlyHint=false.

Layout

src/meraki_mcp/
  server.py         FastMCP app + lifespan (builds gateway, resolver, settings once)
  config.py         env Settings (validated; timeouts, concurrency, feature flags)
  context.py        AppContext handed to every tool (gateway, resolver, settings)
  core/
    gateway.py      the ONE Meraki SDK seam: concurrency + timeout + error normalization
    response.py     ToolResult / Meta / Collector / Response  (the output contract)
    errors.py       ResolutionError + GatewayError hierarchies (each carries an ErrorKind)
    resolver.py     name/serial/MAC -> IDs, TTL-cached
    observability.py logging setup
    pagination.py   aggregate (all pages) vs firehose (capped)
    formatting.py   small trim helpers
  tools/
    _registry.py    register_tool: the single lifecycle wrapper (ctx->app, timeout, errors, logging)
    _shared.py      one-import toolkit for tool modules
    <domain>.py     pure handlers + one-line register() each
    optional/       reports.py (P2) + live.py (non-GET, flagged)
  prompts/playbooks.py   13 packaged playbooks (register_prompts)
tests/
  fakes.py          FakeGateway/FakeSection — the DIP payoff
  test_core.py test_gateway.py test_response.py test_selection.py

Implementation status

All 18 tools are implemented — 13 core (find, health_overview, list_problems, site_status, device_status, client_status, troubleshoot_client, triage_site, site_turnup_readiness, wireless_status, switch_status, security_events, config_review) plus 5 optional/off-by-default (change_audit, firmware_compliance, license_inventory_report, usage_summary, and live_diagnostics — the only non-GET tool, a create→poll job runner). Also 3 resources (meraki://organizations, meraki://networks, meraki://devices) and 13 prompts. Multi-org is supported: MERAKI_ORG_ID is an optional default, every tool takes an optional org, and find() lists orgs/networks for discovery.

Field-shape parsing is based on the Meraki OpenAPI spec and unit-tested with a FakeGateway, but not yet run against a live org — do one smoke test with a read-only key before production (see REVIEW.md B2).

Run

pip install -e ".[dev]"
cp .env.example .env      # set a READ-ONLY MERAKI_DASHBOARD_API_KEY (+ MERAKI_ORG_ID for single-org)
meraki-mcp               # transport from .env (streamable-http by default)

How to test it

  1. Unit tests (offline, no key): pytest -q — 36 tests (gateway, response/budget, resolver, all tools via FakeGateway). Lint: ruff check src tests scripts.

  2. Live smoke (needs a read-only key in .env): python scripts/smoke_test.py — runs find/health_overview/list_problems against your real org and prints each summary + any meta.warnings (field-shape/licensing issues). Add a site name to also exercise site_status/wireless_status: python scripts/smoke_test.py "Rome DC".

  3. In VS Code / Copilot (stdio): .vscode/mcp.json (in the workspace root) launches this server for VS Code's MCP client — open it and start the meraki server.

  4. Over HTTP (Copilot Studio / MCP Inspector):

    MERAKI_MCP_TRANSPORT=streamable-http meraki-mcp     # serves http://127.0.0.1:8000/mcp

    or containerised: docker build -t meraki-mcp . && docker run --rm -p 8000:8000 -e MERAKI_DASHBOARD_API_KEY=... -e MERAKI_ORG_ID=... meraki-mcp. Expose via a dev tunnel and register the https://…/mcp URL in Copilot Studio (No authentication for now — see CONNECT-COPILOT-STUDIO.md).

Never commit a real key. .env is git-ignored; .env.example holds a placeholder only.

meraki-mcp

meraki-mcp

Latest Blog Posts

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/reshif/meraki-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server