vinted-seller-mcp

vinted-seller-mcp

An MCP server that helps sellers create Vinted listings from photos, researching comparable listings, estimating prices, and filling the listing form. It requires explicit confirmation before publishing, deleting, or sending messages.

Category
访问服务器

README

vinted-seller-mcp

Photos in, a ready-to-publish Vinted listing out. An MCP server and CLI that researches comparable listings, estimates a defensible price, finds the right category and brand, writes the listing, fills Vinted's real form — and stops, until you say publish.

Works with Hermes, Claude Desktop, Claude Code, and any MCP client. One stdio server behind all of them.

photos + what you can see
        ↓
search_similar_items   →  real comparables, filtered
        ↓
estimate_price         →  median, range, confidence
        ↓
find_category / find_brand
        ↓
prepare_listing        →  a local draft; nothing sent to Vinted
        ↓
        ⏸  you review it
        ↓
validate_listing       →  fills the real form, STOPS before publishing
        ↓
        ⏸  you approve
        ↓
publish_listing

Install

git clone https://github.com/rachid598/mcpvin.git
cd mcpvin
npm install
npx playwright install chromium
npm run build
./scripts/install-hermes.sh
vinted-seller login --country fr
vinted-seller status --country fr

Requires Node.js 20+. npm install pulls Playwright's Chromium (~150 MB) on first run.

For the vinted-seller command to be on your PATH, either npm link in the checkout or npm install -g .. Otherwise call node dist/cli/index.js — the install script prints the absolute path.


Manual browser authentication

This is the recommended way to sign in.

vinted-seller login-manual --country fr

It opens your own Chrome/Chromium — the one already installed on the machine — on a profile dedicated to this tool, and waits. You sign in. You close the window. That is the whole flow.

/usr/bin/chromium
is now open at https://www.vinted.fr/ using a profile dedicated to this tool:
  /home/you/.vinted-seller-mcp/profile-fr

Sign in yourself. Your password, CAPTCHA, Google login and 2FA stay between you and Vinted -
this program never sees them, and never touches your personal browser profile.

When you are signed in, CLOSE THE BROWSER WINDOW. This command is waiting for that.

Why this exists. On a real machine, the Chromium started by Playwright was shown "Ta session a été bloquée" by Vinted straight away — while ordinary Chromium, same PC, same IP, worked fine. The response is not to disguise the automated browser (this project does not do that, ever) but to take the automation out of the sign-in altogether.

What it does and does not do:

  • Your own browser, launched directly. Playwright is not loaded at all on this path.
  • A dedicated profile, ~/.vinted-seller-mcp/profile-fr/. Your personal Chrome profile is never opened, read, or copied from — your other sessions, history and extensions are untouched.
  • The login is entirely yours. Nothing here asks for, types, stores or transmits your password, and nothing touches a CAPTCHA or 2FA.
  • No anti-bot technique of any kind. The only flags passed are the profile directory and two that suppress first-run noise.

Pick the browser yourself if auto-detection guesses wrong:

VINTED_CHROME_PATH=/usr/bin/google-chrome vinted-seller login-manual --country fr

Check the profile at any time — purely local, no browser, no Vinted request:

vinted-seller profile --country fr
Country: fr
Profile path: /home/you/.vinted-seller-mcp/profile-fr
Profile exists: yes
Initialised: yes

The profile remembers its browser

A Chromium profile is not portable between browser builds. Two things break:

  • Chromium records Last Version and refuses to open a profile written by a newer build.
  • On Linux, cookies are encrypted with a key from whichever password store that build picked (gnome-libsecret, kwallet, or the "basic" fallback).

So a profile created by the system's Chrome 151 is unreadable to Playwright's bundled Chromium 141 — which is exactly how a perfectly good session came back as expired on the first field test.

login-manual therefore records the browser it used, in profile-fr.browser.json next to the profile, and the session layer reopens the profile with that same binary. No environment variable needed anywhere — including in the MCP server Hermes spawns.

status shows which browser it picked:

Country: fr
Profile: OK
Session: authenticated
Browser: /opt/google/chrome/google-chrome (profile metadata (Google Chrome 151.0.7922.137))

Resolution order: recorded browser → VINTED_CHROME_PATH → detected system browser → Playwright's bundled Chromium.

