Tools
Public MCP exposes five tools, all read-only. Every tool takes the same code parameter: the full scanned URL, such as https://qr71.cn/okDISU/qssTnoy.
| Tool | Question it answers |
|---|---|
entity_get | What is this code? |
entity_records_list | What has happened to this thing? |
entity_actions_list | What can be done right now? |
entity_action_get | Which fields does this action require? |
entity_bindings_list | Which things has this code pointed at? |
Note: examples on this page come from a public demo code; they are abridged and personal names have been replaced. The live service returns field values in Chinese — the examples here are translated for illustration. Real responses also carry
representations(URLs of other renderings such as Markdown),category,description,dateCreated,dateModifiedanditems; the full structure is defined by schema.json.
entity_get
Returns the whole entity in one call, including previews of actions and records. Start here.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Code URL |
formats | string[] | No | Which representations to return: json, markdown, jsonld, og. When omitted, returns json |
Response
The response is an object keyed by format name. By default it has a single json key holding the ObjectCard:
{
"json": {
"$schema": "https://a2a.objqr.com/ext/caoliao-entity/v2/schema.json",
"objectCardVersion": "2.0",
"generatedAt": "2026-08-12T17:14:13.264Z",
"thing": {
"id": "thg_qssTnoy",
"idScheme": "code-derived",
"name": "CNC Lathe A08-03-06",
"type": { "id": "generic", "additionalType": "https://schema.org/Thing" }
},
"code": {
"id": "qssTnoy",
"uri": "https://qr71.cn/okDISU/qssTnoy",
"status": "bound"
},
"binding": {
"boundAt": "2025-03-20T17:25:06.000Z",
"historyCount": 1,
"historyVia": "entity_bindings_list"
},
"org": { "id": "okDISU", "name": "Caoliao" },
"properties": [
{ "name": "Equipment", "value": "CNC Lathe" },
{ "name": "Asset No.", "value": "A08-03-06" },
{ "name": "Location", "value": "Hydraulic Remanufacturing Workshop" }
],
"states": [
{
"machine": "state_8985306",
"name": "Operating status",
"current": "Normal",
"updatedAt": "2026-06-23T02:26:36.000Z",
"derivedFrom": "records",
"byRecord": "rec_460807516"
}
],
"records": {
"count": 1,
"preview": [
{
"id": "rec_460807516",
"type": "inspection",
"label": "Inspection form",
"at": "2026-06-23T02:26:36.000Z",
"result": "pass",
"resultLabel": "Normal",
"by": { "name": "Zhang" }
}
],
"listVia": "entity_records_list"
},
"actions": [
{
"action": "record:1556557",
"label": "Inspection form",
"requiresAuth": true,
"formSchemaVia": "entity_action_get",
"humanUrl": "https://qr71.cn/okDISU/qssTnoy"
}
],
"plans": [
{ "action": "record:1556556", "label": "Maintenance record", "cycle": "Weekly", "status": "running" }
],
"endpoints": {
"mcp": { "url": "https://mcp.objqr.com/mcp", "transport": "streamable-http" },
"a2a": {
"url": "https://a2a.objqr.com",
"agentCard": "https://a2a.objqr.com/.well-known/agent-card.json"
}
},
"contract": {
"profile": "read-only-public",
"rules": {
"readOnly": true,
"publicOnly": true,
"singleOrgView": true,
"actionsExecutable": false
}
}
}
}formats determines exactly which keys the response contains — it replaces the set, it does not add to it. Passing ["markdown"] alone returns no json key and no ObjectCard; when you still need the structured card, include "json" in the array, e.g. ["json", "markdown"]. markdown is a human-readable rendering of the same entity; jsonld is a JSON-LD (schema.org vocabulary) representation; og is Open Graph metadata — useful for search and sharing integrations.
Main blocks
| Field | Description |
|---|---|
thing | The thing itself: name and type |
code | The code: identifier, URL, code status (bound active / disabled) |
binding | How the code is bound to the thing, and when |
properties | Property fields, taken from the QR code's content modules |
relations | Relation slots: located in, belongs to, responsible by, part of. An empty object {} until resolvable into structured references |
states | Business states. derivedFrom explains where a state came from |
records | The record ledger (inspections, repairs, maintenance): count is the total, preview holds the most recent entries |
media | Images and attachments |
actions | Actions available right now — listed, never executed |
plans | Recurring plans, such as weekly maintenance |
endpoints | Where this entity is reachable over Public MCP and A2A |
representations | URLs of other renderings of this entity (HTML / Markdown, …) |
contract | The boundaries this response honors |
Top-level blocks are always present (the schema marks them required): when untracked they hold null, {} or [], all of which read as "not tracked" — never as a negative. "Absence" semantics apply to optional sub-fields inside blocks; see consumption rule 2 below.
Four consumption rules
Important: these four affect how you store the data. Read them before you write code.
thing.idis a transitional, code-derived identifier (idSchemeiscode-derived). Do not persist a dependency on it. The stable identifier to store iscode.uri.- An absent sub-field, or a block holding
null/{}/[], means "not tracked" or "cannot be resolved". It does not meanfalseor "does not exist". Never read either as a negative answer. code.statusis the status of the code;statesare business states. They are different things.- Ledger entries can currently be edited by the owning organization. An immutable ledger is the target state; this contract does not promise immutability — do not persist records as tamper-proof audit evidence.
entity_records_list
Returns the full ledger — inspections, repairs, maintenance — newest first, cursor-paginated. entity_get only previews the latest few; use this to page through history.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Code URL |
size | number | No | Page size, default 20, maximum 50 |
page_token | string | No | The nextPageToken from the previous page. Leave empty for the first page |
Response
{
"url": "https://qr71.cn/okDISU/qssTnoy",
"records": [
{
"id": "rec_460807516",
"type": "inspection",
"label": "Inspection form",
"at": "2026-06-23T02:26:36.000Z",
"result": "pass",
"resultLabel": "Normal",
"summary": "Inspection result: normal",
"by": { "name": "Zhang" },
"snapshot": { "codeId": "qssTnoy", "thingId": "thg_qssTnoy" }
}
],
"hasMore": false,
"total": 1
}Each entry carries a snapshot of the code and thing as they were when the record was created. All timestamps are ISO 8601 with timezone.
When hasMore is true, the response also carries a top-level nextPageToken field (absent when hasMore is false, which is why the example above has none); pass it as page_token on the next call.
entity_actions_list
Lists the actions available right now — which forms can be filled in, which state changes exist. Listing only, never execution.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Code URL |
Response
{
"url": "https://qr71.cn/okDISU/qssTnoy",
"actions": [
{
"action": "record:1556557",
"label": "Inspection form",
"requiresAuth": true,
"annotations": {
"readonly": false,
"destructive": false,
"idempotent": "by_request_key",
"confirm": "none"
},
"precondition": null,
"formSchemaVia": "entity_action_get",
"humanUrl": "https://qr71.cn/okDISU/qssTnoy"
},
{
"action": "record:1556556",
"label": "Maintenance record",
"requiresAuth": true,
"annotations": {
"readonly": false,
"destructive": false,
"idempotent": "by_request_key",
"confirm": "none"
},
"precondition": null,
"formSchemaVia": "entity_action_get",
"humanUrl": "https://qr71.cn/okDISU/qssTnoy"
}
]
}| Field | Description |
|---|---|
action | Action identifier such as record:1556557; pass it to entity_action_get |
requiresAuth | Whether submitting this action requires authorization |
annotations | Action nature: read-only, destructive, idempotency mode, confirmation |
humanUrl | Where a person executes this action in a browser |
Important: Public MCP does not perform writes. To actually submit a record, hand
humanUrlto a person to complete on the Caoliao page.
entity_action_get
Returns the complete field schema of one action. entity_actions_list lists actions without fields; use this before filling a form.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Code URL |
action | string | One of the two | Action identifier from entity_actions_list |
tpl_id | number | One of the two | Form template id |
You must provide either action or tpl_id; omitting both returns an error.
Response
{
"url": "https://qr71.cn/okDISU/qssTnoy",
"tplId": 1556557,
"fields": [
{
"id": 10098724,
"title": "Inspection checklist",
"type": "checklist",
"required": true,
"choices": [
{ "id": 53279777, "label": "No abnormal noise, operates normally" },
{ "id": 53279779, "label": "Brakes, gearbox and oil tank normal" }
],
"desc": "Tick when normal, cross when abnormal, and explain any abnormality"
},
{
"id": 10098730,
"title": "Inspection result",
"type": "radio",
"required": true,
"choices": [
{ "id": 53279846, "label": "Normal" },
{ "id": 53279847, "label": "Abnormal" }
]
},
{
"id": 10098732,
"title": "Inspector",
"type": "name",
"required": true
}
]
}Reading the field schema requires no authorization; submitting does.
entity_bindings_list
Returns which things this code has pointed at, for audit purposes.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | string | Yes | Code URL |
Response
{
"url": "https://qr71.cn/okDISU/qssTnoy",
"bindings": [
{
"thingId": "thg_qssTnoy",
"current": true,
"boundAt": "2025-03-20T17:25:06.000Z"
}
],
"note": "..."
}Important: Caoliao does not support rebinding today — a code maps to exactly one thing. This tool currently always returns a single
currentbinding, and thenotefield says so explicitly. Once rebinding ships, this will return a real sequence of binding events. The response shape will not change, so you can write against it now.
Typical call order
entity_get whole entity; check records.count and actions
├─ many records → entity_records_list (page through)
├─ need to fill a form → entity_action_get (fields) → hand humanUrl to a person
└─ audit code ownership → entity_bindings_listNext
- Data Scope and Authorization — what you read by default, and what a credential adds