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 分