The older Playwright login

vinted-seller login --country fr

Still available, and still never handles your password. But it drives Playwright's Chromium, which Vinted's anti-automation has been observed to block immediately. If you see "Ta session a été bloquée", switch to login-manual. This mode is kept until field testing settles which one works.


Checking the session

vinted-seller status --country fr

Unlike profile, this one does open the stored profile with Playwright and ask Vinted who you are.

Your session lives in a Chromium profile at ~/.vinted-seller-mcp/profile-fr/, and the browser keeps Vinted's own tokens fresh from there.

Country: fr
Profile: OK
Session: authenticated
Username: yourname
User ID: 123456

If it lapses:

Country: fr
Profile: OK
Session: expired
Session expired. Reauthentication required: `vinted-seller login --country fr`.

Hermes

Two independent layers, so Hermes never depends on anything proprietary:

1. The MCP server — the actual tools, over stdio:

hermes mcp add vinted-seller --command node --args /ABSOLUTE/PATH/mcpvin/dist/mcp/stdio.js
hermes mcp list
hermes mcp test vinted-seller

2. The skill — integrations/hermes/vinted-seller/SKILL.md, which teaches Hermes how to use those tools well: identify honestly, price with evidence, never publish without asking.

./scripts/install-hermes.sh does both. It is idempotent — re-run it after every git pull, or use ./scripts/update-hermes.sh, which pulls, rebuilds and re-registers in one step. ./scripts/uninstall-hermes.sh removes both (add --purge-data to delete your session and drafts too).

If the hermes CLI is not on your PATH, the script builds everything anyway and prints the exact registration command and JSON config to use by hand.

On another machine

git clone https://github.com/rachid598/mcpvin.git
cd mcpvin
./scripts/install-hermes.sh
vinted-seller login --country fr

Then ask Hermes: "Here are photos of a jacket — prepare me a Vinted listing."


The tools

27 tools, each with a Zod schema, documented parameters and structured errors.

Session whoami · session_status
Research search_items · get_item · search_similar_items · get_similar_items · estimate_price
Taxonomy find_category · find_brand
Drafting prepare_listing · get_listing_draft · list_drafts · update_listing_draft · add_draft_photos · delete_draft
Publishing validate_listing · publish_listing
Shop my_listings · seller_stats · edit_listing · update_price · deactivate_listing · reactivate_listing · delete_listing
Messages conversations · read_messages · send_message

vinted-seller tools lists them. npm run mcp:inspect runs a real MCP handshake and prints what a client discovers.

The three that need permission

publish_listing, delete_listing and send_message all require confirm: true and are annotated destructiveHint. Without it they fail with CONFIRMATION_REQUIRED and a hint telling the assistant to ask you first. This is enforced in the tool schema, not just the prompt — a model that has drifted still cannot publish by accident.

validate_listing fills Vinted's real form completely and never clicks publish. That is the safe end-to-end check.


The CLI

vinted-seller login-manual --country fr      # RECOMMENDED: your own browser, no Playwright
vinted-seller profile  --country fr          # where the profile is; local only
vinted-seller login    --country fr          # Playwright Chromium (may be blocked by Vinted)
vinted-seller status   --country fr          # is the session alive?
vinted-seller whoami   --country fr

vinted-seller search   "nike air max 90"     # catalogue search
vinted-seller similar  --brand Nike --model "Air Max 90" --size 42
vinted-seller price    --brand Nike --model "Air Max 90" --condition very_good
vinted-seller category "baskets homme"
vinted-seller brand    "Nike"

vinted-seller prepare  --brand Nike --model "Air Max 90" --size 42 \
                       --color noir --condition very_good \
                       --defect "usure sur le talon droit" \
                       --photo ./1.jpg --photo ./2.jpg --photo ./3.jpg
vinted-seller drafts                          # list local drafts
vinted-seller draft    <draftId>
vinted-seller validate <draftId> --screenshot # fill the form, stop before publishing

vinted-seller mcp                             # MCP server on stdio
vinted-seller serve-http --port 8787          # MCP over Streamable HTTP

--json on any command for machine-readable output.


Pricing

estimate_price returns the full distribution, three price points, and a confidence level:

