Thingery

io.github.thingery由 GitHub 帳號 thingery 發布v0.1.3更新於 Oct 29, 2026

Open item database and MCP server for AI agents to list, find, and trade second-hand goods.

概覽

AI 產生的概覽

面向二手商品開放市場的 MCP 伺服器:可搜尋商品、帶圖刊登商品,並在買賣雙方之間完成訂單流程。

可以做什麼
  • 檢視商品分類與屬性欄位結構,並以語意加關鍵字搜尋,支援價格、分類、狀況與屬性篩選
  • 依 id 取得單一商品,包括仍待上傳照片的待刊登商品
  • 刊登與管理自己的商品:建立、上傳照片、調整價格或狀態、依狀態列出
  • 處理訂單:下單、查看狀態與物流、取消、確認付款或收貨、提出爭議、撥款
  • 把搜尋儲存為買家意圖,並在之後的工作階段取得新的媒合結果
適用情境
適合讓助理協助出售二手物品(辨識物品、撰寫並定價商品資訊、刊登上架),或依預算尋找二手商品並追蹤後續訂單。
執行需求
可使用託管的遠端端點,也可在本機執行:透過 stdio 執行 npm 套件,或自行架設,需要 Node 20 以及已安裝 pgvector 擴充並匯入隨附 schema 的 Postgres 資料庫。刊登與訂單類工具需要帳號與憑證——OAuth 登入或 Bearer API 金鑰;搜尋可匿名使用。自行架設需設定 DATABASE_URL,以及選用的嵌入、Stripe、EasyPost 與管理員金鑰。
安裝前請注意
寫入類工具會刊登商品、上傳照片、變更價格與狀態、建立或取消訂單;訂單工具可能透過 Stripe Checkout 產生實際付款、暫扣賣家款項並開啟爭議。上傳的圖片會重新編碼並清除中繼資料,託管服務上建立的商品對所有人公開。API 金鑰應視為機密;自行架設的資料只存在你自己的資料庫中。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Thingery,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

[Thingery logo]

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.

[M8ven Score]

[Thingery MCP server]

  • 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

ToolAuthPurpose
describe_categorypublicList 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_itemspublicHybrid semantic + keyword search with price/category/condition/attribute filters (any attribute key, declared or not)
get_itempublicFetch one listing by id. Pending (photoless) listings are returned too — they carry needs_photo: true
create_listingsellerPublish an item — core fields plus a flexible attributes object (declared keys validated, extras pass through and stay searchable). Listings are hidden from search until they have at least one photo: a zero-image create succeeds but stays pending (visible: false + next_steps in the result) until a photo lands
add_item_imagesellerAttach a photo to a listing (base64 in, stored server-side — no URLs needed; max 5 MB decoded). The first photo publishes a pending listing — the result's visible tells you. 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_listingssellerYour listings by status
update_listingsellerMark reserved/sold, adjust price, edit text or attributes

Order flow

ToolAuthPurpose
get_payout_setupsellerStripe Express onboarding URL + status — optional, enables platform payment
create_orderaccountOrder an item — returns a Stripe Checkout URL (stripe mode) or tracks an off-platform order
get_orderaccountOne of your orders (buyer or seller side): status, tracking, deadlines
cancel_orderaccountCancel while cancellable; paid orders refund instead
record_trackingsellerRecord carrier + tracking code; auto carrier tracker when EasyPost is configured
confirm_paymentsellerConfirm an off-platform payment (pending → sold)
confirm_receiptbuyerConfirm delivery — dispute window opens
open_disputebuyerDispute a delivered order; seller payout is held
release_fundsbuyerWaive 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.

Buyer alerts (saved searches)

A buyer who finds nothing good can save the search as an intent; when a matching listing publishes later, their agent reads it as a match on the next session — no email, the buyer's agent pulls my_matches (design record: ADR 0006). Fan-out is bounded: each match is inserted at most once per (intent, item), at most 10 new matches per buyer per day, at most 100 per published listing, and a seller never matches their own listing.

ToolAuthPurpose
save_intentaccountSave what the buyer is looking for — a query and/or the same filters as search_items (at least one of the two; max 25 saved intents, 50 saves/day)
my_intentsaccountYour saved searches, newest first, each with its unread-match count and muted state
update_intentaccountMute or unmute a saved search — muting stops future matches; existing matches stay readable
delete_intentaccountDelete a saved search; its matches go with it
my_matchesaccountNew listings matching a saved search — unread_only, mark_read, limit 1–50 (default 10). Items carry content_provenance: "user_generated"

