mcpgrafana

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 45 Imported by: 1

README

Grafana MCP server

Unit Tests Integration Tests E2E Tests Go Reference MCP Catalog

A Model Context Protocol (MCP) server for Grafana.

This provides access to your Grafana instance and the surrounding ecosystem.

Quick Start

Requires uv. Add the following to your MCP client configuration (e.g. Claude Desktop, Cursor):

{
  "mcpServers": {
    "grafana": {
      "command": "uvx",
      "args": ["mcp-grafana"],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
      }
    }
  }
}

For Grafana Cloud, replace GRAFANA_URL with your instance URL (e.g. https://myinstance.grafana.net). See Usage for more installation options including Docker, binary, and Helm.

Requirements

  • Grafana version 9.0 or later is required for full functionality. Some features, particularly datasource-related operations, may not work correctly with earlier versions due to missing API endpoints.

Features

The following features are currently available in MCP server. This list is for informational purposes only and does not represent a roadmap or commitment to future features.

Dashboards
  • Search for dashboards: Find dashboards by title or other metadata
  • Get dashboard by UID: Retrieve full dashboard details using its unique identifier. Warning: Large dashboards can consume significant context window space.
  • Get dashboard summary: Get a compact overview of a dashboard including title, panel count, panel types, variables, and metadata without the full JSON to minimize context window usage
  • Get dashboard property: Extract specific parts of a dashboard using JSONPath expressions (e.g., $.title, $.panels[*].title) to fetch only needed data and reduce context window consumption
  • Update or create a dashboard: Modify existing dashboards or create new ones. Warning: Requires full dashboard JSON which can consume large amounts of context window space.
  • Patch dashboard: Apply specific changes to a dashboard without requiring the full JSON, significantly reducing context window usage for targeted modifications
  • Get panel queries and datasource info: Get the title, query string, and datasource information (including UID and type, if available) from every panel in a dashboard
Run Panel Query

Note: Run panel query tools are disabled by default. To enable them, add runpanelquery to your --enabled-tools flag.

  • Run panel query: Execute a dashboard panel's query with custom time ranges and variable overrides.
Context Window Management

The dashboard tools now include several strategies to manage context window usage effectively (issue #101):

  • Use get_dashboard_summary for dashboard overview and planning modifications
  • Use get_dashboard_property with JSONPath when you only need specific dashboard parts
  • Avoid get_dashboard_by_uid unless you specifically need the complete dashboard JSON
Datasources
  • List and fetch datasource information: View all configured datasources and retrieve detailed information about each.
    • Supported datasource types: Prometheus, Loki, ClickHouse, CloudWatch, Elasticsearch, OpenSearch, Snowflake, Athena.
Query Examples

Note: Query examples tools are disabled by default. To enable them, add examples to your --enabled-tools flag.

  • Get query examples: Retrieve example queries for different datasource types to learn query syntax.
Prometheus Querying
  • Query Prometheus: Execute PromQL queries (supports both instant and range metric queries) against Prometheus datasources.
  • Query Prometheus metadata: Retrieve metric metadata, metric names, label names, and label values from Prometheus datasources.
  • Query histogram percentiles: Calculate histogram percentile values (p50, p90, p95, p99) using histogram_quantile.
Loki Querying
  • Query Loki logs and metrics: Run both log queries and metric queries using LogQL against Loki datasources.
  • Query Loki metadata: Retrieve label names, label values, and stream statistics from Loki datasources.
  • Query Loki patterns: Retrieve log patterns detected by Loki to identify common log structures and anomalies.
InfluxDB Querying

Note: InfluxDB tools are disabled by default. To enable them, add influxdb to your --enabled-tools flag.

  • Query InfluxDB: Execute queries against InfluxDB datasources using either InfluxQL (v1.x) or Flux (v2.x). The dialect is inferred from the datasource configuration, or can be set explicitly via the dialect parameter.
ClickHouse Querying

Note: ClickHouse tools are disabled by default. To enable them, add clickhouse to your --enabled-tools flag.

  • List ClickHouse tables: List all tables in a ClickHouse database with row counts and sizes.
  • Describe table schema: Get column names, types, and metadata for a ClickHouse table.
  • Query ClickHouse: Execute SQL queries with Grafana macro and variable substitution support.
CloudWatch Querying

Note: CloudWatch tools are disabled by default. To enable them, add cloudwatch to your --enabled-tools flag.

  • List CloudWatch namespaces: Discover available AWS CloudWatch namespaces.
  • List CloudWatch metrics: List metrics available in a specific namespace.
  • List CloudWatch dimensions: Get dimensions for filtering metric queries.
  • Query CloudWatch: Execute CloudWatch metric queries with time range support.
Graphite Querying

Note: Graphite tools are disabled by default. To enable them, add graphite to your --enabled-tools flag.

  • Query Graphite: Execute Graphite render API queries against a Graphite datasource.
  • List Graphite metrics: Browse and discover Graphite metric paths.
  • List Graphite tags: List available Graphite tags and tag values.
  • Query Graphite density: Query Graphite metric density for a given pattern.
Athena Querying

Note: Athena tools are disabled by default. To enable them, add athena to your --enabled-tools flag.

  • List Athena catalogs: Discover available data catalogs (e.g. AwsDataCatalog, Iceberg connectors).
  • List Athena databases: List databases in an Athena catalog.
  • List Athena tables: List tables in an Athena database.
  • Describe Athena table: Get column names for an Athena table.
  • Query Athena: Execute SQL queries against Amazon Athena via Grafana with macro substitution, limit enforcement, and template variable support.
Snowflake Querying

Note: Snowflake tools are disabled by default. To enable them, add snowflake to your --enabled-tools flag.

Queries go through Grafana's Snowflake datasource (Grafana Enterprise plugin grafana-snowflake-datasource), so authentication is handled by the datasource configuration in Grafana — credentials are never seen by the MCP server. This is the same model used for the ClickHouse tools.

  • List Snowflake tables: Discover tables (with database, schema, kind, row count, and size) via INFORMATION_SCHEMA.TABLES. Optional database/schema filters.
  • Describe table schema: Get column names, data types, nullability, defaults, and comments for a Snowflake table.
  • Query Snowflake: Execute SQL queries with macro and variable substitution support. Useful for querying Snowflake's event tables (e.g. SNOWFLAKE.TELEMETRY.EVENTS) for logs and traces, or any user table.
    • Supported macros: $__timeFilter(column), $__timeFrom, $__timeTo, $__from, $__to (Unix ms), $__interval (seconds), $__interval_ms, and ${varname} for template variable substitution.
Elasticsearch/OpenSearch Querying

Note: Elasticsearch/OpenSearch tools are disabled by default. To enable them, add elasticsearch to your --enabled-tools flag.

  • Query Elasticsearch/OpenSearch: Execute search queries against Elasticsearch or OpenSearch datasources using either Lucene query syntax or Elasticsearch Query DSL. Supports filtering by time range and retrieving logs, metrics, or any indexed data. Returns documents with their index, ID, source fields, and optional relevance score.
Quickwit Querying

Note: Quickwit tools are disabled by default. To enable them, add quickwit to your --enabled-tools flag.

  • Query Quickwit: Execute search queries against Quickwit datasources using Lucene query syntax or partial Elasticsearch-compatible Query DSL. Supports filtering by time range and retrieving logs or other indexed documents. Returns documents with their index, ID, source fields, and optional relevance score.
Agent Observability

Note: Agent Observability tools are disabled by default and work only in Grafana Cloud. To enable them, add agento11y to your --enabled-tools flag.

  • List and search conversations: List recent LLM conversations or search them with a filter expression (model, provider, agent, status, error type, eval results, and more) over a time range. Search results include error counts, rating summaries, evaluation summaries, and trace IDs.
  • Get conversation detail: Fetch a single conversation with all its generations, including prompts and outputs.
  • Get generation detail and scores: Fetch a single generation by ID, and its evaluation scores (evaluator, score key, value, passed, explanation).
Incidents
  • Search, create, and update incidents: Manage incidents in Grafana Incident, including searching, creating, and adding activities to incidents.
Sift Investigations
  • List Sift investigations: Retrieve a list of Sift investigations, with support for a limit parameter.
  • Get Sift investigation: Retrieve details of a specific Sift investigation by its UUID.
  • Get Sift analyses: Retrieve a specific analysis from a Sift investigation.
  • Find error patterns in logs: Detect elevated error patterns in Loki logs using Sift.
  • Find slow requests: Detect slow requests using Sift (Tempo).
Alerting
  • List and fetch alert rule information: View alert rules and their statuses (firing/normal/error/etc.) in Grafana. Supports both Grafana-managed rules and datasource-managed rules from Prometheus or Loki datasources.
  • Create and update alert rules: Create new alert rules or modify existing ones.
  • Delete alert rules: Remove alert rules by UID.
  • Manage alerting routing: View notification policies, contact points, and time intervals. Supports both Grafana-managed contact points and receivers from external Alertmanager datasources (Prometheus Alertmanager, Mimir, Cortex).
Grafana OnCall
  • List and manage schedules: View and manage on-call schedules in Grafana OnCall.
  • Get shift details: Retrieve detailed information about specific on-call shifts.
  • Get current on-call users: See which users are currently on call for a schedule.
  • List teams and users: View all OnCall teams and users.
  • List alert groups: View and filter alert groups from Grafana OnCall by various criteria including state, integration, labels, and time range.
  • Get alert group details: Retrieve detailed information about a specific alert group by its ID.
Admin

Note: Admin tools are disabled by default. To enable them, include admin in your --enabled-tools flag.

  • List teams: View all configured teams in Grafana.
  • List Users: View all users in an organization in Grafana.
  • List all roles: List all Grafana roles, with an optional filter for delegatable roles.
  • Get role details: Get details for a specific Grafana role by UID.
  • List assignments for a role: List all users, teams, and service accounts assigned to a role.
  • List roles for users: List all roles assigned to one or more users.
  • List roles for teams: List all roles assigned to one or more teams.
  • List permissions for a resource: List all permissions defined for a specific resource (dashboard, datasource, folder, etc.).
  • Describe a Grafana resource: List available permissions and assignment capabilities for a resource type.
Navigation
  • Generate deeplinks: Create accurate deeplink URLs for Grafana resources instead of relying on LLM URL guessing.
    • Dashboard links: Generate direct links to dashboards using their UID (e.g., http://localhost:3000/d/dashboard-uid)
    • Panel links: Create links to specific panels within dashboards with viewPanel parameter (e.g., http://localhost:3000/d/dashboard-uid?viewPanel=5)
    • Explore links: Generate links to Grafana Explore with pre-configured datasources (e.g., http://localhost:3000/explore?left={"datasource":"prometheus-uid"})
    • Time range support: Add time range parameters to links (from=now-1h&to=now)
    • Custom parameters: Include additional query parameters like dashboard variables or refresh intervals
Annotations
  • Get Annotations: Query annotations with filters. Supports time range, dashboard UID, tags, and match mode.
  • Create Annotation: Create a new annotation on a dashboard or panel.
  • Create Graphite Annotation: Create annotations using Graphite format (what, when, tags, data).
  • Update Annotation: Replace all fields of an existing annotation (full update).
  • Patch Annotation: Update only specific fields of an annotation (partial update).
  • Get Annotation Tags: List available annotation tags with optional filtering.
Snapshots
  • List snapshots: List dashboard snapshots with optional query and limit filters.
  • Get snapshot: Retrieve snapshot metadata and dashboard payload by snapshot key.
  • Create snapshot: Create a dashboard snapshot from a full dashboard payload, with optional expiration and external snapshot options.
  • Delete snapshot: Delete a snapshot by snapshot key.
Rendering
  • Get panel or dashboard image: Render a Grafana dashboard panel or full dashboard as a PNG image. Returns the image as base64 encoded data for use in reports, alerts, or presentations. Supports customizing dimensions, time range, theme, scale, and dashboard variables. Also supports rendering not-yet-applied dashboards from a provisioning repository branch (e.g. a git-sync PR preview) via the optional provisioningPreview parameter.
Provisioning
  • List provisioning repositories: List provisioning repositories configured for this Grafana instance (e.g. git-sync sources), returning each repository's slug along with its source URL, branch, path, sync state, and health.
  • Validate provisioning file: Dry-run-apply a file from a provisioning repository at a given branch or commit. Returns whether it would be accepted, the resource action (create/update), the target resource type, and any structured validation errors — the same admission surface Grafana's PR commenter uses.

The list of tools is configurable, so you can choose which tools you want to make available to the MCP client. This is useful if you don't use certain functionality or if you don't want to take up too much of the context window. To disable a category of tools, use the --disable-<category> flag when starting the server. For example, to disable the OnCall tools, use --disable-oncall, or to disable navigation deeplink generation, use --disable-navigation.

RBAC Permissions

Each tool requires specific RBAC permissions to function properly. When creating a service account for the MCP server, ensure it has the necessary permissions based on which tools you plan to use. The permissions listed are the minimum required actions - you may also need appropriate scopes (e.g., datasources:*, dashboards:*, folders:*) depending on your use case.

Tip: If you're not familiar with Grafana RBAC or you want a quicker, simpler setup instead of configuring many granular scopes, you can assign a built-in role such as Editor to the service account. The Editor role grants broad read/write access that will allow most MCP server operations; it is less granular (and therefore less restrictive) than manually-applied scopes, so use it only when convenience is more important than strict least-privilege access.

Note: Grafana Incident and Sift tools use basic Grafana roles instead of fine-grained RBAC permissions:

  • Viewer role: Required for read-only operations (list incidents, get investigations)
  • Editor role: Required for write operations (create incidents, modify investigations)

For more information about Grafana RBAC, see the official documentation.

RBAC Scopes

Scopes define the specific resources that permissions apply to. Each action requires both the appropriate permission and scope combination.

Common Scope Patterns:

  • Broad access: Use * wildcards for organization-wide access

    • datasources:* - Access to all datasources
    • dashboards:* - Access to all dashboards
    • folders:* - Access to all folders
    • teams:* - Access to all teams
  • Limited access: Use specific UIDs or IDs to restrict access to individual resources

    • datasources:uid:prometheus-uid - Access only to a specific Prometheus datasource
    • dashboards:uid:abc123 - Access only to dashboard with UID abc123
    • folders:uid:xyz789 - Access only to folder with UID xyz789
    • teams:id:5 - Access only to team with ID 5
    • global.users:id:123 - Access only to user with ID 123

Examples:

  • Full MCP server access: Grant broad permissions for all tools

    datasources:* (datasources:read, datasources:query)
    dashboards:* (dashboards:read, dashboards:create, dashboards:write)
    folders:* (for dashboard creation and alert rules)
    teams:* (teams:read)
    global.users:* (users:read)
    
  • Limited datasource access: Only query specific Prometheus and Loki instances

    datasources:uid:prometheus-prod (datasources:query)
    datasources:uid:loki-prod (datasources:query)
    
  • Dashboard-specific access: Read only specific dashboards

    dashboards:uid:monitoring-dashboard (dashboards:read)
    dashboards:uid:alerts-dashboard (dashboards:read)
    
Tools
Tool Category Description Required RBAC Permissions Required Scopes
list_teams Admin List all teams teams:read teams:* or teams:id:1
list_users_by_org Admin List all users in an organization users:read global.users:* or global.users:id:123
list_all_roles Admin List all Grafana roles roles:read roles:*
get_role_details Admin Get details for a Grafana role roles:read roles:uid:editor
get_role_assignments Admin List assignments for a role roles:read roles:uid:editor
list_user_roles Admin List roles for users roles:read global.users:id:123
list_team_roles Admin List roles for teams roles:read teams:id:7
get_resource_permissions Admin List permissions for a resource permissions:read dashboards:uid:abcd1234
get_resource_description Admin Describe a Grafana resource type permissions:read dashboards:*
search_dashboards Search Search for dashboards dashboards:read dashboards:* or dashboards:uid:abc123
get_dashboard_by_uid Dashboard Get a dashboard by uid dashboards:read dashboards:uid:abc123
update_dashboard Dashboard Update or create a new dashboard dashboards:create, dashboards:write dashboards:*, folders:* or folders:uid:xyz789
get_dashboard_panel_queries Dashboard Get panel title, queries, datasource UID and type from a dashboard dashboards:read dashboards:uid:abc123
run_panel_query RunPanelQuery* Execute one or more dashboard panel queries dashboards:read, datasources:query dashboards:uid:*, datasources:uid:*
get_dashboard_property Dashboard Extract specific parts of a dashboard using JSONPath expressions dashboards:read dashboards:uid:abc123
get_dashboard_summary Dashboard Get a compact summary of a dashboard without full JSON dashboards:read dashboards:uid:abc123
list_datasources Datasources List datasources datasources:read datasources:*
get_datasource Datasources Get a datasource by UID or name datasources:read datasources:uid:prometheus-uid
get_query_examples Examples* Get example queries for a datasource type datasources:read datasources:*
query_prometheus Prometheus Execute a query against a Prometheus datasource datasources:query datasources:uid:prometheus-uid
list_prometheus_metric_metadata Prometheus List metric metadata datasources:query datasources:uid:prometheus-uid
list_prometheus_metric_names Prometheus List available metric names datasources:query datasources:uid:prometheus-uid
list_prometheus_label_names Prometheus List label names matching a selector datasources:query datasources:uid:prometheus-uid
list_prometheus_label_values Prometheus List values for a specific label datasources:query datasources:uid:prometheus-uid
query_prometheus_histogram Prometheus Calculate histogram percentile values datasources:query datasources:uid:prometheus-uid
list_incidents Incident List incidents in Grafana Incident Viewer role N/A
create_incident Incident Create an incident in Grafana Incident Editor role N/A
add_activity_to_incident Incident Add an activity item to an incident in Grafana Incident Editor role N/A
get_incident Incident Get a single incident by ID Viewer role N/A
query_loki_logs Loki Query and retrieve logs using LogQL (either log or metric queries) datasources:query datasources:uid:loki-uid
list_loki_label_names Loki List all available label names in logs datasources:query datasources:uid:loki-uid
list_loki_label_values Loki List values for a specific log label datasources:query datasources:uid:loki-uid
query_loki_stats Loki Get statistics about log streams datasources:query datasources:uid:loki-uid
query_loki_patterns Loki Query detected log patterns to identify common structures datasources:query datasources:uid:loki-uid
analyze_loki_labels Loki Audit a Loki label strategy (live or static) and optionally diagnose query performance datasources:query datasources:uid:loki-uid
suggest_loki_alloy_label_config Config Generate an Alloy loki.process snippet enforcing approved labels N/A N/A
query_influxdb InfluxDB Query InfluxDB using InfluxQL (v1) or Flux (v2) datasources:query datasources:uid:influxdb-uid
list_clickhouse_tables ClickHouse* List tables in a ClickHouse database datasources:query datasources:uid:*
describe_clickhouse_table ClickHouse* Get table schema with column types datasources:query datasources:uid:*
query_clickhouse ClickHouse* Execute SQL queries with macro substitution datasources:query datasources:uid:*
list_cloudwatch_namespaces CloudWatch* List available AWS CloudWatch namespaces datasources:query datasources:uid:*
list_cloudwatch_metrics CloudWatch* List metrics in a namespace datasources:query datasources:uid:*
list_cloudwatch_dimensions CloudWatch* List dimensions for a metric datasources:query datasources:uid:*
query_cloudwatch CloudWatch* Execute CloudWatch metric queries datasources:query datasources:uid:*
list_athena_catalogs Athena* List available Athena data catalogs datasources:query datasources:uid:*
list_athena_databases Athena* List databases in an Athena catalog datasources:query datasources:uid:*
list_athena_tables Athena* List tables in an Athena database datasources:query datasources:uid:*
describe_athena_table Athena* Get column names for an Athena table datasources:query datasources:uid:*
query_athena Athena* Execute SQL queries with macro substitution datasources:query datasources:uid:*
query_elasticsearch Elasticsearch/OpenSearch* Query Elasticsearch or OpenSearch using Lucene syntax or Query DSL datasources:query datasources:uid:datasource-uid
query_quickwit Quickwit* Query Quickwit using Lucene syntax or Query DSL datasources:query datasources:uid:quickwit-uid
list_snowflake_tables Snowflake* List tables in a Snowflake database/schema via INFORMATION_SCHEMA datasources:query datasources:uid:*
describe_snowflake_table Snowflake* Get table schema (column types, nullability, defaults, comments) datasources:query datasources:uid:*
query_snowflake Snowflake* Execute SQL queries with macro/variable substitution datasources:query datasources:uid:*
alerting_manage_rules Alerting Manage alert rules (list, get, versions, create, update, delete) alert.rules:read + alert.rules:write for mutations folders:* or folders:uid:alerts-folder
alerting_manage_routing Alerting Manage notification policies, contact points, and time intervals alert.notifications:read Global scope
list_oncall_schedules OnCall List schedules from Grafana OnCall grafana-oncall-app.schedules:read Plugin-specific scopes
get_oncall_shift OnCall Get details for a specific OnCall shift grafana-oncall-app.schedules:read Plugin-specific scopes
get_current_oncall_users OnCall Get users currently on-call for a specific schedule grafana-oncall-app.schedules:read Plugin-specific scopes
list_oncall_teams OnCall List teams from Grafana OnCall grafana-oncall-app.user-settings:read Plugin-specific scopes
list_oncall_users OnCall List users from Grafana OnCall grafana-oncall-app.user-settings:read Plugin-specific scopes
list_alert_groups OnCall List alert groups from Grafana OnCall with filtering options grafana-oncall-app.alert-groups:read Plugin-specific scopes
get_alert_group OnCall Get a specific alert group from Grafana OnCall by its ID grafana-oncall-app.alert-groups:read Plugin-specific scopes
get_sift_investigation Sift Retrieve an existing Sift investigation by its UUID Viewer role N/A
get_sift_analysis Sift Retrieve a specific analysis from a Sift investigation Viewer role N/A
list_sift_investigations Sift Retrieve a list of Sift investigations with an optional limit Viewer role N/A
find_error_pattern_logs Sift Finds elevated error patterns in Loki logs. Editor role N/A
find_slow_requests Sift Finds slow requests from the relevant tempo datasources. Editor role N/A
list_pyroscope_label_names Pyroscope List label names matching a selector datasources:query datasources:uid:pyroscope-uid
list_pyroscope_label_values Pyroscope List label values matching a selector for a label name datasources:query datasources:uid:pyroscope-uid
list_pyroscope_profile_types Pyroscope List available profile types datasources:query datasources:uid:pyroscope-uid
query_pyroscope Pyroscope Query profiles, metrics, or both from Pyroscope datasources:query datasources:uid:pyroscope-uid
get_assertions Asserts Get assertion summary for a given entity Plugin-specific permissions Plugin-specific scopes
agento11y_manage_conversations Agent Observability* List, search, and fetch LLM conversations from Grafana Agent Observability grafana-agento11y-app.conversations:read N/A
agento11y_manage_generations Agent Observability* Fetch LLM generation details and evaluation scores from Grafana Agent Observability grafana-agento11y-app.data:read N/A
generate_deeplink Navigation Generate accurate deeplink URLs for Grafana resources None (read-only URL generation) N/A
get_annotations Annotations Fetch annotations with filters annotations:read annotations:* or annotations:id:123
create_annotation Annotations Create a new annotation (standard or Graphite format) annotations:write annotations:*
update_annotation Annotations Update specific fields of an annotation (partial update) annotations:write annotations:*
get_annotation_tags Annotations List annotation tags with optional filtering annotations:read annotations:*
list_snapshots Snapshot List dashboard snapshots with optional query and limit filters dashboards:read dashboards:* or dashboards:uid:abc123
get_snapshot Snapshot Get snapshot metadata and dashboard payload by snapshot key dashboards:read dashboards:* or dashboards:uid:abc123
create_snapshot Snapshot Create a dashboard snapshot from a full dashboard payload dashboards:write dashboards:* or dashboards:uid:abc123
delete_snapshot Snapshot Delete a dashboard snapshot by snapshot key dashboards:write dashboards:* or dashboards:uid:abc123
get_panel_image Rendering Render a stored dashboard or panel — or a provisioning preview from a repository branch — as a PNG image dashboards:read dashboards:uid:abc123
list_provisioning_repositories Provisioning List provisioning repositories (e.g. git-sync sources) with their source URL, branch, sync state, and health provisioning.repositories:read N/A
validate_provisioning_file Provisioning Dry-run-apply a file from a provisioning repository and report admission validation errors provisioning.repositories:read N/A

* Disabled by default. Add category to --enabled-tools to enable.

CLI Flags Reference

The mcp-grafana binary supports various command-line flags for configuration:

Transport Options:

  • -t, --transport: Transport type (stdio, sse, or streamable-http) - default: stdio
  • --address: The host and port for SSE/streamable-http server - default: localhost:8000
  • --base-path: Base path for the SSE/streamable-http server
  • --endpoint-path: Endpoint path for the streamable-http server - default: /

HTTP Transport Security (SSE / streamable-http only):

Host/Origin validation is enforced on every route on the listener — /sse, /mcp, /healthz, and /metrics — so a DNS-rebinding browser cannot reach any of them. Stdio transport is unaffected.

  • --allowed-hosts: Comma-separated allowlist of Host header values. Defaults to loopback variants of --address (e.g. localhost:8000,127.0.0.1:8000,[::1]:8000). A value that parses to empty (unset, ,, ,, etc.) also falls back to the defaults so a typo cannot silently disable the check. Requests with a Host header outside the allowlist are rejected with 403. Pass * to disable the check — only safe when running behind a trusted reverse proxy that rewrites Host, or in an isolated network. K8s httpGet probes and external /metrics scrapes will need either an explicit hostname in this list, *, or a tcpSocket probe / a separate metrics port (--metrics-address).
  • --allowed-origins: Comma-separated allowlist of Origin header values. Empty by default — any request that carries an Origin header is rejected (browsers always send one for cross-origin requests, and no browser should be calling this server directly). Set to an explicit list to permit browser-based clients, or * to disable the check.

Debug and Logging:

  • --debug: Enable debug mode for detailed HTTP request/response logging
  • --log-level: Log level (debug, info, warn, error) - default: info

Observability:

  • --metrics: Enable Prometheus metrics endpoint at /metrics
  • --metrics-address: Separate address for metrics server (e.g., :9090). If empty, metrics are served on the main server
  • --slow-request-threshold: Log an event when any MCP request (tool invocation, list, resource read, etc.) takes longer than this duration. Accepts Go duration strings (e.g., 500ms, 5s). Default 0 disables slow-request logging. See the Slow-request logging section.
  • --slow-request-log-level: Log level for slow-request events (info or warn) - default: warn.

Session Management:

  • --session-idle-timeout-minutes: Session idle timeout in minutes. Sessions with no activity for this duration are automatically reaped - default: 30. Set to 0 to disable session reaping. Only relevant for SSE and streamable-http transports.

Tool Configuration:

  • --enabled-tools: Comma-separated list of enabled categories - default: all categories except admin, agento11y, athena, clickhouse, cloudwatch, elasticsearch, examples, graphite, quickwit, runpanelquery, and snowflake. To enable disabled categories, add them to the list (e.g., "search,datasource,...,snowflake")
  • --max-loki-log-limit: Maximum number of log lines returned per query_loki_logs call - default: 100. Note: Set this at least 1 below Loki's server-side max_entries_limit_per_query to allow truncation detection (the tool requests limit+1 internally to detect if more data exists).
  • --disable-search: Disable search tools
  • --disable-datasource: Disable datasource tools
  • --disable-incident: Disable incident tools
  • --disable-prometheus: Disable prometheus tools
  • --disable-write: Disable write tools (create/update operations)
  • --disable-loki: Disable loki tools
  • --disable-elasticsearch: Disable elasticsearch and opensearch tools
  • --disable-quickwit: Disable quickwit tools
  • --disable-influxdb: Disable InfluxDB tools
  • --disable-alerting: Disable alerting tools
  • --disable-dashboard: Disable dashboard tools
  • --disable-oncall: Disable oncall tools
  • --disable-asserts: Disable asserts tools
  • --disable-sift: Disable sift tools
  • --disable-admin: Disable admin tools
  • --disable-pyroscope: Disable pyroscope tools
  • --disable-navigation: Disable navigation tools
  • --disable-rendering: Disable rendering tools (panel/dashboard image export)
  • --disable-snapshot: Disable snapshot tools
  • --disable-cloudwatch: Disable CloudWatch tools
  • --disable-examples: Disable query examples tools
  • --disable-clickhouse: Disable ClickHouse tools
  • --disable-snowflake: Disable Snowflake tools
  • --disable-runpanelquery: Disable run panel query tools
  • --disable-graphite: Disable Graphite tools
  • --disable-athena: Disable Athena tools
  • --disable-provisioning: Disable provisioning tools
  • --disable-agento11y: Disable Agent Observability tools
Read-Only Mode

The --disable-write flag provides a way to run the MCP server in read-only mode, preventing any write operations to your Grafana instance. This is useful for scenarios where you want to provide safe, read-only access such as:

  • Using service accounts with limited read-only permissions
  • Providing AI assistants with observability data without modification capabilities
  • Running in production environments where write access should be restricted
  • Testing and development scenarios where you want to prevent accidental modifications

When --disable-write is enabled, the following write operations are disabled:

Dashboard Tools:

  • update_dashboard

Folder Tools:

  • create_folder

Incident Tools:

  • create_incident
  • add_activity_to_incident

Alerting Tools:

  • alerting_manage_rules (create, update, delete operations)

Annotation Tools:

  • create_annotation
  • update_annotation

Sift Tools:

  • find_error_pattern_logs (creates investigations)
  • find_slow_requests (creates investigations)

Snapshot Tools:

  • create_snapshot
  • delete_snapshot

All read operations remain available, allowing you to query dashboards, run PromQL/LogQL queries, list resources, and retrieve data.

Client TLS Configuration (for Grafana connections):

  • --tls-cert-file: Path to TLS certificate file for client authentication
  • --tls-key-file: Path to TLS private key file for client authentication
  • --tls-ca-file: Path to TLS CA certificate file for server verification
  • --tls-skip-verify: Skip TLS certificate verification (insecure)

Server TLS Configuration (streamable-http transport only):

  • --server.tls-cert-file: Path to TLS certificate file for server HTTPS
  • --server.tls-key-file: Path to TLS private key file for server HTTPS

Usage

This MCP server works with both local Grafana instances and Grafana Cloud. For Grafana Cloud, use your instance URL (e.g., https://myinstance.grafana.net) instead of http://localhost:3000 in the configuration examples below.

  1. If using service account token authentication, create a service account in Grafana with enough permissions to use the tools you want to use, generate a service account token, and copy it to the clipboard for use in the configuration file. Follow the Grafana service account documentation for details on creating service account tokens. Tip: If you're not comfortable configuring fine-grained RBAC scopes, a simpler (but less restrictive) option is to assign the built-in Editor role to the service account. This grants broad read/write access that covers most MCP server operations — use it when convenience outweighs strict least-privilege requirements.

    Note: The environment variable GRAFANA_API_KEY is deprecated and will be removed in a future version. Please migrate to using GRAFANA_SERVICE_ACCOUNT_TOKEN instead. The old variable name will continue to work for backward compatibility but will show deprecation warnings.

Reading the service account token from a file

Instead of passing the token inline via GRAFANA_SERVICE_ACCOUNT_TOKEN, you can point GRAFANA_SERVICE_ACCOUNT_TOKEN_FILE at a file path that contains the token. The file is read fresh on every request, so rotated tokens are picked up automatically without restarting the server.

This is particularly useful in Kubernetes, where a Secret mounted as a volume is updated in place when the underlying Secret changes (typically within ~1 minute). Combined with the per-request client cache — which is keyed on the token value — a rotated token transparently produces a new client with no pod restart and no downtime:

env:
  - name: GRAFANA_SERVICE_ACCOUNT_TOKEN_FILE
    value: /var/run/secrets/grafana/token
volumeMounts:
  - name: grafana-token
    mountPath: /var/run/secrets/grafana
    readOnly: true
volumes:
  - name: grafana-token
    secret:
      secretName: grafana-mcp-token

Surrounding whitespace (including a trailing newline) is trimmed from the file contents. If both GRAFANA_SERVICE_ACCOUNT_TOKEN and GRAFANA_SERVICE_ACCOUNT_TOKEN_FILE are set, the inline token takes precedence.

Multi-Organization Support

You can specify which organization to interact with using either:

  • Environment variable: Set GRAFANA_ORG_ID to the numeric organization ID
  • HTTP header: Set X-Grafana-Org-Id when using SSE or streamable HTTP transports (header takes precedence over environment variable - meaning you can set a default org as well).

When an organization ID is provided, the MCP server will set the X-Grafana-Org-Id header on all requests to Grafana, ensuring that operations are performed within the specified organization context.

Example with organization ID:

{
  "mcpServers": {
    "grafana": {
      "command": "mcp-grafana",
      "args": [],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",
        "GRAFANA_USERNAME": "<your username>",
        "GRAFANA_PASSWORD": "<your password>",
        "GRAFANA_ORG_ID": "2"
      }
    }
  }
}
Custom HTTP Headers

You can add arbitrary HTTP headers to all Grafana API requests using the GRAFANA_EXTRA_HEADERS environment variable. The value should be a JSON object mapping header names to values.

Example with custom headers:

{
  "mcpServers": {
    "grafana": {
      "command": "mcp-grafana",
      "args": [],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your token>",
        "GRAFANA_EXTRA_HEADERS": "{\"X-Custom-Header\": \"custom-value\", \"X-Tenant-ID\": \"tenant-123\"}"
      }
    }
  }
}
Forwarding Headers from the Client (SSE/Streamable-HTTP Only)

When the MCP server runs behind a gateway or reverse proxy that handles SSO (e.g. an AWS ALB with OIDC), each user's session cookie must reach Grafana so it can associate the request with the authenticated user. The GRAFANA_FORWARD_HEADERS environment variable enables this by specifying a comma-separated allowlist of header names to copy from the incoming HTTP request to every outbound Grafana API request.

This only applies when using SSE (-t sse) or streamable-http (-t streamable-http) transports. It has no effect in stdio mode.

Example: forward the session cookie

{
  "env": {
    "GRAFANA_URL": "https://grafana.internal",
    "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your token>",
    "GRAFANA_FORWARD_HEADERS": "Cookie"
  }
}

You can forward multiple headers by separating them with commas:

GRAFANA_FORWARD_HEADERS=Cookie,X-Session-Id

Forwarded headers are merged with any headers defined in GRAFANA_EXTRA_HEADERS. If a header name appears in both, the value from the incoming request takes precedence for that request.

  1. You have several options to install mcp-grafana:

    • uvx (recommended): If you have uv installed, no extra setup is needed — uvx will automatically download and run the server:

      uvx mcp-grafana
      
    • Docker image: Use the pre-built Docker image from Docker Hub.

      Important: The Docker image's entrypoint is configured to run the MCP server in SSE mode by default, but most users will want to use STDIO mode for direct integration with AI assistants like Claude Desktop:

      1. STDIO Mode: For stdio mode you must explicitly override the default with -t stdio and include the -i flag to keep stdin open:
      docker pull grafana/mcp-grafana
      # For local Grafana:
      docker run --rm -i -e GRAFANA_URL=http://localhost:3000 -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> grafana/mcp-grafana -t stdio
      # For Grafana Cloud:
      docker run --rm -i -e GRAFANA_URL=https://myinstance.grafana.net -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> grafana/mcp-grafana -t stdio
      
      1. SSE Mode: In this mode, the server runs as an HTTP server that clients connect to. You must expose port 8000 using the -p flag:
      docker pull grafana/mcp-grafana
      docker run --rm -p 8000:8000 -e GRAFANA_URL=http://localhost:3000 -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> grafana/mcp-grafana
      
      1. Streamable HTTP Mode: In this mode, the server operates as an independent process that can handle multiple client connections. You must expose port 8000 using the -p flag: For this mode you must explicitly override the default with -t streamable-http
      docker pull grafana/mcp-grafana
      docker run --rm -p 8000:8000 -e GRAFANA_URL=http://localhost:3000 -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> grafana/mcp-grafana -t streamable-http
      

      For HTTPS streamable HTTP mode with server TLS certificates:

      docker pull grafana/mcp-grafana
      docker run --rm -p 8443:8443 \
        -v /path/to/certs:/certs:ro \
        -e GRAFANA_URL=http://localhost:3000 \
        -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> \
        grafana/mcp-grafana \
        -t streamable-http \
        -addr :8443 \
        --server.tls-cert-file /certs/server.crt \
        --server.tls-key-file /certs/server.key
      
    • Download binary: Download the latest release of mcp-grafana from the releases page and place it in your $PATH.

    • Build from source: If you have a Go toolchain installed you can also build and install it from source, using the GOBIN environment variable to specify the directory where the binary should be installed. This should also be in your $PATH.

      GOBIN="$HOME/go/bin" go install github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest
      
    • Deploy to Kubernetes using Helm: use the Helm chart from the Grafana helm-charts repository

      helm repo add grafana https://grafana.github.io/helm-charts
      helm install --set grafana.apiKey=<Grafana_ApiKey> --set grafana.url=<GrafanaUrl> my-release grafana/grafana-mcp
      
  2. Add the server configuration to your client configuration file. For example, for Claude Desktop:

    If using uvx:

    {
      "mcpServers": {
        "grafana": {
          "command": "uvx",
          "args": ["mcp-grafana"],
          "env": {
            "GRAFANA_URL": "http://localhost:3000",
            "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
          }
        }
      }
    }
    

    If using the binary:

    {
      "mcpServers": {
        "grafana": {
          "command": "mcp-grafana",
          "args": [],
          "env": {
            "GRAFANA_URL": "http://localhost:3000",  // Or "https://myinstance.grafana.net" for Grafana Cloud
            "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>",
            // If using username/password authentication
            "GRAFANA_USERNAME": "<your username>",
            "GRAFANA_PASSWORD": "<your password>",
            // Optional: specify organization ID for multi-org support
            "GRAFANA_ORG_ID": "1"
          }
        }
      }
    }
    

Note: if you see Error: spawn mcp-grafana ENOENT in Claude Desktop, you need to specify the full path to mcp-grafana.

If using Docker:

{
  "mcpServers": {
    "grafana": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "GRAFANA_URL",
        "-e",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN",
        "grafana/mcp-grafana",
        "-t",
        "stdio"
      ],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",  // Or "https://myinstance.grafana.net" for Grafana Cloud
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>",
        // If using username/password authentication
        "GRAFANA_USERNAME": "<your username>",
        "GRAFANA_PASSWORD": "<your password>",
        // Optional: specify organization ID for multi-org support
        "GRAFANA_ORG_ID": "1"
      }
    }
  }
}

Note: The -t stdio argument is essential here because it overrides the default SSE mode in the Docker image.

Using VSCode with remote MCP server

If you're using VSCode and running the MCP server in SSE mode (which is the default when using the Docker image without overriding the transport), make sure your .vscode/settings.json includes the following:

"mcp": {
  "servers": {
    "grafana": {
      "type": "sse",
      "url": "http://localhost:8000/sse"
    }
  }
}

For HTTPS streamable HTTP mode with server TLS certificates:

"mcp": {
  "servers": {
    "grafana": {
      "type": "sse",
      "url": "https://localhost:8443/sse"
    }
  }
}
Debug Mode

You can enable debug mode for the Grafana transport by adding the -debug flag to the command. This will provide detailed logging of HTTP requests and responses between the MCP server and the Grafana API, which can be helpful for troubleshooting.

To use debug mode with the Claude Desktop configuration, update your config as follows:

If using the binary:

{
  "mcpServers": {
    "grafana": {
      "command": "mcp-grafana",
      "args": ["-debug"],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",  // Or "https://myinstance.grafana.net" for Grafana Cloud
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
      }
    }
  }
}

