Skip to main content
Glama
portswigger-cloud

grafana-editor

grafana-editor

An MCP server for editing Grafana, authenticated via Microsoft Entra ID SSO.

Grafana-editing tools are still to come. For now this is the authentication scaffolding plus one tool, whoami, that returns the email address of the signed-in user, so the SSO plumbing can be exercised end to end before any Grafana-specific tool is built on top of it.

It uses:

  • uv for project and dependency management

  • mcp / MCPServer for the MCP protocol, using its resource-server-only OAuth support

  • starlette as the ASGI application

  • hypercorn as the HTTP server

  • pyjwt to validate Entra-issued access tokens against the tenant's JWKS

  • pytest for tests

How authentication works

This server never talks to Entra to log anyone in. Instead:

  1. An MCP client (e.g. Claude) fetches {resource-server-url}/.well-known/oauth-protected-resource from this server, which names Entra as the authorization server to use.

  2. The client runs the OAuth 2.1 authorization code flow (with PKCE) directly against Entra, in the user's browser.

  3. Entra issues an access token scoped to this server's App ID URI, which the client sends as a Bearer token on every MCP request.

  4. This server validates that token's signature (via Entra's JWKS), issuer, audience and expiry, and reads the caller's email out of it. Both v1 (https://sts.windows.net/{tenant}/) and v2 (https://login.microsoftonline.com/{tenant}/v2.0) issuers are accepted, so the app registration works whether or not its manifest sets requestedAccessTokenVersion to 2.

That means setting this up requires an app registration in Entra ID:

  1. Create an app registration. Note its Application (client) ID and your Directory (tenant) ID.

  2. Under "Expose an API", accept the default Application ID URI (api://<client-id>) and add a scope (e.g. mcp.access).

  3. Under "Token configuration", add email as an optional claim on the access token — the preferred_username claim Entra includes by default is usually an email address too, but isn't guaranteed to be one.

  4. Register the MCP client as an authorized client for that scope (Entra's support for OAuth dynamic client registration is limited, so which clients this covers depends on how your MCP client obtains credentials — check its docs for connecting to an Entra-protected MCP server).

Fill in the tenant ID, client ID, and this server's own public URL in config.toml.example. If you name the scope from step 2 anything other than mcp.access, also set scope under [entra] to match. That scope, qualified with the App ID URI, is both advertised as scopes_supported in this server's OAuth Protected Resource Metadata and required on every request. Advertising it is what stops clients falling back to requesting only generic OIDC scopes (openid profile email offline_access), none of which belong to this resource — which Entra rejects with AADSTS9010010.

A note on custom Application ID URIs and trailing slashes

If you customise the Application ID URI to an https:// URL under your own domain (rather than the default api://<client-id>), set audience under [entra] to exactly that value, and keep resource-server-url the same string too. Entra refuses to register an Application ID URI that ends in a slash, so the whole set should be slash-free.

Take care not to let a trailing slash creep back in: pydantic's AnyHttpUrl normalises https://host to https://host/, so AuthSettings is handed plain strings (it sets url_preserve_empty_path, keeping the slash-free form) rather than URLs we built ourselves. A mismatch here shows up as an aud claim that doesn't match audience — the rejection log names both values.

Related MCP server: Grafana FastMCP Server

Run it

cp config.toml.example config.toml
# edit config.toml with your tenant details
uv run grafana-editor --config config.toml

Without Entra configured, for local development only:

uv run grafana-editor --config config.toml --disable-auth

With --disable-auth, the whoami tool has no signed-in user to report and returns an error explaining that — it does not fabricate an email address.

Then connect an MCP client or the inspector to:

http://127.0.0.1:8000/mcp

Exposed MCP data

Tools:

  • whoami — returns {"email": "..."} for the signed-in user.

Test it

uv run pytest

Build a container image

docker build -t grafana-editor .

The image expects its config at /config/grafana-editor.toml by default.

Related MCP Connectors

Related MCP Servers

  • -
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables remote MCP client connections with WorkOS AuthKit authentication, supporting organization-centric user management and permission-based tool access control.
    -
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables MCP-compatible agents to interact with Grafana instances for searching, creating, and updating dashboards, exploring logs via Loki, querying datasources, managing alerts, incidents, and on-call shifts, and accessing observability data.
    8
    Apache 2.0
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables MCP clients to fetch the signed-in Microsoft Entra user's profile from Microsoft Graph via the GET /me endpoint using the provided bearer token.
    Apache 2.0