Photo-required publishing

New listings need at least one photo to be visible to buyers — browse, search_items, and category counts all exclude photoless listings (a single derived predicate; design record: ADR 0005). What that means in practice:

  • create_listing with zero images succeeds — the listing is created but pending: hidden from feeds, its item page shows placeholder art and is noindex. The result carries photo_required, visible: false, and next_steps pointing at add_item_image (or the item page, where the owner can snap a photo in two taps).
  • The first photo publishes it — no second call needed. add_item_image returns visible: true; pending items carry needs_photo: true everywhere items are returned (get_item, my_listings, …).
  • Deleting the last photo returns the listing to pending (hidden again); pre-rollout listings are grandfathered and stay live without photos — on thingery.dev that means created before 2026-10-27; a self-hosted instance grandfathers whatever exists when it first deploys this version.
  • Owners manage photos on the item page (upload, delete, reorder) via the session-cookie routes documented in docs/item-images.md.

Two ways to use it

  • Zero-config (everyone): open /chat.html on 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 /mcp and 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 rotating thgo_ token, with revoke-anytime at /apps. Prefer a key? The manual thg_... API key still works, and self-hosting is below (npx thingery over stdio).

Quickstart

Pick your mode first — hosted and self-hosted expose the same MCP tools but not the same data: listings you create on the hosted service are visible to everyone; listings on your self-hosted instance live only in your database.

ModeEndpointDataBest for
Hostedhttps://thingery.dev/mcpthe shared public marketplacefirst success in under a minute — nothing to install
Self-hostedyour own /mcp, or stdioyour Postgres, your dataprivate deployments, full control, hacking on it

Mode A — hosted: search in one minute

No install. Add https://thingery.dev/mcp as a remote HTTP server in your MCP client (Claude Desktop, ChatGPT connectors, VS Code, Inspector — see Connect an MCP client below) and sign in with OAuth, or grab an API key at /login.html (free account; the thg_... key is shown after signup). Searching needs no account at all — verify the wire format with curl:

bash
curl -s https://thingery.dev/mcp \  -H 'content-type: application/json' \  -H 'accept: application/json, text/event-stream' \  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",       "params":{"name":"search_items","arguments":{"q":"Air Max"}}}'

Responses come back as Server-Sent Events; the JSON-RPC result is on the data: line.

Mode B — self-hosted: the npm package

The thingery npm package runs the whole server against your own Postgres. No git clone needed:

bash
mkdir my-thingery && cd my-thingerynpm install thingery
# 1. a Postgres with the pgvector extension (Docker: `docker run -d -p 5432:5432 \#    -e POSTGRES_PASSWORD=thingery pgvector/pgvector:pg16`; Homebrew: see below)# 2. apply the shipped schema (idempotent, safe to re-run):psql "postgres://postgres:thingery@localhost:5432/postgres" \  -f node_modules/thingery/db/schema.sql
# 3. run it (DATABASE_URL is the only required env var):DATABASE_URL="postgres://postgres:thingery@localhost:5432/postgres" \PORT=3000 node node_modules/thingery/dist/index.js

The server boots at http://localhost:3000 — /mcp, /browse, /healthz, and the HTTP API. Create your account by opening /login.html — your thg_... MCP key is shown right after signup. (On the pre-fix 0.1.2 release the page 404'd — a packaging gap fixed after it; there you can still create the account through the API — the key is returned exactly once, in the signup response:)

bash
curl -s -X POST http://localhost:3000/api/signup \  -H 'content-type: application/json' \  -d '{"email":"[email protected]","password":"at-least-8-chars"}'# → {"seller":{"name":"you","email":"…","api_key":"thg_…"}}

Then point any MCP client at http://localhost:3000/mcp with Authorization: Bearer thg_... — or spawn it over stdio with the npx config below.

Note: the npm package publishes at release cadence and can lag main on thingery.dev by days — /healthz reports the running version. If you need today's features, self-host from source instead:

Self-hosted from source (development)

bash
git clone https://github.com/thingery/thingerycd thingerynpm installdocker compose up -d db          # Postgres 16 + pgvectorcp .env.example .envnpm run db:migratenpm run db:seed                  # demo seller + 3 listings; prints an API keynpm 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):

bash
brew install postgresql@18 pgvectorbrew services start postgresql@18psql 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

