Manage one storefront: listings, bulk catalogue sync, analytics and webhooks.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Bearer credential for one shop, set in the client and sent on every request. No tool takes it as an argument.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Per-listing daily views + leads (deltas) over a date range, plus totals. Keyset-paginated.
YYYY-MM-DD
YYYY-MM-DD
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http partner-api https://eno.vn/api/mcp \ --header 'Authorization: Bearer YOUR_TOKEN'
Headers
Authorization (required): Bearer credential for one shop, set in the client and sent on every request. No tool takes it as an argument.Per-listing daily views + leads (deltas) over a date range, plus totals. Keyset-paginated.
| Argument | Type | Description |
|---|---|---|
| to | string | YYYY-MM-DD |
| from | string | YYYY-MM-DD |
| limit | integer | |
| cursor | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"to": {
"type": "string",
"description": "YYYY-MM-DD"
},
"from": {
"type": "string",
"description": "YYYY-MM-DD"
},
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1
},
"cursor": {
"type": "string"
}
},
"additionalProperties": false
}Shop-level totals: views, leads, and listing counts by status.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"additionalProperties": false
}Create up to 200 listings in one call. Each is independent; image URLs are re-hosted.
| Argument | Type | Description |
|---|---|---|
| listings* | array |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"listings"
],
"properties": {
"listings": {
"type": "array",
"items": {
"type": "object",
"required": [
"categorySlug",
"title",
"price"
],
"properties": {
"price": {
"type": "number",
"minimum": 0,
"description": "Price in VND"
},
"title": {
"type": "string",
"minLength": 3,
"description": "Listing title (≥3 chars, no phone numbers)"
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"description": "Public image URLs — auto re-hosted to first-party storage"
},
"district": {
"type": "string"
},
"condition": {
"type": "string",
"description": "e.g. \"new\", \"used\""
},
"externalId": {
"type": "string",
"description": "Your own id for this listing (for sync/upsert)"
},
"description": {
"type": "string"
},
"categorySlug": {
"type": "string",
"description": "Category slug, e.g. \"electronics\""
}
},
"additionalProperties": false
},
"maxItems": 200,
"minItems": 1
}
},
"additionalProperties": false
}Create a listing. Image URLs may be public — they are re-hosted to first-party storage automatically.
| Argument | Type | Description |
|---|---|---|
| brand | string | |
| model | string | |
| price | number | Price in VND — required, except for a job (category "jobs"), whose pay is `salaryM`. A job sent with only a price has it read as the monthly salary in VND. |
| title* | string | Listing title (≥3 chars, no phone numbers) |
| images | array | Public image URLs — auto re-hosted to first-party storage |
| salaryM | integer | A job (category "jobs"): the monthly salary in million VND; omit it when the pay is agreed with the candidate. A job's stored price is derived from it, and a job takes no offers. |
| district | string | |
| condition | string | e.g. "new", "used" |
| negotiable | boolean | |
| description | string |
Delete one of the shop's listings. While the shop or the listing is under investigation (an open report, or the account is held/suspended) it is hidden instead — the result then says `deleted: false, hidden: true` and why.
| Argument | Type | Description |
|---|---|---|
| id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
}
},
"additionalProperties": false
}Unregister a webhook endpoint by id.
| Argument | Type | Description |
|---|---|---|
| id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
}
},
"additionalProperties": false
}Get one of the shop's listings by id (any status).
| Argument | Type | Description |
|---|---|---|
| id* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
}
},
"additionalProperties": false
}Get the authenticated shop's storefront profile, trust score, and live listing count.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"additionalProperties": false
}List the shop's listings (all statuses). Keyset-paginated — pass the returned next_cursor to page.
| Argument | Type | Description |
|---|---|---|
| limit | integer | |
| cursor | string | |
| status | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1
},
"cursor": {
"type": "string"
},
"status": {
"enum": [
"active",
"sold",
"hidden"
],
"type": "string"
}
},
"additionalProperties": false
}List the shop's registered webhook endpoints (secrets are never returned) with delivery health.
No arguments.
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {},
"additionalProperties": false
}Register an HTTPS endpoint for signed listing events. The signing secret is returned ONCE — store it.
| Argument | Type | Description |
|---|---|---|
| url* | string | Public HTTPS endpoint |
| events | array | Omit for all events |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "Public HTTPS endpoint"
},
"events": {
"type": "array",
"items": {
"enum": [
"listing.created",
"listing.updated",
"listing.status_changed",
"listing.deleted"
],
"type": "string"
},
"description": "Omit for all events"
}
},
"additionalProperties": false
}Set a listing's availability: active | sold | hidden. Re-activating a listing that is waiting for eno's licence check is refused with `licence_check_required` until eno approves it.
| Argument | Type | Description |
|---|---|---|
| id* | string | |
| status* | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id",
"status"
],
"properties": {
"id": {
"type": "string"
},
"status": {
"enum": [
"active",
"sold",
"hidden"
],
"type": "string"
}
},
"additionalProperties": false
}Upsert the catalogue by your own externalId. mode "partial" only touches sent rows; "full" also retires listings absent from the payload. Up to 200 rows.
| Argument | Type | Description |
|---|---|---|
| mode | string | |
| listings* | array |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"listings"
],
"properties": {
"mode": {
"enum": [
"partial",
"full"
],
"type": "string"
},
"listings": {
"type": "array",
"items": {
"type": "object",
"required": [
"categorySlug",
"title",
"price",
"externalId"
],
"properties": {
"price": {
"type": "number",
"minimum": 0,
"description": "Price in VND"
},
"title": {
"type": "string",
"minLength": 3,
"description": "Listing title (≥3 chars, no phone numbers)"
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"description": "Public image URLs — auto re-hosted to first-party storage"
},
"status": {
"enum": [
"active",
"sold",
"hidden"
],
"type": "string"
},
"district": {
"type": "string"
},
"condition": {
"type": "string",
"description": "e.g. \"new\", \"used\""
},
"externalId": {
"type": "string"
},
"description": {
"type": "string"
},
"categorySlug": {
"type": "string",
"description": "Category slug, e.g. \"electronics\""
}
},
"additionalProperties": false
},
"maxItems": 200,
"minItems": 1
}
},
"additionalProperties": false
}Edit one of the shop's listings (sparse). Image URLs may be public — re-hosted automatically. A job's pay is `salaryM`; a `price` sent for a job is read as its monthly salary in VND. An edit of a listing that is waiting for eno's licence check answers `held: true`: the listing stays unpublished until eno approves it (this tool cannot change a subcategory or the licence values).
| Argument | Type | Description |
|---|---|---|
| id* | string | |
| price | number | |
| title | string | |
| images | array | |
| salaryM | any | A job: the monthly salary in million VND; null clears it (pay agreed with the candidate). Wins over `price`. |
| district | string | |
| condition | string | |
| description | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0
},
"title": {
"type": "string"
},
"images": {
"type": "array",
"items": {
"type": "string"
}
},
"salaryM": {
"anyOf": [
{
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0
},
{
"type": "null"
}
],
"description": "A job: the monthly salary in million VND; null clears it (pay agreed with the candidate). Wins over `price`."
},
"district": {
"type": "string"
},
"condition": {
"type": "string"
},
"description": {
"type": "string"
}
},
"additionalProperties": false
}Edit the storefront profile. Sparse — only the fields you send change.
| Argument | Type | Description |
|---|---|---|
| bio | string | |
| name | string | |
| phone | string | |
| location | string | |
| avatarUrl | string |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"bio": {
"type": "string"
},
"name": {
"type": "string"
},
"phone": {
"type": "string"
},
"location": {
"type": "string"
},
"avatarUrl": {
"type": "string"
}
},
"additionalProperties": false
}We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 11, 2026.
No readOnlyHint / destructiveHint annotations, so clients can't tell a read from a write without asking.
Fix: Add annotations to each tool, e.g. { readOnlyHint: true } for tools that only read.
| listingType | string |
| categorySlug* | string | Category slug, e.g. "electronics" |
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"categorySlug",
"title"
],
"properties": {
"brand": {
"type": "string"
},
"model": {
"type": "string"
},
"price": {
"type": "number",
"minimum": 0,
"description": "Price in VND — required, except for a job (category \"jobs\"), whose pay is `salaryM`. A job sent with only a price has it read as the monthly salary in VND."
},
"title": {
"type": "string",
"minLength": 3,
"description": "Listing title (≥3 chars, no phone numbers)"
},
"images": {
"type": "array",
"items": {
"type": "string"
},
"description": "Public image URLs — auto re-hosted to first-party storage"
},
"salaryM": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "A job (category \"jobs\"): the monthly salary in million VND; omit it when the pay is agreed with the candidate. A job's stored price is derived from it, and a job takes no offers."
},
"district": {
"type": "string"
},
"condition": {
"type": "string",
"description": "e.g. \"new\", \"used\""
},
"negotiable": {
"type": "boolean"
},
"description": {
"type": "string"
},
"listingType": {
"type": "string"
},
"categorySlug": {
"type": "string",
"description": "Category slug, e.g. \"electronics\""
}
},
"additionalProperties": false
}