feat: harden API safety and expand command docs with full test coverage

This commit is contained in:
Jacob Magar
2026-02-15 22:15:51 -05:00
parent d791c6b6b7
commit abb7915672
60 changed files with 7122 additions and 1247 deletions

240
docs/DESTRUCTIVE_ACTIONS.md Normal file
View File

@@ -0,0 +1,240 @@
# Destructive Actions Inventory
This file lists all destructive actions across the unraid-mcp tools. Fill in the "Testing Strategy" column to specify how each should be tested in the mcporter integration test suite.
**Last Updated:** 2026-02-15
---
## Summary
- **Total Destructive Actions:** 8 (after removing 4 array operations)
- **Tools with Destructive Actions:** 6
- **Environment Variable Gates:** 6 (one per tool)
---
## Destructive Actions by Tool
### 1. Docker (1 action)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `remove` | Permanently delete a Docker container | **HIGH** - Data loss, irreversible | `UNRAID_ALLOW_DOCKER_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Container must be stopped first
- Removes container config and any non-volume data
- Cannot be undone
---
### 2. Virtual Machines (2 actions)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `force_stop` | Forcefully power off a running VM (equivalent to pulling power cord) | **MEDIUM** - Severe but recoverable, risk of data corruption | `UNRAID_ALLOW_VM_DESTRUCTIVE` | **TODO: Specify testing approach** |
| `reset` | Hard reset a VM (power cycle without graceful shutdown) | **MEDIUM** - Severe but recoverable, risk of data corruption | `UNRAID_ALLOW_VM_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Both bypass graceful shutdown procedures
- May corrupt VM filesystem if used during write operations
- Use `stop` action instead for graceful shutdown
---
### 3. Notifications (2 actions)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `delete` | Permanently delete a notification | **HIGH** - Data loss, irreversible | `UNRAID_ALLOW_NOTIFICATIONS_DESTRUCTIVE` | **TODO: Specify testing approach** |
| `delete_archived` | Permanently delete all archived notifications | **HIGH** - Bulk data loss, irreversible | `UNRAID_ALLOW_NOTIFICATIONS_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Cannot recover deleted notifications
- `delete_archived` affects ALL archived notifications (bulk operation)
---
### 4. Rclone (1 action)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `delete_remote` | Permanently delete an rclone remote configuration | **HIGH** - Data loss, irreversible | `UNRAID_ALLOW_RCLONE_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Removes cloud storage connection configuration
- Does NOT delete data in the remote storage
- Must reconfigure remote from scratch if deleted
---
### 5. Users (1 action)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `delete` | Permanently delete a user account | **HIGH** - Data loss, irreversible | `UNRAID_ALLOW_USERS_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Removes user account and permissions
- Cannot delete the root user
- User's data may remain but become orphaned
---
### 6. API Keys (1 action)
| Action | Description | Risk Level | Env Var Gate | Testing Strategy |
|--------|-------------|------------|--------------|------------------|
| `delete` | Permanently delete an API key | **HIGH** - Data loss, irreversible, breaks integrations | `UNRAID_ALLOW_KEYS_DESTRUCTIVE` | **TODO: Specify testing approach** |
**Notes:**
- Immediately revokes API key access
- Will break any integrations using the deleted key
- Cannot be undone - must create new key
---
## Removed Actions (No Longer Exposed)
These actions were previously marked as destructive but have been **removed** from the array tool per the implementation plan:
| Action | Former Risk Level | Reason for Removal |
|--------|-------------------|-------------------|
| `start` | CRITICAL | System-wide impact - should not be exposed via MCP |
| `stop` | CRITICAL | System-wide impact - should not be exposed via MCP |
| `shutdown` | CRITICAL | System-wide impact - could cause data loss |
| `reboot` | CRITICAL | System-wide impact - disrupts all services |
---
## Testing Strategy Options
Choose one of the following for each action in the "Testing Strategy" column:
### Option 1: Mock/Validation Only
- Test parameter validation
- Test `confirm=True` requirement
- Test env var gate requirement
- **DO NOT** execute the actual action
### Option 2: Dry-Run Testing
- Test with `confirm=false` to verify rejection
- Test without env var to verify gate
- **DO NOT** execute with both gates passed
### Option 3: Test Server Execution
- Execute on a dedicated test Unraid server (e.g., shart)
- Requires pre-created test resources (containers, VMs, notifications)
- Verify action succeeds and state changes as expected
- Clean up after test
### Option 4: Manual Test Checklist
- Document manual verification steps
- Do not automate in mcporter suite
- Requires human operator to execute and verify
### Option 5: Skip Testing
- Too dangerous to automate
- Rely on unit tests only
- Document why testing is skipped
---
## Example Testing Strategies
**Safe approach (recommended for most):**
```
Option 1: Mock/Validation Only
- Verify action requires UNRAID_ALLOW_DOCKER_DESTRUCTIVE=true
- Verify action requires confirm=True
- Do not execute actual deletion
```
**Comprehensive approach (for test server only):**
```
Option 3: Test Server Execution on 'shart'
- Create test container 'mcporter-test-container'
- Execute remove with gates enabled
- Verify container is deleted
- Clean up not needed (container already removed)
```
**Hybrid approach:**
```
Option 1 + Option 4: Mock validation + Manual checklist
- Automated: Test gate requirements
- Manual: Human operator verifies on test server
```
---
## Usage in mcporter Tests
Each tool test script will check the testing strategy:
```bash
# Example from test_docker.sh
test_remove_action() {
local strategy="TODO: Specify testing approach" # From this file
case "$strategy" in
*"Option 1"*|*"Mock"*)
# Mock/validation testing
test_remove_requires_env_var
test_remove_requires_confirm
;;
*"Option 3"*|*"Test Server"*)
# Real execution on test server
if [[ "$UNRAID_TEST_SERVER" != "unraid-shart" ]]; then
echo "SKIP: Destructive test only runs on test server"
return 2
fi
test_remove_real_execution
;;
*"Option 5"*|*"Skip"*)
echo "SKIP: Testing disabled for this action"
return 2
;;
esac
}
```
---
## Security Model
**Two-tier security for destructive actions:**
1. **Environment Variable Gate** (first line of defense)
- Must be explicitly enabled per tool
- Defaults to disabled (safe)
- Prevents accidental execution
2. **Runtime Confirmation** (second line of defense)
- Must pass `confirm=True` in each call
- Forces explicit acknowledgment per operation
- Cannot be cached or preset
**Both must pass for execution.**
---
## Next Steps
1. **Fill in Testing Strategy column** for each action above
2. **Create test fixtures** if using Option 3 (test containers, VMs, etc.)
3. **Implement tool test scripts** following the specified strategies
4. **Document any special setup** required for destructive testing
---
## Questions to Consider
For each action, ask:
- Is this safe to automate on a test server?
- Do we have test fixtures/resources available?
- What cleanup is required after testing?
- What's the blast radius if something goes wrong?
- Can we verify the action worked without side effects?

203
docs/MARKETPLACE.md Normal file
View File

@@ -0,0 +1,203 @@
# Claude Code Marketplace Setup
This document explains the Claude Code marketplace and plugin structure for the Unraid MCP project.
## What Was Created
### 1. Marketplace Manifest (`.claude-plugin/marketplace.json`)
The marketplace catalog that lists all available plugins in this repository.
**Location:** `.claude-plugin/marketplace.json`
**Contents:**
- Marketplace metadata (name, version, owner, repository)
- Plugin catalog with the "unraid" skill
- Categories and tags for discoverability
### 2. Plugin Manifest (`skills/unraid/.claude-plugin/plugin.json`)
The individual plugin configuration for the Unraid skill.
**Location:** `skills/unraid/.claude-plugin/plugin.json`
**Contents:**
- Plugin name, version, author
- Repository and homepage links
- Plugin-specific metadata
### 3. Documentation
- `.claude-plugin/README.md` - Marketplace installation guide
- Updated root `README.md` with plugin installation section
### 4. Validation Script
- `scripts/validate-marketplace.sh` - Automated validation of marketplace structure
## Installation Methods
### Method 1: GitHub Distribution (Recommended for Users)
Once you push this to GitHub, users can install via:
```bash
# Add your marketplace
/plugin marketplace add jmagar/unraid-mcp
# Install the Unraid skill
/plugin install unraid @unraid-mcp
```
### Method 2: Local Installation (Development)
For testing locally before publishing:
```bash
# Add local marketplace
/plugin marketplace add /home/jmagar/workspace/unraid-mcp
# Install the plugin
/plugin install unraid @unraid-mcp
```
### Method 3: Direct URL
Users can also install from a specific commit or branch:
```bash
# From specific branch
/plugin marketplace add jmagar/unraid-mcp#main
# From specific commit
/plugin marketplace add jmagar/unraid-mcp#abc123
```
## Plugin Structure
```
unraid-mcp/
├── .claude-plugin/ # Marketplace manifest
│ ├── marketplace.json
│ └── README.md
├── skills/unraid/ # Plugin directory
│ ├── .claude-plugin/ # Plugin manifest
│ │ └── plugin.json
│ ├── SKILL.md # Skill documentation
│ ├── README.md # Plugin documentation
│ ├── examples/ # Example scripts
│ ├── scripts/ # Helper scripts
│ └── references/ # API reference docs
└── scripts/
└── validate-marketplace.sh # Validation tool
```
## Marketplace Metadata
### Categories
- `infrastructure` - Server management and monitoring tools
### Tags
- `unraid` - Unraid-specific functionality
- `monitoring` - System monitoring capabilities
- `homelab` - Homelab automation
- `graphql` - GraphQL API integration
- `docker` - Docker container management
- `virtualization` - VM management
## Publishing Checklist
Before publishing to GitHub:
1. **Validate Structure**
```bash
./scripts/validate-marketplace.sh
```
2. **Update Version Numbers**
- Bump version in `.claude-plugin/marketplace.json`
- Bump version in `skills/unraid/.claude-plugin/plugin.json`
- Update version in `README.md` if needed
3. **Test Locally**
```bash
/plugin marketplace add .
/plugin install unraid @unraid-mcp
```
4. **Commit and Push**
```bash
git add .claude-plugin/ skills/unraid/.claude-plugin/
git commit -m "feat: add Claude Code marketplace configuration"
git push origin main
```
5. **Create Release Tag** (Optional)
```bash
git tag -a v0.2.0 -m "Release v0.2.0"
git push origin v0.2.0
```
## User Experience
After installation, users will:
1. **See the skill in their skill list**
```bash
/skill list
```
2. **Access Unraid functionality directly**
- Claude Code will automatically detect when to invoke the skill
- Users can explicitly invoke with `/unraid`
3. **Have access to all helper scripts**
- Example scripts in `examples/`
- Utility scripts in `scripts/`
- API reference in `references/`
## Maintenance
### Updating the Plugin
To release a new version:
1. Make changes to the plugin
2. Update version in `skills/unraid/.claude-plugin/plugin.json`
3. Update marketplace catalog in `.claude-plugin/marketplace.json`
4. Run validation: `./scripts/validate-marketplace.sh`
5. Commit and push
Users with the plugin installed will see the update available and can upgrade with:
```bash
/plugin update unraid
```
### Adding More Plugins
To add additional plugins to this marketplace:
1. Create new plugin directory: `skills/new-plugin/`
2. Add plugin manifest: `skills/new-plugin/.claude-plugin/plugin.json`
3. Update marketplace catalog: add entry to `.plugins[]` array in `.claude-plugin/marketplace.json`
4. Validate: `./scripts/validate-marketplace.sh`
## Support
- **Repository:** https://github.com/jmagar/unraid-mcp
- **Issues:** https://github.com/jmagar/unraid-mcp/issues
- **Documentation:** See `.claude-plugin/README.md` and `skills/unraid/README.md`
## Validation
Run the validation script anytime to ensure marketplace integrity:
```bash
./scripts/validate-marketplace.sh
```
This checks:
- Manifest file existence and validity
- JSON syntax
- Required fields
- Plugin structure
- Source path accuracy
- Documentation completeness
All 17 checks must pass before publishing.