{
  "count": 12, "currency": "EUR",
  "minimum": 45, "lowerPercentile": 55, "median": 62,
  "average": 63.5, "upperPercentile": 70, "maximum": 85,
  "quickSalePrice": 53, "recommendedPrice": 62, "optimisticPrice": 72,
  "confidence": "high",
  "rationale": "Based on 12 comparable listings on Vinted. 2 outliers (1 EUR, 900 EUR) were excluded."
}

It refuses to look precise when it isn't. Below three usable comparables the confidence is insufficient_data and the rationale says so — a median of two listings is not a market price.

Comparables are found by trying several phrasings (Vinted's search is literal), then filtering out duplicates, accessories, broken/for-parts listings, mismatched storage capacities and wrong sizes. Every exclusion comes back in rejected with its reason, so a price can be defended.


Drafts

~/.vinted-seller-mcp/
├── profile-fr/              # your Chromium profile — never leaves this machine
└── drafts/
    └── 01M05Z76YAAF8G9F64RHQ07M1D/
        ├── listing.json     # fields, comparables, estimate, validation, history
        └── photos/
            ├── 01.jpg
            ├── 02.jpg
            └── 03.jpg

Statuses: draft → validated → published, or failed.

Photos are copied into the draft — your originals are never moved, modified or deleted. Plain files, so you can read, diff, back up or hand-edit any of it.


Configuration

Everything is optional; see .env.example. There are no credentials in it.

Variable Default
VINTED_SELLER_HOME ~/.vinted-seller-mcp profiles, drafts, photos
VINTED_COUNTRY fr default storefront
VINTED_RATE_LIMIT_PER_SEC 2 per country
VINTED_CACHE_TTL_MS 60000 GET cache; 0 disables
VINTED_HEADLESS true login always shows a window
VINTED_LOG_LEVEL info JSON logs on stderr
VINTED_MCP_TOKEN — bearer token for serve-http

23 storefronts: fr be de at es it nl pt lu ie fi gr sk lt hr uk pl cz hu ro se dk us.


First real test on the Hermes PC

The first run against real Vinted is the one that matters. Do the steps in order and stop at the first one that fails — each depends on the previous.

  1. install the Hermes MCP integration
  2. login-manual — sign in with your own browser
  3. close Chromium once signed in
  4. status — does the saved profile still yield a session?
  5. whoami
  6. search
  7. comparables and pricing
  8. validate --headed only if 4-7 worked
  9. never publish during testing

Step 4 is the real unknown: it is the first time Playwright reopens the profile you created by hand. If Vinted blocks that, stop and say so — do not work around it.

# 1. install
git clone https://github.com/rachid598/mcpvin.git
cd mcpvin
npm install
npx playwright install chromium
npm run build

# 2. sign in with YOUR OWN browser - it opens, you log in, you close it
node dist/cli/index.js login-manual --country fr

# 3. confirm the session took (this one uses Playwright on the saved profile)
node dist/cli/index.js status --country fr

Expected:

Country: fr
Profile: OK
Session: authenticated
Username: yourname
User ID: 123456
# 4. check the catalogue is reachable
node dist/cli/index.js search "nike air max 90" --country fr --limit 5

# 5. build a draft from real market data, with real photos
node dist/cli/index.js prepare \
  --brand Nike --model "Air Max 90" --size 42 --color Noir \
  --condition very_good --category baskets \
  --photo ./photo1.jpg --photo ./photo2.jpg --photo ./photo3.jpg \
  --country fr

# 6. fill the REAL form with a visible browser - this does NOT publish
node dist/cli/index.js validate <draft-id> --headed --screenshot

Step 6 fills Vinted's actual new-listing form and stops. Watch the window: photos upload, the category dropdown opens, the brand/size/condition/colour block appears, the price goes in. Then read the report:

valid: true
ready_to_publish: true
publish button: enabled
photos uploaded: 3/3
filled: photos (3), title, description, category (Baskets), brand (Nike), size (42), …
missing: -
The form was NOT submitted.

Do not click Upload yourself, and do not run publish_listing. Close the window; nothing was created.

If a field failed, the report names it, the selectors that were tried, and the page URL:

VINTED_DEBUG_BROWSER=1 node dist/cli/index.js validate <draft-id> --headed

adds a screenshot and a structural dump of the page's interactive elements under ~/.vinted-seller-mcp/debug/ — enough to fix src/browser/selectors.ts. It never writes page HTML, cookies or tokens.


Known environment gotchas (Linux)

Snap Chromium cannot write to dot-directories. It is confined and refuses ~/.vinted-seller-mcp/, so login-manual opens no window at all. Use the .deb Chrome or Chromium instead, or point VINTED_CHROME_PATH at one.

Playwright's bundled Chromium fails to start on Ubuntu 24.04+ with FATAL: No usable sandbox! — kernel.apparmor_restrict_unprivileged_userns=1 blocks the user-namespace sandbox, and the bundled chrome-sandbox is not setuid. The system Chrome .deb has a setuid chrome-sandbox and an AppArmor profile, so it works. Since the profile now records the system browser, this resolves itself after login-manual. The launch error says all of this if you hit it.

VINTED_NO_SANDBOX=1 also works but genuinely reduces isolation — prefer the system browser.


If Playwright stays blocked

Vinted may refuse the automated browser even after a manual sign-in. That would take away the browser half of this tool — not the useful half.

Everything below runs over the HTTP client and the local draft store, and does not need Playwright to drive a form:

Still works Tool
Comparable search search_items, search_similar_items, get_item
Price estimation estimate_price
Category and brand ids find_category, find_brand
Title and description prepare_listing
Drafts and photos prepare_listing, update_listing_draft, add_draft_photos, get_listing_draft

So the assistant can still do the work that takes you the longest — research the market, justify a price, write the listing, organise the photos — and hand you a finished draft:

vinted-seller draft <draft-id>              # the full record
vinted-seller draft <draft-id> --json       # machine-readable

The draft directory holds the copied photos ready to upload:

~/.vinted-seller-mcp/drafts/<draft-id>/
├── listing.json
└── photos/01.jpg 02.jpg 03.jpg

You then create the listing in Vinted yourself, copying title, description, price, category, brand, size, condition and colour across, and picking the photos from photos/. Only the final form-filling is manual.

Only validate_listing, publish_listing, edit_listing's browser fallback and status/whoami need Playwright. If browser automation turns out to be incompatible with Vinted's protections, that is a real answer and the architecture will be adapted — not worked around.


Development

npm run lint
npm run typecheck
npm run build
npm test          # 124 tests
npm run check     # all four

The browser suite drives the real Playwright code against a local replica of Vinted's listing form (tests/fixtures/listing-form.html) that reproduces what breaks naive automation: a hidden file input, custom dropdowns with a search box, and the brand/size/condition/colour block that only mounts after a category is chosen. The MCP suite speaks the wire protocol to the built server exactly as Hermes does.

Tests never touch the real Vinted. Live tests are opt-in with VINTED_LIVE_TESTS=1 and never publish, delete or send a message.

See docs/DECISIONS.md for why the project is shaped this way, docs/REFERENCES.md for what was learned from prior work and under which licences, and WORKLOG.md for current state and known limitations.


Security and good behaviour

  • Your Vinted password is never requested, stored or handled. Sign-in is manual, in a window you control.
  • CAPTCHAs and 2FA are never circumvented.
  • Cookies, tokens and session material are redacted from logs and never returned by a tool.
  • Nothing is published, deleted or sent to another person without an explicit confirmation for that specific action.
  • Requests are rate-limited to 2/second per storefront by default.
  • .gitignore excludes browser profiles, drafts, photos, .env and databases.

This project drives the same endpoints and the same web form your own browser uses, on your own account. Vinted publishes no public API; using this is subject to their Terms of Service and that call is yours.


Publishing this to its own repository

This tree currently lives on a branch of another repository. To give it the standalone vinted-seller-mcp repo it is named for, from a machine with an authenticated gh:

gh auth status                       # confirm you are signed in
gh repo create vinted-seller-mcp --private --source=. --remote=vinted --push

Or without gh: create an empty private repository on GitHub, then

git remote add vinted git@github.com:rachid598/vinted-seller-mcp.git
git push -u vinted HEAD:main

Nothing sensitive is tracked — see Security — so the history is safe to move as is.


Licence

MIT.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选