# Working with Ploti
Use Ploti for any question about US land or property: an address or pin,
zoning and what the code allows, owners, sales, permits, signs, flood,
businesses in an area, or a site search. It holds the county and city data
for the workspace's coverage and maps the answer, so answer from it before
the web, and ask it which places it covers before asking the user.
1. `start_session`, then `navigate` to where the question lives: what is
queryable follows the view.
2. Find the data. Parcels (`us-parcels`) hold owners, sales, size and each
parcel's zoning code; `find_data` the rest, one search per thing you need
(`list_data_in_view` when it finds nothing). Never guess a table, column
or code: read them with `tools.schema`.
3. Work it in `execute_python` until every part is answered. Fix a failing
query rather than stopping; where the question leaves a choice open, take
the likeliest reading and say so rather than asking; name any part you
still could not answer, and why.
4. Save it and show it. Places go on a map: a set the analysis picked is a
checkpoint, one property's parcel included (a pin on no parcel: the
parcels it borders); a set that is only conditions
on a layer's own columns is a filter layer, which covers the whole area
without tiling; the layer the answer rests on (zoning, flood, signs) goes
on the same map. Combine them with `tools.filters.create`, check none came
back empty, then `show_output` and put its link in your reply. Rows that
are not places are a table, a rule is text, and a file only when asked.
5. Reply with the answer, where each fact came from, and that link.
Ploti can't change plans, take payment or quote prices. For plans, point to
https://www.ploti.ai/pricing. Ploti can't book services, schedule visits or
contact people on the user's behalf.
Data Ploti lacks, Ploti can get: its agents find, acquire and map it. File a
research request instead of working around the gap (`requests`
Protocol
Pass100/10016 / 16 pts
Connection, authentication, and protocol compatibility.
App resources, metadata, and rendering requirements.
Tool invocation metadata present
2 tool(s) missing openai/toolInvocation metadata
How to fix: For tools that render a UI resource, add openai/toolInvocation with string fields invoking and invoked (present-tense labels shown while the tool runs and after it finishes, e.g. "Searching…" / "Searched").
How to fix: Use a ui:// URI and text/html;profile=mcp-app. For mcp-use v1, use widgetMetadata.metadata.widgetDescription; for v2, use the bound tool's view.description. The current description check reads _meta.ui.widgetDescription or _meta["openai/widgetDescription"] on resource contents or linked tools, not resource.description. Consequently, v2 view.description alone may leave this warning: report the checker compatibility gap instead of adding unsupported v1 fields or claiming the warning is fixed.
All view resources are readable and use ui:// with a supported MCP Apps HTML MIME type
View resources declare a domain
36 ChatGPT UI domain issue(s) found
How to fix: For ChatGPT submission, set one exact HTTPS origin such as "https://app.example.com" on every view via _meta.ui.domain (or openai/widgetDomain). Paths are not valid and all views in one app must share the origin.
The title clearly communicates the tool’s action and subject. Keep it concise.
Description99/100
Open a working session; the first call for any question about a US address, parcel, zoning, owner, sale, permit, sign or site. Returns the session id needed by session-scoped tools, the session's Ploti link, and the guides to start from. Optionally sets the initial viewport. Call this first.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
title
Short label for this session, shown to the user in Ploti. Describe the task, e.g. 'Industrial sites near Tampa'.
Clear enough to supply this argument.
place
Optional starting place, equivalent to calling navigate straight after — see navigate for zoom guidance and framing behavior.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "start_session",
"title": "Start a Ploti session",
"description": "Open a working session; the first call for any question about a US address, parcel, zoning, owner, sale, permit, sign or site. Returns the session id needed by session-scoped tools, the session's Ploti link, and the guides to start from. Optionally sets the initial viewport. Call this first.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"title"
],
"properties": {
"place": {
"type": "string",
"maxLength": 200,
"description": "Optional starting place, equivalent to calling navigate straight after — see navigate for zoom guidance and framing behavior."
},
"title": {
"type": "string",
"maxLength": 100,
"minLength": 1,
"description": "Short label for this session, shown to the user in Ploti. Describe the task, e.g. 'Industrial sites near Tampa'."
}
}
},
"annotations": {
"title": "Start a Ploti session",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description88/100
Report a session's viewport, sandbox state and link. Call without a session id to list this workspace's open sessions — useful for recovering an id you have lost.
Describe the returned information so the agent knows whether it will answer the request.
Inputs90/100
Input documentation gives an agent enough context to populate the arguments.
session_id
Omit to list this workspace's open sessions.
Clear enough to supply this argument.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "get_session",
"title": "Get session state",
"description": "Report a session's viewport, sandbox state and link. Call without a session id to list this workspace's open sessions — useful for recovering an id you have lost.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"session_id": {
"type": "string",
"description": "Omit to list this workspace's open sessions."
}
}
},
"annotations": {
"title": "Get session state",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
Optional cleanup — sessions expire on their own after an hour of inactivity; call this to release the sandbox early when a task is done. Saved outputs (checkpoints, tables, files) and the session's Ploti link survive; anything left in the sandbox filesystem does not.
The description explains the capability, when to use it, and what to expect.
Inputs0/100
0 of 1 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "end_session",
"title": "End a session",
"description": "Optional cleanup — sessions expire on their own after an hour of inactivity; call this to release the sandbox early when a task is done. Saved outputs (checkpoints, tables, files) and the session's Ploti link survive; anything left in the sandbox filesystem does not.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id"
],
"properties": {
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "End a session",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description96/100
Move the session's viewport — where you are looking and how close. Data availability follows the view: wide views expose more regional layers, close views unlock parcel-level data, and every query is scoped to the current viewport. Navigate as often as you need; each call replaces the view. Pass either a place name or a latitude/longitude center, never both. Every response reports the resulting zoom, the span in km, and whether parcels are queryable — read it and navigate again if the framing is wrong, rather than guessing a zoom up front.
The description explains the capability, when to use it, and what to expect.
Inputs60/100
4 of 5 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
place
Place to navigate to, e.g. 'Winter Garden, FL'.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
latitude
Latitude of the place to look at, never the user's own location.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
longitude
Longitude of the place to look at, never the user's own location.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
zoom
Zoom level; fractional values are accepted (10.5 sits between the metro and city presets). Spans are maximums at that zoom, and actual views are often narrower: 3 country, 8 region (up to ~140 km), 10 metro (up to ~35 km — the widest view where parcels are queryable), 13 city (~4 km), 15 neighborhood (~1 km), 17 block (~250 m). With a place, overrides the place's natural extent; with a center, defaults to 13.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "navigate",
"title": "Navigate the map",
"description": "Move the session's viewport — where you are looking and how close. Data availability follows the view: wide views expose more regional layers, close views unlock parcel-level data, and every query is scoped to the current viewport. Navigate as often as you need; each call replaces the view. Pass either a place name or a latitude/longitude center, never both. Every response reports the resulting zoom, the span in km, and whether parcels are queryable — read it and navigate again if the framing is wrong, rather than guessing a zoom up front.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id"
],
"properties": {
"zoom": {
"type": "number",
"maximum": 19,
"minimum": 3,
"description": "Zoom level; fractional values are accepted (10.5 sits between the metro and city presets). Spans are maximums at that zoom, and actual views are often narrower: 3 country, 8 region (up to ~140 km), 10 metro (up to ~35 km — the widest view where parcels are queryable), 13 city (~4 km), 15 neighborhood (~1 km), 17 block (~250 m). With a place, overrides the place's natural extent; with a center, defaults to 13."
},
"place": {
"type": "string",
"maxLength": 200,
"description": "Place to navigate to, e.g. 'Winter Garden, FL'."
},
"latitude": {
"type": "number",
"maximum": 85,
"minimum": -85,
"description": "Latitude of the place to look at, never the user's own location."
},
"longitude": {
"type": "number",
"maximum": 180,
"minimum": -180,
"description": "Longitude of the place to look at, never the user's own location."
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Navigate the map",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
Fetch a guide on how to work with Ploti's data and sandbox. Call without an id to list them. 'discovering-data' is worth reading for any task; for a quick single lookup it is often enough. Read the others when the task calls for them: 'navigation' before choosing where and how close to look, 'querying' before your first substantial SQL, 'parcel-data' before filtering parcels, 'spatial-analysis' before cross-layer spatial work, 'map-rendering' before custom-colored maps, 'ordinances' before municipal-code research, 'delivering-results' before creating checkpoints, tables, or files for the user, 'satellite-imagery' before rendering map images, 'branded-pdf-reports' before any PDF. python/<module> ids (python/checkpoint, python/schema, ...) return the exact deployed signatures — read one before your first call into that module. A guide read once stays read for the conversation; most questions need two or three, so read them together in one call with guide_ids.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
guide_id
Guide id, e.g. 'querying' or 'python/database'.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
guide_ids
Several guide ids, read in one call, e.g. ['discovering-data', 'parcel-data'].
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "read_guide",
"title": "Read a Ploti guide",
"description": "Fetch a guide on how to work with Ploti's data and sandbox. Call without an id to list them. 'discovering-data' is worth reading for any task; for a quick single lookup it is often enough. Read the others when the task calls for them: 'navigation' before choosing where and how close to look, 'querying' before your first substantial SQL, 'parcel-data' before filtering parcels, 'spatial-analysis' before cross-layer spatial work, 'map-rendering' before custom-colored maps, 'ordinances' before municipal-code research, 'delivering-results' before creating checkpoints, tables, or files for the user, 'satellite-imagery' before rendering map images, 'branded-pdf-reports' before any PDF. python/<module> ids (python/checkpoint, python/schema, ...) return the exact deployed signatures — read one before your first call into that module. A guide read once stays read for the conversation; most questions need two or three, so read them together in one call with guide_ids.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"guide_id": {
"type": "string",
"description": "Guide id, e.g. 'querying' or 'python/database'."
},
"guide_ids": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 8,
"description": "Several guide ids, read in one call, e.g. ['discovering-data', 'parcel-data']."
}
}
},
"annotations": {
"title": "Read a Ploti guide",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
Find the layers, tables, documents (ordinances, plans, reports) and scanned maps that can answer a question, by their titles, descriptions, schemas, values and content; documents are also matched by meaning. Searches what the session's viewport offers; place searches a named county, city or state instead, and scope='coverage' the workspace's whole coverage. Each result says how to use it (a layer and its table, a table and its columns, a document's dataset id for tools.text_documents) and where it matched. Search once for each thing the question needs (the parcel, the zoning, the signs), each in two to four phrasings in the words the data might use. Use it before list_layers when you know what you need. When nothing fits, check list_data_requests before offering a request: the user may already have asked for it.
The description explains the capability, when to use it, and what to expect.
Inputs25/100
2 of 6 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
queries
One to four phrasings of what you need, in the words the data might use.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
types
Not provided
Missing description. Explain what this value means and how to supply it.
place
A county, city or state, e.g. "Lake County, OR".
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
scope
Not provided
Missing description. Explain what this value means and how to supply it.
limit
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "find_data",
"title": "Find data",
"description": "Find the layers, tables, documents (ordinances, plans, reports) and scanned maps that can answer a question, by their titles, descriptions, schemas, values and content; documents are also matched by meaning. Searches what the session's viewport offers; place searches a named county, city or state instead, and scope='coverage' the workspace's whole coverage. Each result says how to use it (a layer and its table, a table and its columns, a document's dataset id for tools.text_documents) and where it matched. Search once for each thing the question needs (the parcel, the zoning, the signs), each in two to four phrasings in the words the data might use. Use it before list_layers when you know what you need. When nothing fits, check list_data_requests before offering a request: the user may already have asked for it.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"queries"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 30,
"minimum": 1
},
"place": {
"type": "string",
"maxLength": 120,
"description": "A county, city or state, e.g. \"Lake County, OR\"."
},
"scope": {
"enum": [
"view",
"coverage"
],
"type": "string"
},
"types": {
"type": "array",
"items": {
"enum": [
"layer",
"table",
"document",
"map"
],
"type": "string"
},
"minItems": 1
},
"queries": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"maxItems": 4,
"minItems": 1,
"description": "One to four phrasings of what you need, in the words the data might use."
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Find data",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description97/100
List every dataset the session's viewport offers (layers, tables, documents, scanned maps): the same list the Ploti map shows for this view and zoom, unranked, as titles. Use it when find_data doesn't find what you need or you are unsure it found the right thing. Rows (type, title, id, and a lock with what opens it) come grouped by place: the view's own places first, then state and national data. The header counts everything in view by place, category and type. Narrow it with types or categories to what the question needs, then page with offset until no next offset is given; leaving them out lists everything, which in a dense view runs to many pages. Pass ids from the rows to read those datasets in detail: description, category and how to read each. Then check a few rows (tools.schema, SQL) or a document's sections before using one.
The description explains the capability, when to use it, and what to expect.
Inputs33/100
3 of 6 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
types
Not provided
Missing description. Explain what this value means and how to supply it.
categories
Dataset categories, as the listing's header names them.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
ids
Dataset ids from the listing, or layer ids from find_data, to read in detail.
Explain the meaning of this value, not just its name.
limit
Rows per page (default 300, maximum 500).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
offset
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "List data in view",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_data_in_view",
"title": "List data in view",
"description": "List every dataset the session's viewport offers (layers, tables, documents, scanned maps): the same list the Ploti map shows for this view and zoom, unranked, as titles. Use it when find_data doesn't find what you need or you are unsure it found the right thing. Rows (type, title, id, and a lock with what opens it) come grouped by place: the view's own places first, then state and national data. The header counts everything in view by place, category and type. Narrow it with types or categories to what the question needs, then page with offset until no next offset is given; leaving them out lists everything, which in a dense view runs to many pages. Pass ids from the rows to read those datasets in detail: description, category and how to read each. Then check a few rows (tools.schema, SQL) or a document's sections before using one.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id"
],
"properties": {
"ids": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 50,
"minItems": 1,
"description": "Dataset ids from the listing, or layer ids from find_data, to read in detail."
},
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "Rows per page (default 300, maximum 500)."
},
"types": {
"type": "array",
"items": {
"enum": [
"layer",
"table",
"document",
"map"
],
"type": "string"
},
"minItems": 1
},
"offset": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0
},
"categories": {
"type": "array",
"items": {
"enum": [
"parcels-property",
"zoning-land-use",
"subdivisions-platting",
"development-activity",
"environmental-constraints",
"flood-hazard-zones",
"special-districts",
"utilities-infrastructure",
"transportation-roads",
"public-services-facilities",
"easements-government",
"critical-assets",
"physical-environmental",
"imagery-basemaps",
"demographics-social",
"boundaries-jurisdictions",
"emergency-public-safety",
"economic-development",
"agriculture-natural-resources",
"historic-cultural",
"housing",
"government-records",
"coastal-marine",
"health-human-services"
],
"type": "string"
},
"minItems": 1,
"description": "Dataset categories, as the listing's header names them."
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "List data in view",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description93/100
What data exists in this session's viewport — requires a viewport, so call navigate first. By default, returns only queryable layers. query is a substring match over layer ids, names, tables, categories and descriptions — direct matches rank first and description-only matches trail, so expect a low-relevance tail. include_locked=true lists everything; locked results say why they are locked. Use tools.schema inside execute_python for fields, types and sampled values.
The description explains the capability, when to use it, and what to expect.
Inputs70/100
4 of 5 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
query
Optional search across layer ids, names, table names and descriptions, e.g. 'zoning' or 'flood'.
Clear enough to supply this argument.
include_locked
Also list plan-locked catalog layers in the unfiltered listing. With query, locked matches are always included regardless of this flag. Defaults to false. See discovering-data for when to surface locked data.
Clear enough to supply this argument.
limit
Maximum layers to return (default 25, maximum 100).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
offset
Zero-based offset for paging through matching layers.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "List available data layers",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_layers",
"title": "List available data layers",
"description": "What data exists in this session's viewport — requires a viewport, so call navigate first. By default, returns only queryable layers. query is a substring match over layer ids, names, tables, categories and descriptions — direct matches rank first and description-only matches trail, so expect a low-relevance tail. include_locked=true lists everything; locked results say why they are locked. Use tools.schema inside execute_python for fields, types and sampled values.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 100,
"minimum": 1,
"description": "Maximum layers to return (default 25, maximum 100)."
},
"query": {
"type": "string",
"description": "Optional search across layer ids, names, table names and descriptions, e.g. 'zoning' or 'flood'."
},
"offset": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"description": "Zero-based offset for paging through matching layers."
},
"session_id": {
"type": "string"
},
"include_locked": {
"type": "boolean",
"description": "Also list plan-locked catalog layers in the unfiltered listing. With query, locked matches are always included regardless of this flag. Defaults to false. See discovering-data for when to surface locked data."
}
}
},
"annotations": {
"title": "List available data layers",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
Execute Python in this session's sandbox. Import tools or a specific module in every call (database, schema, checkpoint, materialized_tables, saved_query, filters, location, municode, text_documents, files, map_image, boundary, spatial_relations, branding) — the python/<module> guides carry the exact signatures.
Requires a viewport — call navigate first. The sandbox can reach any host on the internet. Each execution starts with a fresh namespace: imports and variables do not carry into the next call, but files under /home/daytona persist for the session. stdout and stderr both come back (print() output and tracebacks); the last expression's value is not returned. Tracebacks cite your code as File "<agent-code>" with correct line numbers; frames labeled "<string>" are runtime wrapper, not your code.
Calls that finish within ~45 seconds return synchronously; longer calls are promoted to a background job automatically — you get the job id, the code keeps running (up to 15 minutes), and get_result returns the output (kept for one hour). The wait covers everything in the call, not each query separately. Set background:true to skip the wait for known-heavy work (map_image renders, broad scans). Executions run one at a time, so another execute_python call will queue. If a call still fails with a transport error, the code may have run: check list_outputs before re-running anything that creates artifacts. Only checkpoints can be deleted (delete_checkpoint); tables, saved queries, filters and files cannot.
Do a whole step in one call (query, save the checkpoints, build the map) rather than a call per line: hosts cap how many tool calls a reply may make. When a call fails, fix it rather than stopping: correct a schema or SQL error from what it says instead of rerunning it, split a query that timed out into smaller ones, and say a step could not be done only after a narrower attempt also failed. Never select bare geometry columns or print whole tables. Something is saved only once its create call has returned it; tell the user plainly when one failed.
The description explains the capability, when to use it, and what to expect.
Inputs56/100
3 of 4 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
description
One line on what this code does, shown to the user, e.g. 'Querying vacant industrial parcels'.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
code
Python to run.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
background
Run without waiting and return a job id. Use for anything that may exceed the synchronous cap. Results are kept for one hour after completion — poll get_result within that window. Sandbox executions are serial, so another execute_python call will queue.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations100/100
All four behavior hints are declared.
{
"title": "Run Python in the Ploti sandbox",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "execute_python",
"title": "Run Python in the Ploti sandbox",
"description": "Execute Python in this session's sandbox. Import tools or a specific module in every call (database, schema, checkpoint, materialized_tables, saved_query, filters, location, municode, text_documents, files, map_image, boundary, spatial_relations, branding) — the python/<module> guides carry the exact signatures.\n\nRequires a viewport — call navigate first. The sandbox can reach any host on the internet. Each execution starts with a fresh namespace: imports and variables do not carry into the next call, but files under /home/daytona persist for the session. stdout and stderr both come back (print() output and tracebacks); the last expression's value is not returned. Tracebacks cite your code as File \"<agent-code>\" with correct line numbers; frames labeled \"<string>\" are runtime wrapper, not your code.\n\nCalls that finish within ~45 seconds return synchronously; longer calls are promoted to a background job automatically — you get the job id, the code keeps running (up to 15 minutes), and get_result returns the output (kept for one hour). The wait covers everything in the call, not each query separately. Set background:true to skip the wait for known-heavy work (map_image renders, broad scans). Executions run one at a time, so another execute_python call will queue. If a call still fails with a transport error, the code may have run: check list_outputs before re-running anything that creates artifacts. Only checkpoints can be deleted (delete_checkpoint); tables, saved queries, filters and files cannot.\n\nDo a whole step in one call (query, save the checkpoints, build the map) rather than a call per line: hosts cap how many tool calls a reply may make. When a call fails, fix it rather than stopping: correct a schema or SQL error from what it says instead of rerunning it, split a query that timed out into smaller ones, and say a step could not be done only after a narrower attempt also failed. Never select bare geometry columns or print whole tables. Something is saved only once its create call has returned it; tell the user plainly when one failed.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"description",
"code"
],
"properties": {
"code": {
"type": "string",
"minLength": 1,
"description": "Python to run."
},
"background": {
"type": "boolean",
"description": "Run without waiting and return a job id. Use for anything that may exceed the synchronous cap. Results are kept for one hour after completion — poll get_result within that window. Sandbox executions are serial, so another execute_python call will queue."
},
"session_id": {
"type": "string"
},
"description": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "One line on what this code does, shown to the user, e.g. 'Querying vacant industrial parcels'."
}
}
},
"annotations": {
"title": "Run Python in the Ploti sandbox",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description75/100
Shell access to the session's sandbox for file operations, inspecting /home/daytona/tools/*.py, and running saved scripts, which can save to the workspace as execute_python does. The sandbox can reach any host on the internet. Analysis belongs in execute_python. Call navigate first — the sandbox cannot start before a viewport exists.
The description explains the capability, when to use it, and what to expect.
Inputs0/100
0 of 2 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
command
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations100/100
All four behavior hints are declared.
{
"title": "Run a shell command in the sandbox",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "execute_bash",
"title": "Run a shell command in the sandbox",
"description": "Shell access to the session's sandbox for file operations, inspecting /home/daytona/tools/*.py, and running saved scripts, which can save to the workspace as execute_python does. The sandbox can reach any host on the internet. Analysis belongs in execute_python. Call navigate first — the sandbox cannot start before a viewport exists.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"command"
],
"properties": {
"command": {
"type": "string",
"minLength": 1
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Run a shell command in the sandbox",
"readOnlyHint": true,
"openWorldHint": false,
"idempotentHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Poll a job started by execute_python — explicitly with background:true, or auto-promoted when a call outlived the synchronous wait. Waits up to ~45s for it to finish, then returns its output, or says it is still running; results are kept for one hour.
The description explains the capability, when to use it, and what to expect.
Inputs0/100
0 of 2 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
job_id
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "get_result",
"title": "Get a background execution's result",
"description": "Poll a job started by execute_python — explicitly with background:true, or auto-promoted when a call outlived the synchronous wait. Waits up to ~45s for it to finish, then returns its output, or says it is still running; results are kept for one hour.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"job_id"
],
"properties": {
"job_id": {
"type": "string"
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Get a background execution's result",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description83/100
Checkpoints, materialized tables, saved queries, filters and saved files created in this session, plus one link to the session in Ploti. Per-artifact viewUrls come from show_output. Files must be saved with tools.files.save() to appear here.
The description explains the capability, when to use it, and what to expect.
Inputs0/100
0 of 1 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "List what this session produced",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_outputs",
"title": "List what this session produced",
"description": "Checkpoints, materialized tables, saved queries, filters and saved files created in this session, plus one link to the session in Ploti. Per-artifact viewUrls come from show_output. Files must be saved with tools.files.save() to appear here.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id"
],
"properties": {
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "List what this session produced",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description89/100
Delete a checkpoint this session created, such as one saved by mistake or superseded by a corrected copy. Only this session's checkpoints can be deleted, and not while a saved filter in this session uses it. Deletion cannot be undone; confirm with the user first unless you are removing your own mistake.
The description explains the capability, when to use it, and what to expect.
Inputs0/100
0 of 2 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
checkpoint_id
Not provided
Missing description. Explain what this value means and how to supply it.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "delete_checkpoint",
"title": "Delete a checkpoint",
"description": "Delete a checkpoint this session created, such as one saved by mistake or superseded by a corrected copy. Only this session's checkpoints can be deleted, and not while a saved filter in this session uses it. Deletion cannot be undone; confirm with the user first unless you are removing your own mistake.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"checkpoint_id"
],
"properties": {
"session_id": {
"type": "string"
},
"checkpoint_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Delete a checkpoint",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description80/100
Recover the durable download link for a file saved earlier in this session. tools.files.save() already returns durableUrl for a file you just saved, so no extra call is needed then.
Describe the returned information so the agent knows whether it will answer the request.
Inputs0/100
0 of 2 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
file_id
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "Get a download link for a saved file",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "get_file",
"title": "Get a download link for a saved file",
"description": "Recover the durable download link for a file saved earlier in this session. tools.files.save() already returns durableUrl for a file you just saved, so no extra call is needed then.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"file_id"
],
"properties": {
"file_id": {
"type": "string"
},
"session_id": {
"type": "string"
}
}
},
"annotations": {
"title": "Get a download link for a saved file",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
List the data requests this user filed in the workspace, newest first, with each one's status and the datasets it has delivered so far. Stages run submitted, searching for the source, building, first results (the user's own places first), then ready; a request can also end not available or need review. A delivered dataset is in the catalog: find it with find_data. A request waiting on the user's answer is answered in Ploti. Check it before filing a research request: data this user already asked for is listed here, and filing again only repeats the work.
The description explains the capability, when to use it, and what to expect.
Inputs75/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
limit
Requests to list (default and maximum 50).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
Where to start: the nextCursor of the previous page.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_data_requests",
"title": "List data requests",
"description": "List the data requests this user filed in the workspace, newest first, with each one's status and the datasets it has delivered so far. Stages run submitted, searching for the source, building, first results (the user's own places first), then ready; a request can also end not available or need review. A delivered dataset is in the catalog: find it with find_data. A request waiting on the user's answer is answered in Ploti. Check it before filing a research request: data this user already asked for is listed here, and filing again only repeats the work.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "Requests to list (default and maximum 50)."
},
"cursor": {
"type": "string",
"maxLength": 64,
"description": "Where to start: the nextCursor of the previous page."
}
}
},
"annotations": {
"title": "List data requests",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description74/100
The counties in the workspace's coverage areas. Ploti's local data opens, and Ploti gathers new data, only inside them; the user adds counties in Ploti.
Describe the returned information so the agent knows whether it will answer the request.
Inputs100/100
This tool declares no inputs, so argument descriptions are not needed.
No input fields.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "get_coverage",
"title": "Coverage areas",
"description": "The counties in the workspace's coverage areas. Ploti's local data opens, and Ploti gathers new data, only inside them; the user adds counties in Ploti.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
},
"annotations": {
"title": "Coverage areas",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description95/100
Have Ploti POST to an https URL whenever one of the member's data or research requests changes: a dataset delivered or failed, a question waiting on their answer, a research request's report ready or its answer in. Use it to wake a routine or alert the user wherever they are instead of polling. Each POST is JSON {id, type, timestamp, data} signed with Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature headers) under the returned secret, which is shown once. Docs: https://www.ploti.ai/docs/mcp/webhooks. When the receiver needs its own credential (a routine's Bearer [redacted]), pass it as header; it is sent with every POST and never shown again. A member may have 20 webhooks. Events: data_request.sourcing, .matched, .checkpoint_released (first results), .ready, .failed, .cancelled, .paid_source, .cycle_refused, .question (Ploti needs the member's answer) and .question_reminder; research_request.report_ready (the data check reported what is there and what is missing), .question (the data check needs the member's answer; data.questions has the questions, and answer_research_request_questions sends the answers), .answered (data.answer) and .needs_attention (the research cannot finish on its own); data.url opens the request in Ploti. dataset.updated: a dataset the member watches (watch_dataset) has a new version; data.id is the dataset, data.version and data.promotedAt the new version, data.changeLog and data.counts what it added, changed and removed (list_dataset_changes lists those rows).
Describe the returned information so the agent knows whether it will answer the request.
Inputs0/100
0 of 4 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
url
Not provided
Missing description. Explain what this value means and how to supply it.
description
Not provided
Missing description. Explain what this value means and how to supply it.
header › name
Not provided
Missing description. Explain what this value means and how to supply it.
header › value
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "create_webhook",
"title": "Create a webhook",
"description": "Have Ploti POST to an https URL whenever one of the member's data or research requests changes: a dataset delivered or failed, a question waiting on their answer, a research request's report ready or its answer in. Use it to wake a routine or alert the user wherever they are instead of polling. Each POST is JSON {id, type, timestamp, data} signed with Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature headers) under the returned secret, which is shown once. Docs: https://www.ploti.ai/docs/mcp/webhooks. When the receiver needs its own credential (a routine's Bearer [redacted]), pass it as header; it is sent with every POST and never shown again. A member may have 20 webhooks. Events: data_request.sourcing, .matched, .checkpoint_released (first results), .ready, .failed, .cancelled, .paid_source, .cycle_refused, .question (Ploti needs the member's answer) and .question_reminder; research_request.report_ready (the data check reported what is there and what is missing), .question (the data check needs the member's answer; data.questions has the questions, and answer_research_request_questions sends the answers), .answered (data.answer) and .needs_attention (the research cannot finish on its own); data.url opens the request in Ploti. dataset.updated: a dataset the member watches (watch_dataset) has a new version; data.id is the dataset, data.version and data.promotedAt the new version, data.changeLog and data.counts what it added, changed and removed (list_dataset_changes lists those rows).",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"maxLength": 2000
},
"header": {
"type": "object",
"required": [
"name",
"value"
],
"properties": {
"name": {
"type": "string",
"pattern": "^[A-Za-z0-9-]{1,100}$"
},
"value": {
"type": "string",
"pattern": "^[\\x20-\\x7e]+$",
"maxLength": 4000
}
},
"description": "A header the receiver requires, e.g. {\"name\": \"Authorization\", \"value\": \"Bearer …\"}."
},
"description": {
"type": "string",
"maxLength": 200
}
}
},
"annotations": {
"title": "Create a webhook",
"readOnlyHint": false,
"openWorldHint": true,
"destructiveHint": false
}
}
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_webhooks",
"title": "List webhooks",
"description": "The member's webhooks: where Ploti POSTs when their data requests or research requests change, and dataset.updated for the datasets they watch.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
},
"annotations": {
"title": "List webhooks",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "delete_webhook",
"title": "Delete a webhook",
"description": "Stop Ploti posting to one of the member's webhooks. Events not yet sent to it are cancelled.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 32,
"minLength": 1,
"description": "The webhook, by the id create_webhook gave."
}
}
},
"annotations": {
"title": "Delete a webhook",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description94/100
Send one signed `ping` event to a webhook now and report how the receiver answered: delivered, or error as one of no answer, refused, timed out, TLS failed or HTTP <status>.
The description explains the capability, when to use it, and what to expect.
Inputs93/100
Input documentation gives an agent enough context to populate the arguments.
id
The webhook, by the id create_webhook gave.
Clear enough to supply this argument.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "test_webhook",
"title": "Test a webhook",
"description": "Send one signed `ping` event to a webhook now and report how the receiver answered: delivered, or error as one of no answer, refused, timed out, TLS failed or HTTP <status>.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 32,
"minLength": 1,
"description": "The webhook, by the id create_webhook gave."
}
}
},
"annotations": {
"title": "Test a webhook",
"readOnlyHint": false,
"openWorldHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description89/100
Be told each time a dataset gets a new version: Ploti sends a dataset.updated event to the member's webhooks (create_webhook makes one) with the dataset's id, the new version and when it went live. Use it to have an agent check new rows for what the user is after, such as new permits matching their criteria, instead of polling. Watch a dataset find_data found; one the workspace's coverage or plan does not open cannot be watched. Watching again changes nothing. A member may watch 100 datasets.
The description explains the capability, when to use it, and what to expect.
Inputs89/100
Input documentation gives an agent enough context to populate the arguments.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "watch_dataset",
"title": "Watch a dataset",
"description": "Be told each time a dataset gets a new version: Ploti sends a dataset.updated event to the member's webhooks (create_webhook makes one) with the dataset's id, the new version and when it went live. Use it to have an agent check new rows for what the user is after, such as new permits matching their criteria, instead of polling. Watch a dataset find_data found; one the workspace's coverage or plan does not open cannot be watched. Watching again changes nothing. A member may watch 100 datasets.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"datasetId"
],
"properties": {
"datasetId": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "The dataset, by the id find_data gives."
}
}
},
"annotations": {
"title": "Watch a dataset",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_dataset_watches",
"title": "List watched datasets",
"description": "The datasets the member watches: each new version of one is sent to their webhooks as dataset.updated.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {}
},
"annotations": {
"title": "List watched datasets",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
The rows a version of a dataset added, changed or removed, so an agent told of a new version (dataset.updated) reads only those instead of the whole dataset. Pass the version the event named; without one it is the latest. An added or changed row of the latest version comes with the row itself (no geometry); a removed row, or one of an older version, comes with its key. When the changes were not recorded (changeLog counts_only, full_refresh or first), the note says so: query the dataset with your own filters instead.
The description explains the capability, when to use it, and what to expect.
Inputs95/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
datasetId
The dataset, by the id find_data gives.
Clear enough to supply this argument.
version
The version, as dataset.updated names it. Defaults to the latest.
Clear enough to supply this argument.
limit
How many changes to return: 200 unless given, at most 500.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
cursor
Where to start: the nextCursor of the previous page.
Clear enough to supply this argument.
offset
Where to start, by position; cursor replaces it.
Clear enough to supply this argument.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_dataset_changes",
"title": "List a dataset version's changes",
"description": "The rows a version of a dataset added, changed or removed, so an agent told of a new version (dataset.updated) reads only those instead of the whole dataset. Pass the version the event named; without one it is the latest. An added or changed row of the latest version comes with the row itself (no geometry); a removed row, or one of an older version, comes with its key. When the changes were not recorded (changeLog counts_only, full_refresh or first), the note says so: query the dataset with your own filters instead.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"datasetId"
],
"properties": {
"limit": {
"type": "integer",
"maximum": 500,
"minimum": 1,
"description": "How many changes to return: 200 unless given, at most 500."
},
"cursor": {
"type": "string",
"maxLength": 64,
"description": "Where to start: the nextCursor of the previous page."
},
"offset": {
"type": "integer",
"maximum": 9007199254740991,
"minimum": 0,
"deprecated": true,
"description": "Where to start, by position; cursor replaces it."
},
"version": {
"type": "integer",
"maximum": 9007199254740991,
"description": "The version, as dataset.updated names it. Defaults to the latest.",
"exclusiveMinimum": 0
},
"datasetId": {
"type": "string",
"maxLength": 255,
"minLength": 1,
"description": "The dataset, by the id find_data gives."
}
}
},
"annotations": {
"title": "List a dataset version's changes",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
File a research request: Ploti answers the user's question end to end when it needs data Ploti does not have. Ploti first checks which data the question needs (minutes), searching the web for sources, and reports what it already has and what is missing; when the check reports, Ploti files the missing pieces as data requests itself, which uses the workspace's research credits as the data is delivered. Its agents get the data, and when it arrives Ploti runs the research and emails the user the answer. Use it whenever the user needs data Ploti does not have, whether or not you know where it is published. Write the brief as the user would to an analyst: the question, the place, and what a good answer looks like; never the user's private parcels, owners or addresses unless the user asks for them to be researched. Creating the request and its data check costs nothing. Follow it with get_research_request.
The description explains the capability, when to use it, and what to expect.
Inputs44/100
2 of 4 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
title
A short name for the question.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
brief
The question, the place it is about, and what a good answer looks like.
Clear enough to supply this argument.
sources › items › url
Not provided
Missing description. Explain what this value means and how to supply it.
sources › items › details
Not provided
Missing description. Explain what this value means and how to supply it.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "create_research_request",
"title": "Request research",
"description": "File a research request: Ploti answers the user's question end to end when it needs data Ploti does not have. Ploti first checks which data the question needs (minutes), searching the web for sources, and reports what it already has and what is missing; when the check reports, Ploti files the missing pieces as data requests itself, which uses the workspace's research credits as the data is delivered. Its agents get the data, and when it arrives Ploti runs the research and emails the user the answer. Use it whenever the user needs data Ploti does not have, whether or not you know where it is published. Write the brief as the user would to an analyst: the question, the place, and what a good answer looks like; never the user's private parcels, owners or addresses unless the user asks for them to be researched. Creating the request and its data check costs nothing. Follow it with get_research_request.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"title",
"brief"
],
"properties": {
"brief": {
"type": "string",
"maxLength": 50000,
"minLength": 1,
"description": "The question, the place it is about, and what a good answer looks like."
},
"title": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "A short name for the question."
},
"sources": {
"type": "array",
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"details": {
"type": "string",
"maxLength": 4000
}
}
},
"maxItems": 20,
"description": "Pages the user already knows are relevant, if any."
}
}
},
"annotations": {
"title": "Request research",
"readOnlyHint": false,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description87/100
A research request's progress: whether its data check is still running or waits on the user's answers to its questions (status needs_answer), its report (what Ploti already has, with each dataset's id, what is missing, how much of the question the data covers), the data requests filed for the missing pieces and how far each has got, and once it is answered, the answer. When the data the question needs is in Ploti, run the analysis yourself with find_data and execute_python on those datasets, then record your answer with answer_research_request.
The description explains the capability, when to use it, and what to expect.
Inputs94/100
Input documentation gives an agent enough context to populate the arguments.
id
The research request, by the id create_research_request gave.
Clear enough to supply this argument.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "Get a research request",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "get_research_request",
"title": "Get a research request",
"description": "A research request's progress: whether its data check is still running or waits on the user's answers to its questions (status needs_answer), its report (what Ploti already has, with each dataset's id, what is missing, how much of the question the data covers), the data requests filed for the missing pieces and how far each has got, and once it is answered, the answer. When the data the question needs is in Ploti, run the analysis yourself with find_data and execute_python on those datasets, then record your answer with answer_research_request.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "The research request, by the id create_research_request gave."
}
}
},
"annotations": {
"title": "Get a research request",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "list_research_requests",
"title": "List research requests",
"description": "The research requests the member can see, newest first, with where each one is.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"limit": {
"type": "integer",
"maximum": 50,
"minimum": 1,
"description": "Requests to list (default 20)."
},
"cursor": {
"type": "string",
"maxLength": 64,
"description": "Where to start: the nextCursor of the previous page."
}
}
},
"annotations": {
"title": "List research requests",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description96/100
Record the answer you found yourself for a research request, once you have run the analysis on Ploti's data. This closes the request: Ploti shows your answer on the request, says you gave it, and does not run its own research on it. Data requests already filed for it still deliver their datasets. Record it when you have answered the user's question, whether all the data was there or you answered with what was; write the answer as the user should read it later in Ploti, with the figures and the datasets it rests on. Pass the id of the session you did the analysis in, so the checkpoints, tables and files you saved there show on the request beside your answer. The request's owner is told in their Ploti inbox, by email when another member's assistant answered, and on their webhooks, which receive the answer. Refused while Ploti's own research run is answering it already.
The description explains the capability, when to use it, and what to expect.
Inputs83/100
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
id
The research request, by the id create_research_request gave.
Clear enough to supply this argument.
answer
The answer, in Markdown, as the user should read it in Ploti.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
session_id
The Ploti session you ran the analysis in (start_session), if any.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations100/100
All four behavior hints are declared.
{
"title": "Record a research request's answer",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "answer_research_request",
"title": "Record a research request's answer",
"description": "Record the answer you found yourself for a research request, once you have run the analysis on Ploti's data. This closes the request: Ploti shows your answer on the request, says you gave it, and does not run its own research on it. Data requests already filed for it still deliver their datasets. Record it when you have answered the user's question, whether all the data was there or you answered with what was; write the answer as the user should read it later in Ploti, with the figures and the datasets it rests on. Pass the id of the session you did the analysis in, so the checkpoints, tables and files you saved there show on the request beside your answer. The request's owner is told in their Ploti inbox, by email when another member's assistant answered, and on their webhooks, which receive the answer. Refused while Ploti's own research run is answering it already.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id",
"answer"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "The research request, by the id create_research_request gave."
},
"answer": {
"type": "string",
"maxLength": 50000,
"minLength": 1,
"description": "The answer, in Markdown, as the user should read it in Ploti."
},
"session_id": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "The Ploti session you ran the analysis in (start_session), if any."
}
}
},
"annotations": {
"title": "Record a research request's answer",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": true
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description90/100
Send the user's answers to the questions a research request's data check asked (get_research_request shows them while its status is needs_answer); the check then goes on. Ask the user each question in your own chat and send what they said: never guess or answer for them. Answer each question by its id: an option by its id or its label, or the user's own words as text; a question left out is skipped, when the questions allow it. The check searches the web as it goes on, and once it reports, Ploti files the missing data itself, as after create_research_request.
Describe the returned information so the agent knows whether it will answer the request.
Inputs93/100
Input documentation gives an agent enough context to populate the arguments.
id
The research request, by the id create_research_request gave.
Clear enough to supply this argument.
answers
Each question's answer by question id: an option id or label, the user's own text, a list for a question that takes several, or null to skip it.
Clear enough to supply this argument.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "Answer a research request's questions",
"readOnlyHint": false,
"openWorldHint": false,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "answer_research_request_questions",
"title": "Answer a research request's questions",
"description": "Send the user's answers to the questions a research request's data check asked (get_research_request shows them while its status is needs_answer); the check then goes on. Ask the user each question in your own chat and send what they said: never guess or answer for them. Answer each question by its id: an option by its id or its label, or the user's own words as text; a question left out is skipped, when the questions allow it. The check searches the web as it goes on, and once it reports, Ploti files the missing data itself, as after create_research_request.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id",
"answers"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "The research request, by the id create_research_request gave."
},
"answers": {
"type": "object",
"description": "Each question's answer by question id: an option id or label, the user's own text, a list for a question that takes several, or null to skip it.",
"propertyNames": {
"type": "string"
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 2000
},
{
"type": "array",
"items": {
"type": "string",
"maxLength": 2000
}
},
{
"type": "null"
}
]
}
}
}
},
"annotations": {
"title": "Answer a research request's questions",
"readOnlyHint": false,
"openWorldHint": false,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description89/100
File the missing data Ploti could not file itself when the research request's data check reported: it files what it can then, and this files the rest once whatever stopped it is resolved (research credits ran short, or a place was outside the workspace's coverage). Filed data uses the workspace's research credits. A request needing more than 20 datasets files the first 20 and waits: call again with continue=true when the user wants the rest. Filing again is safe: a piece already filed is not filed twice.
The description explains the capability, when to use it, and what to expect.
Inputs88/100
Explain what happens when this optional value is omitted.
id
The research request, by the id create_research_request gave.
Clear enough to supply this argument.
continue
File past the ceiling, when the user wants the rest.
Explain what happens when this optional value is omitted.
Annotations100/100
All four behavior hints are declared.
{
"title": "File a research request's missing data",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
Output schema0/100
No output schema is declared. Add one for structured results so clients can interpret returned fields.
Not declared
Original tool metadata
{
"name": "file_research_gaps",
"title": "File a research request's missing data",
"description": "File the missing data Ploti could not file itself when the research request's data check reported: it files what it can then, and this files the rest once whatever stopped it is resolved (research credits ran short, or a place was outside the workspace's coverage). Filed data uses the workspace's research credits. A request needing more than 20 datasets files the first 20 and waits: call again with continue=true when the user wants the rest. Filing again is safe: a piece already filed is not filed twice.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"minLength": 1,
"description": "The research request, by the id create_research_request gave."
},
"continue": {
"type": "boolean",
"description": "File past the ceiling, when the user wants the rest."
}
}
},
"annotations": {
"title": "File a research request's missing data",
"readOnlyHint": false,
"openWorldHint": false,
"idempotentHint": true,
"destructiveHint": false
}
}
The title clearly communicates the tool’s action and subject. Keep it concise.
Description98/100
Display a checkpoint's entities, a materialized table's rows, a saved query's live results, or a saved filter's map. Returns viewUrl — the user-facing Ploti link for the artifact; create calls do not return one. Each call also creates a view-only link that needs no sign-in and lasts 7 days. Also returns a compact row preview as text and, in supported clients, an interactive panel that loads the full artifact separately. Use limit, offset, and columns to page or narrow the preview; expression columns from a checkpoint's table_config render in Ploti but are omitted from the preview. Use this tool to hand a result to the user rather than pasting rows into your reply. An answer about places is handed over as one filter (tools.filters.create) holding the result and the layer it rests on, and its viewUrl goes in your reply.
The description explains the capability, when to use it, and what to expect.
Inputs67/100
5 of 6 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values.
session_id
Not provided
Missing description. Explain what this value means and how to supply it.
kind
Which output list the id came from, per list_outputs.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
id
Checkpoint, materialized table, saved query, or filter id.
Clear enough to supply this argument.
limit
Rows to include in the model-facing preview (default 10, maximum 25).
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
offset
Zero-based row offset for preview pagination.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
columns
Columns to include in the preview (maximum 12). Checkpoint table_config columns are used by default when available.
Remove validation rules already expressed in this field’s schema; describe its meaning and usage instead.
Annotations75/100
Declare idempotentHint based on the tool’s actual behavior.
{
"title": "Show an output to the user",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
Output schema79/100
The output schema explains the structured result.
View output schema
{
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"kind",
"views",
"title",
"subtitle",
"columns",
"rows",
"totalRows",
"viewUrl",
"dataUrl",
"embedUrl"
],
"properties": {
"kind": {
"enum": [
"checkpoint",
"table",
"query",
"filter"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"title": {
"type": "string"
},
"views": {
"type": "array",
"items": {
"enum": [
"map",
"table"
],
"type": "string"
}
},
"columns": {
"type": "array",
"items": {
"type": "string"
}
},
"dataUrl": {
"type": "string",
"description": "Internal data endpoint for the interactive panel."
},
"hasMore": {
"type": "boolean"
},
"viewUrl": {
"type": "string",
"description": "User-facing Ploti link to include in the answer."
},
"embedUrl": {
"type": "string",
"description": "Internal embed URL used by compatible MCP hosts."
},
"subtitle": {
"type": "string"
},
"totalRows": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Exact total when known; null for a truncated live query whose full count was not executed."
},
"primaryKey": {
"type": "string",
"description": "Column that identifies each row, for matching rows to the map."
},
"rowsReturned": {
"type": "number"
}
},
"additionalProperties": false
}
Original tool metadata
{
"name": "show_output",
"title": "Show an output to the user",
"description": "Display a checkpoint's entities, a materialized table's rows, a saved query's live results, or a saved filter's map. Returns viewUrl — the user-facing Ploti link for the artifact; create calls do not return one. Each call also creates a view-only link that needs no sign-in and lasts 7 days. Also returns a compact row preview as text and, in supported clients, an interactive panel that loads the full artifact separately. Use limit, offset, and columns to page or narrow the preview; expression columns from a checkpoint's table_config render in Ploti but are omitted from the preview. Use this tool to hand a result to the user rather than pasting rows into your reply. An answer about places is handed over as one filter (tools.filters.create) holding the result and the layer it rests on, and its viewUrl goes in your reply.",
"inputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"session_id",
"kind",
"id"
],
"properties": {
"id": {
"type": "string",
"description": "Checkpoint, materialized table, saved query, or filter id."
},
"kind": {
"enum": [
"checkpoint",
"table",
"query",
"filter"
],
"type": "string",
"description": "Which output list the id came from, per list_outputs."
},
"limit": {
"type": "integer",
"maximum": 25,
"minimum": 1,
"description": "Rows to include in the model-facing preview (default 10, maximum 25)."
},
"offset": {
"type": "integer",
"maximum": 5000,
"minimum": 0,
"description": "Zero-based row offset for preview pagination."
},
"columns": {
"type": "array",
"items": {
"type": "string",
"minLength": 1
},
"maxItems": 12,
"description": "Columns to include in the preview (maximum 12). Checkpoint table_config columns are used by default when available."
},
"session_id": {
"type": "string"
}
}
},
"outputSchema": {
"type": "object",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"required": [
"kind",
"views",
"title",
"subtitle",
"columns",
"rows",
"totalRows",
"viewUrl",
"dataUrl",
"embedUrl"
],
"properties": {
"kind": {
"enum": [
"checkpoint",
"table",
"query",
"filter"
],
"type": "string"
},
"rows": {
"type": "array",
"items": {
"type": "object",
"propertyNames": {
"type": "string"
},
"additionalProperties": {}
}
},
"title": {
"type": "string"
},
"views": {
"type": "array",
"items": {
"enum": [
"map",
"table"
],
"type": "string"
}
},
"columns": {
"type": "array",
"items": {
"type": "string"
}
},
"dataUrl": {
"type": "string",
"description": "Internal data endpoint for the interactive panel."
},
"hasMore": {
"type": "boolean"
},
"viewUrl": {
"type": "string",
"description": "User-facing Ploti link to include in the answer."
},
"embedUrl": {
"type": "string",
"description": "Internal embed URL used by compatible MCP hosts."
},
"subtitle": {
"type": "string"
},
"totalRows": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"description": "Exact total when known; null for a truncated live query whose full count was not executed."
},
"primaryKey": {
"type": "string",
"description": "Column that identifies each row, for matching rows to the map."
},
"rowsReturned": {
"type": "number"
}
},
"additionalProperties": false
},
"annotations": {
"title": "Show an output to the user",
"readOnlyHint": true,
"openWorldHint": false,
"destructiveHint": false
}
}
Loading score analytics…
Put your MCP to the test
A few tasks worth trying with this server, chosen from its reviewed tools. These have not been run.
01List data requestsList the data requests this user filed in the workspace, newest first, with each one's status and the datasets it has delivered so far.
02Read a Ploti guideFetch a guide on how to work with Ploti's data and sandbox.
03Find dataFind the layers, tables, documents (ordinances, plans, reports) and scanned maps that can answer a question, by their titles, descriptions, schemas, values a…
04List research requestsThe research requests the member can see, newest first, with where each one is.
05List data in viewList every dataset the session's viewport offers (layers, tables, documents, scanned maps): the same list the Ploti map shows for this view and zoom, unranke…
Want to run these tasks against your MCP server with us?
ploti setup, tools, quality score, publishing readiness, and alternatives.
How do I add ploti to Claude, ChatGPT, or Cursor?
Add ploti to Claude, ChatGPT, or Cursor by connecting its remote MCP URL in each client's MCP settings. In Claude, add a custom connector. In ChatGPT, add an MCP app. In Cursor, add an MCP server under Settings. The MCP URL is https://mcp.ploti.ai/api/mcp.
Is ploti MCP ready for ChatGPT and Claude?
As of October 15, 2026, ploti scores 87/100 on Manufact's MCP publishing checklist. The scan does not yet meet ChatGPT publishing checks and meets Claude publishing checks. Passing these checks does not guarantee marketplace approval.
How clear is ploti's MCP tool documentation?
As of October 15, 2026, ploti scores 74/100 in Manufact's review of tool titles, descriptions, input and output schemas, and behavior annotations. The review covers 31 of 31 discovered tools. The quality score measures documentation clarity; runtime behavior and marketplace readiness require separate checks.
What are the best alternatives to ploti?
As of October 16, 2026, alternatives to ploti in data-analytics on the Manufact MCP catalog include Epicure, Newsflash, Point Topic, Pricing Optimizer AI, and StatCite.
How can ploti improve its MCP documentation?
get_file is the lowest-scoring reviewed tool at 55/100. 0 of 2 inputs have descriptions. Add descriptions to the missing inputs, including where to obtain IDs and how to supply values. Open its review for feedback and a copyable fix prompt, then rerun the documentation review after updating your tool metadata.
Not your MCP?
Review any public or OAuth MCP server for protocol compatibility, tool documentation, and ChatGPT and Claude publishing readiness. No account required.