MCP for AI assistants
Nextfarm's Model Context Protocol server lets any MCP-capable assistant (ChatGPT, Claude, Copilot, Cursor, Gemini, Grok…) or your own read farm data. Two ways to connect: just the URL (the user signs in to NextFarm, picks a farm, ticks permissions — OAuth 2.1) or an API key for machines.
| Address | |
|---|---|
| Production | https://mcp.nextfarm.vn/mcp |
| Sandbox | https://mcp-dev.nextfarm.vn/mcp |
Option 1 — Just the URL (OAuth)
Paste https://mcp.nextfarm.vn/mcp into the assistant you use — ChatGPT (Apps & Connectors, Developer mode), claude.ai / Claude Code (Connectors), Copilot Studio (Tools › Model Context Protocol), Cursor, Gemini CLI, Grok (Connectors › Custom), VS Code… (per-client config on the AI hub). The assistant opens the NextFarm sign-in → pick a farm → tick permissions → Allow. It can only do what the signed-in person can do on the farm; Write is unticked by default and the owner can switch it off entirely. Disconnect in the app › System › Apps › AI apps ⇒ the next call is refused.
Add an MCP server / connector in your AI assistant URL: https://mcp.nextfarm.vn/mcp → Connect → sign in to NextFarm → pick a farm → Allow
| OAuth scope | Allows | Write? |
|---|---|---|
gis:read | Map, parcels, zones, seasons, farmers | |
iot:read | Devices, sensor readings, alerts | |
harvest:read | Harvests, traceability, QR codes, certificates, prices | |
office:read | Farm books, tasks, calendar, automation rules | |
reports:read | View and export reports |
Standards: OAuth 2.1 + PKCE (S256), Protected Resource Metadata (RFC 9728), Authorization Server Metadata (RFC 8414), Dynamic Client Registration (RFC 7591) or Client ID Metadata Document, Resource Indicators (RFC 8707). Access tokens last 1 hour, refresh tokens rotate. Missing scope ⇒ 403 insufficient_scope (the assistant asks for more). The assistant's token never reaches the API: the MCP server exchanges it for a short-lived delegated token via Token Exchange (RFC 8693), capped at exactly what you consented to.
Option 2 — API key (machines, n8n, your server)
Create a key
In System › API keys, create a dedicated key for the assistant with only the read scopes it needs.
Connect the assistant
{
"url": "https://mcp.nextfarm.vn/mcp",
"transport": "streamable-http",
"headers": { "Authorization": "Bearer nfk_…" }
}Tools
| Tool | What it does | Scope |
|---|---|---|
farm_overview | Parcel area grouped by household, zone or crop: number of households, parcels, total area (ha) and parcels without a boundary. Sorted by area, largest first. | gis:read |
gis_crop_types_list | The farm's crop type catalogue: code, name and category. Use the crop type ID to filter parcels, seasons and yields. | gis:read |
gis_farmers_list | Farmer households / members of the sourcing area: name, zone, group and member type. Filter by zone, group or type. | gis:read |
gis_parcels_get | One parcel: name, code, area, zone, owner, current crop and status. | gis:read |
gis_parcels_list | The farm's parcels: code, area (ha), zone, owner, current crop and status. Filter by zone, crop or status. | gis:read |
gis_search | Search parcels, seasons, farmers and zones by keyword; groups outside the key's scopes come back empty. | gis:read |
gis_seasons_groups | Seasons with the same crop and season name across parcels in one year (e.g. Rice · Winter-Spring): parcel count, total area, earliest/latest planting date. | gis:read |
gis_seasons_list | Seasons for one parcel (cultivation history) or for a year: crop, season name, planting date, expected harvest date and status. Provide a parcel or a year. | gis:read |
gis_zones_stats | Number of parcels and total area (ha) of one zone. | gis:read |
certification_expiring | Certificates (VietGAP, GlobalGAP, organic…) about to expire or already expired, sorted by expiry date. | harvest:read |
harvest_phi_active | Parcels, ponds and pens currently under a pesticide pre-harvest interval or veterinary withdrawal period: earliest safe date and the blocking application. | harvest:read |
harvest_record_list | Declared harvest records: parcel, crop, declared yield (kg), status (draft · confirmed · verified · collected · void), harvest date and traceability lot. | harvest:read |
harvest_search | Search harvest records, QR labels and certificates by keyword (record code, label code, certificate name). | harvest:read |
harvest_summary | Harvested yield (confirmed records only) grouped by household, zone, crop or parcel within a harvest date range. Sorted by total kg, largest first. | harvest:read |
harvest_trace_get | One traceability lot: cultivation log, certificates and photos — what a buyer sees when scanning the QR label. | harvest:read |
harvest_trace_list | Published traceability lots of the farm: parcel, zone, product, certification, harvest date, farmer and number of stages. | harvest:read |
phi_status | Whether a parcel is still within a pesticide pre-harvest interval, the earliest safe harvest date and the application holding it. | harvest:read |
iot_alerts_list | Sensor threshold alerts: device, metric, value, threshold, severity and handling status. | iot:read |
iot_command_status | Latest commands sent to one controller: command type, status, sent time and whether the device acknowledged it. | iot:read |
iot_device_list | The farm's IoT devices: name, type, online or offline, last report time. Filter by status, parcel or zone. | iot:read |
iot_devices_search | Search devices by name or code. | iot:read |
iot_irrigation_outputs | Irrigation outputs of one controller: type (pump, zone valve, fertigation), index and the user-given name (e.g. 'Block A'). | iot:read |
iot_readings_history | Sensor reading history for a device or zone within a time range, newest first. | iot:read |
iot_readings_latest | The latest value of each sensor (temperature, humidity, EC, pH…) for the whole farm or one zone. | iot:read |
iot_schedule_upcoming | Upcoming scheduled irrigation runs: controller, start time, duration (seconds), valves, skipped or already run. | iot:read |
office_automations_list | The farm's automation rules: name, status, trigger type and last run. | office:read |
office_books_fields | Columns of one table: name, data type and options — needed to read row values correctly. | office:read |
office_books_list | The farm's custom books (logs, inventory, costs…): name, description and number of tables. | office:read |
office_books_records | Read rows of a book table, paginated; values keyed by column ID. | office:read |
office_books_tables | Tables inside one farm book. | office:read |
report_catalog | Available farm reports: code, name, group and the number of data blocks you can see. | reports:read |
report_query | Run a report by code over a date range: KPIs, time series, breakdowns and tables. Without dates the last 30 days are used. | reports:read |
farm_overview — Parcel area summary
Parcel area grouped by household, zone or crop: number of households, parcels, total area (ha) and parcels without a boundary. Sorted by area, largest first.
{
"additionalProperties": false,
"properties": {
"crop_type_id": {
"description": "Only one crop type",
"format": "uuid",
"type": "string"
},
"group_by": {
"description": "Group by: owner · zone · crop",
"type": "string"
},
"page_size": {
"description": "Maximum number of groups",
"type": "integer"
},
"status": {
"description": "Parcel status",
"type": "string"
},
"zone_id": {
"description": "Only one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"group_by": "zone"
}gis_crop_types_list — Crop types
The farm's crop type catalogue: code, name and category. Use the crop type ID to filter parcels, seasons and yields.
{
"additionalProperties": false,
"properties": {
"category": {
"description": "Crop category (e.g. rice, fruit trees)",
"type": "string"
}
},
"required": [],
"type": "object"
}gis_farmers_list — Households
Farmer households / members of the sourcing area: name, zone, group and member type. Filter by zone, group or type.
{
"additionalProperties": false,
"properties": {
"groupId": {
"description": "Only one household group",
"format": "uuid",
"type": "string"
},
"memberType": {
"description": "Member type",
"type": "string"
},
"pageSize": {
"description": "Maximum rows",
"type": "integer"
},
"zoneId": {
"description": "Only one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}gis_parcels_get — Parcel detail
One parcel: name, code, area, zone, owner, current crop and status.
{
"additionalProperties": false,
"properties": {
"id": {
"description": "Parcel ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
}{
"id": "0190a3c2-5b1e-7c3d-8e4f-000000000001"
}gis_parcels_list — Parcels
The farm's parcels: code, area (ha), zone, owner, current crop and status. Filter by zone, crop or status.
{
"additionalProperties": false,
"properties": {
"crop_type_id": {
"description": "Only one crop type",
"format": "uuid",
"type": "string"
},
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"status": {
"description": "Parcel status",
"type": "string"
},
"zone_id": {
"description": "Only one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"page": 1,
"page_size": 50
}gis_search — Search parcels, zones, farmers, seasons
Search parcels, seasons, farmers and zones by keyword; groups outside the key's scopes come back empty.
{
"additionalProperties": false,
"properties": {
"limit": {
"description": "Maximum results per group",
"type": "integer"
},
"q": {
"description": "Keyword, diacritics optional",
"type": "string"
}
},
"required": [
"q"
],
"type": "object"
}{
"q": "A-12"
}gis_seasons_groups — Season groups of a year
Seasons with the same crop and season name across parcels in one year (e.g. Rice · Winter-Spring): parcel count, total area, earliest/latest planting date.
{
"additionalProperties": false,
"properties": {
"branchId": {
"description": "Only one branch / cooperative",
"format": "uuid",
"type": "string"
},
"year": {
"description": "Year",
"type": "integer"
}
},
"required": [
"year"
],
"type": "object"
}{
"year": 2026
}gis_seasons_list — Seasons
Seasons for one parcel (cultivation history) or for a year: crop, season name, planting date, expected harvest date and status. Provide a parcel or a year.
{
"additionalProperties": false,
"properties": {
"parcelId": {
"description": "Parcel ID",
"format": "uuid",
"type": "string"
},
"year": {
"description": "Year (e.g. 2026)",
"type": "integer"
}
},
"required": [],
"type": "object"
}{
"year": 2026
}gis_zones_stats — Zone statistics
Number of parcels and total area (ha) of one zone.
{
"additionalProperties": false,
"properties": {
"id": {
"description": "Zone ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
}certification_expiring — Expiring certificates
Certificates (VietGAP, GlobalGAP, organic…) about to expire or already expired, sorted by expiry date.
{
"additionalProperties": false,
"properties": {
"include_expired": {
"description": "Include already expired certificates",
"type": "boolean"
},
"within_days": {
"description": "Expiring within this many days (default 30)",
"type": "integer"
}
},
"required": [],
"type": "object"
}{
"within_days": 60
}harvest_phi_active — Parcels under pre-harvest interval
Parcels, ponds and pens currently under a pesticide pre-harvest interval or veterinary withdrawal period: earliest safe date and the blocking application.
{
"additionalProperties": false,
"properties": {
"on": {
"description": "Check date (yyyy-MM-dd, default today)",
"format": "date",
"type": "string"
}
},
"required": [],
"type": "object"
}harvest_record_list — Harvest records
Declared harvest records: parcel, crop, declared yield (kg), status (draft · confirmed · verified · collected · void), harvest date and traceability lot.
{
"additionalProperties": false,
"properties": {
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"status": {
"description": "Filter by status",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"status": "confirmed"
}harvest_search — Search harvests, labels, certificates
Search harvest records, QR labels and certificates by keyword (record code, label code, certificate name).
{
"additionalProperties": false,
"properties": {
"limit": {
"description": "Maximum results per group",
"type": "integer"
},
"q": {
"description": "Keyword",
"type": "string"
}
},
"required": [
"q"
],
"type": "object"
}{
"q": "VietGAP"
}harvest_summary — Yield summary
Harvested yield (confirmed records only) grouped by household, zone, crop or parcel within a harvest date range. Sorted by total kg, largest first.
{
"additionalProperties": false,
"properties": {
"crop_type_id": {
"description": "Only one crop type",
"format": "uuid",
"type": "string"
},
"from": {
"description": "Harvest date from (yyyy-MM-dd)",
"format": "date",
"type": "string"
},
"group_by": {
"description": "parcel · crop · zone · owner",
"type": "string"
},
"to": {
"description": "Harvest date to (yyyy-MM-dd)",
"format": "date",
"type": "string"
},
"zone_id": {
"description": "Only one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"from": "2026-10-01",
"group_by": "crop",
"to": "2026-10-31"
}harvest_trace_get — Traceability lot detail
One traceability lot: cultivation log, certificates and photos — what a buyer sees when scanning the QR label.
{
"additionalProperties": false,
"properties": {
"id": {
"description": "Traceability lot ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"id"
],
"type": "object"
}harvest_trace_list — Traceability lots
Published traceability lots of the farm: parcel, zone, product, certification, harvest date, farmer and number of stages.
{
"additionalProperties": false,
"properties": {},
"required": [],
"type": "object"
}phi_status — Pre-harvest interval of a parcel
Whether a parcel is still within a pesticide pre-harvest interval, the earliest safe harvest date and the application holding it.
{
"additionalProperties": false,
"properties": {
"on": {
"description": "Check date (yyyy-MM-dd, default today)",
"format": "date",
"type": "string"
},
"parcel_id": {
"description": "Parcel ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"parcel_id"
],
"type": "object"
}iot_alerts_list — Sensor alerts
Sensor threshold alerts: device, metric, value, threshold, severity and handling status.
{
"additionalProperties": false,
"properties": {
"device_id": {
"description": "One device",
"format": "uuid",
"type": "string"
},
"from": {
"description": "From",
"format": "date",
"type": "string"
},
"page": {
"description": "Page",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"severity": {
"description": "Severity",
"type": "string"
},
"status": {
"description": "Status (e.g. triggered, acknowledged, resolved)",
"type": "string"
},
"to": {
"description": "To",
"format": "date",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"status": "triggered"
}iot_command_status — Device commands
Latest commands sent to one controller: command type, status, sent time and whether the device acknowledged it.
{
"additionalProperties": false,
"properties": {
"device_id": {
"description": "Controller ID (UUID)",
"format": "uuid",
"type": "string"
},
"limit": {
"description": "Number of commands (max 20)",
"type": "integer"
}
},
"required": [
"device_id"
],
"type": "object"
}iot_device_list — IoT devices
The farm's IoT devices: name, type, online or offline, last report time. Filter by status, parcel or zone.
{
"additionalProperties": false,
"properties": {
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"parcel_id": {
"description": "Only devices of one parcel",
"format": "uuid",
"type": "string"
},
"status": {
"description": "online · offline · active · inactive · maintenance",
"type": "string"
},
"zone_id": {
"description": "Only devices of one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"status": "offline"
}iot_devices_search — Search devices
Search devices by name or code.
{
"additionalProperties": false,
"properties": {
"limit": {
"description": "Maximum results",
"type": "integer"
},
"q": {
"description": "Keyword",
"type": "string"
}
},
"required": [
"q"
],
"type": "object"
}{
"q": "NK2"
}iot_irrigation_outputs — Controller valves and pumps
Irrigation outputs of one controller: type (pump, zone valve, fertigation), index and the user-given name (e.g. 'Block A').
{
"additionalProperties": false,
"properties": {
"device_id": {
"description": "Controller ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"device_id"
],
"type": "object"
}iot_readings_history — Reading history
Sensor reading history for a device or zone within a time range, newest first.
{
"additionalProperties": false,
"properties": {
"device_id": {
"description": "One device",
"format": "uuid",
"type": "string"
},
"from": {
"description": "From (ISO 8601)",
"format": "date",
"type": "string"
},
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page (max 500)",
"type": "integer"
},
"to": {
"description": "To (ISO 8601)",
"format": "date",
"type": "string"
},
"zone_id": {
"description": "One zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"from": "2026-10-27",
"to": "2026-10-28"
}iot_readings_latest — Latest readings
The latest value of each sensor (temperature, humidity, EC, pH…) for the whole farm or one zone.
{
"additionalProperties": false,
"properties": {
"zone_id": {
"description": "Only sensors of one zone",
"format": "uuid",
"type": "string"
}
},
"required": [],
"type": "object"
}iot_schedule_upcoming — Upcoming irrigation runs
Upcoming scheduled irrigation runs: controller, start time, duration (seconds), valves, skipped or already run.
{
"additionalProperties": false,
"properties": {
"days": {
"description": "Number of days ahead (1–7)",
"type": "integer"
},
"device_id": {
"description": "Only one controller",
"format": "uuid",
"type": "string"
},
"from": {
"description": "From date (yyyy-MM-dd, default today)",
"format": "date",
"type": "string"
}
},
"required": [],
"type": "object"
}{
"days": 2
}office_automations_list — Automation rules
The farm's automation rules: name, status, trigger type and last run.
{
"additionalProperties": false,
"properties": {
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"search": {
"description": "Keyword",
"type": "string"
},
"status": {
"description": "Status",
"type": "string"
}
},
"required": [],
"type": "object"
}office_books_fields — Table columns
Columns of one table: name, data type and options — needed to read row values correctly.
{
"additionalProperties": false,
"properties": {
"table_id": {
"description": "Table ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"table_id"
],
"type": "object"
}office_books_list — Farm books
The farm's custom books (logs, inventory, costs…): name, description and number of tables.
{
"additionalProperties": false,
"properties": {
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"search": {
"description": "Keyword",
"type": "string"
}
},
"required": [],
"type": "object"
}office_books_records — Table rows
Read rows of a book table, paginated; values keyed by column ID.
{
"additionalProperties": false,
"properties": {
"page": {
"description": "Page, from 1",
"type": "integer"
},
"page_size": {
"description": "Rows per page",
"type": "integer"
},
"table_id": {
"description": "Table ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"table_id"
],
"type": "object"
}office_books_tables — Book tables
Tables inside one farm book.
{
"additionalProperties": false,
"properties": {
"app_id": {
"description": "Book ID (UUID)",
"format": "uuid",
"type": "string"
}
},
"required": [
"app_id"
],
"type": "object"
}report_catalog — Report catalogue
Available farm reports: code, name, group and the number of data blocks you can see.
{
"additionalProperties": false,
"properties": {},
"required": [],
"type": "object"
}report_query — Run a report
Run a report by code over a date range: KPIs, time series, breakdowns and tables. Without dates the last 30 days are used.
{
"additionalProperties": false,
"properties": {
"code": {
"description": "Report code (from the report catalogue)",
"type": "string"
},
"from": {
"description": "From date (yyyy-MM-dd)",
"format": "date",
"type": "string"
},
"grain": {
"description": "Time grain: day · week · month · quarter · year",
"type": "string"
},
"to": {
"description": "To date (yyyy-MM-dd)",
"format": "date",
"type": "string"
}
},
"required": [
"code"
],
"type": "object"
}{
"code": "SX-01",
"from": "2026-10-01",
"grain": "week",
"to": "2026-10-28"
}The server is stateless and never stores keys: each request uses exactly the key you send, and every scope rule applies as if you called the API directly.
Debugging MCP
| Symptom | Fix |
|---|---|
| 401 at initialize | Wrong / revoked / expired key, or a sandbox key on production — create a new key in the app. |
| Empty tools/list | The key has no matching scope — edit scopes in System › API keys. |
| 406 / 415 | Your client misses Accept: application/json, text/event-stream or Content-Type. |
| 405 on GET | The server is stateless (no SSE GET stream) — use POST only. |
| 429 | Over 600 calls/minute for the key — slow down. |
| AI assistant "couldn't connect" | OAuth is not live yet — use a client that takes an API key. |
Full table and live checks: MCP Tester →
Full security rules for partners: Security →