Fastest path: the zero-dependency quickstart — node examples/quickstart/quickstart.mjs runs the whole first workflow against the hosted server (search → publish a photo'd listing → fetch its public page, ~3 minutes including signup). Client-by-client configs (Claude Desktop, Cursor, VS Code, Inspector, stdio, raw curl) and troubleshooting live in docs/integrations.md.

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:

json
{  "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 is thingery on npm and runs as a local stdio MCP server against your own Postgres, spawnable by Claude Desktop and similar clients:

json
{  "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). The stdio server needs the same prerequisites as Mode B: a Postgres with the schema applied (psql -f node_modules/thingery/db/schema.sql) and, for write tools, a key that exists in that database (from that deployment's /login.html or /api/signup).

Troubleshooting

SymptomCauseFix
search_items returns 0 for a common word like "sneakers"thingery.dev runs keyword-only search (no embeddings configured): it matches literal words in titles/descriptions — and no live listing contains the word "sneakers"search brand/product words ("Air Max", "Samba"), or filter by category instead
create_listing returns visible: falsephoto-required publishing: zero-image listings stay pending and hiddencall add_item_image — the first photo publishes the listing
HTTP 401 authentication requiredprotected tool called without a credentialsign in with OAuth, or send Authorization: Bearer thg_...
HTTP 406 from /mcpthe MCP Streamable HTTP transport requires the SSE Accept headersend accept: application/json, text/event-stream
npm run db:migrate fails inside an installed packagescripts/ isn't published — the schema ships as db/schema.sqlpsql "$DATABASE_URL" -f node_modules/thingery/db/schema.sql
stdio warning: THINGERY_API_KEY set but not found in the databasethe key belongs to a different deployment's databaseget a key from this deployment (/login.html or /api/signup)
Self-hosted behaves differently from thingery.devthe npm release can lag main (e.g. photo-gating)compare /healthz versions; self-host from source for today's main

Try it with curl or a 5-line script

No client needed — plain JSON-RPC over HTTP works (the quickstart is a single zero-dependency Node file built on this):

bash
# 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.

Use an agent framework (LangChain, LlamaIndex, Composio)

Framework toolkits reach the same /mcp endpoint over streamable HTTP — copy-paste quickstarts with verified end-to-end tool calls live in examples/integrations/. Client setup for Claude Desktop, Cursor, VS Code, Inspector and plain HTTP is in docs/integrations.md.

Configuration

Copy .env.example to .env. Key variables:

VariablePurpose
DATABASE_URLPostgres connection string
PORT / PUBLIC_BASE_URLHTTP port and base URL used in item links
EMBED_API_URL / EMBED_API_KEY / EMBED_MODELAny OpenAI-compatible embeddings endpoint; leave the key empty for keyword-only search
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRETOrder 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_SECRETOptional automatic carrier tracking; without it, buyers/sellers confirm delivery manually
ADMIN_API_KEYAdmin 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 listing
  • skills/find-item — the buyer pipeline: intent → scored matches → one best proposal → saved-search alerts

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 JSONB for 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 categories registry that declares expected attribute definitions per category. create_listing validates 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 via describe_category.
  • sellers.profile JSONB for 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_items attribute 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_listing and update_listing: at most 64 attribute entries, keys up to 128 chars, string values up to 500 chars. Over the cap, the call fails with invalid_attributes.

Roadmap

  1. 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.
  2. Seller skill upgrade — photo capture shipped: frictionless item-page capture, placeholder art, and photo-gated publishing (ADR 0005). Comparable-based pricing is deferred until listings are numerous enough to be real comparables.
  3. Buyer alerts — next up: "new match for your intent" event stream.
  4. Federation protocol — instance-to-instance sync so anyone can host a node of the Item Graph. Deferred.
  5. Agent-to-agent negotiation — bounded-mandate negotiation between buyer and seller agents. Deferred.

We'll revisit federation and negotiation when there's real traction: more than 10 new listings per week for 2 consecutive weeks, or concrete pull from self-host adopters.

Security

What's verified and what isn't: docs/security-note.md. To report a vulnerability privately, see SECURITY.md.

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

Apache-2.0

來源:README.md,提交 8c8872e

工具

0
SourceWeft 曾在隔離沙箱中以無憑證方式啟動該軟體包一次,以列出其工具,但未能完成:伺服器未能啟動。

版本歷史

3
  1. v0.1.3最新Oct 29, 2026
  2. v0.1.2Oct 25, 2026
  3. v0.1.1Oct 12, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分