If using Docker:

{
  "mcpServers": {
    "grafana": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-e",
        "GRAFANA_URL",
        "-e",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN",
        "grafana/mcp-grafana",
        "-t",
        "stdio",
        "-debug"
      ],
      "env": {
        "GRAFANA_URL": "http://localhost:3000",  // Or "https://myinstance.grafana.net" for Grafana Cloud
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
      }
    }
  }
}

Note: As with the standard configuration, the -t stdio argument is required to override the default SSE mode in the Docker image.

TLS Configuration

If your Grafana instance is behind mTLS or requires custom TLS certificates, you can configure the MCP server to use custom certificates. The server supports the following TLS configuration options:

  • --tls-cert-file: Path to TLS certificate file for client authentication
  • --tls-key-file: Path to TLS private key file for client authentication
  • --tls-ca-file: Path to TLS CA certificate file for server verification
  • --tls-skip-verify: Skip TLS certificate verification (insecure, use only for testing)

Example with client certificate authentication:

{
  "mcpServers": {
    "grafana": {
      "command": "mcp-grafana",
      "args": [
        "--tls-cert-file",
        "/path/to/client.crt",
        "--tls-key-file",
        "/path/to/client.key",
        "--tls-ca-file",
        "/path/to/ca.crt"
      ],
      "env": {
        "GRAFANA_URL": "https://secure-grafana.example.com",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
      }
    }
  }
}