218
docs/PUBLISHING.md Normal file
View File

@@ -0,0 +1,218 @@
# Publishing Guide for unraid-mcp
This guide covers how to publish `unraid-mcp` to PyPI so it can be installed via `uvx` or `pip` from anywhere.
## Prerequisites
1. **PyPI Account**: Create accounts on both:
- [Test PyPI](https://test.pypi.org/account/register/) (for testing)
- [PyPI](https://pypi.org/account/register/) (for production)
2. **API Tokens**: Generate API tokens for automated publishing:
- Test PyPI: https://test.pypi.org/manage/account/token/
- PyPI: https://pypi.org/manage/account/token/
3. **Save Tokens Securely**:
```bash
# Add to ~/.pypirc (never commit this file!)
cat > ~/.pypirc << 'EOF'
[distutils]
index-servers =
pypi
testpypi
[pypi]
username = __token__
password = pypi-YOUR-API-TOKEN-HERE
[testpypi]
username = __token__
password = pypi-YOUR-TEST-API-TOKEN-HERE
repository = https://test.pypi.org/legacy/
EOF
chmod 600 ~/.pypirc # Secure the file
```
## Version Management
Before publishing, update the version in `pyproject.toml`:
```toml
[project]
version = "0.2.1" # Follow semantic versioning: MAJOR.MINOR.PATCH
```
**Semantic Versioning Guide:**
- **MAJOR** (1.0.0): Breaking changes
- **MINOR** (0.2.0): New features (backward compatible)
- **PATCH** (0.2.1): Bug fixes (backward compatible)
## Publishing Workflow
### 1. Clean Previous Builds
```bash
# Remove old build artifacts
rm -rf dist/ build/ *.egg-info/
```
### 2. Run Quality Checks
```bash
# Lint and format code
uv run ruff check unraid_mcp/
uv run ruff format unraid_mcp/
# Type check
uv run ty check unraid_mcp/
# Run tests
uv run pytest
# Check coverage
uv run pytest --cov=unraid_mcp --cov-report=html
```
### 3. Build the Package
```bash
# Build both wheel and source distribution
uv run python -m build
```
This creates:
- `dist/unraid_mcp-VERSION-py3-none-any.whl` (wheel)
- `dist/unraid_mcp-VERSION.tar.gz` (source distribution)
### 4. Validate the Package
```bash
# Check that the package meets PyPI requirements
uv run twine check dist/*
```
Expected output: `PASSED` for both files.
### 5. Test on Test PyPI (IMPORTANT!)
Always test on Test PyPI first:
```bash
# Upload to Test PyPI
uv run twine upload --repository testpypi dist/*
# Test installation from Test PyPI
uvx --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ unraid-mcp-server
# Or with pip in a test environment
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ unraid-mcp
```
**Note**: `--extra-index-url https://pypi.org/simple/` is needed because dependencies come from production PyPI.
### 6. Publish to PyPI (Production)
Once testing passes:
```bash
# Upload to production PyPI
uv run twine upload dist/*
```
### 7. Verify Installation
```bash
# Install and run from PyPI using uvx (no installation required!)
uvx unraid-mcp-server --help
# Or install globally
uv tool install unraid-mcp
# Or install in a project
uv add unraid-mcp
```
## Post-Publishing Checklist
- [ ] Create a GitHub Release with the same version tag
- [ ] Update CHANGELOG.md with release notes
- [ ] Test installation on a fresh machine
- [ ] Update documentation if API changed
- [ ] Announce release (if applicable)
## Running from Any Machine with uvx
Once published to PyPI, users can run the server without installing:
```bash
# Run directly with uvx (recommended)
uvx unraid-mcp-server
# Or with custom environment variables
UNRAID_API_URL=https://your-server uvx unraid-mcp-server
```
**Benefits of uvx:**
- No installation required
- Automatic virtual environment management
- Always uses the latest version (or specify version: `uvx unraid-mcp-server@0.2.0`)
- Clean execution environment
## Automation with GitHub Actions (Future)
Consider adding `.github/workflows/publish.yml`:
```yaml
name: Publish to PyPI
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Build package
run: uv run python -m build
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: uv run twine upload dist/*
```
> **Tip:** Consider using [PyPI Trusted Publishing](https://docs.pypi.org/trusted-publishers/) instead of API token secrets. Trusted Publishing uses OpenID Connect (OIDC) to authenticate directly from GitHub Actions without storing long-lived secrets.
## Troubleshooting
### "File already exists" Error
PyPI doesn't allow re-uploading the same version. Options:
1. Increment version number in `pyproject.toml`
2. Delete old dist files and rebuild
### Missing Dependencies
If installation fails due to missing dependencies:
1. Check that all dependencies are in `pyproject.toml` `dependencies` section
2. Ensure dependency version constraints are correct
3. Test in a clean virtual environment
### Import Errors After Installation
If the package installs but imports fail:
1. Verify package structure in wheel: `unzip -l dist/*.whl`
2. Check that `__init__.py` files exist in all package directories
3. Ensure `packages = ["unraid_mcp"]` in `[tool.hatch.build.targets.wheel]`
## Resources
- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/)
- [Semantic Versioning](https://semver.org/)
- [Python Packaging User Guide](https://packaging.python.org/)
- [uv Documentation](https://docs.astral.sh/uv/)
- [Twine Documentation](https://twine.readthedocs.io/)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,290 @@
# Unraid GraphQL API Operations
Generated via live introspection at `2026-02-15 23:45:50Z`.
## Schema Summary
- Query root: `Query`
- Mutation root: `Mutation`
- Subscription root: `Subscription`
- Total types: **164**
- Total directives: **6**
- Type kinds:
- `ENUM`: 32
- `INPUT_OBJECT`: 16
- `INTERFACE`: 2
- `OBJECT`: 103
- `SCALAR`: 10
- `UNION`: 1
## Queries
Total: **46**
### `apiKey(id: PrefixedID!): ApiKey`
#### Required Permissions: - Action: **READ_ANY** - Resource: **API_KEY**
Arguments:
- `id`: `PrefixedID!`
### `apiKeyPossiblePermissions(): [Permission!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **PERMISSION** #### Description: All possible permissions for API keys
### `apiKeyPossibleRoles(): [Role!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **PERMISSION** #### Description: All possible roles for API keys
### `apiKeys(): [ApiKey!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **API_KEY**
### `array(): UnraidArray!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ARRAY**
### `config(): Config!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG**
### `customization(): Customization`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CUSTOMIZATIONS**
### `disk(id: PrefixedID!): Disk!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **DISK**
Arguments:
- `id`: `PrefixedID!`
### `disks(): [Disk!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **DISK**
### `docker(): Docker!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **DOCKER**
### `flash(): Flash!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **FLASH**
### `getApiKeyCreationFormSchema(): ApiKeyFormSettings!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **API_KEY** #### Description: Get JSON Schema for API key creation form
### `getAvailableAuthActions(): [AuthAction!]!`
Get all available authentication actions with possession
### `getPermissionsForRoles(roles: [Role!]!): [Permission!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **PERMISSION** #### Description: Get the actual permissions that would be granted by a set of roles
Arguments:
- `roles`: `[Role!]!`
### `info(): Info!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **INFO**
### `isInitialSetup(): Boolean!`
### `isSSOEnabled(): Boolean!`
### `logFile(lines: Int, path: String!, startLine: Int): LogFileContent!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **LOGS**
Arguments:
- `lines`: `Int`
- `path`: `String!`
- `startLine`: `Int`
### `logFiles(): [LogFile!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **LOGS**
### `me(): UserAccount!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ME**
### `metrics(): Metrics!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **INFO**
### `notifications(): Notifications!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **NOTIFICATIONS** #### Description: Get all notifications
### `oidcConfiguration(): OidcConfiguration!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG** #### Description: Get the full OIDC configuration (admin only)
### `oidcProvider(id: PrefixedID!): OidcProvider`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG** #### Description: Get a specific OIDC provider by ID
Arguments:
- `id`: `PrefixedID!`
### `oidcProviders(): [OidcProvider!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG** #### Description: Get all configured OIDC providers (admin only)
### `online(): Boolean!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ONLINE**
### `owner(): Owner!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **OWNER**
### `parityHistory(): [ParityCheck!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ARRAY**
### `plugins(): [Plugin!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG** #### Description: List all installed plugins with their metadata
### `previewEffectivePermissions(permissions: [AddPermissionInput!], roles: [Role!]): [Permission!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **PERMISSION** #### Description: Preview the effective permissions for a combination of roles and explicit permissions
Arguments:
- `permissions`: `[AddPermissionInput!]`
- `roles`: `[Role!]`
### `publicOidcProviders(): [PublicOidcProvider!]!`
Get public OIDC provider information for login buttons
### `publicPartnerInfo(): PublicPartnerInfo`
### `publicTheme(): Theme!`
### `rclone(): RCloneBackupSettings!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **FLASH**
### `registration(): Registration`
#### Required Permissions: - Action: **READ_ANY** - Resource: **REGISTRATION**
### `server(): Server`
#### Required Permissions: - Action: **READ_ANY** - Resource: **SERVERS**
### `servers(): [Server!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **SERVERS**
### `services(): [Service!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **SERVICES**
### `settings(): Settings!`
### `shares(): [Share!]!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **SHARE**
### `upsConfiguration(): UPSConfiguration!`
### `upsDeviceById(id: String!): UPSDevice`
Arguments:
- `id`: `String!`
### `upsDevices(): [UPSDevice!]!`
### `validateOidcSession(token: String!): OidcSessionValidation!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **CONFIG** #### Description: Validate an OIDC session token (internal use for CLI validation)
Arguments:
- `token`: `String!`
### `vars(): Vars!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **VARS**
### `vms(): Vms!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **VMS** #### Description: Get information about all VMs on the system
## Mutations
Total: **22**
### `addPlugin(input: PluginManagementInput!): Boolean!`
#### Required Permissions: - Action: **UPDATE_ANY** - Resource: **CONFIG** #### Description: Add one or more plugins to the API. Returns false if restart was triggered automatically, true if manual restart is required.
Arguments:
- `input`: `PluginManagementInput!`
### `apiKey(): ApiKeyMutations!`
### `archiveAll(importance: NotificationImportance): NotificationOverview!`
Arguments:
- `importance`: `NotificationImportance`
### `archiveNotification(id: PrefixedID!): Notification!`
Marks a notification as archived.
Arguments:
- `id`: `PrefixedID!`
### `archiveNotifications(ids: [PrefixedID!]!): NotificationOverview!`
Arguments:
- `ids`: `[PrefixedID!]!`
### `array(): ArrayMutations!`
### `configureUps(config: UPSConfigInput!): Boolean!`
Arguments:
- `config`: `UPSConfigInput!`
### `createNotification(input: NotificationData!): Notification!`
Creates a new notification record
Arguments:
- `input`: `NotificationData!`
### `customization(): CustomizationMutations!`
### `deleteArchivedNotifications(): NotificationOverview!`
Deletes all archived notifications on server.
### `deleteNotification(id: PrefixedID!, type: NotificationType!): NotificationOverview!`
Arguments:
- `id`: `PrefixedID!`
- `type`: `NotificationType!`
### `docker(): DockerMutations!`
### `initiateFlashBackup(input: InitiateFlashBackupInput!): FlashBackupStatus!`
Initiates a flash drive backup using a configured remote.
Arguments:
- `input`: `InitiateFlashBackupInput!`
### `parityCheck(): ParityCheckMutations!`
### `rclone(): RCloneMutations!`
### `recalculateOverview(): NotificationOverview!`
Reads each notification to recompute & update the overview.
### `removePlugin(input: PluginManagementInput!): Boolean!`
#### Required Permissions: - Action: **DELETE_ANY** - Resource: **CONFIG** #### Description: Remove one or more plugins from the API. Returns false if restart was triggered automatically, true if manual restart is required.
Arguments:
- `input`: `PluginManagementInput!`
### `unarchiveAll(importance: NotificationImportance): NotificationOverview!`
Arguments:
- `importance`: `NotificationImportance`
### `unarchiveNotifications(ids: [PrefixedID!]!): NotificationOverview!`
Arguments:
- `ids`: `[PrefixedID!]!`
### `unreadNotification(id: PrefixedID!): Notification!`
Marks a notification as unread.
Arguments:
- `id`: `PrefixedID!`
### `updateSettings(input: JSON!): UpdateSettingsResponse!`
#### Required Permissions: - Action: **UPDATE_ANY** - Resource: **CONFIG**
Arguments:
- `input`: `JSON!`
### `vm(): VmMutations!`
## Subscriptions
Total: **11**
### `arraySubscription(): UnraidArray!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ARRAY**
### `logFile(path: String!): LogFileContent!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **LOGS**
Arguments:
- `path`: `String!`
### `notificationAdded(): Notification!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **NOTIFICATIONS**
### `notificationsOverview(): NotificationOverview!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **NOTIFICATIONS**
### `ownerSubscription(): Owner!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **OWNER**
### `parityHistorySubscription(): ParityCheck!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **ARRAY**
### `serversSubscription(): Server!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **SERVERS**
### `systemMetricsCpu(): CpuUtilization!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **INFO**
### `systemMetricsCpuTelemetry(): CpuPackages!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **INFO**
### `systemMetricsMemory(): MemoryUtilization!`
#### Required Permissions: - Action: **READ_ANY** - Resource: **INFO**
### `upsUpdates(): UPSDevice!`