Documentation
¶
Index ¶
- Constants
- Variables
- func BuildTransport(cfg *GrafanaConfig, base http.RoundTripper, opts ...TransportOption) (http.RoundTripper, error)
- func ComposeHTTPContextFuncs(funcs ...httpContextFunc) server.HTTPContextFunc
- func ComposeSSEContextFuncs(funcs ...httpContextFunc) server.SSEContextFunc
- func ComposeStdioContextFuncs(funcs ...server.StdioContextFunc) server.StdioContextFunc
- func ComposedHTTPContextFunc(config GrafanaConfig, cache ...*ClientCache) server.HTTPContextFunc
- func ComposedSSEContextFunc(config GrafanaConfig, cache ...*ClientCache) server.SSEContextFunc
- func ComposedStdioContextFunc(config GrafanaConfig) server.StdioContextFunc
- func ConvertTool[T any, R any](name, description string, toolHandler ToolHandlerFunc[T, R], ...) (mcp.Tool, server.ToolHandlerFunc, error)
- func DNSRebindingProtectionMiddleware(policy HostOriginPolicy) func(http.Handler) http.Handler
- func DashboardNamespace(ctx context.Context) (namespace string, fromSettings bool)
- func DefaultAllowedHosts(address string) []string
- func IncidentClientFromContext(ctx context.Context) *incident.Client
- func LoggerFromContext(ctx context.Context) *slog.Logger
- func MustWithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) context.Context
- func NewUIContentMeta(kind string) *mcp.Meta
- func RegisterAppResources(s *server.MCPServer)
- func UserAgent() string
- func ValidateGrafanaURL(u string) error
- func ValidateGrafanaURLMiddleware(next http.Handler) http.Handler
- func WithGrafanaClient(ctx context.Context, c *GrafanaClient) context.Context
- func WithGrafanaConfig(ctx context.Context, config GrafanaConfig) context.Context
- func WithIncidentClient(ctx context.Context, client *incident.Client) context.Context
- func WithKubernetesClient(ctx context.Context, c *KubernetesClient) context.Context
- func WithOnBehalfOfAuth(ctx context.Context, accessToken, userToken string) (context.Context, error)
- func WithProxiedTools(enabled bool) toolManagerOption
- func WithToolManagerLogger(logger *slog.Logger) toolManagerOption
- func WithUIResource(resourceURI string) mcp.ToolOption
- type APIGroup
- type APIGroupList
- type AuthRoundTripper
- type ClientCache
- func (c *ClientCache) Close()
- func (c *ClientCache) GetOrCreateGrafanaClient(key clientCacheKey, createFn func() *GrafanaClient) *GrafanaClient
- func (c *ClientCache) GetOrCreateIncidentClient(key clientCacheKey, createFn func() *incident.Client) *incident.Client
- func (c *ClientCache) GetOrCreateK8sClient(key clientCacheKey, createFn func() *KubernetesClient) *KubernetesClient
- func (c *ClientCache) Size() (grafana, incident, k8s int)
- type DiscoveredDatasource
- type ExtraHeadersRoundTripper
- type GrafanaClient
- type GrafanaConfig
- type GroupVersionInfo
- type HardError
- type HostOriginPolicy
- type KubernetesAPIError
- type KubernetesClient
- func (c *KubernetesClient) Create(ctx context.Context, desc ResourceDescriptor, namespace string, ...) (map[string]interface{}, error)
- func (c *KubernetesClient) Discover(ctx context.Context) (*ResourceRegistry, error)
- func (c *KubernetesClient) Get(ctx context.Context, desc ResourceDescriptor, namespace, name string) (map[string]interface{}, error)
- func (c *KubernetesClient) GroupVersions(ctx context.Context, group string) ([]string, error)
- func (c *KubernetesClient) List(ctx context.Context, desc ResourceDescriptor, namespace string, ...) (*ResourceList, error)
- func (c *KubernetesClient) SupportsGroupVersion(ctx context.Context, group, version string) bool
- func (c *KubernetesClient) Update(ctx context.Context, desc ResourceDescriptor, namespace, name string, ...) (map[string]interface{}, error)
- type ListOptions
- type MCPDatasourceConfig
- type OrgIDRoundTripper
- type ProxiedClient
- type ProxiedToolHandler
- type ResourceDescriptor
- type ResourceGroup
- type ResourceList
- type ResourceRegistry
- type SessionManager
- func (sm *SessionManager) Close()
- func (sm *SessionManager) CreateSession(ctx context.Context, session server.ClientSession)
- func (sm *SessionManager) GetProxiedClient(ctx context.Context, datasourceType, datasourceUID string) (*ProxiedClient, error)
- func (sm *SessionManager) GetSession(sessionID string) (*SessionState, bool)
- func (sm *SessionManager) RemoveSession(ctx context.Context, session server.ClientSession)
- func (sm *SessionManager) SetMCPServer(s *server.MCPServer)
- type SessionManagerOption
- type SessionState
- type TLSConfig
- type Tool
- type ToolHandlerFunc
- type ToolManager
- type TransportOption
- type UserAgentTransport
Constants ¶
const ( PanelViewerResourceURI = "ui://mcp-grafana/panel-viewer.html" // UIContentKindDeeplink is the `_meta.ui.kind` value for a Grafana deeplink. UIContentKindDeeplink = "deeplink" )
const ( // DefaultGrafanaClientTimeout is the default timeout for Grafana HTTP client requests. DefaultGrafanaClientTimeout = 10 * time.Second )
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 ¶
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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 ¶
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
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
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
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
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
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
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 ¶
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
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
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
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
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).
type HostOriginPolicy ¶ added in v0.17.1
type KubernetesAPIError ¶ added in v0.11.4
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:
- AccessToken + IDToken (on-behalf-of)
- APIKey (bearer token)
- 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
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
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
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
func (h *ProxiedToolHandler) Handle(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error)
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
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
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
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
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.
type ToolHandlerFunc ¶
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.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
linters/jsonschema
command
|
|
|
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. |