Example with Docker:

{
  "mcpServers": {
    "grafana": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-v",
        "/path/to/certs:/certs:ro",
        "-e",
        "GRAFANA_URL",
        "-e",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN",
        "grafana/mcp-grafana",
        "-t",
        "stdio",
        "--tls-cert-file",
        "/certs/client.crt",
        "--tls-key-file",
        "/certs/client.key",
        "--tls-ca-file",
        "/certs/ca.crt"
      ],
      "env": {
        "GRAFANA_URL": "https://secure-grafana.example.com",
        "GRAFANA_SERVICE_ACCOUNT_TOKEN": "<your service account token>"
      }
    }
  }
}

The TLS configuration is applied to all HTTP clients used by the MCP server, including:

  • The main Grafana OpenAPI client
  • Prometheus datasource clients
  • Loki datasource clients
  • Incident management clients
  • Sift investigation clients
  • Alerting clients
  • Asserts clients

Direct CLI Usage Examples:

For testing with self-signed certificates:

./mcp-grafana --tls-skip-verify -debug

With client certificate authentication:

./mcp-grafana \
  --tls-cert-file /path/to/client.crt \
  --tls-key-file /path/to/client.key \
  --tls-ca-file /path/to/ca.crt \
  -debug

With custom CA certificate only:

