Your agent’s workerReads metadata locallyphotos.main.files(…)Start in a fresh project.
The skill teaches your agent how to build a VGI worker and test it through DuckDB. Install it in your project, selecting your coding agent when prompted.
npx skills add Query-farm/vgi-skills --skill vgi-worker
The installer uses Node.js. The saved Python example below also uses uv. Agent-specific installation help →
Use the sample photos.
Download the sample inputs and unzip them in your project. You’ll get an inputs/photos folder and an empty folder for testing.
These are tiny synthetic images with fictional metadata. The folder also contains a corrupt file and a nested photo: useful tests of what the worker should skip.
Paste the request. Choose your language.
This is the same request offered on the quickstart. It leaves the folder and language open, so the agent should ask before writing code.
Use the vgi-worker skill to make my photo metadata queryable in DuckDB: file paths, camera models, dates, and GPS coordinates where available. Ask me which folder and language to use. Keep the files unchanged and the data local. Build and test the worker, then show me how to ATTACH and query it.
Which folder should it read? Which language should it use? What should happen with subfolders or unreadable files?
Summary of the questions from the verified Claude Code run.In this run, we chose Python and narrowed the scope to JPEGs directly in one folder. You can choose any supported language for your own build.
- C#
- Go
- Java
- Python
- Rust
- TypeScript
Use Python. Keep this local and use stock DuckDB with VGI from query_farm. The photo folder is inputs/photos. For this first version read .jpg and .jpeg files directly in that folder, without recursing or following symlinks. Keep every input unchanged. Valid photos without EXIF get a row with NULL metadata; skip corrupt files with a diagnostic on stderr.
Expose photos.main.files(folder VARCHAR), with columns path, size_bytes, camera_model, taken_at, latitude, and longitude. NULL folder should return no rows. I want to group photos by camera and year and find ones with GPS coordinates. Include the ATTACH and example queries when it works.
Replace inputs/photos with your folder’s path. The example reply uses a relative path; the evaluation supplied the same folder as an absolute path.
This saved build predates the starter-kit workflow. Current skills begin with the kit for your chosen language; use the paths and launch command your agent returns for that project.
A worker you can inspect and run.
The agent creates a program that reads EXIF metadata and exposes photos.main.files(folder). But a missing date or unreadable file must not silently become a believable value. The tests check those cases through real SQL.
- Photos without EXIF return NULL metadata.
- Corrupt files are skipped with a diagnostic; subfolders and symlinks are not followed.
- Empty and NULL folder inputs return no rows.
- A newly added photo appears on the next scan.
- The original input files remain unchanged.
The saved worker passed nine agent-written SQL checks and nine independent checks. Its source was copied unchanged into the downloadable example. The build was verified on 2026-11-07; it is one measured example, not a completion-time guarantee.
ATTACH the worker. Ask a useful question.
Your agent returns the launch command for its worker. After VGI is loaded, ATTACH adds the photos catalog to your DuckDB session. Its main schema contains the files(folder) table function.
ATTACH 'photos' AS photos (
TYPE vgi,
LOCATION 'uv run --frozen --project photo_worker python photo_worker/photo_worker.py'
);
Now ask which cameras and years are represented in the folder:
SELECT
coalesce(camera_model, '(unknown)') AS camera,
year(taken_at) AS year,
count(*) AS photos
FROM photos.main.files('inputs/photos')
GROUP BY ALL
ORDER BY camera;
| camera | year | photos |
|---|---|---|
| (unknown) | NULL | 1 |
| Fixture Camera A | 2025 | 1 |
| Fixture Camera B | 2024 | 1 |
Actual SQL results from the supplied synthetic images. The photo without metadata stays visible as an unknown camera and year.
The same catalog can answer another question: which photos contain location coordinates?
SELECT path, latitude, longitude
FROM photos.main.files('inputs/photos')
WHERE latitude IS NOT NULL AND longitude IS NOT NULL;
The agent’s work is done. DuckDB calls the worker when you query it; the agent is no longer part of that execution.
Run the exact worker from this example.
Download the complete example, unzip it, and run this from its directory:
uv run demo.py
The runner sets up VGI from query_farm, runs the saved SQL assertions, and prints the ATTACH statement and result for your local paths. Package and extension downloads need network access during setup; photo metadata processing stays local.
Download sizes and SHA-256 checksums · Onboarding evaluation results