unraid-code-mode-mcp
Allows interaction with the Unraid API via GraphQL, enabling AI agents to query and manage Unraid server resources such as shares, VMs, Docker containers, array, and more.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@unraid-code-mode-mcpWhat's the current array status and list shares?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Two tools, one API
This Model Context Protocol server exposes search and execute.
The agent searches the API reference, then runs JavaScript inside a QuickJS WASM sandbox.
API calls go through the host; credentials remain outside the sandbox.
Search the schema. Introspection with a bundled SDL fallback.
Call typed operations.
unraid.local.query.*andunraid.local.mutation.*, plus raw GraphQL.Batch independent reads. Sequential
awaitandPromise.alluse the Promise-callback host bridge.Choose credentials per request. Environment variables for stdio;
X-Unraid-*headers for HTTP.
Related MCP server: mcp-graphql-bridge
Get started
Install from source and point your MCP client at the built dist/index.js.
Requirements
Node.js 22.19.0 or newer and npm. CI checks Node 22 and 24.
Unraid 7.2+ with an API key and a reachable GraphQL API.
Build from source
git clone https://github.com/jmpijll/unraid-code-mode-mcp.git
cd unraid-code-mode-mcp
npm ci
cp .env.example .env
# Edit .env: UNRAID_BASE_URL and UNRAID_API_KEY.
npm run build
npm startThe shell examples use Bash. In PowerShell, use Copy-Item .env.example .env and
set variables with $env:NAME = 'value'.
Configure your MCP client with node /absolute/path/to/unraid-code-mode-mcp/dist/index.js.
Use an absolute path and supply credentials through the client's environment configuration
when its working directory does not contain your .env file.
See the client setup and usage guide.
For hosted use, set MCP_TRANSPORT=http and follow the per-request credential contract.
Docker instructions are in docker-compose.yml.
Example session
After discovering the operation with the search tool, use the execute tool:
const info = await unraid.local.query.info({ fields: 'os { distro release kernel }' });
return info;See the usage guide for search recipes, configuration and additional call shapes.
Know the boundaries
Area | Current boundary |
API coverage | Local GraphQL only; |
Mutations | VM start/stop was historically verified; other mutations need separate validation. |
Workers | Scaffold; full MCP transport is not implemented. |
Sandbox | Resource limits bound each invocation; allowed API calls still act with the supplied account's permissions. |
Verification status
Earlier maintainer runs cover one Unraid 7.2 box, VM start/stop, cursor-agent and an opencode schema-only run. The opencode live execute run encountered an upstream CSRF failure. Hosted multi-tenancy and other hardware remain unverified. See the setup and verification reference for the detailed historical evidence and remaining work. New verification reports should identify the server revision, client, upstream version and operations actually exercised.
Project status
Public beta · v0.1.0-beta.3. Install from source; the package remains private and is not published to npm.
Privacy
The host sends API requests to the service configured for this server. Tool results and captured sandbox logs are returned to your MCP client; that client may send them to its configured model provider. Spec caches may be written locally.
Keep .env files and credentials private. Redact account identifiers, IP addresses and
service data before sharing logs or verification reports. See SECURITY.md
for vulnerability reporting.
Development and contribution
npm run checkcheck runs lint, formatting, typecheck, mocked tests and the build.
It also verifies the built MCP server version and its two tools without tenant credentials
or upstream network access. npm run cf:check validates the Worker bundle without deploying it. See CONTRIBUTING.md
for the repository layout and contribution checks, and AGENTS.md for
architectural invariants. Live API tests require separate credentials and verification scope.
Documentation
License and acknowledgements
MIT. Built with TypeScript, the MCP SDK and QuickJS, following the Cloudflare Code Mode pattern.
Available Tools
2 toolsexecuteExecute Unraid GraphQL operationsA
Run Unraid GraphQL operations by writing JavaScript that uses the unraid.local namespace.
Although the sandbox is JavaScript, the calls below appear synchronous — await is supported and recommended. Each call returns once the host-side HTTP request settles.
Surfaces:
unraid.local.graphql({ query, variables, operationName })— POST any GraphQL document directly to/graphql. Returns the unwrappeddatafield.unraid.local.query.<fieldName>({ args, fields })— typed query call.argsmap to GraphQL variables;fieldsis either a GraphQL selection-set string (no wrapping braces) or a shorthand array of leaf field names.fieldsis required for non-scalar return types.unraid.local.mutation.<fieldName>({ args, fields })— typed mutation call, same rules.unraid.local.request({ method, path, body, headers })— raw HTTP escape hatch for endpoints outside GraphQL. Returns{ status, body, headers }.unraid.local.spec—{ title, version, sourceUrl, queryCount, mutationCount }for diagnostics.
Operations are async — use await. The final expression is the tool result.
Examples
// Read system info via a typed query.
const info = await unraid.local.query.info({
fields: ['os { distro release kernel }', 'versions { unraid api }', 'cpu { manufacturer brand cores }'].join(' '),
});
return info;// List Docker containers with a shorthand selection.
const containers = await unraid.local.query.dockerContainers({
fields: ['id', 'names', 'state', 'status', 'image'],
});
return containers.map(function (c) { return { name: c.names[0], state: c.state }; });// Raw GraphQL document — handy for fragments and aliases.
const data = await unraid.local.graphql({
query: 'query { array { state capacity { kilobytes { free total } } } }',
});
return data;Limits
Hard ceiling on API calls per execute; exceeded → error.
Sandbox memory + time bounded.
Credentials never enter the sandbox.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript code to execute against the live Unraid GraphQL API. Use await — operations are async. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description fully discloses behavioral traits: async execution, sandbox memory/time bounds, credential isolation, and API call limits leading to errors. It also explains return value structures, leaving little ambiguity about the tool's behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with clear sections (overview, methods, examples, limits) and front-loads the purpose. While each section contributes value, the examples are somewhat lengthy, and the overall length could be trimmed slightly without losing clarity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no output schema, the description covers return types, error conditions, limits, and provides multiple examples. It sufficiently equips an agent to use the tool correctly, lacking only explicit comparison to siblings but otherwise complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The single parameter 'code' has a basic schema description, but the tool description adds immense value by detailing how to write the JavaScript, available methods, and examples. This significantly enhances the agent's understanding beyond the schema alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's purpose as executing JavaScript to run Unraid GraphQL operations using the `unraid.local` namespace. It differentiates from the sibling 'search' by implying a code execution context rather than a simple search, and provides specific methods and examples.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description offers extensive guidance on internal usage: async/await, typed vs raw calls, examples, and limits. However, it does not explicitly contrast with the sibling tool 'search' or state when to use this tool versus that alternative, which would improve cross-tool decision-making.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
searchSearch Unraid GraphQL schemaA
Search the Unraid GraphQL schema by writing JavaScript.
The sandbox is read-only — no network. Use this tool to discover what to call before invoking execute.
Globals
index—{ namespace, title, version, sourceUrl, operations[], types }. Each operation is the compact form:{ kind: 'query'|'mutation', namespace: 'local', name, description, args, returnType, returnTypeFields, deprecated }.unraid.local— same shape asindexfor parity with the execute tool. May benullif no spec is loaded.searchOperations(query, limit?)— text-ranked search across name, description, args, and return type.getOperation(name)— full operation info including arg types and return-type field hints.findOperationsByName(substring)— list operations whose name contains the substring (case-insensitive).getType(typeName)— full named-type info (fields for OBJECT/INTERFACE/INPUT_OBJECT, members for UNION, enumValues for ENUM).console.log()— captured into the tool output.
Examples
// All queries that mention "docker"
searchOperations('docker', 20).map(function (op) { return op.name; });// Full detail on the `info` query
getOperation('info');// Inspect the Array type to see its fields
getType('Array');| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | JavaScript code to execute against the operation index. The final expression is returned. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries full burden for behavioral disclosure. It explicitly states the sandbox is read-only and has no network, which are critical traits. It also describes the sandbox environment, available globals, and that console.log output is captured. This provides sufficient transparency, though it could mention response format or error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured with sections for globals, functions, and examples. Every sentence adds value. However, it is somewhat verbose; the examples and function lists could be slightly condensed without losing clarity. Still, the structure aids comprehension.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (JavaScript execution in a sandbox with multiple globals and no output schema), the description is remarkably complete. It covers the environment, all available functions with their purpose, input/output shapes, and concrete examples. An agent could immediately use the tool correctly based on this description alone.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has one parameter 'code' with a minimal description. The tool's description compensates extensively by detailing the global functions and variables available (e.g., searchOperations, getOperation, getType), their signatures, and usage examples. This adds immense meaning beyond the schema, effectively teaching the agent how to construct the JavaScript code.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Search the Unraid GraphQL schema by writing JavaScript.' It specifies the resource and action, and distinguishes from sibling 'execute' by positioning this as a discovery tool before invocation. The verb 'search' and resource 'Unraid GraphQL schema' are specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context: use this tool to discover what to call before invoking 'execute', and notes the sandbox is read-only with no network. It does not explicitly state when not to use it, but the sibling relationship and discovery purpose imply that. The examples further guide usage, making it nearly complete.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
2 tool updates
v0.1.0-beta.3- First observed
execute - First observed
search
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: 'execute' runs GraphQL operations, while 'search' explores the schema. There is no overlap, and their descriptions unambiguously differentiate them.
Both tool names are single, lowercase verbs that directly describe their function. Although they lack a verb_noun pattern, they follow a consistent, simple style that is easy to understand.
With only 2 tools, each covers a fundamental aspect of interacting with a GraphQL API: discovery and execution. The count is perfectly scoped for the server's purpose, avoiding unnecessary complexity.
The server provides all necessary functionality: you can explore the schema with 'search' and then execute any discovered operation with 'execute'. There are no obvious gaps in coverage.
Maintenance
Related MCP Connectors
The Grafbase MCP server sits in front of a GraphQL API and exposes an MCP protocol-compliant interface that allows AI agents and LLMs to explore and query GraphQL APIs using natural language. It provides tools to search schemas, introspect types and fields, and execute GraphQL queries while minimizing context bloat by returning only relevant schema subsets, with built-in support for authentication, authorization, and configurable access control.
An MCP server that gives your AI access to the source code and docs of all public github repos
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA MCP server that exposes GraphQL schema information to LLMs like Claude. This server allows an LLM to explore and understand large GraphQL schemas through a set of specialized tools, without needing to load the whole schema into the context35 npm46MIT
- AlicenseAqualityAmaintenanceA generic MCP (Model Context Protocol) server that bridges any GraphQL API to Claude Code. It introspects your GraphQL schema and exposes each query and mutation as an individual tool, letting Claude interact with your API directly.214 npm1MIT
- AlicenseNot gradedqualityDmaintenanceA Node.js server that enables LLMs to inspect GraphQL schemas and retrieve information about queries, mutations, and types via MCP.7 npm1MIT
- AlicenseNot gradedqualityCmaintenanceMCP server that exposes GraphQL operations as typed tools for AI models, with schema exploration, validation, execution, hot reload, OAuth, prompts, MCP Apps widgets, and OpenTelemetry support.14 npmMIT