./mcp-grafana --tls-ca-file /path/to/ca.crt

Programmatic Usage:

If you're using this library programmatically, you can also create TLS-enabled context functions:

// Using struct literals
tlsConfig := &mcpgrafana.TLSConfig{
    CertFile: "/path/to/client.crt",
    KeyFile:  "/path/to/client.key",
    CAFile:   "/path/to/ca.crt",
}
grafanaConfig := mcpgrafana.GrafanaConfig{
    Debug:     true,
    TLSConfig: tlsConfig,
}
contextFunc := mcpgrafana.ComposedStdioContextFunc(grafanaConfig)

// Or inline
grafanaConfig := mcpgrafana.GrafanaConfig{
    Debug: true,
    TLSConfig: &mcpgrafana.TLSConfig{
        CertFile: "/path/to/client.crt",
        KeyFile:  "/path/to/client.key",
        CAFile:   "/path/to/ca.crt",
    },
}
contextFunc := mcpgrafana.ComposedStdioContextFunc(grafanaConfig)

URL validation when wiring your own HTTP server:

When library consumers wire mcp-grafana's context functions into their own http.Server, install ValidateGrafanaURLMiddleware to reject malformed X-Grafana-URL headers with 400 Bad Request (matching the binary's behavior):

mux.Handle(path, mcpgrafana.ValidateGrafanaURLMiddleware(yourMCPHandler))

When calling NewGrafanaClient directly (stdio or programmatic construction), pre-validate untrusted URLs to avoid a reachable panic:

if err := mcpgrafana.ValidateGrafanaURL(urlFromHeader); err != nil {
    http.Error(w, err.Error(), http.StatusBadRequest)
    return
}
client := mcpgrafana.NewGrafanaClient(ctx, urlFromHeader, apiKey, nil)

Both patterns share ValidateGrafanaURL as the single validator.

Server TLS Configuration (Streamable HTTP Transport Only)

When using the streamable HTTP transport (-t streamable-http), you can configure the MCP server to serve HTTPS instead of HTTP. This is useful when you need to secure the connection between your MCP client and the server itself.

The server supports the following TLS configuration options for the streamable HTTP transport:

  • --server.tls-cert-file: Path to TLS certificate file for server HTTPS (required for TLS)
  • --server.tls-key-file: Path to TLS private key file for server HTTPS (required for TLS)

Note: These flags are completely separate from the client TLS flags documented above. The client TLS flags configure how the MCP server connects to Grafana, while these server TLS flags configure how clients connect to the MCP server when using streamable HTTP transport.

Example with HTTPS streamable HTTP server:

./mcp-grafana \
  -t streamable-http \
  --server.tls-cert-file /path/to/server.crt \
  --server.tls-key-file /path/to/server.key \
  -addr :8443

This would start the MCP server on HTTPS port 8443. Clients would then connect to https://localhost:8443/ instead of http://localhost:8000/.

Docker example with server TLS:

docker run --rm -p 8443:8443 \
  -v /path/to/certs:/certs:ro \
  -e GRAFANA_URL=http://localhost:3000 \
  -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your service account token> \
  grafana/mcp-grafana \
  -t streamable-http \
  -addr :8443 \
  --server.tls-cert-file /certs/server.crt \
  --server.tls-key-file /certs/server.key
Health Check Endpoint

When using the SSE (-t sse) or streamable HTTP (-t streamable-http) transports, the MCP server exposes a health check endpoint at /healthz. This endpoint can be used by load balancers, monitoring systems, or orchestration platforms to verify that the server is running and accepting connections.

Endpoint: GET /healthz

Response:

  • Status Code: 200 OK
  • Body: ok

Example usage:

# For streamable HTTP or SSE transport on default port
curl http://localhost:8000/healthz

# With custom address
curl http://localhost:9090/healthz

Note: The health check endpoint is only available when using SSE or streamable HTTP transports. It is not available when using the stdio transport (-t stdio), as stdio does not expose an HTTP server.

Observability

The MCP server supports Prometheus metrics, OpenTelemetry distributed tracing, and OpenTelemetry log export, following the OTel MCP semantic conventions. Tracing and log export are configured via standard OTEL_* environment variables and work with any transport.

Note: mcp-grafana currently only supports the OTLP/gRPC transport for both traces and logs. OTEL_EXPORTER_OTLP_PROTOCOL (and its _TRACES_PROTOCOL / _LOGS_PROTOCOL variants) are not honored — gRPC is used regardless.

Metrics

When using the SSE or streamable HTTP transports, enable Prometheus metrics with the --metrics flag:

# Metrics served on the main server at /metrics
./mcp-grafana -t streamable-http --metrics

# Metrics served on a separate address
./mcp-grafana -t streamable-http --metrics --metrics-address :9090

Available Metrics:

Metric Type Description
mcp_server_operation_duration_seconds Histogram Duration of MCP operations (labels: mcp_method_name, gen_ai_tool_name, error_type, network_transport, mcp_protocol_version)
mcp_server_session_duration_seconds Histogram Duration of MCP client sessions (labels: network_transport, mcp_protocol_version)
http_server_request_duration_seconds Histogram Duration of HTTP server requests (from otelhttp)

Note: Metrics are only available when using SSE or streamable HTTP transports. They are not available with the stdio transport.

Slow-request logging

The --slow-request-threshold flag emits a structured log event whenever an MCP request (tool invocation, list, resource read, etc.) exceeds the given duration. It is useful for diagnosing slow queries and tool calls without drowning in the full debug log.

# Warn on any request slower than 500ms (works on all transports)
./mcp-grafana -t streamable-http --slow-request-threshold 500ms

# Same thing on stdio (the feature is transport-agnostic, unlike --metrics)
./mcp-grafana -t stdio --slow-request-threshold 500ms

# Log at INFO level instead of WARN (useful during investigation)
./mcp-grafana -t streamable-http --slow-request-threshold 500ms --slow-request-log-level info

The log event carries these structured attributes:

Attribute Description
mcp.method The MCP method (e.g., tools/call, tools/list, resources/read)
duration Observed request duration
threshold Configured threshold
tool Tool name (only present for tools/call methods)
error Error value, when the request failed (best-effort context; content is controlled by upstream error wrapping)
error.type Bounded-cardinality error classification (_OTHER for untyped errors)

Slow-request logging works on all transports (including stdio) and does not require --metrics. The default threshold of 0 disables it entirely. Proxied tools flow through tools/call and are covered automatically.

Tracing

Distributed tracing is configured via standard OTEL_* environment variables and works independently of the --metrics flag. When OTEL_EXPORTER_OTLP_ENDPOINT (or the signal-specific OTEL_EXPORTER_OTLP_TRACES_ENDPOINT) is set, the server exports traces via OTLP/gRPC:

# Send traces to a local Tempo instance
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \
OTEL_EXPORTER_OTLP_INSECURE=true \
./mcp-grafana -t streamable-http

# Send traces to Grafana Cloud with authentication
OTEL_EXPORTER_OTLP_ENDPOINT=https://tempo-us-central1.grafana.net:443 \
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic ..." \
./mcp-grafana -t streamable-http

Tool call spans follow semconv naming (tools/call <tool_name>) and include attributes like gen_ai.tool.name, mcp.method.name, and mcp.session.id. The server also supports W3C trace context propagation from the _meta field of tool call requests.

Logs

When OTEL_EXPORTER_OTLP_ENDPOINT (or the signal-specific OTEL_EXPORTER_OTLP_LOGS_ENDPOINT) is set, the server also exports structured logs via OTLP/gRPC in addition to the existing plain-text stderr output. The otelslog bridge automatically attaches trace_id and span_id from the active span, so log records correlate with the traces the server already emits.

