Upper Route Planner MCP by usefulapi
Read Upper drivers and stops; create and optimize routes, assign drivers, share routes to the app.
我该使用它吗
质量与安全性
基于对工具定义和协议合规性的自动分析。
上下文开销
这是每次将服务器的工具加载到模型上下文窗口时所消耗的大致 token 数。数值越高,可用于其他任务的注意力就越少。
安装
一键安装
将以下内容添加到你的 `claude_desktop_config.json` 文件中:
{
"mcpServers": {
"upper-route-planner": {
"url": "https://upper-route-planner.usefulapi.io/mcp"
}
}
}远程端点
https://upper-route-planner.usefulapi.io/mcpstreamable-http它能做什么
工具清单
工具(12)
🟢upper_list_drivers
List the active drivers on the Upper team that owns the API token: id, name, email and phone. Use the ids with upper_assign_drivers and upper_share_route_to_drivers. GET /ext-get-drivers.
输入模式
{
"type": "object",
"properties": {},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢upper_list_stops(route_id, scheduled, unscheduled, limit, offset)
List the team's stops in optimized order: address, coordinates, contact, time window, duration, status, route_id and custom ids. Pass route_id for one route (optionally scheduled=true/false); pass unscheduled=true (without route_id) for stops not on any route; with no filter all active stops are returned. Upper has no paging, so this tool pages locally: `limit` stops (default 100, max 500) starting at `offset`; the reply says if more remain (truncated, next_offset). GET /ext-get-stops.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"description": "Only the stops on this route.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"scheduled": {
"description": "With route_id only: true = scheduled stops, false = unscheduled stops of that route.",
"type": "boolean"
},
"unscheduled": {
"description": "Without route_id: true = unscheduled stops across all routes.",
"type": "boolean"
},
"limit": {
"description": "Most stops to return, 1-500 (default 100).",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 500
},
"offset": {
"description": "Skip this many stops (use next_offset from the previous reply to read on).",
"type": "integer",
"minimum": 0,
"maximum": 9007199254740991
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢upper_get_optimization_status(route_id)
Check whether a route is optimized: status is optimized, pending (still running) or not_optimized, plus the queue_id of the last job. Poll this after upper_optimize_route. GET /ext-optimization-status.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The route id."
}
},
"required": [
"route_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢upper_get_custom_fields
The workspace's five stop custom-field slots (custom1 - custom5: type, label, enabled, required) and which are enabled. Needs the Custom fields feature in the Upper plan. GET /ext-get-custom-fields.
输入模式
{
"type": "object",
"properties": {},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟢upper_get_capacity_fields
The workspace's five capacity-field slots (capacity1 - capacity5: label, unit, enabled) used for load planning. GET /ext-get-capacity-fields.
输入模式
{
"type": "object",
"properties": {},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟡upper_create_route(route_name, route_date, optimize_for, units, pickup_settings, ...)
WRITE: create a new, empty route (name, date, optimization preference, units, pickup sequencing, service time). Omitted settings fall back to the account's defaults. Returns the new route_id; add stops with upper_create_stop. Calling it twice creates two routes. POST /ext-create-route.
输入模式
{
"type": "object",
"properties": {
"route_name": {
"description": "Route name (default: New Route).",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"route_date": {
"description": "Route start date, yyyy-mm-dd (default today).",
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"optimize_for": {
"description": "Optimize for time or distance.",
"type": "string",
"enum": [
"time",
"distance"
]
},
"units": {
"description": "Distance units: miles or km.",
"type": "string",
"enum": [
"miles",
"km"
]
},
"pickup_settings": {
"description": "When pickups are sequenced: auto, before or after the deliveries.",
"type": "string",
"enum": [
"auto",
"before",
"after"
]
},
"service_time": {
"type": "integer",
"minimum": 0,
"maximum": 86400,
"description": "Per-stop service (dwell) time in seconds."
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴upper_update_route(route_id, route_name, route_date, optimize_for, units, ...)
WRITE: change settings of an existing route (name, date, optimize_for, units, pickup_settings, service_time). Only the fields you pass change. POST /ext-update-route.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The route to update."
},
"route_name": {
"description": "New route name.",
"type": "string",
"minLength": 1,
"maxLength": 200
},
"route_date": {
"description": "New start date, yyyy-mm-dd.",
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"optimize_for": {
"description": "Optimize for time or distance.",
"type": "string",
"enum": [
"time",
"distance"
]
},
"units": {
"description": "Distance units: miles or km.",
"type": "string",
"enum": [
"miles",
"km"
]
},
"pickup_settings": {
"description": "When pickups are sequenced: auto, before or after.",
"type": "string",
"enum": [
"auto",
"before",
"after"
]
},
"service_time": {
"type": "integer",
"minimum": 0,
"maximum": 86400,
"description": "Per-stop service (dwell) time in seconds."
}
},
"required": [
"route_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟡upper_create_stop(address, route_id, city, state, zipcode, ...)
WRITE: create a stop. The address is geocoded by Upper. Pass route_id to put it on a route (the route must not be active/in progress; an optimized route is flagged for re-optimization); omit it for an unscheduled stop. Calling it twice creates two stops. POST /ext-create-stop.
输入模式
{
"type": "object",
"properties": {
"address": {
"type": "string",
"minLength": 1,
"maxLength": 500,
"description": "Street address of the stop (geocoded by Upper)."
},
"route_id": {
"description": "Route to attach the stop to; omit for an unscheduled stop.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"city": {
"description": "City (overrides the geocoded value).",
"type": "string",
"maxLength": 200
},
"state": {
"description": "State or region (overrides the geocoded value).",
"type": "string",
"maxLength": 200
},
"zipcode": {
"description": "Postal code (overrides the geocoded value).",
"type": "string",
"maxLength": 40
},
"contact_name": {
"description": "Contact (recipient) name.",
"type": "string",
"maxLength": 200
},
"phone": {
"description": "Contact phone number; non-digits are stripped by Upper.",
"type": "string",
"maxLength": 40
},
"phone_country_code": {
"description": "Phone dialing code, for example 1.",
"type": "string",
"pattern": "^\\d{1,4}$"
},
"email": {
"description": "Contact email.",
"type": "string",
"maxLength": 254
},
"note": {
"description": "Free-text note shown to the driver.",
"type": "string",
"maxLength": 2000
},
"company_name": {
"description": "Company name for the stop.",
"type": "string",
"maxLength": 200
},
"nick_name": {
"description": "Short label for the stop.",
"type": "string",
"maxLength": 100
},
"pin_color": {
"description": "Map pin colour, hex like #FF6600.",
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$"
},
"start_time": {
"description": "Earliest service time (time window start), HH:MM or HH:MM:SS.",
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?$"
},
"end_time": {
"description": "Latest service time (time window end), HH:MM or HH:MM:SS.",
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?$"
},
"stop_duration": {
"description": "Service duration at the stop, in seconds.",
"type": "integer",
"minimum": 0,
"maximum": 86400
},
"stop_type": {
"description": "Stop type, for example Delivery or Pickup.",
"type": "string",
"maxLength": 50
},
"stop_priority": {
"description": "Stop priority, for example Normal, High, Urgent.",
"type": "string",
"maxLength": 50
},
"parcel_count": {
"description": "Number of parcels at the stop.",
"type": "integer",
"minimum": 0,
"maximum": 100000
},
"customer_system_id": {
"description": "Your own identifier for the stop (for cross-referencing).",
"type": "string",
"maxLength": 200
},
"custom_fields": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 2000
},
{
"type": "number"
},
{
"type": "boolean"
}
]
},
"description": "Custom field values keyed by slot (custom1 - custom5). Only fields enabled in the workspace (and in the plan) are stored."
},
"capacity_fields": {
"type": "object",
"properties": {
"capacity1": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity2": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity3": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity4": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity5": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"description": "Capacity values (integers) for load planning, keys capacity1 - capacity5."
}
},
"required": [
"address"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴upper_update_stop(stop_id, address, route_id, city, state, ...)
WRITE: change an existing stop; only the fields you pass change. A new address is re-geocoded (city/state/zipcode you pass with it are ignored); city/state/zipcode apply only without an address. Passing route_id moves the stop to that route. Editing a stop on an optimized route flags the route for re-optimization; stops on an active route cannot be edited. POST /ext-update-stop.
输入模式
{
"type": "object",
"properties": {
"stop_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The stop to update."
},
"address": {
"description": "New street address (re-geocoded).",
"type": "string",
"minLength": 1,
"maxLength": 500
},
"route_id": {
"description": "Move the stop to this route.",
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"city": {
"description": "City (only used when no address is passed).",
"type": "string",
"maxLength": 200
},
"state": {
"description": "State or region (only used when no address is passed).",
"type": "string",
"maxLength": 200
},
"zipcode": {
"description": "Postal code (only used when no address is passed).",
"type": "string",
"maxLength": 40
},
"contact_name": {
"description": "Contact (recipient) name.",
"type": "string",
"maxLength": 200
},
"phone": {
"description": "Contact phone number; non-digits are stripped by Upper.",
"type": "string",
"maxLength": 40
},
"phone_country_code": {
"description": "Phone dialing code, for example 1.",
"type": "string",
"pattern": "^\\d{1,4}$"
},
"email": {
"description": "Contact email.",
"type": "string",
"maxLength": 254
},
"note": {
"description": "Free-text note shown to the driver.",
"type": "string",
"maxLength": 2000
},
"company_name": {
"description": "Company name for the stop.",
"type": "string",
"maxLength": 200
},
"nick_name": {
"description": "Short label for the stop.",
"type": "string",
"maxLength": 100
},
"pin_color": {
"description": "Map pin colour, hex like #FF6600.",
"type": "string",
"pattern": "^#[0-9a-fA-F]{6}$"
},
"start_time": {
"description": "Earliest service time (time window start), HH:MM or HH:MM:SS.",
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?$"
},
"end_time": {
"description": "Latest service time (time window end), HH:MM or HH:MM:SS.",
"type": "string",
"pattern": "^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?$"
},
"stop_duration": {
"description": "Service duration at the stop, in seconds.",
"type": "integer",
"minimum": 0,
"maximum": 86400
},
"stop_type": {
"description": "Stop type, for example Delivery or Pickup.",
"type": "string",
"maxLength": 50
},
"stop_priority": {
"description": "Stop priority, for example Normal, High, Urgent.",
"type": "string",
"maxLength": 50
},
"parcel_count": {
"description": "Number of parcels at the stop.",
"type": "integer",
"minimum": 0,
"maximum": 100000
},
"customer_system_id": {
"description": "Your own identifier for the stop (for cross-referencing).",
"type": "string",
"maxLength": 200
},
"custom_fields": {
"type": "object",
"propertyNames": {
"type": "string",
"minLength": 1,
"maxLength": 64
},
"additionalProperties": {
"anyOf": [
{
"type": "string",
"maxLength": 2000
},
{
"type": "number"
},
{
"type": "boolean"
}
]
},
"description": "Custom field values keyed by slot (custom1 - custom5). Only fields enabled in the workspace (and in the plan) are stored."
},
"capacity_fields": {
"type": "object",
"properties": {
"capacity1": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity2": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity3": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity4": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
},
"capacity5": {
"type": "integer",
"minimum": -9007199254740991,
"maximum": 9007199254740991
}
},
"description": "Capacity values (integers) for load planning, keys capacity1 - capacity5."
}
},
"required": [
"stop_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴upper_assign_drivers(route_id, driver_ids)
WRITE: assign one or more drivers (ids from upper_list_drivers) to a route, replacing the route's drivers. Every id must be an active driver on the team. If the route was already optimized it is reset to unoptimized (needs_optimization: true), so run upper_optimize_route again. POST /ext-assign-drivers.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The route."
},
"driver_ids": {
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
"description": "Driver ids to assign."
}
},
"required": [
"route_id",
"driver_ids"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🔴upper_optimize_route(route_id, timezone)
WRITE: start optimizing a route's stop order. The route needs at least one stop and one assigned driver (upper_assign_drivers). It runs asynchronously: this returns status pending and a queue_id, then poll upper_get_optimization_status. If a job is already running, that job is returned. POST /ext-optimize-route.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The route to optimize."
},
"timezone": {
"description": "IANA timezone for the optimization, for example America/New_York.",
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9_+\\-/]+$"
}
},
"required": [
"route_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}🟡upper_share_route_to_drivers(route_id, driver_ids, timezone)
WRITE: push a route to the drivers' mobile app NOW, which sends each driver a notification. Without driver_ids the route's assigned drivers are used; with them, every id must be an active driver on the team. Sending twice notifies twice. Confirm with the user before calling. POST /ext-share-to-driver.
输入模式
{
"type": "object",
"properties": {
"route_id": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991,
"description": "The route to share."
},
"driver_ids": {
"description": "Drivers to notify (default: the route's assigned drivers).",
"minItems": 1,
"maxItems": 100,
"type": "array",
"items": {
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
}
},
"timezone": {
"description": "IANA timezone, for example America/New_York.",
"type": "string",
"minLength": 1,
"maxLength": 64,
"pattern": "^[A-Za-z0-9_+\\-/]+$"
}
},
"required": [
"route_id"
],
"$schema": "https://json-schema.org/draft/2020-12/schema"
}社区
证据