Initial Marktplaats CLI scaffold
Create mrktplaats-cli from twdehands-cli, updating module/import names, command branding, and token paths while preserving command behavior and payload formats.
This commit is contained in:
437
README.md
Normal file
437
README.md
Normal file
@@ -0,0 +1,437 @@
|
||||
# mrktplaats CLI
|
||||
|
||||
Command-line interface to [marktplaats.nl](https://www.marktplaats.nl), built on the
|
||||
[mrktplaats Go SDK](../mrktplaats). Designed to be used by both humans and LLM agents.
|
||||
|
||||
---
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
cd mrktplaats-cli
|
||||
go build -o mrktplaats .
|
||||
# optional: install globally
|
||||
go install .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentication
|
||||
|
||||
Run `login` once to authenticate. The token is saved to
|
||||
`~/.config/mrktplaats/token` and reused automatically.
|
||||
|
||||
```bash
|
||||
mrktplaats login --email you@example.com --password yourpassword
|
||||
```
|
||||
|
||||
If the account has SMS 2FA enabled the first call returns a verification prompt.
|
||||
Re-run with the code you received:
|
||||
|
||||
```bash
|
||||
mrktplaats login --email you@example.com --password yourpassword --code 123456
|
||||
```
|
||||
|
||||
**Alternative: environment variable**
|
||||
|
||||
```bash
|
||||
export MRKTPLAATS_TOKEN=your-token-here
|
||||
mrktplaats me
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output format
|
||||
|
||||
All commands write **pretty-printed JSON** to stdout.
|
||||
Errors are written to stderr and exit with code 1.
|
||||
|
||||
---
|
||||
|
||||
## Command reference
|
||||
|
||||
### `login`
|
||||
|
||||
```
|
||||
mrktplaats login --email EMAIL --password PASS [--code 2FA_CODE]
|
||||
```
|
||||
|
||||
Authenticate and persist the token. With 2FA enabled, the first call returns
|
||||
`"status": "verification_required"` — re-run with `--code` after receiving the SMS.
|
||||
|
||||
**Output (success)**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"token_saved": "/home/you/.config/mrktplaats/token",
|
||||
"access_token": "8fc90f77-..."
|
||||
}
|
||||
```
|
||||
|
||||
**Output (2FA required)**
|
||||
```json
|
||||
{
|
||||
"status": "verification_required",
|
||||
"method": "sms",
|
||||
"message": "We sent a code to +32 4xx xxx xxx",
|
||||
"request_id": "abc123",
|
||||
"next_step": "re-run with --code CODE after receiving the SMS"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `search`
|
||||
|
||||
```
|
||||
mrktplaats search --query Q [--category ID] [--page N] [--size N] [--sort FIELD]
|
||||
```
|
||||
|
||||
Search listings. No authentication required.
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--query` | *(required)* | Search query |
|
||||
| `--category` | 0 (all) | Category ID filter |
|
||||
| `--page` | 1 | Page number |
|
||||
| `--size` | 20 | Results per page (max 100) |
|
||||
| `--sort` | `SORT_INDEX` | `SORT_INDEX` \| `DATE_DESC` \| `PRICE_ASC` \| `PRICE_DESC` |
|
||||
| `--seller` | — | Filter by seller ID |
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"total": 2573,
|
||||
"page": 1,
|
||||
"size": 3,
|
||||
"listings": [
|
||||
{
|
||||
"urn": "m2372861012",
|
||||
"title": "Seiko SARV001",
|
||||
"price_cents": 25000,
|
||||
"price_label": "€ 250,00",
|
||||
"price_type": "FIXED",
|
||||
"city": "Gent",
|
||||
"seller_name": "Jonathan",
|
||||
"url": "https://..."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`price_type` values: `FIXED`, `MIN_BID`, `FAST_BID`, `FREE`, `ON_REQUEST`.
|
||||
|
||||
---
|
||||
|
||||
### `listing`
|
||||
|
||||
```
|
||||
mrktplaats listing --urn URN
|
||||
```
|
||||
|
||||
Fetch full details for a listing. Requires authentication.
|
||||
|
||||
**Output** — full listing object including:
|
||||
- `adCore.title`, `.description`, `.price`, `.adAddress`, `.pictures`, `.attributes`
|
||||
- `sellerInformation.id`, `.name` — seller's ID is needed for `Relevant.Get`
|
||||
- `bids` — array of current bids
|
||||
- `currentMinimumBid` — minimum next bid in cents
|
||||
|
||||
---
|
||||
|
||||
### `conversations`
|
||||
|
||||
```
|
||||
mrktplaats conversations [--limit N]
|
||||
```
|
||||
|
||||
List all messaging conversations.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"unread": 0,
|
||||
"conversations": [
|
||||
{
|
||||
"id": "1cgx:5qgvx3h:2p1gxt95d",
|
||||
"item_urn": "m2372861012",
|
||||
"title": "NVIDEA 100000 V2",
|
||||
"other_party": "Mattia",
|
||||
"other_party_id": 57506580,
|
||||
"unread": 0,
|
||||
"latest_message": "Is this still available?"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `messages`
|
||||
|
||||
```
|
||||
mrktplaats messages --conv CONV_ID [--limit N] [--offset N]
|
||||
```
|
||||
|
||||
Read messages in a conversation. Use `conversations` to find IDs.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"total": 4,
|
||||
"messages": [
|
||||
{
|
||||
"id": "abc123",
|
||||
"sender_id": 40921019,
|
||||
"text": "Is this still available?",
|
||||
"date": "2026-03-03T18:48:00Z",
|
||||
"type": "chat",
|
||||
"read": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `send`
|
||||
|
||||
```
|
||||
mrktplaats send --urn URN --text TEXT
|
||||
```
|
||||
|
||||
Start a **new** conversation with the seller of a listing.
|
||||
Use `reply` to respond to an existing conversation.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"conversation_id": "1cgx:5qgvx3h:2p1gxt95d",
|
||||
"item_urn": "m2372861012",
|
||||
"status": "sent"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `reply`
|
||||
|
||||
```
|
||||
mrktplaats reply --conv CONV_ID --text TEXT
|
||||
```
|
||||
|
||||
Send a message in an **existing** conversation.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"message_id": "029c3014-...",
|
||||
"conversation_id": "1cgx:5qgvx3h:2p1gxt95d",
|
||||
"status": "sent"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `bid`
|
||||
|
||||
```
|
||||
mrktplaats bid --urn URN --amount EUROS [--message TEXT]
|
||||
```
|
||||
|
||||
Place a bid. The listing must have `price_type` of `MIN_BID` or `FAST_BID`.
|
||||
Amount is in **euros** (e.g. `--amount 20` for €20.00).
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"bids": [
|
||||
{"id": 1533844324, "value": 2000, "date": "...", "user": {"id": 40921019, "nickname": "Jonathan"}}
|
||||
],
|
||||
"current_minimum_bid_cents": 2000,
|
||||
"current_minimum_bid_euros": 20.0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `remove-bid`
|
||||
|
||||
```
|
||||
mrktplaats remove-bid --id BID_ID
|
||||
```
|
||||
|
||||
Remove a bid. `BID_ID` is the numeric `id` field from the `bids` array.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"bids": [],
|
||||
"current_minimum_bid_cents": 2000,
|
||||
"current_minimum_bid_euros": 20.0
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `favorites`
|
||||
|
||||
```
|
||||
mrktplaats favorites [--limit N]
|
||||
```
|
||||
|
||||
List saved listings.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"total": 3,
|
||||
"more": false,
|
||||
"items": [
|
||||
{"item_urn": "m2372861012", "saved_at": ""}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `add-favorite` / `remove-favorite`
|
||||
|
||||
```
|
||||
mrktplaats add-favorite --urn URN
|
||||
mrktplaats remove-favorite --urn URN
|
||||
```
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{"status": "added", "item_urn": "m2372861012"}
|
||||
{"status": "removed", "item_urn": "m2372861012"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `me`
|
||||
|
||||
```
|
||||
mrktplaats me
|
||||
```
|
||||
|
||||
Get the authenticated user's profile (id, name, email, postcode, etc.).
|
||||
|
||||
---
|
||||
|
||||
### `my-ads`
|
||||
|
||||
```
|
||||
mrktplaats my-ads [--status active|inactive|sold] [--limit N]
|
||||
```
|
||||
|
||||
List own listings.
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{
|
||||
"total": 1,
|
||||
"ads": [
|
||||
{
|
||||
"item_id": "m2373048475",
|
||||
"title": "Seiko SARV001 - SDK integratietest",
|
||||
"status": "active",
|
||||
"price_cents": 25000,
|
||||
"price_euros": 250.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `delete-ad`
|
||||
|
||||
```
|
||||
mrktplaats delete-ad --urn URN [--reason N]
|
||||
```
|
||||
|
||||
Delete one of your own listings.
|
||||
|
||||
| Reason | Meaning |
|
||||
|--------|---------|
|
||||
| 1 | Sold on marktplaats.nl |
|
||||
| 2 | Sold elsewhere |
|
||||
| 3 | No longer selling |
|
||||
| 4 | Other (default) |
|
||||
|
||||
**Output**
|
||||
```json
|
||||
{"status": "deleted", "item_urn": "m2373048475"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LLM agent usage guide
|
||||
|
||||
This tool is designed to be called by an LLM agent as a subprocess. All output
|
||||
is structured JSON so the agent can parse results directly.
|
||||
|
||||
### Typical workflow for buying
|
||||
|
||||
```
|
||||
1. search for items
|
||||
mrktplaats search --query "laptop" --size 10
|
||||
|
||||
2. inspect a promising listing
|
||||
mrktplaats listing --urn <urn from search>
|
||||
|
||||
3. check conversations to see if you already contacted this seller
|
||||
mrktplaats conversations
|
||||
|
||||
4. contact the seller (new conversation)
|
||||
mrktplaats send --urn <urn> --text "Is this still available? Can you do €X?"
|
||||
|
||||
OR reply to an existing conversation
|
||||
mrktplaats reply --conv <conv_id> --text "..."
|
||||
|
||||
5. if the listing supports bidding (price_type = MIN_BID or FAST_BID)
|
||||
mrktplaats bid --urn <urn> --amount <euros>
|
||||
# to cancel: mrktplaats remove-bid --id <bid_id>
|
||||
|
||||
6. save interesting listings for later
|
||||
mrktplaats add-favorite --urn <urn>
|
||||
```
|
||||
|
||||
### Typical workflow for selling
|
||||
|
||||
```
|
||||
1. check own listings
|
||||
mrktplaats my-ads
|
||||
|
||||
2. delete a listing
|
||||
mrktplaats delete-ad --urn <urn>
|
||||
|
||||
3. check and reply to incoming messages
|
||||
mrktplaats conversations
|
||||
mrktplaats messages --conv <conv_id>
|
||||
mrktplaats reply --conv <conv_id> --text "..."
|
||||
```
|
||||
|
||||
### Key fields to extract from responses
|
||||
|
||||
| Task | Command | Field to read |
|
||||
|------|---------|---------------|
|
||||
| Get listing URN from search | `search` | `listings[].urn` |
|
||||
| Get seller ID (for bids/contact) | `listing` | `sellerInformation.id` |
|
||||
| Get bid ID (to cancel) | `listing` or `bid` | `bids[].id` |
|
||||
| Get conversation ID | `conversations` or `send` | `conversations[].id` / `conversation_id` |
|
||||
| Check if bid-enabled | `listing` | `adCore.price.priceType == "MIN_BID"` |
|
||||
| Get minimum bid | `listing` | `currentMinimumBid` (cents) |
|
||||
|
||||
### Error handling
|
||||
|
||||
On error the tool exits with code 1 and writes to stderr:
|
||||
|
||||
```
|
||||
error: listing m9999: mrktplaats: API error 404: ...
|
||||
```
|
||||
|
||||
The agent should check the exit code and parse the error message from stderr.
|
||||
Reference in New Issue
Block a user