Traces and logs resolve their endpoints independently, so the two signals can be enabled separately: setting only OTEL_EXPORTER_OTLP_TRACES_ENDPOINT enables tracing without log export, setting only OTEL_EXPORTER_OTLP_LOGS_ENDPOINT enables log export without tracing, and the generic OTEL_EXPORTER_OTLP_ENDPOINT enables both.

Stderr logging is unchanged when OTLP logging is enabled; you can continue to rely on container logs or pipe stderr to /dev/null if you prefer.

# Send both logs and traces to a local OTel collector
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317 \
OTEL_EXPORTER_OTLP_INSECURE=true \
./mcp-grafana -t streamable-http

The transport is OTLP/gRPC (default port 4317). Logs can be sent directly to any managed backend that accepts OTLP/gRPC — for example, Grafana Cloud — by pointing OTEL_EXPORTER_OTLP_LOGS_ENDPOINT (or the generic OTEL_EXPORTER_OTLP_ENDPOINT) at the remote gRPC endpoint and supplying auth via OTEL_EXPORTER_OTLP_LOGS_HEADERS (or OTEL_EXPORTER_OTLP_HEADERS), mirroring the tracing example above. A local OTel collector is optional — useful for fan-out, batching, or multi-backend routing, but not required.

The signal-specific variants OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, OTEL_EXPORTER_OTLP_LOGS_HEADERS, OTEL_EXPORTER_OTLP_LOGS_INSECURE, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, and OTEL_EXPORTER_OTLP_LOGS_COMPRESSION are honored and override their generic OTEL_EXPORTER_OTLP_* counterparts — see the OTel exporter spec for the full list and precedence rules.

If the configured collector is unreachable, log records are buffered in memory (default queue: 2048) and the oldest records are dropped once the queue fills. The process continues without blocking the service. Configure a local OTel collector if you need lossless buffering during outages.

Logs are also exported under the stdio transport, which makes it easy to centralize logs from local mcp-grafana instances invoked by IDE clients.

Docker example with metrics, tracing, and logs:

docker run --rm -p 8000:8000 \
  -e GRAFANA_URL=http://localhost:3000 \
  -e GRAFANA_SERVICE_ACCOUNT_TOKEN=<your token> \
  -e OTEL_EXPORTER_OTLP_ENDPOINT=http://tempo:4317 \
  -e OTEL_EXPORTER_OTLP_INSECURE=true \
  grafana/mcp-grafana \
  -t streamable-http --metrics

Troubleshooting

Grafana Version Compatibility

If you encounter the following error when using datasource-related tools:

get datasource by uid : [GET /datasources/uid/{uid}][400] getDataSourceByUidBadRequest {"message":"id is invalid"}

This typically indicates that you are using a Grafana version earlier than 9.0. The /datasources/uid/{uid} API endpoint was introduced in Grafana 9.0, and datasource operations will fail on earlier versions.

Solution: Upgrade your Grafana instance to version 9.0 or later to resolve this issue.

Development

Contributions are welcome! Please open an issue or submit a pull request if you have any suggestions or improvements.

This project is written in Go. Install Go following the instructions for your platform.

To run the server locally in STDIO mode (which is the default for local development), use:

make run

To run the server locally in SSE mode, use:

go run ./cmd/mcp-grafana --transport sse

You can also run the server using the SSE transport inside a custom built Docker image. Just like the published Docker image, this custom image's entrypoint defaults to SSE mode. To build the image, use:

make build-image

And to run the image in SSE mode (the default), use:

docker run -it --rm -p 8000:8000 mcp-grafana:latest

If you need to run it in STDIO mode instead, override the transport setting:

docker run -it --rm mcp-grafana:latest -t stdio
Testing

There are three types of tests available:

  1. Unit Tests (no external dependencies required):
make test-unit

You can also run unit tests with:

make test
  1. Integration Tests (requires docker containers to be up and running):
make test-integration
  1. Cloud Tests (requires cloud Grafana instance and credentials):
make test-cloud

Note: Cloud tests are automatically configured in CI. For local development, you'll need to set up your own Grafana Cloud instance and credentials.

More comprehensive integration tests will require a Grafana instance to be running locally on port 3000; you can start one with Docker Compose:

docker-compose up -d

The integration tests can be run with:

make test-all

If you're adding more tools, please add integration tests for them. The existing tests should be a good starting point.

Linting

To lint the code, run:

make lint

This includes a custom linter that checks for unescaped commas in jsonschema struct tags. The commas in description fields must be escaped with \\, to prevent silent truncation. You can run just this linter with:

make lint-jsonschema

See the JSONSchema Linter documentation for more details.

License

This project is licensed under the Apache License, Version 2.0.

Documentation

Index

Constants

View Source
const (
	PanelViewerResourceURI = "ui://mcp-grafana/panel-viewer.html"

	// UIContentKindDeeplink is the `_meta.ui.kind` value for a Grafana deeplink.
	UIContentKindDeeplink = "deeplink"
)
View Source
const (
	// DefaultGrafanaClientTimeout is the default timeout for Grafana HTTP client requests.
	DefaultGrafanaClientTimeout = 10 * time.Second
)
View Source
const (
	// DefaultSessionTTL is the default time-to-live for idle sessions.
	// Sessions with no activity for this duration are reaped.
	DefaultSessionTTL = 30 * time.Minute
)

Variables

View Source
var ErrInvalidGrafanaURL = errors.New("invalid Grafana URL")

ErrInvalidGrafanaURL is returned (wrapped) by ValidateGrafanaURL when the input is not an absolute HTTP or HTTPS URL with a non-empty host. Detect with errors.Is.

The nav.go guard in tools/navigation.go:generateDeeplink wraps this sentinel when config.URL is malformed (e.g. coming from a bad /api/frontend/settings appUrl response), distinguishing that case from missing-URL cases for callers that discriminate via errors.Is.

View Source
var ExtractGrafanaClientFromEnv server.StdioContextFunc = func(ctx context.Context) context.Context {

	logger := LoggerFromContext(ctx)
	grafanaURL, apiKey := urlAndAPIKeyFromEnv(logger)
	if grafanaURL == "" {
		grafanaURL = defaultGrafanaURL
	}
	auth := userAndPassFromEnv()
	grafanaClient := NewGrafanaClient(ctx, grafanaURL, apiKey, auth)
	return WithGrafanaClient(ctx, grafanaClient)
}

ExtractGrafanaClientFromEnv is a StdioContextFunc that creates and injects a Grafana client into the context. It uses configuration from GRAFANA_URL, GRAFANA_SERVICE_ACCOUNT_TOKEN (or deprecated GRAFANA_API_KEY), GRAFANA_USERNAME/PASSWORD environment variables to initialize the client with proper authentication.

View Source
var ExtractGrafanaClientFromHeaders httpContextFunc = func(ctx context.Context, req *http.Request) context.Context {
	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()
	if config.OrgID == 0 {
		logger.Warn("No org ID found in request headers or environment variables, using default org. Set GRAFANA_ORG_ID or pass X-Grafana-Org-Id header to target a specific org.")
	}

	u, apiKey, basicAuth, _, _ := extractKeyGrafanaInfoFromReq(req, logger)
	logger.Debug("Creating Grafana client", "url", u, "api_key_set", apiKey != "", "basic_auth_set", basicAuth != nil)

	grafanaClient := NewGrafanaClient(ctx, u, apiKey, basicAuth)
	return WithGrafanaClient(ctx, grafanaClient)
}

ExtractGrafanaClientFromHeaders is a HTTPContextFunc that creates and injects a Grafana client into the context. It prioritizes configuration from HTTP headers (X-Grafana-URL, X-Grafana-API-Key) over environment variables for multi-tenant scenarios.

View Source
var ExtractGrafanaInfoFromEnv server.StdioContextFunc = func(ctx context.Context) context.Context {

	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()

	u, apiKey, basicAuth, orgID := extractKeyGrafanaInfoFromEnv(logger)
	parsedURL, err := url.Parse(u)
	if err != nil {
		panic(fmt.Errorf("invalid Grafana URL %s: %w", u, err))
	}

	extraHeaders := extraHeadersFromEnv(logger)

	logger.Info("Using Grafana configuration", "url", parsedURL.Redacted(), "api_key_set", apiKey != "", "basic_auth_set", basicAuth != nil, "org_id", orgID, "extra_headers_count", len(extraHeaders))
	config.URL = u
	config.APIKey = apiKey
	config.BasicAuth = basicAuth
	config.OrgID = orgID
	config.ExtraHeaders = extraHeaders
	return WithGrafanaConfig(ctx, config)
}

ExtractGrafanaInfoFromEnv is a StdioContextFunc that extracts Grafana configuration from environment variables. It reads GRAFANA_URL and GRAFANA_SERVICE_ACCOUNT_TOKEN (or deprecated GRAFANA_API_KEY) environment variables and adds the configuration to the context for use by Grafana clients.

View Source
var ExtractGrafanaInfoFromHeaders httpContextFunc = func(ctx context.Context, req *http.Request) context.Context {

	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()

	u, apiKey, basicAuth, orgID, envCredsAllowed := extractKeyGrafanaInfoFromReq(req, logger)

	config.URL = u
	config.APIKey = apiKey
	config.BasicAuth = basicAuth
	config.OrgID = orgID

	// Environment extra headers may carry credentials (e.g. an Authorization
	// header), so they are bound to the environment-configured URL just like the
	// service-account token. When a request targets a foreign URL, only headers
	// the operator explicitly opted to forward (GRAFANA_FORWARD_HEADERS) are sent.
	var envHeaders map[string]string
	if envCredsAllowed {
		envHeaders = extraHeadersFromEnv(logger)
	}
	config.ExtraHeaders = mergeHeaders(envHeaders, forwardedHeadersFromRequest(req))
	return WithGrafanaConfig(ctx, config)
}

ExtractGrafanaInfoFromHeaders is a HTTPContextFunc that extracts Grafana configuration from HTTP request headers. It reads X-Grafana-URL and X-Grafana-API-Key headers, falling back to environment variables if headers are not present. Headers listed in GRAFANA_FORWARD_HEADERS are copied from the incoming request and merged with GRAFANA_EXTRA_HEADERS.

View Source
var ExtractIncidentClientFromEnv server.StdioContextFunc = func(ctx context.Context) context.Context {
	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()
	grafanaURL, apiKey := urlAndAPIKeyFromEnv(logger)
	if grafanaURL == "" {
		grafanaURL = defaultGrafanaURL
	}
	incidentURL := fmt.Sprintf("%s/api/plugins/grafana-irm-app/resources/api/v1/", grafanaURL)
	parsedURL, err := url.Parse(incidentURL)
	if err != nil {
		panic(fmt.Errorf("invalid incident URL %s: %w", incidentURL, err))
	}
	logger.Debug("Creating Incident client", "url", parsedURL.Redacted(), "api_key_set", apiKey != "")
	client := incident.NewClient(incidentURL, apiKey)

	transport, err := BuildTransport(&config, nil, WithoutAuth())
	if err != nil {
		logger.Error("Failed to create custom transport for incident client, using default", "error", err)
	} else {
		client.HTTPClient.Transport = transport
	}

	return context.WithValue(ctx, incidentClientKey{}, client)
}

ExtractIncidentClientFromEnv is a StdioContextFunc that creates and injects a Grafana Incident client into the context. It configures the client using environment variables and applies any custom TLS settings from the context.

View Source
var ExtractIncidentClientFromHeaders httpContextFunc = func(ctx context.Context, req *http.Request) context.Context {
	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()
	grafanaURL, apiKey, _, orgID, _ := extractKeyGrafanaInfoFromReq(req, logger)
	incidentURL := fmt.Sprintf("%s/api/plugins/grafana-irm-app/resources/api/v1/", grafanaURL)
	client := incident.NewClient(incidentURL, apiKey)

	config.OrgID = orgID
	transport, err := BuildTransport(&config, nil, WithoutAuth())
	if err != nil {
		logger.Error("Failed to create custom transport for incident client, using default", "error", err)
	} else {
		client.HTTPClient.Transport = transport
	}

	return context.WithValue(ctx, incidentClientKey{}, client)
}

ExtractIncidentClientFromHeaders is a HTTPContextFunc that creates and injects a Grafana Incident client into the context. It uses HTTP headers for configuration with environment variable fallbacks, enabling per-request incident management configuration.

