MCP server guide
Last updated: 2026-10-31
The Garage Sale QR MCP server lets an MCP host such as Claude Code or Claude Desktop create and manage your listings from local photo files, using your personal access token.
Overview
The server never calls a language model itself. It only exposes tools. The host model looks at the previews, proposes the title, category, condition and price, and you approve or adjust them in the conversation.
Photos are converted to JPEG, resized and stripped of all EXIF and GPS data on your machine before any byte leaves it, both for previews and for uploads. Prefer a terminal workflow? See the CLI guide.
Install
From a checkout of the garage-qr repository, with Node 20 or newer:
pnpm install && pnpm --filter @garage-qr/mcp buildThis produces apps/mcp/dist/index.js, a self-contained executable. Native image dependencies are loaded from node_modules, so keep the checkout in place.
Create a token
- Sign in and open your account page.
- In the API tokens section create a token with the scopes listings:read, listings:write and photos:write.
- Copy the value that starts with gqr_pat_. It is shown once.
Configure
| Variable | Required | Default | Purpose |
|---|---|---|---|
| GQR_TOKEN | yes | none | Your personal access token. Never logged. |
| GQR_API_URL | no | https://api.garageqr.app | API base URL. Use http://localhost:8787 for local development. |
| GQR_WEB_URL | no | https://garageqr.app | Used for listing URLs and the host encoded in QR codes. |
The process exits with a message on stderr if the token is missing. There are no config files; stdout carries only the MCP protocol and logs go to stderr.
Claude Code
claude mcp add garage-qr -e GQR_TOKEN=gqr_pat_xxx -- node /abs/path/to/garage-qr/apps/mcp/dist/index.jsClaude Desktop
{
"mcpServers": {
"garage-qr": {
"command": "node",
"args": ["/abs/path/to/garage-qr/apps/mcp/dist/index.js"],
"env": { "GQR_TOKEN": "gqr_pat_xxx" }
}
}
}Example session
Illustrative, not a recording.
You: I have photos of my bike in ./bike. Create a listing for it.
Model: (calls preview_photos with the three files) This looks like a red 21-speed road bike in good condition. Proposed: title "Red 21-speed road bike", category sports_outdoors, condition good, price $120. Shall I create it?
You: Make it $90 and mention it comes with a lock.
Model: (calls create_listing, then upload_photos, then get_qr with a PDF path) Done. All 3 photos uploaded with location data removed. The printable sticker is saved next to the photos.
Tools
| Tool | Purpose |
|---|---|
| preview_photos | Downscaled, metadata-free previews of up to 8 local images as image blocks. Per-file errors do not fail the call. |
| create_listing | Create a listing from text fields. Returns id, slug, opaque id and URL. |
| upload_photos | Upload local photos to a listing one by one, with a per-file result. |
| list_listings | List your listings, optionally by status. |
| get_listing | One listing in full, with its photos. |
| update_listing | Patch listing fields. |
| mark_sold / mark_reserved / mark_active | Change listing status. |
| delete_listing | Permanent delete. Refuses unless confirm is true; the model must ask you first. |
| get_qr | QR sticker as SVG text or a PDF resource, with an optional path to save it. |
There are deliberately no account, token or auth tools.
Troubleshooting
- unauthorized (401): the token is wrong, revoked or expired. Create a new one on your account page and update the host config. Repeated bad tokens from one address are rate limited.
- insufficient_scope (403): the error names the required scope. Create a token that has it.
- rate_limited (429): wait the number of seconds given. The SDK already retried short waits automatically.
- unsupported_media_type or exif_rejected_local: that file could not be converted safely. It is skipped and reported; other files still proceed.
- Server not listed in the host: check that the path to dist/index.js is absolute and that the build step was run.