Server data from the Official MCP Registry
Open item database and MCP server for AI agents to list, find, and trade second-hand goods.
About
Open item database and MCP server for AI agents to list, find, and trade second-hand goods.
Security Report
Thingery is a second-hand marketplace MCP server with Bearer API-key auth for write tools, public read-only search, and parameterized SQL throughout. The main gaps are minor: update_listing skips category attribute validation, internal error messages are echoed to clients, and the auth module (src/auth.ts) was not provided, so key storage and comparison could not be verified. Permissions match the server's purpose. Supply chain analysis found 8 known vulnerabilities in dependencies (2 critical, 4 high severity). Package verification found 1 issue.
7 files analyzed · 15 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: DATABASE_URL
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-thingery-thingery": {
"env": {
"DATABASE_URL": "your-database-url-here"
},
"args": [
"-y",
"thingery"
],
"command": "npx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
Thingery
Thingery is the open item database and MCP server for AI agents to list, find, and trade second-hand goods.
Say "sell this" to your AI assistant — it identifies the item, writes the listing, prices it, and publishes it. Say "find me a used camera under €300" — it searches every listing and proposes the best one. Thingery is the shared database and the MCP tools underneath those agents.
- Free for everyone. No take rate, no platform lock-in.
- Open source (Apache-2.0). Self-host the whole thing, or use the flagship instance.
- Agent-native: every capability is an MCP tool; any LLM can drive it.
- Self-hostable: Postgres + pgvector + Node 20.
Chinese-market name: 物坊 (Wùfāng).
MCP tools
| Tool | Auth | Purpose |
|---|---|---|
describe_category | public | List categories and their declared attribute fields — the self-describing schema for agents. Declared fields get validated on create; undeclared keys are allowed, searchable, and their real use is surfaced via undeclared_in_use |
search_items | public | Hybrid semantic + keyword search with price/category/condition/attribute filters (any attribute key, declared or not) |
get_item | public | Fetch one listing by id |
create_listing | seller | Publish an item — core fields plus a flexible attributes object (declared keys validated, extras pass through and stay searchable) |
add_item_image | seller | Attach a photo to a listing (base64 in, stored server-side — no URLs needed; max 5 MB decoded). Images are re-encoded at ingest: metadata (EXIF/GPS/XMP) stripped, downscaled to ≤ 2000 px, animated GIFs stored as stills; large PNGs may be rejected — prefer JPEG |
my_listings | seller | Your listings by status |
update_listing | seller | Mark reserved/sold, adjust price, edit text or attributes |
Order flow
| Tool | Auth | Purpose |
|---|---|---|
get_payout_setup | seller | Stripe Express onboarding URL + status — optional, enables platform payment |
create_order | account | Order an item — returns a Stripe Checkout URL (stripe mode) or tracks an off-platform order |
get_order | account | One of your orders (buyer or seller side): status, tracking, deadlines |
cancel_order | account | Cancel while cancellable; paid orders refund instead |
record_tracking | seller | Record carrier + tracking code; auto carrier tracker when EasyPost is configured |
confirm_payment | seller | Confirm an off-platform payment (pending → sold) |
confirm_receipt | buyer | Confirm delivery — dispute window opens |
open_dispute | buyer | Dispute a delivered order; seller payout is held |
release_funds | buyer | Waive the dispute window, release the seller's payout now |
Orders close a deal on-platform. Stripe mode (buyer protection: platform charges the
buyer, holds funds, pays the seller after delivery + dispute window) runs when the deployment
has Stripe configured and the seller finished payout onboarding; otherwise orders run in
off-platform mode — Thingery tracks state and deadlines, parties settle payment themselves.
States: pending → sold → label_issued → in_transit → delivered → payout_pending → paid_out
with disputed/cancelled/refunded off-ramps; deadlines are enforced by a server-side sweep
(unpaid cancels 24h, dispute window 48h, never-shipped disputes auto-refund 72h — defaults,
env-tunable). Design record: ADR 0003.
Sellers authenticate with OAuth (one-click sign-in from any MCP host — see below) or a
thg_... API key (Authorization: Bearer); search is anonymous.
Two ways to use it
- Zero-config (everyone): open
/chat.htmlon any deployment and talk to the Thingery assistant — describe an item to sell or to find. Searching needs no account; publishing needs a free one (/login.html, email + password; your MCP API key is shown after signup). The chat uses an LLM configured on the server (LLM_*env vars) — you bring the key, any OpenAI-compatible endpoint works; without it, chat is disabled while MCP and signup still work. - Bring your own agent (power users): point any MCP client at
/mcpand sign in with OAuth — Claude Desktop, ChatGPT connectors, VS Code and Inspector discover the sign-in flow automatically (RFC 9728 resource metadata + RFC 7591 dynamic client registration); you approve once on a consent screen and the host gets a rotatingthgo_token, with revoke-anytime at/apps. Prefer a key? The manualthg_...API key still works, and self-hosting is below (npx thingeryover stdio).
Quickstart
git clone https://github.com/thingery/thingery
cd thingery
npm install
docker compose up -d db # Postgres 16 + pgvector
cp .env.example .env
npm run db:migrate
npm run db:seed # demo seller + 3 listings; prints an API key
npm run dev # http://localhost:3000
No Docker? Local Postgres
npm run db:migrate needs a Postgres with the vector extension — the schema creates it
unconditionally, even in keyword-only search mode. On macOS, Homebrew's pgvector bottle
supports PostgreSQL 17 and 18 (not 16, the version the Docker setup uses — any of them works
for Thingery):
brew install postgresql@18 pgvector
brew services start postgresql@18
psql postgres -c "CREATE ROLE thingery LOGIN PASSWORD 'thingery';" \
-c "CREATE DATABASE thingery OWNER thingery;"
psql -d thingery -c "CREATE EXTENSION vector;" # run as the Postgres superuser
The default DATABASE_URL in .env.example already matches this setup. On any other
Postgres, run the same two SQL statements as the superuser and point DATABASE_URL at it.
Connect an MCP client
Most hosts (Claude Desktop, ChatGPT connectors, VS Code, Inspector) can sign in with OAuth:
just add https://thingery.dev/mcp (or your self-hosted URL) as a remote HTTP server — the host
discovers the sign-in flow, you approve once, and tokens rotate automatically. On thingery.dev
you can review and revoke connected applications at /apps.
Hosts without OAuth support use a thg_... API key (from /login.html) as a static bearer
token instead:
{
"mcpServers": {
"thingery": {
"type": "http",
"url": "http://localhost:3000/mcp",
"headers": { "Authorization": "Bearer thg_YOUR_KEY" }
}
}
}
Omit the Authorization header for buyer-only (public search) usage — anonymous access to the
public read tools is always available. Design note: the decision record for the OAuth flow is
docs/adr/0002-mcp-oauth-onboarding.md; set
OAUTH_DISABLED=1 to turn OAuth routes off entirely (hosts then fall back to keys).
Run locally over stdio (npx)
Self-host without any web server — the package runs as a local stdio MCP server against your own Postgres, spawnable by Claude Desktop and similar clients:
{
"mcpServers": {
"thingery": {
"command": "npx",
"args": ["-y", "thingery"],
"env": {
"DATABASE_URL": "postgres://thingery:thingery@localhost:5432/thingery",
"THINGERY_API_KEY": "thg_YOUR_KEY"
}
}
}
}
THINGERY_API_KEY is optional; without it the server runs read-only (search + get).
Try it with curl
# List tools (the Accept header is required by the MCP Streamable HTTP transport)
curl -s http://localhost:3000/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# Search (finds the seeded Nike Air Max: €85 is within the €100 filter)
curl -s http://localhost:3000/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"search_items","arguments":{"q":"air max","max_price_cents":10000}}}'
Responses come back as Server-Sent Events; the JSON-RPC result is on the data: line.
Configuration
Copy .env.example to .env. Key variables:
| Variable | Purpose |
|---|---|
DATABASE_URL | Postgres connection string |
PORT / PUBLIC_BASE_URL | HTTP port and base URL used in item links |
EMBED_API_URL / EMBED_API_KEY / EMBED_MODEL | Any OpenAI-compatible embeddings endpoint; leave the key empty for keyword-only search |
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRET | Order flow payments (Stripe Connect). Empty key = platform payment off (orders run off-platform); key without webhook secret = payments stay pending until the webhook is configured (fail-closed) |
EASYPOST_API_KEY / EASYPOST_WEBHOOK_SECRET | Optional automatic carrier tracking; without it, buyers/sellers confirm delivery manually |
ADMIN_API_KEY | Admin dispute-resolution route; disabled unless ≥32 chars |
Order-flow windows and abuse caps (PAYMENT_DEADLINE_HOURS, DISPUTE_WINDOW_HOURS,
PICKUP_AUTOCONFIRM_DAYS, MAX_PENDING_ORDERS_PER_BUYER, …) are documented inline in
.env.example with their defaults.
Agent skills
skills/sell-item— the seller pipeline: photos → identified item → priced listingskills/find-item— the buyer pipeline: intent → scored matches → one best proposal
Data conventions
- Prices are integer cents:
12500= EUR 125.00 - Conditions:
new,like_new,good,fair,poor - Shipping tiers:
locker(shoebox-sized),parcel(≤30kg),bulky(furniture),local(pickup) - Machine discovery:
llms.txt·agents.json
Flexible schema
No fixed column set can cover sneakers, cameras and sofas at once, so the Item Graph is a hybrid:
- Rigid core columns for what every item shares: identity, price, condition, status, category, shipping estimation, embedding. These drive indexing and business logic.
attributes JSONBfor category-specific fields (size_eu,colorway,shutter_count,ISBN...), GIN-indexed for containment filters ({"size_eu": 43}) and flattened into the semantic embedding so they're meaning-searchable too.- A
categoriesregistry that declares expected attribute definitions per category.create_listingvalidates declared keys (types, required, enums) but unknown keys pass through — flexible by design, and federation-friendly since instances can diverge on attributes without breaking sync. Agents self-serve the schema viadescribe_category. sellers.profile JSONBfor user-side flexibility (locale, currency, notification prefs, negotiation mandates) — reserved for upcoming features.
Growth path: when an attribute becomes a hot filter, promote it to a Postgres generated column + index — no API change needed.
Which undeclared keys are actually in use is discoverable too: describe_category returns undeclared_in_use — undeclared attribute keys observed on active listings from multiple sellers, with item and distinct-seller counts. These keys are never validated (they pass through as-is), so treat them as search hints, not contracts — a signal of what buyers can filter on today.
Reading undeclared_in_use as an agent:
- It lists at most 20 keys, and only keys carried by ≥2 active listings from ≥2 distinct sellers. The field is omitted entirely when no key qualifies.
- Values are free-form: the same undeclared key can hold different values (even different types) on each listing. Use the key in
search_itemsattribute filters — they match any key, declared or not — but never treat one listing's value as the category's contract. - Writes are shape-capped on
create_listingandupdate_listing: at most 64 attribute entries, keys up to 128 chars, string values up to 500 chars. Over the cap, the call fails withinvalid_attributes.
Roadmap
Order state machine— shipped: orders with Stripe Connect payouts, carrier webhooks, disputes, and deadline sweeps (ADR 0003). Next: live-mode Stripe after legal/ToS sign-off.- Seller skill upgrade — photo capture flow, comparable-based pricing from live data.
- Buyer alerts — event stream: "new match for your intent".
- Federation protocol — instance-to-instance sync so anyone can host a node of the Item Graph.
- Agent-to-agent negotiation — bounded-mandate negotiation between buyer and seller agents.
Contributing
Built by an agent team with a human architect — see AGENTS.md for the working
conventions. PRs welcome; CI (typecheck, build, tests) must pass. New here? Start with
docs/community.md — good first issues, dev setup in under 10 minutes,
and where to ask questions.
License
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Worldmonitor
Freeby Koala73 · Developer Tools
Live markets, conflicts, country risk, chokepoints, energy, and China decision signals. 93 tools.
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