View Source
var ExtractKubernetesClientFromEnv server.StdioContextFunc = func(ctx context.Context) context.Context {
	logger := LoggerFromContext(ctx)
	client, err := NewKubernetesClient(ctx)
	if err != nil {
		logger.Warn("Failed to create Kubernetes client; k8s APIs will be unavailable", "error", err)
		return WithKubernetesClient(ctx, nil)
	}
	return WithKubernetesClient(ctx, client)
}

ExtractKubernetesClientFromEnv is a StdioContextFunc that creates and injects a Kubernetes-style API client into the context, used by tools that talk to Grafana's app-platform APIs (e.g. dashboard.grafana.app). On failure it injects a nil client; callers fall back to the legacy API.

View Source
var ExtractKubernetesClientFromHeaders httpContextFunc = func(ctx context.Context, req *http.Request) context.Context {
	config := GrafanaConfigFromContext(ctx)
	logger := config.LoggerOrDefault()
	client, err := NewKubernetesClient(ctx)
	if err != nil {
		logger.Warn("Failed to create Kubernetes client; k8s APIs will be unavailable", "error", err)
		return WithKubernetesClient(ctx, nil)
	}
	return WithKubernetesClient(ctx, client)
}

ExtractKubernetesClientFromHeaders is a HTTPContextFunc that creates and injects a Kubernetes-style API client into the context for HTTP/SSE transports.

View Source
var Version = sync.OnceValue(func() string {
	if version != "" {
		return version
	}
	if bi, ok := debug.ReadBuildInfo(); ok && bi.Main.Version != "" {
		return bi.Main.Version
	}
	return "(devel)"
})

Version returns the version of the mcp-grafana binary. It prefers an ldflags-injected value, then falls back to runtime/debug build info, and finally returns "(devel)" for local development builds.

Functions

func BuildTransport added in v0.10.0

func BuildTransport(cfg *GrafanaConfig, base http.RoundTripper, opts ...TransportOption) (http.RoundTripper, error)

BuildTransport constructs an http.RoundTripper with the standard middleware chain derived from cfg. The default chain (innermost to outermost) is:

base → TLS → debugLogging → Auth → ExtraHeaders → OrgID → UserAgent → otelhttp

Auth is innermost among the header-setting layers so that credentials take precedence over any forwarded/extra headers with the same keys.

When cfg.Debug is true a debug-logging layer is added just above the base transport. It sees the fully-decorated request (all headers set by outer layers) and redacts sensitive values (Authorization, X-Access-Token, etc.) before writing request/response details to the logger.

Individual layers can be disabled with WithoutAuth, WithoutOrgID, etc.

func ComposeHTTPContextFuncs added in v0.4.0

func ComposeHTTPContextFuncs(funcs ...httpContextFunc) server.HTTPContextFunc

ComposeHTTPContextFuncs composes multiple HTTPContextFuncs into a single one. This enables chaining of context modifications for HTTP transport, allowing modular setup of authentication, clients, and configuration.

func ComposeSSEContextFuncs

func ComposeSSEContextFuncs(funcs ...httpContextFunc) server.SSEContextFunc

ComposeSSEContextFuncs composes multiple SSEContextFuncs into a single one. This enables chaining of context modifications for Server-Sent Events transport, such as extracting headers and setting up clients.

func ComposeStdioContextFuncs

func ComposeStdioContextFuncs(funcs ...server.StdioContextFunc) server.StdioContextFunc

ComposeStdioContextFuncs composes multiple StdioContextFuncs into a single one. Functions are applied in order, allowing each to modify the context before passing it to the next.

func ComposedHTTPContextFunc added in v0.4.0

func ComposedHTTPContextFunc(config GrafanaConfig, cache ...*ClientCache) server.HTTPContextFunc

ComposedHTTPContextFunc returns a HTTPContextFunc that comprises all predefined HTTPContextFuncs. It provides the complete context setup for HTTP transport, including header-based authentication and client configuration. If cache is non-nil, clients are cached by credentials to avoid per-request transport allocation.

func ComposedSSEContextFunc

func ComposedSSEContextFunc(config GrafanaConfig, cache ...*ClientCache) server.SSEContextFunc

ComposedSSEContextFunc returns a SSEContextFunc that comprises all predefined SSEContextFuncs. It sets up the complete context for SSE transport, extracting configuration from HTTP headers with environment variable fallbacks. If cache is non-nil, clients are cached by credentials to avoid per-request transport allocation.

func ComposedStdioContextFunc

func ComposedStdioContextFunc(config GrafanaConfig) server.StdioContextFunc

ComposedStdioContextFunc returns a StdioContextFunc that comprises all predefined StdioContextFuncs. It sets up the complete context for stdio transport including Grafana configuration, client initialization from environment variables, and incident management support.

func ConvertTool

func ConvertTool[T any, R any](name, description string, toolHandler ToolHandlerFunc[T, R], options ...mcp.ToolOption) (mcp.Tool, server.ToolHandlerFunc, error)

ConvertTool converts a toolHandler function to an MCP Tool and ToolHandlerFunc. The toolHandler must accept a context.Context and a struct with jsonschema tags for parameter documentation. The struct fields define the tool's input schema, while the return value can be a string, struct, or *mcp.CallToolResult. This function automatically generates JSON schema from the struct type and wraps the handler with OpenTelemetry instrumentation.

func DNSRebindingProtectionMiddleware added in v0.17.1

func DNSRebindingProtectionMiddleware(policy HostOriginPolicy) func(http.Handler) http.Handler

DNSRebindingProtectionMiddleware rejects requests whose Host (or Origin, when present) is not in the configured allowlists, defending HTTP/SSE transports against DNS-rebinding attacks. An empty AllowedOrigins rejects any request carrying an Origin header; a literal "*" disables either check.

func DashboardNamespace added in v0.16.0

func DashboardNamespace(ctx context.Context) (namespace string, fromSettings bool)

DashboardNamespace returns the Kubernetes-style namespace to use for dashboard.grafana.app API calls, given the Grafana config in ctx, and whether it was resolved from Grafana's /api/frontend/settings (fromSettings=true) or fell back to the OrgID-derived value (fromSettings=false).

It prefers the namespace reported by /api/frontend/settings, which is correct for both single-tenant ("default" / "org-N") and Grafana Cloud ("stacks-{id}"), caching successful results per (URL, OrgID). If the settings endpoint is unavailable or omits the namespace, it falls back to deriving the namespace from the OrgID — which is correct on-prem but may be wrong on Grafana Cloud, so callers can use fromSettings to qualify a subsequent not-found.

func DefaultAllowedHosts added in v0.17.1

func DefaultAllowedHosts(address string) []string

DefaultAllowedHosts derives a Host allowlist from a bind address. Wildcard binds (0.0.0.0, ::, empty host) return all loopback variants; "localhost" adds 127.0.0.1 and [::1]; specific hostnames return only themselves.

func IncidentClientFromContext

func IncidentClientFromContext(ctx context.Context) *incident.Client

IncidentClientFromContext retrieves the Grafana Incident client from the context. Returns nil if no client has been set, indicating that incident management features are not available.

func LoggerFromContext added in v0.12.1

func LoggerFromContext(ctx context.Context) *slog.Logger

LoggerFromContext extracts the logger from the GrafanaConfig in the context. Returns slog.Default() if no config or logger is set.

func MustWithOnBehalfOfAuth added in v0.3.0

func MustWithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) context.Context

MustWithOnBehalfOfAuth adds the access and user tokens to the context, panicking if either are empty. This is a convenience wrapper around WithOnBehalfOfAuth for cases where token validation has already occurred.

func NewUIContentMeta added in v1.0.0

func NewUIContentMeta(kind string) *mcp.Meta

NewUIContentMeta builds an *mcp.Meta that sets `_meta.ui.kind = kind` on a tool-result content item. Use the UIContentKind* constants.

func RegisterAppResources added in v1.0.0

func RegisterAppResources(s *server.MCPServer)

RegisterAppResources registers MCP App UI resources with the server.

func UserAgent added in v0.6.3

func UserAgent() string

UserAgent returns the user agent string for HTTP requests. It includes the mcp-grafana identifier and version number for proper request attribution and debugging.

func ValidateGrafanaURL added in v0.12.0

func ValidateGrafanaURL(u string) error

ValidateGrafanaURL returns nil if u is an absolute HTTP or HTTPS URL with a non-empty host. Trailing slashes are trimmed before validation so callers do not need to pre-normalize; this is the single canonicalization point shared by ValidateGrafanaURLMiddleware and the nav.go guard. On failure the returned error wraps ErrInvalidGrafanaURL.

url.Parse alone is too lenient: it accepts relative references (/foo), unusual schemes (javascript:alert(1)), and URLs without a host (http://). ParseRequestURI plus a scheme allow-list plus a host check is the standard pattern for validating request-supplied URL headers.

func ValidateGrafanaURLMiddleware added in v0.12.0

func ValidateGrafanaURLMiddleware(next http.Handler) http.Handler

ValidateGrafanaURLMiddleware returns an http.Handler middleware that rejects requests whose X-Grafana-URL header is present but fails ValidateGrafanaURL, responding with 400 Bad Request. Requests without the header pass through unchanged (downstream extractors apply the env-variable fallback).

Library consumers that wire mcp-grafana's context functions into their own http.Server should install this middleware to match the binary's defensive behavior. Consumers that call NewGrafanaClient directly (stdio or programmatic construction) should pre-validate the URL with ValidateGrafanaURL instead.

func WithGrafanaClient

func WithGrafanaClient(ctx context.Context, c *GrafanaClient) context.Context

WithGrafanaClient sets the Grafana client in the context. The client can be retrieved using GrafanaClientFromContext and will be used by all Grafana-related tools in the MCP server.

func WithGrafanaConfig added in v0.5.0

func WithGrafanaConfig(ctx context.Context, config GrafanaConfig) context.Context

WithGrafanaConfig adds Grafana configuration to the context. This configuration includes API credentials, debug settings, and TLS options that will be used by all Grafana clients created from this context.

func WithIncidentClient

func WithIncidentClient(ctx context.Context, client *incident.Client) context.Context

WithIncidentClient sets the Grafana Incident client in the context. This client is used for managing incidents, activities, and other IRM (Incident Response Management) operations.

func WithKubernetesClient added in v0.16.0

func WithKubernetesClient(ctx context.Context, c *KubernetesClient) context.Context

WithKubernetesClient sets the Kubernetes-style API client in the context.

func WithOnBehalfOfAuth added in v0.3.0

func WithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) (context.Context, error)

WithOnBehalfOfAuth adds the Grafana access token and user token to the Grafana config. These tokens enable on-behalf-of authentication in Grafana Cloud, allowing the MCP server to act on behalf of a specific user with their permissions.

func WithProxiedTools added in v0.7.8

func WithProxiedTools(enabled bool) toolManagerOption

WithProxiedTools sets whether proxied tools are enabled

func WithToolManagerLogger added in v0.12.1

func WithToolManagerLogger(logger *slog.Logger) toolManagerOption

WithToolManagerLogger sets the logger for the ToolManager.

func WithUIResource added in v1.0.0

func WithUIResource(resourceURI string) mcp.ToolOption

WithUIResource attaches a _meta.ui.resourceUri to a tool definition, linking it to an MCP App HTML resource for inline rendering.

Types

type APIGroup added in v0.11.4

type APIGroup struct {
	Name             string             `json:"name"`
	Versions         []GroupVersionInfo `json:"versions"`
	PreferredVersion GroupVersionInfo   `json:"preferredVersion"`
}

APIGroup represents a single API group in the discovery response.

type APIGroupList added in v0.11.4

type APIGroupList struct {
	Kind   string     `json:"kind"`
	Groups []APIGroup `json:"groups"`
}

APIGroupList represents the response from GET /apis (Kubernetes API discovery).

type AuthRoundTripper added in v0.12.0

type AuthRoundTripper struct {
	// contains filtered or unexported fields
}

AuthRoundTripper wraps an http.RoundTripper to add authentication headers. It supports on-behalf-of (OBO) auth via access/ID tokens, API key bearer auth, and HTTP basic auth, in that priority order.

func NewAuthRoundTripper added in v0.12.0

func NewAuthRoundTripper(rt http.RoundTripper, accessToken, idToken, apiKey string, basicAuth *url.Userinfo) *AuthRoundTripper

func (*AuthRoundTripper) RoundTrip added in v0.12.0

