meraki-mcp
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.metacarriesscope,count,truncated,elapsed_ms,partial/warnings(a partially-failed call declares what's missing), andnote.Response-size budget. The registrar runs
enforce_budgeton every result: if it serializes pastMERAKI_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_toolwraps every tool with timeout, exception→ToolResultnormalization,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
Resolverturns 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.FakeGatewayexercises real tool logic offline, no API key.Read-only by default. The sole non-GET tool (
live_diagnostics) is behind a flag and annotatedreadOnlyHint=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.pyImplementation 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
Unit tests (offline, no key):
pytest -q— 36 tests (gateway, response/budget, resolver, all tools via FakeGateway). Lint:ruff check src tests scripts.Live smoke (needs a read-only key in
.env):python scripts/smoke_test.py— runsfind/health_overview/list_problemsagainst your real org and prints each summary + anymeta.warnings(field-shape/licensing issues). Add a site name to also exercisesite_status/wireless_status:python scripts/smoke_test.py "Rome DC".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 themerakiserver.Over HTTP (Copilot Studio / MCP Inspector):
MERAKI_MCP_TRANSPORT=streamable-http meraki-mcp # serves http://127.0.0.1:8000/mcpor 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 thehttps://…/mcpURL in Copilot Studio (No authentication for now — seeCONNECT-COPILOT-STUDIO.md).
Never commit a real key.
.envis git-ignored;.env.exampleholds a placeholder only.
meraki-mcp
meraki-mcp
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/reshif/meraki-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server