Mostly safe โ a couple of notes worth reading.
Scanned 5/13/2026, 5:03:49 AMยทCached resultยทDeep Scanยท91 rulesยทHow we decide โ
AIVSS Score
Low
Severity Breakdown
0
critical
0
high
14
medium
0
low
MCP Server Information
Findings
This package earned a B grade with a safety score of 85/100 but carries moderate risk due to 14 medium-severity findings concentrated in server configuration and resource exhaustion vulnerabilities. The configuration issues could expose the server to misuse or instability if not properly hardened, and the resource exhaustion risks suggest it may be susceptible to denial-of-service attacks under certain conditions. You should review the specific configuration recommendations and resource limits before deployment, particularly if this will run in a production environment.
AIPer-finding remediation generated by bedrock-claude-haiku-4-5 โ 14 of 14 findings. Click any finding to read.
No known CVEs found for this package or its dependencies.
Scan Details
Done
Sign in to save scan history and re-scan automatically on new commits.
Building your own MCP server?
Same rules, same LLM judges, same grade. Private scans stay isolated to your account and never appear in the public registry. Required for code your team hasnโt shipped yet.
14 of 14 findings
14 findings
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 168 | console.error('Verifying checksum...'); |
| 169 | const VERSION_NUMBER = VERSION.replace(/^v/, ''); // Remove 'v' prefix |
| 170 | const CHECKSUMS_URL = `https://github.com/grafana/mcp-grafana/releases/download/${VERSION}/mcp-grafana_${VERSION_NUMBER}_checksums.txt`; |
| 171 | const checksumResponse = await fetch(CHECKSUMS_URL); |
| 172 | if (!checksumResponse.ok) { |
| 173 | throw new Error(`Failed to download checksums: ${checksumResponse.statusText}`); |
| 174 | } |
RemediationAI
The problem is that the `fetch()` call to download the checksums file has no timeout, allowing a hung or malicious GitHub server to block indefinitely. Add an AbortController with a timeout to the fetch call: `const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); const checksumResponse = await fetch(CHECKSUMS_URL, { signal: controller.signal });` and clear the timeout after completion. This ensures the request fails fast if the upstream server is unresponsive, preventing thread exhaustion. Verify by testing with a timeout value and confirming the fetch rejects with an AbortError when the server does not respond within the specified window.
Network / IO / subprocess call without an explicit timeout. A malicious or hung upstream (HTTP host, socket peer, child process) can pin threads, exhaust connection/process pools, and make the MCP server unresponsive. Always pass a bounded timeout. v2 extends v1 with subprocess coverage (R03 from the legacy readiness audit).
Evidence
| 80 | // Download file from URL |
| 81 | async function downloadFile(url, destPath) { |
| 82 | const response = await fetch(url); |
| 83 | if (!response.ok) { |
| 84 | throw new Error(`Failed to download ${url}: ${response.statusText}`); |
| 85 | } |
RemediationAI
The problem is that `downloadFile()` calls `fetch(url)` without a timeout parameter, allowing a malicious or hung server to pin the thread indefinitely. Wrap the fetch in an AbortController with a bounded timeout: `const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); const response = await fetch(url, { signal: controller.signal }); clearTimeout(timeoutId);` This ensures the download fails fast if the remote host does not respond within 30 seconds, freeing the thread for other work. Test by calling `downloadFile()` against a non-responsive server and confirming it rejects with an AbortError within the timeout window.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Windsurf |
| 3 | menuTitle: Windsurf |
| 4 | description: Set up the Grafana MCP server for Windsurf. |
| 5 | keywords: |
| 6 | - Windsurf |
| 7 | - Codeium |
| 8 | - MCP |
| 9 | - client |
| 10 | weight: 7 |
| 11 | aliases: [] |
| 12 | --- |
| 13 | |
| 14 | # Windsurf |
| 15 | |
| 16 | This guide helps you set up the `mcp-grafana` server for Windsurf. |
| 17 | |
| 18 | ## Prerequisites |
| 19 | |
| 20 | - Windsurf IDE installed |
| 21 | - Grafana 9.0+ with a service account token |
| 22 | - `mcp-grafana` binary in your PATH |
| 23 | |
| 24 | ## Configuration |
| 25 | |
| 26 | Configuration file location: |
| 27 | |
| 28 | | OS | Path | |
| 29 | | |
RemediationAI
The problem is that the Windsurf client documentation does not declare any authentication mechanism (bearer token, API key, OAuth, etc.) in the MCP manifest, making it unclear how the server validates requests and whether it relies on network-layer or host-level security. Add an explicit `auth` or `apiKey` field to the manifest JSON and document in the setup guide which authentication method is required (e.g., 'Grafana API token passed via GRAFANA_TOKEN environment variable'). This allows security reviewers to audit the actual authentication flow and ensures users understand what credentials are needed. Verify by checking that the manifest includes an `auth` field and the documentation clearly states which credentials or environment variables must be configured.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Client configuration examples |
| 3 | menuTitle: Client configuration examples |
| 4 | description: Example MCP client JSON for uvx, binary, Docker, VS Code, and debug mode. |
| 5 | keywords: |
| 6 | - MCP |
| 7 | - configuration |
| 8 | - Claude |
| 9 | - Docker |
| 10 | weight: 5 |
| 11 | aliases: [] |
| 12 | --- |
| 13 | |
| 14 | # Client configuration examples |
| 15 | |
| 16 | This page walks through credentials, installation options, and MCP client JSON patterns for common editors and runtimes. |
| 17 | |
| 18 | The MCP server works with local Grafana and [Grafana Cloud](https://grafana.com/docs/grafan |
RemediationAI
The problem is that the client configuration examples documentation does not declare any authentication mechanism in the MCP manifest, leaving reviewers unable to verify how the server protects against unauthorized access. Add an explicit `auth` or `apiKey` field to all example manifests shown in the documentation and include a section explaining the authentication requirement (e.g., 'All examples require a Grafana API token set via GRAFANA_TOKEN'). This clarifies the security posture and ensures users know they must provide credentials. Verify by reviewing the documentation to confirm all code examples include an `auth` field and a clear statement of which credentials are required.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Gemini CLI |
| 3 | menuTitle: Gemini CLI |
| 4 | description: Set up the Grafana MCP server for the Google Gemini CLI. |
| 5 | keywords: |
| 6 | - Gemini |
| 7 | - Google |
| 8 | - MCP |
| 9 | - client |
| 10 | weight: 6 |
| 11 | aliases: [] |
| 12 | --- |
| 13 | |
| 14 | # Gemini CLI |
| 15 | |
| 16 | This guide helps you set up the `mcp-grafana` server for the Google Gemini CLI. |
| 17 | |
| 18 | ## Prerequisites |
| 19 | |
| 20 | - Gemini CLI installed (`npm install -g @google/gemini-cli`) |
| 21 | - Grafana 9.0+ with a service account token |
| 22 | - `mcp-grafana` binary in your PATH |
| 23 | |
| 24 | ## Configuration |
| 25 | |
| 26 | Gemini CLI stores MCP configuration |
RemediationAI
The problem is that the Gemini CLI client documentation does not declare any authentication mechanism in the MCP manifest, making the security model opaque to reviewers. Add an explicit `auth` or `apiKey` field to the manifest and document in the setup guide that users must provide a Grafana API token via environment variable or configuration (e.g., 'Set GRAFANA_TOKEN before running the server'). This makes the authentication requirement explicit and auditable. Verify by confirming the documentation includes a clear statement of which authentication method is required and that the manifest includes an `auth` field.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | { |
| 2 | "name": "grafana", |
| 3 | "version": "0.7.0", |
| 4 | "mcpServers": { |
| 5 | "grafana": { |
| 6 | "command": "${extensionPath}${/}mcp-grafana" |
| 7 | } |
| 8 | }, |
| 9 | "settings": [ |
| 10 | { |
| 11 | "name": "Grafana URL", |
| 12 | "description": "The base URL of your Grafana instance (e.g., https://grafana.example.com)", |
| 13 | "envVar": "GRAFANA_URL", |
| 14 | "required": true |
| 15 | }, |
| 16 | { |
| 17 | "name": "Service Account Token", |
| 18 | "description": "Your Grafana Service Account Token (glsa_...)", |
| 19 | "envVar": "GRAFANA_SERVICE_ |
RemediationAI
The problem is that `gemini-extension.json` declares MCP tools but does not include an `auth` or `apiKey` field in the manifest, leaving the authentication mechanism undeclared and unauditable. Add an explicit authentication field to the JSON manifest, such as `"auth": { "type": "apiKey", "name": "GRAFANA_TOKEN" }`, and document in the extension setup that users must provide a Grafana API token. This makes the security requirement explicit and allows reviewers to verify the authentication flow. Verify by checking that the manifest includes an `auth` field and confirming the extension documentation states which credentials must be configured.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Grafana MCP server |
| 3 | menuTitle: Grafana MCP server |
| 4 | description: Connect AI assistants and LLM clients to Grafana using the Model Context Protocol. |
| 5 | keywords: |
| 6 | - MCP |
| 7 | - Model Context Protocol |
| 8 | - Grafana |
| 9 | - AI |
| 10 | - LLM |
| 11 | weight: 1 |
| 12 | aliases: [] |
| 13 | --- |
| 14 | |
| 15 | # Grafana MCP server |
| 16 | |
| 17 | This documentation helps you install the Grafana MCP server, connect MCP-compatible clients, and configure authentication, transports, and tools. |
| 18 | |
| 19 | The Grafana MCP server is a [Model Context Protocol (MCP)](https://modelcont |
RemediationAI
The problem is that the main Grafana MCP server documentation does not declare any authentication mechanism in the MCP manifest, making it unclear how the server validates requests. Add an explicit `auth` field to the manifest (e.g., `"auth": { "type": "apiKey", "name": "GRAFANA_TOKEN" }`) and include a security section in the documentation explaining the authentication model and how users must provide credentials. This ensures reviewers can audit the security posture and users understand the requirements. Verify by confirming the documentation includes a dedicated security or authentication section and that all manifest examples include an `auth` field.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Claude Desktop |
| 3 | menuTitle: Claude Desktop |
| 4 | description: Set up the Grafana MCP server for Claude Desktop. |
| 5 | keywords: |
| 6 | - Claude Desktop |
| 7 | - MCP |
| 8 | - client |
| 9 | weight: 1 |
| 10 | aliases: [] |
| 11 | --- |
| 12 | |
| 13 | # Claude Desktop |
| 14 | |
| 15 | This guide helps you set up the `mcp-grafana` server for Claude Desktop. |
| 16 | |
| 17 | ## Prerequisites |
| 18 | |
| 19 | - Claude Desktop installed |
| 20 | - Grafana 9.0+ with a service account token |
| 21 | - `mcp-grafana` binary in your PATH |
| 22 | |
| 23 | ## Installation |
| 24 | |
| 25 | ### Option 1: Go install |
| 26 | |
| 27 | ```bash |
| 28 | GOBIN="$HOME/go/bin" go install github.co |
RemediationAI
The problem is that the Claude Desktop client documentation does not declare any authentication mechanism in the MCP manifest, leaving the security model unspecified. Add an explicit `auth` or `apiKey` field to the manifest and include a setup section stating that users must provide a Grafana API token via environment variable (e.g., 'Set GRAFANA_TOKEN before starting Claude Desktop'). This makes the authentication requirement clear and auditable. Verify by checking that the documentation includes a clear statement of the required credentials and that the manifest includes an `auth` field.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | { |
| 2 | "name": "grafana", |
| 3 | "version": "0.7.6", |
| 4 | "description": "A Model Context Protocol (MCP) server for Grafana providing access to dashboards, datasources, and querying capabilities", |
| 5 | "author": { |
| 6 | "name": "Grafana Labs" |
| 7 | }, |
| 8 | "homepage": "https://github.com/grafana/mcp-grafana", |
| 9 | "repository": "https://github.com/grafana/mcp-grafana", |
| 10 | "license": "Apache-2.0", |
| 11 | "mcpServers": { |
| 12 | "grafana": { |
| 13 | "command": "node", |
| 14 | "args": ["${CLAUDE_PLUGIN_ROOT}/.claude-plugin/install-binary |
RemediationAI
The problem is that `.claude-plugin/plugin.json` declares MCP tools but does not include an `auth` or `apiKey` field, leaving the authentication mechanism undeclared and unauditable. Add an explicit authentication field to the manifest, such as `"auth": { "type": "apiKey", "name": "GRAFANA_TOKEN" }`, and document in the plugin setup guide which credentials are required. This makes the security model explicit and allows reviewers to verify the authentication flow. Verify by confirming the manifest includes an `auth` field and the plugin documentation clearly states which credentials must be provided.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Cursor |
| 3 | menuTitle: Cursor |
| 4 | description: Set up the Grafana MCP server for Cursor. |
| 5 | keywords: |
| 6 | - Cursor |
| 7 | - MCP |
| 8 | - client |
| 9 | weight: 2 |
| 10 | aliases: [] |
| 11 | --- |
| 12 | |
| 13 | # Cursor |
| 14 | |
| 15 | This guide helps you set up the `mcp-grafana` server for Cursor. |
| 16 | |
| 17 | ## Prerequisites |
| 18 | |
| 19 | - Cursor IDE installed |
| 20 | - Grafana 9.0+ with a service account token |
| 21 | - `mcp-grafana` binary in your PATH |
| 22 | |
| 23 | ## Configuration |
| 24 | |
| 25 | Two options for configuration location: |
| 26 | |
| 27 | | Scope | Path | |
| 28 | | :------------------- |
RemediationAI
The problem is that the Cursor client documentation does not declare any authentication mechanism in the MCP manifest, making the security requirements opaque. Add an explicit `auth` or `apiKey` field to the manifest and include a setup section stating that users must configure a Grafana API token (e.g., 'Set GRAFANA_TOKEN in your environment'). This clarifies the authentication requirement and allows reviewers to audit the security model. Verify by confirming the documentation includes a clear statement of the required credentials and that the manifest includes an `auth` field.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | # Grafana MCP server |
| 2 | |
| 3 | [](https://github.com/grafana/mcp-grafana/actions/workflows/unit.yml) |
| 4 | [](https://github.com/grafana/mcp-grafana/actions/workflows/integration.yml) |
| 5 | [](https://github.com/grafana/mcp-grafana/actions |
RemediationAI
The problem is that the README does not declare any authentication mechanism in the MCP manifest, leaving reviewers unable to verify the security model. Add an explicit `auth` or `apiKey` field to the manifest and include a security or authentication section in the README explaining how users must provide credentials (e.g., 'Set GRAFANA_TOKEN environment variable'). This makes the authentication requirement clear and auditable. Verify by checking that the README includes a security section and that all manifest examples include an `auth` field.
MCP manifest declares tools but no authentication field is present (none of: auth, authorization, bearer, oauth, mtls, apiKey, api_key, basic, token, authToken). Absence is a weak signal โ confirm whether the server relies on network-layer or host-level auth, or declare the real mechanism explicitly so reviewers can audit it.
Evidence
| 1 | --- |
| 2 | title: Install with uvx |
| 3 | menuTitle: Install with uvx |
| 4 | description: Run the Grafana MCP server with uvx for a zero-install setup. |
| 5 | keywords: |
| 6 | - uvx |
| 7 | - uv |
| 8 | - MCP |
| 9 | - quick start |
| 10 | weight: 1 |
| 11 | aliases: [] |
| 12 | --- |
| 13 | |
| 14 | # Install with uvx |
| 15 | |
| 16 | [`uv`](https://docs.astral.sh/uv/) is Astral's Python package manager and toolchain. [`uvx`](https://docs.astral.sh/uv/guides/tools/) runs a command from a published package in an isolated environment, without installing that package globally. It is similar to `npx` for No |
RemediationAI
The problem is that the uvx installation documentation does not declare any authentication mechanism in the MCP manifest, leaving the security model unspecified. Add an explicit `auth` or `apiKey` field to the manifest and include a setup section in the documentation stating which credentials are required (e.g., 'Set GRAFANA_TOKEN before running uvx'). This ensures users understand the authentication requirement and reviewers can audit the security flow. Verify by confirming the documentation includes a clear statement of the required credentials and that the manifest includes an `auth` field.
GitHub Actions `uses:` reference is not pinned to a 40-character commit SHA. Tags (`@v4`) and branches (`@main`) are mutable โ a compromised maintainer or a tag rewrite can substitute malicious code into your CI pipeline silently. Pin to a SHA: `uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab`. For readability, include the version as a trailing comment: `# v4.1.1`. Tools like `pinact` / `ratchet` automate this. Allowed unpinned forms (excluded by the rule): - Local actions `.
Evidence
| 18 | - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 19 | with: |
| 20 | persist-credentials: false |
| 21 | - uses: grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1 # zizmor: ignore[unpinned-uses] |
| 22 | with: |
| 23 | website_directory: content/docs/mcp-grafana/next |
RemediationAI
The problem is that the GitHub Actions workflow uses `grafana/writers-toolkit/publish-technical-documentation@publish-technical-documentation/v1`, which is a mutable tag reference that could be silently replaced by a compromised maintainer or tag rewrite. Replace the tag reference with a pinned 40-character commit SHA: `uses: grafana/writers-toolkit/publish-technical-documentation@<FULL_SHA> # publish-technical-documentation/v1`. Use tools like `pinact` or `ratchet` to automate SHA pinning, or manually look up the commit hash for the tag and update the workflow. This ensures the exact version of the action is immutable and cannot be substituted with malicious code. Verify by running `git ls-remote https://github.com/grafana/writers-toolkit refs/tags/publish-technical-documentation/v1` to obtain the commit SHA, then confirm the workflow uses the full SHA.
Time-of-check-to-time-of-use race. Code calls `os.path.exists` / `fs.existsSync` to check a path, then `open` / `readFileSync` / `unlink` on the same name within a few lines โ without a lock or atomic-open. An attacker who can race the filesystem (symlink, file replacement) between the check and the use gets the action applied to a different target. Replace the check-then-use pattern with the action's own error handling: try the open and catch FileNotFoundError / ENOENT. For atomic creation use
Evidence
| 1 | #!/usr/bin/env node |
| 2 | |
| 3 | import { spawn } from 'node:child_process'; |
| 4 | import { createHash } from 'node:crypto'; |
| 5 | import { createWriteStream, existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs'; |
| 6 | import { tmpdir } from 'node:os'; |
| 7 | import { join } from 'node:path'; |
| 8 | import process from 'node:process'; |
| 9 | import { pipeline } from 'node:stream/promises'; |
| 10 | |
| 11 | const PLUGIN_ROOT = process.env.CLAUDE_PLUGIN_ROOT; |
| 12 | if (!PLUGIN_ROOT) { |
| 13 | console.error('Error: CLAUDE_PLUGIN_ROOT environment vari |
RemediationAI
The problem is that `install-binary.mjs` checks if a file exists with `existsSync()` and then reads or writes it within a few lines, creating a race condition where an attacker can replace the file (via symlink or direct replacement) between the check and the use. Replace the check-then-use pattern by removing the `existsSync()` call and wrapping the `readFileSync()` or `writeFileSync()` in a try-catch block that handles ENOENT errors: `try { const data = readFileSync(path); } catch (err) { if (err.code === 'ENOENT') { /* handle missing file */ } }`. This eliminates the race window by performing the action atomically and handling the error if the file does not exist. Verify by testing that the code correctly handles file-not-found errors and that no separate existence check precedes the file operation.