func (rt *AuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type ClientCache added in v0.11.4

type ClientCache struct {
	// contains filtered or unexported fields
}

ClientCache caches HTTP clients keyed by credentials to avoid creating new transports per request. This prevents the memory leak described in https://github.com/grafana/mcp-grafana/issues/682.

func NewClientCache added in v0.11.4

func NewClientCache(logger *slog.Logger) *ClientCache

NewClientCache creates a new client cache.

func (*ClientCache) Close added in v0.11.4

func (c *ClientCache) Close()

Close cleans up cached clients. For incident clients, idle connections are closed via the underlying HTTP transport. Grafana clients use a go-openapi runtime whose transport is set via reflection, so we clear the map and let the GC reclaim resources.

func (*ClientCache) GetOrCreateGrafanaClient added in v0.11.4

func (c *ClientCache) GetOrCreateGrafanaClient(key clientCacheKey, createFn func() *GrafanaClient) *GrafanaClient

GetOrCreateGrafanaClient returns a cached Grafana client for the given key, or creates one using createFn if no cached client exists. The createFn is called outside the cache lock via singleflight to avoid blocking concurrent cache reads during slow client creation (e.g. network I/O).

func (*ClientCache) GetOrCreateIncidentClient added in v0.11.4

func (c *ClientCache) GetOrCreateIncidentClient(key clientCacheKey, createFn func() *incident.Client) *incident.Client

GetOrCreateIncidentClient returns a cached incident client for the given key, or creates one using createFn if no cached client exists. The createFn is called outside the cache lock via singleflight to avoid blocking concurrent cache reads during slow client creation.

func (*ClientCache) GetOrCreateK8sClient added in v0.16.0

func (c *ClientCache) GetOrCreateK8sClient(key clientCacheKey, createFn func() *KubernetesClient) *KubernetesClient

GetOrCreateK8sClient returns a cached Kubernetes client for the given key, or creates one using createFn if no cached client exists. createFn may return nil (e.g. if the transport could not be built); nil results are not cached, so the next call retries. The createFn is called outside the cache lock via singleflight to avoid blocking concurrent cache reads during slow creation.

func (*ClientCache) Size added in v0.11.4

func (c *ClientCache) Size() (grafana, incident, k8s int)

Size returns the number of cached clients (for testing/metrics).

type DiscoveredDatasource added in v0.7.8

type DiscoveredDatasource struct {
	UID    string
	Name   string
	Type   string
	MCPURL string // The MCP endpoint URL
}

DiscoveredDatasource represents a datasource that supports MCP

type ExtraHeadersRoundTripper added in v0.10.0

type ExtraHeadersRoundTripper struct {
	// contains filtered or unexported fields
}

func NewExtraHeadersRoundTripper added in v0.10.0

func NewExtraHeadersRoundTripper(rt http.RoundTripper, headers map[string]string) *ExtraHeadersRoundTripper

func (*ExtraHeadersRoundTripper) RoundTrip added in v0.10.0

func (t *ExtraHeadersRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type GrafanaClient added in v0.11.4

type GrafanaClient struct {
	*client.GrafanaHTTPAPI

	// PublicURL is the public-facing URL of the Grafana instance, fetched from
	// /api/frontend/settings (the appUrl field). It may differ from the configured
	// URL when the MCP server accesses Grafana via an internal URL behind a load
	// balancer or reverse proxy.
	PublicURL string
}

GrafanaClient wraps the Grafana HTTP API client with additional metadata fetched from the Grafana instance, such as the public URL. This allows the MCP server to generate user-facing links using the public URL even when it accesses Grafana via an internal URL.

func GrafanaClientFromContext

func GrafanaClientFromContext(ctx context.Context) *GrafanaClient

GrafanaClientFromContext retrieves the Grafana client from the context. Returns nil if no client has been set, which tools should handle gracefully with appropriate error messages.

func NewGrafanaClient added in v0.4.0

func NewGrafanaClient(ctx context.Context, grafanaURL, apiKey string, auth *url.Userinfo) *GrafanaClient

NewGrafanaClient creates a Grafana client with the provided URL and API key. The client is automatically configured with the correct HTTP scheme, debug settings from context, custom TLS configuration if present, and OpenTelemetry instrumentation for distributed tracing. It also fetches the Grafana instance's public URL from /api/frontend/settings for use in deep link generation. The org ID is read from the GrafanaConfig in the context, which should be set by ExtractGrafanaInfoFromEnv or ExtractGrafanaInfoFromHeaders before calling this function.

type GrafanaConfig added in v0.5.0

type GrafanaConfig struct {
	// Debug enables debug mode for the Grafana client.
	Debug bool

	// IncludeArgumentsInSpans enables logging of tool arguments in OpenTelemetry spans.
	// This should only be enabled in non-production environments or when you're certain
	// the arguments don't contain PII. Defaults to false for safety.
	// Note: OpenTelemetry spans are always created for context propagation, but arguments
	// are only included when this flag is enabled.
	IncludeArgumentsInSpans bool

	// URL is the URL of the Grafana instance.
	URL string

	// APIKey is the API key or service account token for the Grafana instance.
	// It may be empty if we are using on-behalf-of auth.
	APIKey string

	// Credentials if user is using basic auth
	BasicAuth *url.Userinfo

	// OrgID is the organization ID to use for multi-org support.
	// When set, it will be sent as X-Grafana-Org-Id header regardless of authentication method.
	// Works with service account tokens, API keys, and basic authentication.
	OrgID int64

	// AccessToken is the Grafana Cloud access policy token used for on-behalf-of auth in Grafana Cloud.
	AccessToken string
	// IDToken is an ID token identifying the user for the current request.
	// It comes from the `X-Grafana-Id` header sent from Grafana to plugin backends.
	// It is used for on-behalf-of auth in Grafana Cloud.
	IDToken string

	// TLSConfig holds TLS configuration for all Grafana clients.
	TLSConfig *TLSConfig

	// Timeout specifies a time limit for requests made by the Grafana client.
	// A Timeout of zero means no timeout.
	// Default is 10 seconds.
	Timeout time.Duration

	// ExtraHeaders contains additional HTTP headers to send with all Grafana API requests.
	// Parsed from GRAFANA_EXTRA_HEADERS environment variable as JSON object.
	ExtraHeaders map[string]string

	// MaxLokiLogLimit is the maximum number of log lines that can be returned
	// from Loki queries.
	MaxLokiLogLimit int

	// BaseTransport is an optional base HTTP transport used as the innermost
	// layer of the middleware chain in NewGrafanaClient. When set, it replaces
	// the default http.Transport that NewGrafanaClient would otherwise create.
	// The caller can use this to provide a pre-configured transport with custom
	// connection pooling, timeouts, or tracing instrumentation.
	// Note: NewGrafanaClient still wraps this transport with ExtraHeaders,
	// OrgID, UserAgent, and otelhttp layers.
	BaseTransport http.RoundTripper

	// Logger is an optional structured logger. When set, functions that have
	// access to the GrafanaConfig will use this logger instead of the global
	// slog.Default(). This allows callers (e.g. the hosted Cloud MCP server)
	// to inject their own slog.Logger for consistent structured logging with
	// per-request context such as tenant_id.
	Logger *slog.Logger
}

GrafanaConfig represents the full configuration for Grafana clients. It includes connection details, authentication credentials, debug settings, and TLS options used throughout the MCP server's lifecycle.

func GrafanaConfigFromContext added in v0.5.0

func GrafanaConfigFromContext(ctx context.Context) GrafanaConfig

GrafanaConfigFromContext extracts Grafana configuration from the context. If no config is found, returns a zero-value GrafanaConfig. This function is typically used by internal components to access configuration set earlier in the request lifecycle.

func (GrafanaConfig) HTTPTransport added in v0.12.0

func (c GrafanaConfig) HTTPTransport() http.RoundTripper

HTTPTransport returns the base HTTP transport for this config. If BaseTransport is set it is returned; otherwise http.DefaultTransport.

func (GrafanaConfig) LoggerOrDefault added in v0.12.1

func (c GrafanaConfig) LoggerOrDefault() *slog.Logger

LoggerOrDefault returns the configured logger, or slog.Default() if none is set.

type GroupVersionInfo added in v0.11.4

type GroupVersionInfo struct {
	GroupVersion string `json:"groupVersion"`
	Version      string `json:"version"`
}

GroupVersionInfo contains version information for an API group.

type HardError added in v0.9.0

type HardError struct {
	Err error
}

HardError wraps an error to indicate it should propagate as a JSON-RPC protocol error rather than being converted to CallToolResult with IsError=true. Use sparingly for non-recoverable failures (e.g., missing auth).

func (*HardError) Error added in v0.9.0

func (e *HardError) Error() string

func (*HardError) Unwrap added in v0.9.0

func (e *HardError) Unwrap() error

type HostOriginPolicy added in v0.17.1

type HostOriginPolicy struct {
	AllowedHosts   []string
	AllowedOrigins []string
}

type KubernetesAPIError added in v0.11.4

type KubernetesAPIError struct {
	StatusCode int
	Status     string
	Body       string
}

KubernetesAPIError is returned when the server responds with a non-2xx status.

func (*KubernetesAPIError) Error added in v0.11.4

func (e *KubernetesAPIError) Error() string

type KubernetesClient added in v0.11.4

type KubernetesClient struct {
	// BaseURL is the root URL of the Grafana instance (e.g. "http://localhost:3000").
	BaseURL string

	// HTTPClient is the underlying HTTP client used for requests.
	// If nil, http.DefaultClient is used.
	HTTPClient *http.Client
	// contains filtered or unexported fields
}

KubernetesClient is a lightweight, generic HTTP client for Grafana's Kubernetes-style APIs (/apis/...). It uses unstructured data (map[string]interface{}) so callers are not tied to specific Go types.

Authentication is read from the GrafanaConfig in the request context, following the same priority as the rest of mcp-grafana:

  1. AccessToken + IDToken (on-behalf-of)
  2. APIKey (bearer token)
  3. BasicAuth

func KubernetesClientFromContext added in v0.16.0

func KubernetesClientFromContext(ctx context.Context) *KubernetesClient

KubernetesClientFromContext retrieves the Kubernetes-style API client from the context. Returns nil if no client has been set (or creation failed); callers should handle nil by falling back to the legacy Grafana API.

func NewKubernetesClient added in v0.11.4

func NewKubernetesClient(ctx context.Context) (*KubernetesClient, error)

NewKubernetesClient creates a KubernetesClient from the GrafanaConfig in ctx. It reuses BuildTransport so TLS, extra headers, OrgID, and user-agent are handled the same way as for the legacy OpenAPI client.

func (*KubernetesClient) Create added in v0.16.0

func (c *KubernetesClient) Create(ctx context.Context, desc ResourceDescriptor, namespace string, obj map[string]interface{}) (map[string]interface{}, error)

Create creates a new resource (POST to the collection endpoint) and returns the created object.

func (*KubernetesClient) Discover added in v0.11.4

func (c *KubernetesClient) Discover(ctx context.Context) (*ResourceRegistry, error)

Discover calls GET /apis and returns a ResourceRegistry describing available API groups and their versions.

func (*KubernetesClient) Get added in v0.11.4

func (c *KubernetesClient) Get(ctx context.Context, desc ResourceDescriptor, namespace, name string) (map[string]interface{}, error)

Get fetches a single resource by name. Returns the full Kubernetes-style object as unstructured data.

func (*KubernetesClient) GroupVersions added in v0.16.0

func (c *KubernetesClient) GroupVersions(ctx context.Context, group string) ([]string, error)

GroupVersions returns the API versions served for the given group, fetched from GET /apis/<group> and cached once per client. A non-nil empty slice means the group is not served (the discovery endpoint returned 404). Only definitive results (200 or 404) are cached; transient errors are returned without caching so the next call retries.

func (*KubernetesClient) List added in v0.11.4

func (c *KubernetesClient) List(ctx context.Context, desc ResourceDescriptor, namespace string, opts *ListOptions) (*ResourceList, error)

List fetches a collection of resources.

func (*KubernetesClient) SupportsGroupVersion added in v0.16.0

func (c *KubernetesClient) SupportsGroupVersion(ctx context.Context, group, version string) bool

SupportsGroupVersion reports whether the given group serves the given version (using the cached discovery from GroupVersions). On a transient discovery error it returns false, so callers conservatively fall back to the legacy API.

func (*KubernetesClient) Update added in v0.16.0

func (c *KubernetesClient) Update(ctx context.Context, desc ResourceDescriptor, namespace, name string, obj map[string]interface{}) (map[string]interface{}, error)

Update replaces an existing resource (PUT to the resource endpoint) and returns the updated object. The supplied object must carry the current metadata.resourceVersion (from a prior Get) for optimistic concurrency.

type ListOptions added in v0.11.4

type ListOptions struct {
	// LabelSelector filters results by label (e.g. "app=foo").
	LabelSelector string
	// Limit caps the number of items returned.
	Limit int
	// Continue is a pagination token from a previous list response.
	Continue string
}

ListOptions controls the behaviour of a List call.

type MCPDatasourceConfig added in v0.7.8

type MCPDatasourceConfig struct {
	Type         string
	EndpointPath string // e.g., "/api/mcp"
}

MCPDatasourceConfig defines configuration for a datasource type that supports MCP

type OrgIDRoundTripper added in v0.7.8

type OrgIDRoundTripper struct {
	// contains filtered or unexported fields
}

OrgIDRoundTripper wraps an http.RoundTripper to add the X-Grafana-Org-Id header.

func NewOrgIDRoundTripper added in v0.7.8

func NewOrgIDRoundTripper(rt http.RoundTripper, orgID int64) *OrgIDRoundTripper

func (*OrgIDRoundTripper) RoundTrip added in v0.7.8

func (t *OrgIDRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

type ProxiedClient added in v0.7.8

type ProxiedClient struct {
	DatasourceUID  string
	DatasourceName string
	DatasourceType string
	Client         *mcp_client.Client
	Tools          []mcp.Tool
	// contains filtered or unexported fields
}

ProxiedClient represents a connection to a remote MCP server (e.g., Tempo datasource)

func NewProxiedClient added in v0.7.8

func NewProxiedClient(ctx context.Context, datasourceUID, datasourceName, datasourceType, mcpEndpoint string) (*ProxiedClient, error)

NewProxiedClient creates a new connection to a remote MCP server

func (*ProxiedClient) CallTool added in v0.7.8

func (pc *ProxiedClient) CallTool(ctx context.Context, toolName string, arguments map[string]any) (*mcp.CallToolResult, error)

CallTool forwards a tool call to the remote MCP server

func (*ProxiedClient) Close added in v0.7.8

func (pc *ProxiedClient) Close() error

Close closes the connection to the remote MCP server

func (*ProxiedClient) ListTools added in v0.7.8

func (pc *ProxiedClient) ListTools() []mcp.Tool

ListTools returns the tools available from this remote server Note: This method doesn't take a context parameter as the tools are cached locally

type ProxiedToolHandler added in v0.7.8

type ProxiedToolHandler struct {
	// contains filtered or unexported fields
}

ProxiedToolHandler implements the CallToolHandler interface for proxied tools

func NewProxiedToolHandler added in v0.7.8

func NewProxiedToolHandler(sm *SessionManager, tm *ToolManager, toolName string) *ProxiedToolHandler

NewProxiedToolHandler creates a new handler for a proxied tool

func (*ProxiedToolHandler) Handle added in v0.7.8

Handle forwards the tool call to the appropriate remote MCP server

type ResourceDescriptor added in v0.11.4

type ResourceDescriptor struct {
	Group    string // e.g. "dashboard.grafana.app"
	Version  string // e.g. "v2beta1"
	Resource string // plural name, e.g. "dashboards"
}

ResourceDescriptor describes a Kubernetes-style API resource in Grafana. It contains enough information to construct API paths for any k8s-style resource.

func (ResourceDescriptor) BasePath added in v0.11.4

func (d ResourceDescriptor) BasePath(namespace string) string

BasePath returns the API path prefix for this resource, including namespace. For example: /apis/dashboard.grafana.app/v2beta1/namespaces/default/dashboards

type ResourceGroup added in v0.11.4

type ResourceGroup struct {
	Name             string
	PreferredVersion string
	AllVersions      []string
}

ResourceGroup holds information about a single API group discovered from /apis.

type ResourceList added in v0.11.4

type ResourceList struct {
	Kind       string                   `json:"kind"`
	APIVersion string                   `json:"apiVersion"`
	Items      []map[string]interface{} `json:"items"`
	Metadata   map[string]interface{}   `json:"metadata,omitempty"`
}

ResourceList is the response shape for a Kubernetes-style list request.

type ResourceRegistry added in v0.11.4

type ResourceRegistry struct {
	// contains filtered or unexported fields
}

ResourceRegistry maps API group names to their available resources and versions. It is built from the /apis discovery response (APIGroupList).

ResourceRegistry is immutable after construction via NewResourceRegistry and is safe for concurrent reads from multiple goroutines without synchronization.

func NewResourceRegistry added in v0.11.4

func NewResourceRegistry(apiGroupList *APIGroupList) *ResourceRegistry

NewResourceRegistry creates a ResourceRegistry from an APIGroupList.

func (*ResourceRegistry) GetGroup added in v0.11.4

func (r *ResourceRegistry) GetGroup(name string) *ResourceGroup

GetGroup returns the ResourceGroup for the given API group name, or nil if not found.

func (*ResourceRegistry) Groups added in v0.11.4

func (r *ResourceRegistry) Groups() []string

Groups returns a list of all known API group names.

func (*ResourceRegistry) HasGroup added in v0.11.4

func (r *ResourceRegistry) HasGroup(name string) bool

HasGroup returns true if the registry contains the given API group.

func (*ResourceRegistry) PreferredVersion added in v0.11.4

func (r *ResourceRegistry) PreferredVersion(group string) string

PreferredVersion returns the preferred version for the given API group. Returns an empty string if the group is not found.

type SessionManager added in v0.7.8

type SessionManager struct {
	// contains filtered or unexported fields
}

SessionManager manages client sessions and their state

func NewSessionManager added in v0.7.8

func NewSessionManager(opts ...SessionManagerOption) *SessionManager

func (*SessionManager) Close added in v0.11.4

func (sm *SessionManager) Close()

Close stops the reaper goroutine and cleans up all remaining sessions. It is safe to call concurrently and multiple times.

func (*SessionManager) CreateSession added in v0.7.8

func (sm *SessionManager) CreateSession(ctx context.Context, session server.ClientSession)

func (*SessionManager) GetProxiedClient added in v0.7.8

func (sm *SessionManager) GetProxiedClient(ctx context.Context, datasourceType, datasourceUID string) (*ProxiedClient, error)

GetProxiedClient retrieves a proxied client for the given datasource

func (*SessionManager) GetSession added in v0.7.8

func (sm *SessionManager) GetSession(sessionID string) (*SessionState, bool)

func (*SessionManager) RemoveSession added in v0.7.8

func (sm *SessionManager) RemoveSession(ctx context.Context, session server.ClientSession)

func (*SessionManager) SetMCPServer added in v0.12.0

func (sm *SessionManager) SetMCPServer(s *server.MCPServer)

SetMCPServer sets the MCP server reference for session cleanup. When set, the reaper will call MCPServer.UnregisterSession for reaped sessions to prevent a memory leak in the SDK's internal session map.

type SessionManagerOption added in v0.11.4

type SessionManagerOption func(*SessionManager)

SessionManagerOption configures a SessionManager.

func WithSessionLogger added in v0.12.1

func WithSessionLogger(logger *slog.Logger) SessionManagerOption

WithSessionLogger sets the logger for the SessionManager. If not set, slog.Default() is used.

func WithSessionTTL added in v0.11.4

func WithSessionTTL(ttl time.Duration) SessionManagerOption

WithSessionTTL sets the TTL for idle sessions. Sessions idle longer than this duration are automatically reaped. A zero or negative value disables the reaper.

type SessionState added in v0.7.8

type SessionState struct {
	// contains filtered or unexported fields
}

SessionState holds the state for a single client session

type TLSConfig added in v0.6.0

type TLSConfig struct {
	CertFile   string
	KeyFile    string
	CAFile     string
	SkipVerify bool
}

TLSConfig holds TLS configuration for Grafana clients. It supports mutual TLS authentication with client certificates, custom CA certificates for server verification, and development options like skipping certificate verification.

func (*TLSConfig) CreateTLSConfig added in v0.6.0

func (tc *TLSConfig) CreateTLSConfig() (*tls.Config, error)

CreateTLSConfig creates a *tls.Config from TLSConfig. It supports client certificates, custom CA certificates, and the option to skip TLS verification for development environments.

func (*TLSConfig) HTTPTransport added in v0.6.0

func (tc *TLSConfig) HTTPTransport(defaultTransport *http.Transport) (http.RoundTripper, error)

HTTPTransport creates an HTTP transport with custom TLS configuration. It clones the provided transport and applies the TLS settings, preserving other transport configurations like timeouts and connection pools.

type Tool

type Tool struct {
	Tool    mcp.Tool
	Handler server.ToolHandlerFunc
}

Tool represents a tool definition and its handler function for the MCP server. It encapsulates both the tool metadata (name, description, schema) and the function that executes when the tool is called. The simplest way to create a Tool is to use MustTool for compile-time tool creation, or ConvertTool if you need runtime tool creation with proper error handling.

func MustTool

func MustTool[T any, R any](
	name, description string,
	toolHandler ToolHandlerFunc[T, R],
	options ...mcp.ToolOption,
) Tool

MustTool creates a new Tool from the given name, description, and toolHandler. It panics if the tool cannot be created, making it suitable for compile-time tool definitions where creation errors indicate programming mistakes.

func (*Tool) Register

func (t *Tool) Register(mcp *server.MCPServer)

Register adds the Tool to the given MCPServer. It is a convenience method that calls server.MCPServer.AddTool with the Tool's metadata and handler, allowing fluent tool registration in a single statement:

mcpgrafana.MustTool(name, description, toolHandler).Register(server)

type ToolHandlerFunc

type ToolHandlerFunc[T any, R any] = func(ctx context.Context, request T) (R, error)

ToolHandlerFunc is the type of a handler function for a tool. T is the request parameter type (must be a struct with jsonschema tags), and R is the response type which can be a string, struct, or *mcp.CallToolResult.

type ToolManager added in v0.7.8

type ToolManager struct {
	// contains filtered or unexported fields
}

ToolManager manages proxied tools (either per-session or server-wide)

func NewToolManager added in v0.7.8

func NewToolManager(sm *SessionManager, mcpServer *server.MCPServer, opts ...toolManagerOption) *ToolManager

NewToolManager creates a new ToolManager

func (*ToolManager) GetServerClient added in v0.7.8

func (tm *ToolManager) GetServerClient(datasourceType, datasourceUID string) (*ProxiedClient, error)

GetServerClient retrieves a proxied client from server-level storage (for stdio transport)

func (*ToolManager) InitializeAndRegisterProxiedTools added in v0.7.8

func (tm *ToolManager) InitializeAndRegisterProxiedTools(ctx context.Context, session server.ClientSession)

InitializeAndRegisterProxiedTools discovers datasources, creates clients, and registers tools per-session This should be called in OnBeforeListTools and OnBeforeCallTool hooks for HTTP/SSE transports

func (*ToolManager) InitializeAndRegisterServerTools added in v0.7.8

func (tm *ToolManager) InitializeAndRegisterServerTools(ctx context.Context) error

InitializeAndRegisterServerTools discovers datasources and registers tools on the server (for stdio transport) This should be called once at server startup for single-tenant stdio servers

type TransportOption added in v0.12.0

type TransportOption func(*transportOptions)

TransportOption configures optional behaviour of BuildTransport.

func WithoutAuth added in v0.12.0

func WithoutAuth() TransportOption

WithoutAuth skips the authentication middleware layer. Use this when the HTTP client library handles auth itself (e.g. OnCall, incident).

func WithoutOrgID added in v0.12.0

func WithoutOrgID() TransportOption

WithoutOrgID skips the X-Grafana-Org-Id header layer.

func WithoutOtel added in v0.12.0

func WithoutOtel() TransportOption

WithoutOtel skips the otelhttp tracing wrapper.

func WithoutUserAgent added in v0.12.0

func WithoutUserAgent() TransportOption

WithoutUserAgent skips the User-Agent header layer.

type UserAgentTransport added in v0.6.3

type UserAgentTransport struct {
	UserAgent string
	// contains filtered or unexported fields
}

UserAgentTransport wraps an http.RoundTripper to add a custom User-Agent header. This ensures all HTTP requests from the MCP server are properly identified with version information for debugging and analytics.

func NewUserAgentTransport added in v0.6.3

func NewUserAgentTransport(rt http.RoundTripper, userAgent ...string) *UserAgentTransport

NewUserAgentTransport creates a new UserAgentTransport with the specified user agent. If no user agent is provided, it uses the default UserAgent() with version information. The transport wraps the provided RoundTripper, defaulting to http.DefaultTransport if nil.

func (*UserAgentTransport) RoundTrip added in v0.6.3

func (t *UserAgentTransport) RoundTrip(req *http.Request) (*http.Response, error)

Directories

Path Synopsis
cmd
linters/openapi command
mcp-grafana command
internal
Package observability provides OpenTelemetry-based metrics, tracing, and log export for the MCP Grafana server.
Package observability provides OpenTelemetry-based metrics, tracing, and log export for the MCP Grafana server.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL