Skip to main content
Glama

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
ROOTYesA directory to allow. Can be specified multiple times (e.g., --root /path1 --root /path2).

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": false
}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}

Tools

Functions exposed to the LLM to take actions

NameDescription
scan_libraryA

Recursively scan a directory and summarize what is in it.

Walks root recursively (symlinks are not followed) and reports file counts by extension, total bytes, and the earliest/latest file modification time. Stops after 50000 files and reports truncation if the library is larger than that. Does not read file contents or EXIF; use library_stats for EXIF aggregates.

Args: params (ScanLibraryInput): Validated input containing: - root (str): Directory to scan, must be inside an allowed --root

Returns: dict: { "schema_version": 1, "root": str, "total_files": int, "total_bytes": int, "counts_by_extension": {".jpg": int, ...}, "earliest_mtime": str | None, # ISO 8601 UTC "latest_mtime": str | None, # ISO 8601 UTC "truncated": bool, "truncation_note": str | None, } On error: {"schema_version": 1, "error": str}

Examples: - Use when: "How many files are in my 2026 shoot folder and how big is it?" - Don't use when: you need EXIF camera/lens breakdowns (use library_stats)

library_statsA

Aggregate EXIF metadata across a library: cameras, lenses, ISO, focal length, dates.

Scans EXIF-readable files (jpg, jpeg, tif, tiff, png, dng, arw, raf, nef, cr2, cr3) under root. If more files are found than sample_limit, the newest files by modification time are sampled and this is noted in the result. Files with no readable EXIF are counted in skipped_no_exif.

Args: params (LibraryStatsInput): Validated input containing: - root (str): Directory to aggregate over, must be inside an allowed --root - sample_limit (int): Max files to inspect, default 2000, 1-50000

Returns: dict: { "schema_version": 1, "root": str, "files_considered": int, "files_sampled": int, "sampled": bool, "sample_note": str | None, "skipped_no_exif": int, "counts_by_camera_model": {str: int, ...}, "counts_by_lens": {str: int, ...}, "counts_by_focal_length_bucket": {str: int, ...}, "counts_by_iso_bucket": {str: int, ...}, "counts_by_year_month": {"YYYY-MM": int, ...}, } On error: {"schema_version": 1, "error": str}

Examples: - Use when: "What cameras and lenses did I use most this year?" - Use when: "Break down this shoot by ISO." - Don't use when: you need per-file EXIF (use read_exif)

read_exifA

Read flattened EXIF fields for a single image file.

Best effort across jpg, jpeg, tif, tiff, png, dng, arw, raf, nef, cr2, cr3. A file with no EXIF data, or an unsupported extension, is not an error: fields comes back empty with a note explaining why.

Args: params (ReadExifInput): Validated input containing: - path (str): Path to one image file, must be inside an allowed --root

Returns: dict: { "schema_version": 1, "path": str, "fields": { "camera_make": str, "camera_model": str, "lens_model": str, "focal_length": str, "focal_length_35mm_equiv": str, "iso": str, "exposure_time": str, "f_number": str, "exposure_program": str, "date_time_original": str, "date_time": str, "width": str, "height": str, "orientation": str, "software": str, "gps_latitude": float, "gps_longitude": float, }, # only keys actually present in the file are included "note": str | None, # e.g. "no EXIF data found" } On error: {"schema_version": 1, "error": str}

Examples: - Use when: "What lens and settings were used for this specific frame?" - Don't use when: you want aggregates across many files (use library_stats)

find_duplicatesA

Find duplicate files under a directory, exact or visually similar.

'exact' mode groups byte-identical files (any file type) by sha256 hash. 'visual' mode groups jpg/png images whose perceptual hash (phash) is within a hamming distance of 5, catching near-duplicate bursts even when bytes differ. Visual mode internally samples up to 3000 newest files for performance on very large libraries. Returns at most 200 duplicate groups; truncated indicates whether more groups exist beyond that cap.

Args: params (FindDuplicatesInput): Validated input containing: - root (str): Directory to scan, must be inside an allowed --root - mode ("exact" | "visual"): Duplicate detection mode, default "exact"

Returns: dict: { "schema_version": 1, "root": str, "mode": str, "files_considered": int, "duplicate_group_count": int, "truncated": bool, "groups": [{"count": int, "files": [str, ...]}, ...], # visual mode additionally includes: files_hashed, sampled, sample_note, truncation_note # exact mode additionally includes: skipped, total_duplicate_groups_found } On error: {"schema_version": 1, "error": str}

Examples: - Use when: "Find exact duplicate files I can safely delete one copy of." - Use when: "Find near-duplicate frames from burst shooting." - Don't use when: you want blur ranking, not duplication (use blur_scores)

blur_scoresA

Score jpg files under a directory for blur using Laplacian variance.

jpg/jpeg only in the MVP. Lower variance means less high-frequency detail, i.e. more likely blurry. Results are sorted ascending (blurriest first); the bottom decile is flagged likely_blurry.

Args: params (BlurScoresInput): Validated input containing: - root (str): Directory of jpg files to score, must be inside an allowed --root - limit (int): Max files to score, default 500, 1-5000

Returns: dict: { "schema_version": 1, "root": str, "files_considered": int, "files_scored": int, "sampled": bool, "sample_note": str | None, "skipped": int, "results": [ {"path": str, "laplacian_variance": float, "likely_blurry": bool}, ... ], # sorted ascending by laplacian_variance } On error: {"schema_version": 1, "error": str}

Examples: - Use when: "Which jpgs in this folder are probably out of focus?" - Don't use when: the library has RAW files only (RAW pixel decode is out of scope for MVP)

cull_reportA

Build a markdown cull report combining scan, EXIF stats, dupes, and blur.

Runs scan_library, library_stats, find_duplicates (exact mode), and blur_scores under the hood, and combines them into one markdown report: library summary, top EXIF stats, exact duplicate groups, and the blurriest 20 jpg files. Returns markdown text only; never writes a file.

Args: params (CullReportInput): Validated input containing: - root (str): Directory to report on, must be inside an allowed --root

Returns: str: Markdown-formatted report, or "Error: " on failure.

Examples: - Use when: "Give me a summary I can read before I start culling this shoot." - Don't use when: you need machine-readable output (use the individual tools instead)

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A4.6/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a clearly distinct level of analysis: whole-library scanning, EXIF aggregation, single-file EXIF, duplicate detection, blur scoring, and a combined report. Descriptions explicitly cross-reference each other with 'use X instead' notes, leaving no realistic chance of misselection.

Naming Consistency4/5

All names use consistent snake_case, which is the important part. Most are verb_noun or clear noun_phrase (scan_library, read_exif, find_duplicates), though library_stats, blur_scores, and cull_report are noun-first, a minor stylistic deviation.

Tool Count5/5

Six tools is well scoped for a photo-library analysis server. Each tool earns its place, and the composite cull_report cleanly wraps the primitives without duplicating their interfaces.

Completeness4/5

The analysis surface is strong: scanning, metadata aggregation, per-file inspection, deduplication, blur detection, and a report. Minor gaps remain for a culling workflow — no way to flag, move, or delete rejected files, and blur scoring is jpg-only — but these are acknowledged limitations rather than dead ends for the stated read-only purpose.

Maintenance

ActivitySlowing
ResponsivenessNo issues