Skip to main content
Glama

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 exits

State 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 build

Run the development MCP entrypoint:

pnpm mcp

Run the built entrypoint:

pnpm start

The default data directory is:

~/.offerupbot/
├── state.sqlite
├── browser-profile/
└── agents.json          # only after restricted agents are created

Override it with OFFERUPBOT_DATA_DIR when isolation is required.

OfferUp login

pnpm auth:login

A 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.manage

Store 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

offerup.health

Local runtime and cached session health

none

offerup.auth_status

Verify current OfferUp account identity/capabilities

account.read

offerup.research_market

Search with validated OfferUp filters

market.read

offerup.inspect_listing

Full listing evidence; optionally cache ordered photos as readable local files with download_photos

market.read

offerup.watch_market

Create, inspect, pause, resume, delete, or run a watch

watch.manage

offerup.run_due

Execute every overdue watch once

watch.manage

offerup.next_events

Read and acknowledge durable events

events.read

offerup.sync_listings

Synchronize active, archived, and saved account state

account.read

offerup.sync_inbox

Synchronize inbox threads and buyer-message changes

account.read

offerup.get_thread

Retrieve one full conversation and listing context

account.read

offerup.send_message

Send one idempotent, externally verified message

message.send

offerup.validate_listing

Validate the current consumer listing contract

none

offerup.publish_listing

Publish and verify a listing

listing.publish

offerup.update_listing

Edit and verify supported fields

listing.update

offerup.close_listing

Mark sold or archive, then verify account state

listing.close

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:due

Keep watch scheduling alive in the foreground only when continuous polling is deliberately needed:

pnpm monitor

monitor 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 build

Read-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.ts

Safe 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.ts

The 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).

-
license - not tested
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

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.

View all MCP Connectors

Latest Blog Posts

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