---
title: "Concepts & architecture"
description: "How the Open edX MCP fits together — MCP proxy, LMS vs CMS facade, native APIs, live authorization."
source: "https://docs.blend-ed.com/openedx-mcp/concepts/"
---

# Concepts & architecture

## The pieces
```
Claude / MCP client ──(Bearer key)──▶ openedxmcp container (MCP proxy)                                         │  X-MCP-Key                             ┌───────────┴───────────┐                       LMS /api/mcp/           CMS /api/mcp/cms/                    (people, enroll,          (course authoring,                     analytics, certs,          modulestore)                     reports, retire)
```
- **MCP server** ( `tutor-contrib-openedxmcp` ) — a stateless MCP
streamable-http proxy, fronted by Caddy at `mcp.<LMS_HOST>` . It holds no
secrets; each request carries its own key, forwarded as `X-MCP-Key` .
- **Facade** ( `openedx-mcp` ) — a standard Open edX djangoapp plugin exposing the
REST endpoints and enforcing all authorization. Installed into the openedx image.

## Why two processes
Course-authoring APIs write the **modulestore**, which is only writable in the **CMS/Studio** process, so authoring tools mount at `/api/mcp/cms/`. Everything else — people, access, enrollment, analytics, certificates, reports — runs in the **LMS** at `/api/mcp/`. One Django app ships both `AppConfig`s and shares one key table.

## Native only
Every operation calls importable `openedx-platform` functions — no core fork, no vendored logic, no third-party stack. That keeps the plugin upgradable with the platform and trustworthy to run against a production instance.

## Authorization in one line
The platform’s own `is_staff` / `is_superuser` is the sole authority, re-checked **live on every request**. MCP keys are Django models; scopes only narrow what an already-privileged user may do through a given credential. See [Security & scopes](/openedx-mcp/security/).
