OfferUpBot
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OfferUpBotResearch used road bikes under $500 in Seattle and summarize the top 3 listings."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OfferUpBot
OfferUpBot is the OfferUp execution layer for external agents. Agents identify items, choose prices, write copy, and decide responses. OfferUpBot handles OfferUp access, login state, persistence, browser work, retries, duplicate prevention, and external verification.
Agent guidance
Agents working without conversation history start at AGENTS.md. Direct OfferUp execution uses skills/operating-offerupbot/SKILL.md; end-to-end physical-item ownership uses skills/operating-seller-operator/SKILL.md; consequential image reasoning uses skills/inspecting-visual-evidence/SKILL.md. Each skill loads deeper material only when the task requires it.
Runtime decision
The default interface is on-demand MCP stdio:
agent launches OfferUpBot
↓
MCP tools execute against shared SQLite state and browser profile
↓
agent disconnects
↓
OfferUpBot exitsState survives in SQLite and the dedicated OfferUp browser profile. The default process does not start a scheduler, HTTP listener, or background monitor.
Continuous monitor mode is optional and defaults off. run:due performs one explicit catch-up pass and exits.
Requirements
macOS with Google Chrome installed for authenticated operations
Node.js 22 or later
pnpm
No Docker, external database, Redis, or separately managed runtime service is required.
Install and build
git clone https://github.com/yinkev/OfferUpBot.git
cd OfferUpBot
pnpm install
pnpm buildRun the development MCP entrypoint:
pnpm mcpRun the built entrypoint:
pnpm startThe default data directory is:
~/.offerupbot/
├── state.sqlite
├── browser-profile/
└── agents.json # only after restricted agents are createdOverride it with OFFERUPBOT_DATA_DIR when isolation is required.
OfferUp login
pnpm auth:loginA dedicated Chrome window opens. Enter credentials directly on OfferUp. Login is accepted only when OfferUp's authenticated account query returns a real account ID. Credentials are not requested through chat or stored in SQLite.
MCP configuration
After pnpm build, point the agent host at the built stdio entrypoint:
{
"mcpServers": {
"offerupbot": {
"command": "node",
"args": [
"/absolute/path/to/OfferUpBot/dist/src/index.js"
],
"env": {
"OFFERUPBOT_DATA_DIR": "/absolute/path/to/.offerupbot"
}
}
}
}With no agent token, the local stdio process runs as the trusted full operator.
Create a restricted identity when an agent should receive narrower authority:
pnpm agent:create -- \
--id research-agent \
--name "Research Agent" \
--permissions market.read,events.read,watch.manageStore the returned token in that agent's MCP environment as OFFERUPBOT_AGENT_TOKEN. OfferUpBot stores only its SHA-256 hash.
Agent tools
Tool | Purpose | Permission |
| Local runtime and cached session health | none |
| Verify current OfferUp account identity/capabilities |
|
| Search with validated OfferUp filters |
|
| Full listing evidence; optionally cache ordered photos as readable local files with |
|
| Create, inspect, pause, resume, delete, or run a watch |
|
| Execute every overdue watch once |
|
| Read and acknowledge durable events |
|
| Synchronize active, archived, and saved account state |
|
| Synchronize inbox threads and buyer-message changes |
|
| Retrieve one full conversation and listing context |
|
| Send one idempotent, externally verified message |
|
| Validate the current consumer listing contract | none |
| Publish and verify a listing |
|
| Edit and verify supported fields |
|
| Mark sold or archive, then verify account state |
|
Start at skills/operating-offerupbot/SKILL.md; its adjacent REFERENCE.md and WORKFLOWS.md hold the detailed contracts and procedures.
Seller-operator tools
Above the execution tools sits a second MCP surface for agents that own
physical items end to end: 30 seller.* tools covering case/item intake,
photos and provenance-tracked evidence, OCR/deterministic extraction and
identity selection, evidence-sufficiency clarifications, comp research,
pricing and disposition decisions, grounded listing drafts with verified
publish/close, buyer classification, offers, exclusive reservations,
appointments (conflict-checked, with idempotent reminders), the due-task
operator loop, handoff recording, and lifecycle analytics.
Permissions: seller.read (reads), seller.write (domain writes;
publish/close additionally require listing.publish/listing.close),
seller.communicate (buyer ops). Messages still go through the verified
offerup.send_message. Start at skills/operating-seller-operator/SKILL.md; its adjacent REFERENCE.md and WORKFLOWS.md hold the evidence, market, buyer, and lifecycle guidance.
IDs and writes
OfferUp uses two distinct listing identifiers:
listing_id: UUID used for public detail, conversations, verification, and edit mutation input.item_id: numeric seller-inventory ID used by/selling, mark-sold, and archive operations.
Every consequential tool requires a durable idempotency key. Reuse the same key after a timeout or crash. Changing the key can convert a recovery attempt into a duplicate external action.
Explicit scheduling modes
Run all overdue watches once and exit:
pnpm run:dueKeep watch scheduling alive in the foreground only when continuous polling is deliberately needed:
pnpm monitormonitor is never started by pnpm mcp, pnpm start, installation, or login. Closing it stops continuous polling. A later run:due or MCP offerup.run_due call catches up from persisted watch state.
Handoff surface (opt-in)
pnpm handoff starts a small foreground-only HTTP surface for the one human
touchpoint: the in-person handoff. It lists due/pending handoff cards (buyer,
agreed price, appointment, accessories, defects, payment policy, backup
buyers) at GET /, exposes GET /api/handoffs, and records outcomes via
POST /api/handoffs/:itemId/result against the same SQLite state.
It binds 127.0.0.1 (override with OFFERUPBOT_HANDOFF_HOST/OFFERUPBOT_HANDOFF_PORT)
and requires a bearer token from OFFERUPBOT_HANDOFF_TOKEN; when unset, a
token is generated and printed once at startup. Like monitor, it is never
started by anything else — the default runtime stays on-demand MCP stdio with
no listener.
Failure semantics
A write is complete only when its response contains verified: true.
Important codes:
OFFERUP_SESSION_EXPIRED/AUTH_REQUIRED: complete OfferUp login before authenticated work.RESOURCE_BUSY: another process owns the account or browser lease; retry later with the same idempotency key.RESOURCE_LOCK_LOST: do not assume the write completed; call the relevant read or sync tool before retrying.IDEMPOTENCY_CONFLICT: the same key was presented with different intent; reconcile the calling agent's state.OFFERUP_FILTER_MISMATCH: OfferUp silently ignored a search constraint; do not use the returned set as valid comps.AUTO_CATEGORY_MISMATCH: OfferUp enforced a category different from the submitted draft.CATEGORY_PATH_NOT_TERMINAL: the path stopped on a parent category rather than a selectable final category.
Verification
Routine verification:
pnpm test
pnpm typecheck
pnpm buildRead-only live acceptance against the configured OfferUp profile and public site:
OFFERUPBOT_LIVE_TESTS=1 pnpm exec tsx --test \
tests/account/live.test.ts \
tests/auth/live.test.ts \
tests/offerup/public/live.test.tsSafe intercepted write-contract acceptance:
OFFERUPBOT_LIVE=1 pnpm exec tsx --test --test-concurrency=1 \
tests/messages/live-direct.test.ts \
tests/listings/live-browser.test.ts \
tests/listings/live-close.test.tsThe intercepted tests exercise the current message, consumer publish, edit, media-upload, mark-sold, and archive request paths against the live OfferUp UI while fulfilling the consequential mutations locally. They do not create, modify, sell, or archive a real listing.
Current acceptance boundary
The code, public reads, authenticated reads, current web message mutations, consumer publish/edit UI contracts, and close mutations are implemented and tested. A controlled in-memory agent test covers research through publish, inbox, message, update, and close.
A designated real listing lifecycle was completed on 2026-08-24 against the configured acceptance account: a Philips Norelco BG7030/49 was published with six photos and externally verified, updated from $60 to $55 and re-verified with its New condition preserved, then archived and verified in authenticated inventory as UNLISTED. The run exposed and closed two live defects: terminal category selection while the outer post dialog remained visible, and condition loss during price-only edits.
The remaining real-message transport gate was completed the same day: offerup.send_message sent one $50 cash offer through the direct adapter, the returned message ID was reread in the exact thread, and authenticated inbox sync confirmed the conversation. The seller later replied, “Its a pokemon center etb,” exposing a separate reasoning failure. The listing's first photo visibly identified the Pokémon Center exclusive variant, but the agent had valued the standard ETB. Exact TCGplayer product 692949 was near $120, so the $50 offer was about 42% of exact market—not 78%. The exactly-once write acceptance passed; variant identification, valuation, and target selection failed. The corrected postmortem is in docs/acceptance/2026-08-24-real-message-send.md.
Both designated consequential-write transport acceptances are complete. Future real writes still require an explicitly designated listing or conversation; repeatable regression tests remain intercepted and must not mutate arbitrary account objects.
Seller-operator layer (added 2026-08)
Above this execution layer sits the seller-operator system: canonical physical
items with provenance-tracked evidence, identity resolution, market/pricing
research with realized-vs-active comp separation, gated listing orchestration,
buyer/offer/reservation/appointment state, handoff outcome recording, and
reconstructable case context. It is fully wired into the production runtime
(all schemas ensured at startup; runtime.runDue() runs one catch-up pass
over watches and seller tasks; account listing syncs append a
views/discussions/price time series) and exposed to agents through the
seller.* tools above.
Optional integrations, all off by default:
OFFERUPBOT_READ_ONLY=1— blocks every consequential OfferUp write (publish/update/close/send, orchestrator path included).OFFERUPBOT_EBAY_CLIENT_ID/OFFERUPBOT_EBAY_CLIENT_SECRET— enable the eBay comp sources (active asking + sold, official APIs). Without them the sources report unavailable and comps stay empty; nothing is scraped.OCR runs through macOS Vision when available (darwin + osascript) and degrades to deterministic extraction only elsewhere.
Outcome analytics (src/analytics) compute the full metric set over sold and
non-sold items alike, and category priors from completed outcomes feed back
into pricing and disposition (bounded, sample-size shrunk, recorded in the
decision rationale). See docs/seller-operator-architecture.md for
the layer map and invariants, docs/seller-operator-tracker.md for build
status, and tests/e2e/seller-operator.test.ts for the controlled full-lifecycle
acceptance (mocked external writes — no real marketplace mutations).
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Runtime permission, approval, and audit layer for AI agent tool execution.
Reliable async execution for agent tool calls: schema gating, retries, idempotency, audit trail.
A witness layer for AI agent tool calls.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/yinkev/OfferUpBot'
If you have feedback or need assistance with the MCP directory API, please join our Discord server