Skip to main content

CLI (mr)

mr is a command-line client for the mahresources API. It covers all entity types with CRUD operations, bulk actions, file upload/download, version management, search, and plugin control.

Installation

Build from source (requires Go):

npm run build-cli

This produces the mr binary in the project root. Move it to a directory in your PATH to use it globally.

Server Connection

By default, mr connects to http://localhost:8181. Override with:

# Flag
mr --server http://myserver:9090 resources list

# Environment variable
export MAHRESOURCES_URL=http://myserver:9090
mr resources list

Flag takes precedence over the environment variable.

Global Flags

FlagDescription
--serverServer URL (default http://localhost:8181, env: MAHRESOURCES_URL)
--jsonOutput raw JSON instead of formatted tables
--no-headerOmit table column headers
--quietOnly output IDs
--pagePage number for list commands (default page size: 50)

Output Modes

By default, list commands display aligned tables. Single-entity commands display key-value pairs.

# Table output
mr tags list

# Raw JSON
mr tags list --json

# IDs only (useful for scripting)
mr tags list --quiet

# No column headers
mr tags list --no-header

# Pagination
mr resources list --page 3

Commands

Commands follow a consistent pattern: singular commands (tag, resource, note) operate on individual entities, plural commands (tags, resources, notes) handle lists and bulk operations.

tag / tags

# Get a tag
mr tag get <id>

# Create
mr tag create --name "Photography" --description "Photo-related"

# Edit
mr tag edit-name <id> "New Name"
mr tag edit-description <id> "New description"

# Delete
mr tag delete <id>

# List (with filters)
mr tags list --name "Photo" --description "keyword"

# Merge tags (move all associations from losers to winner, delete losers)
mr tags merge --winner 1 --losers 2,3,4

# Bulk delete
mr tags delete --ids 5,6,7

category / categories

Group categories. Same CRUD pattern as tags, plus custom template fields.

mr category get <id>
mr category create --name "People" --description "Person groups"
mr category create --name "Projects" --custom-header "<h2>Projects</h2>" --meta-schema '{"type":"object"}'
mr category edit-name <id> "New Name"
mr category edit-description <id> "New description"
mr category delete <id>
mr categories list --name "filter"

resource-category / resource-categories

Resource categories. Same CRUD pattern as group categories.

mr resource-category get <id>
mr resource-category create --name "Screenshots" --description "Screen captures"
mr resource-category edit-name <id> "New Name"
mr resource-category delete <id>
mr resource-categories list

note-type / note-types

mr note-type get <id>
mr note-type create --name "Meeting Notes" --custom-header "<h2>Meetings</h2>"
mr note-type edit --id 1 --name "Updated Name" --custom-sidebar "<p>sidebar</p>"
mr note-type edit-name <id> "New Name"
mr note-type edit-description <id> "New description"
mr note-type delete <id>
mr note-types list --name "filter"

resource / resources

# Get a resource
mr resource get <id>

# Upload a file
mr resource upload photo.jpg --name "Vacation Photo" --owner-id 1 --meta '{"location":"beach"}'

# Download
mr resource download <id> -o photo.jpg

# Download a scaled thumbnail
mr resource preview <id> --width 200 --height 200 -o thumb.jpg

# Create from URL (server fetches the file)
mr resource from-url --url "https://example.com/image.png" --name "Remote Image" --tags 1,2

# Create from a path on the server filesystem
mr resource from-local --path /data/files/document.pdf --name "Local Doc"

# Edit metadata
mr resource edit <id> --name "New Name" --tags 1,2,3 --groups 4,5 --meta '{"key":"value"}'

# Inline edits
mr resource edit-name <id> "New Name"
mr resource edit-description <id> "New description"

# Rotate an image
mr resource rotate <id> --degrees 90

# Recalculate dimensions
mr resource recalculate-dimensions <id>

# Delete
mr resource delete <id>

Resource Versions

# List versions
mr resource versions <resource-id>

# Get a specific version
mr resource version <version-id>

# Upload a new version
mr resource version-upload <resource-id> updated-file.jpg --comment "Higher resolution"

# Download a specific version
mr resource version-download <version-id> -o old-version.jpg

# Restore a previous version
mr resource version-restore --resource-id 1 --version-id 3 --comment "Reverting"

# Delete a version
mr resource version-delete --resource-id 1 --version-id 2

# Compare two versions
mr resource versions-compare <resource-id> --v1 1 --v2 2

# Clean up old versions
mr resource versions-cleanup <resource-id> --keep 5 --dry-run
mr resource versions-cleanup <resource-id> --older-than-days 90

Bulk Resource Operations

# List with filters
mr resources list --content-type image/png --owner-id 1 --tags 1,2 --groups 3
mr resources list --created-after 2025-01-01 --min-width 1920 --sort-by "created_at desc"

# Bulk tag operations
mr resources add-tags --ids 1,2,3 --tags 10,11
mr resources remove-tags --ids 1,2,3 --tags 10
mr resources replace-tags --ids 1,2,3 --tags 20,21

# Add groups
mr resources add-groups --ids 1,2,3 --groups 5,6

# Add metadata
mr resources add-meta --ids 1,2,3 --meta '{"reviewed":true}'

# Set dimensions
mr resources set-dimensions --ids 1,2 --width 1920 --height 1080

# Merge (move associations from losers to winner, delete losers)
mr resources merge --winner 1 --losers 2,3

# Bulk delete
mr resources delete --ids 4,5,6

# List unique metadata keys
mr resources meta-keys

# Bulk version cleanup
mr resources versions-cleanup --keep 3 --owner-id 1 --dry-run

note / notes

# Get a note
mr note get <id>

# Create
mr note create --name "Meeting Notes" --description "Q1 review" \
--owner-id 1 --note-type-id 2 --tags 1,3 --groups 5 --resources 10,11

# Edit
mr note edit-name <id> "Updated Title"
mr note edit-description <id> "Updated content"

# Share / unshare
mr note share <id>
mr note unshare <id>

# Delete
mr note delete <id>

# List with filters
mr notes list --name "meeting" --tags 1,2 --owner-id 1 --note-type-id 2
mr notes list --created-before 2025-06-01

# Bulk operations
mr notes add-tags --ids 1,2 --tags 5,6
mr notes remove-tags --ids 1,2 --tags 5
mr notes add-groups --ids 1,2 --groups 3,4
mr notes add-meta --ids 1,2 --meta '{"status":"reviewed"}'
mr notes delete --ids 3,4,5
mr notes meta-keys

note-block / note-blocks

# Get a block
mr note-block get <id>

# Create a block
mr note-block create --note-id 1 --type text --content '{"text":"Hello"}' --position "a"

# Update content
mr note-block update <id> --content '{"text":"Updated"}'

# Update state
mr note-block update-state <id> --state '{"collapsed":true}'

# List available block types
mr note-block types

# Delete
mr note-block delete <id>

# List blocks for a note
mr note-blocks list --note-id 1

# Reorder blocks
mr note-blocks reorder --note-id 1 --positions '{"1":"a","2":"b"}'

# Rebalance positions
mr note-blocks rebalance --note-id 1

group / groups

# Get a group
mr group get <id>

# Create
mr group create --name "Acme Corp" --category-id 1 --owner-id 2 \
--tags 1,3 --groups 5 --meta '{"industry":"tech"}' --url "https://acme.com"

# Edit
mr group edit-name <id> "New Name"
mr group edit-description <id> "New description"

# Navigate hierarchy
mr group parents <id>
mr group children <id>

# Clone
mr group clone <id>

# Delete
mr group delete <id>

# List with filters
mr groups list --category-id 1 --owner-id 2 --tags 1,2 --url "acme"
mr groups list --created-after 2025-01-01

# Bulk operations
mr groups add-tags --ids 1,2 --tags 5,6
mr groups remove-tags --ids 1,2 --tags 5
mr groups add-meta --ids 1,2 --meta '{"region":"EU"}'
mr groups merge --winner 1 --losers 2,3
mr groups delete --ids 4,5
mr groups meta-keys

relation / relation-type / relation-types

# Create a relation between groups
mr relation create --from-group-id 1 --to-group-id 2 --relation-type-id 1 \
--name "Partnership" --description "Since 2024"

# Edit
mr relation edit-name <id> "New Name"
mr relation edit-description <id> "New description"

# Delete
mr relation delete <id>

# Relation types
mr relation-type create --name "Subsidiary" --reverse-name "Parent Company" \
--from-category 1 --to-category 2
mr relation-type edit --id 1 --name "Updated Name"
mr relation-type delete <id>
mr relation-types list --name "filter"

series

mr series get <id>
mr series create --name "Photo Series 2025"
mr series edit <id> --name "Updated Name" --meta '{"season":2}'
mr series delete <id>
mr series remove-resource <resource-id>
mr series list --name "filter" --slug "photo"

query / queries

# Get a saved query
mr query get <id>

# Create
mr query create --name "Untagged Resources" \
--text "SELECT id, name FROM resources r LEFT JOIN resource_tags rt ON r.id = rt.resource_id WHERE rt.resource_id IS NULL"

# Run by ID
mr query run <id>

# Run by name
mr query run-by-name --name "Untagged Resources"

# Edit
mr query edit-name <id> "New Name"
mr query edit-description <id> "Description"

# Show database schema (for building queries)
mr query schema

# Delete
mr query delete <id>

# List
mr queries list --name "filter"

mrql

Execute MRQL (Mahresources Query Language) queries and manage saved queries. See the MRQL documentation for the full query language reference.

Execute a query

# Inline query
mr mrql 'type = resource AND tags = "photo" ORDER BY created DESC'

# Read from a file
mr mrql -f query.mrql

# Read from stdin
echo 'tags = "urgent" AND updated > -7d' | mr mrql -

# Limit results
mr mrql --limit 20 'type = note AND TEXT ~ "meeting"'

# Paginate
mr mrql --page 2 --limit 50 'type = resource AND contentType ~ "image/*"'
FlagDescription
-f, --fileRead query from a file
--limitMaximum number of results (default 50)

Subcommands

# Save a query for later reuse
mr mrql save "Untagged Resources" 'type = resource AND tags IS EMPTY'
mr mrql save "Recent Notes" 'type = note AND created > -7d ORDER BY created DESC' \
--description "Notes from the past week"

# List all saved queries
mr mrql list

# Run a saved query by name or numeric ID
mr mrql run "Untagged Resources"
mr mrql run 3

# Delete a saved query by ID
mr mrql delete 3

Output modes

Like all mr commands, mrql supports the standard output flags:

# Formatted table (default)
mr mrql 'type = resource AND tags = "photo"'

# Raw JSON
mr mrql --json 'type = resource AND fileSize > 10mb'

# IDs only (useful for piping)
mr mrql --quiet 'type = resource AND tags IS EMPTY'

# No column headers
mr mrql --no-header 'type = note AND updated > -30d'

Piping MRQL results

# Delete all untagged resources (use with caution)
mr mrql --quiet 'type = resource AND tags IS EMPTY' | while read id; do
mr resource delete "$id"
done

# Add a tag to resources matching a query
mr mrql --quiet 'type = resource AND contentType ~ "image/*" AND created > -7d' | while read id; do
mr resources add-tags --ids "$id" --tags 10
done

# Export MRQL results as JSON
mr mrql --json 'type = note AND tags = "important"' > important-notes.json

Global search across all entity types.

mr search "vacation photos"
mr search "meeting" --types resources,notes --limit 50
FlagDescription
--typesComma-separated entity types to search (e.g. resources,notes,groups)
--limitMaximum results (default 20)

log / logs

# Get a log entry
mr log get <id>

# Get history for a specific entity
mr log entity --entity-type resource --entity-id 42

# List with filters
mr logs list --level error --action delete --entity-type resource
mr logs list --created-after 2025-03-01 --message "upload"

job / jobs

Download queue management.

# Submit URLs for download
mr job submit --urls "https://example.com/a.jpg,https://example.com/b.jpg" \
--tags 1,2 --groups 3 --owner-id 1

# Control jobs
mr job cancel <id>
mr job pause <id>
mr job resume <id>
mr job retry <id>

# View the queue
mr jobs list

plugin / plugins

# Enable / disable
mr plugin enable <name>
mr plugin disable <name>

# Update settings
mr plugin settings <name> --data '{"apiKey":"abc123"}'

# Purge all plugin data
mr plugin purge-data <name>

# List installed plugins
mr plugins list

Scripting Examples

Tag all PNGs in a group

TAG_ID=5
mr resources list --groups 1 --content-type image/png --quiet | while read id; do
mr resources add-tags --ids "$id" --tags "$TAG_ID"
done

Export resource list as JSON

mr resources list --json > resources.json

Bulk download all resources matching a filter

mr resources list --tags 10 --quiet | while read id; do
mr resource download "$id" -o "resource_${id}"
done