Koskamo

Stopful MCP

An official MCP server provided by Stopful that allows AI agents to plot AI-planned road trips onto an editable map. It supports adding stops, calculating drive times and distances, finding hotels and EV chargers, and estimating trip costs. The server returns a shareable link for the traveler to view, tweak, and book from.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Stopful MCP logo, by StopfulVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-07Operated by Stopful
MCP endpoint (Streamable HTTP)
https://stopful.com/api/mcp
Vendor documentation

What its tools do

1 tool, grouped by the hints the server advertises. Hints are self-declared, not verified behavior.

Can change data (1)

1 of 1 have a description · 1 of 1 declare behavior hints · 1 of 1 with parameters document them all

Checks and signals

Each line is separate evidence, not a trust score.

  • Passed Endpoint answered the live check ·
  • Passed Official MCP Registry namespace is tied to stopful.com ·
  • Passed Endpoint is on the registry namespace domain ·
  • Availability 1 of 1 checks passed in 30 days

What you can do with it

  • Plot an AI-generated road trip itinerary onto an interactive map
  • Calculate drive times and distances between stops
  • Find hotels and EV chargers along the route
  • Estimate the total cost of the trip before booking

Source and maintenance

Vendor evidenceRegistry 1.0.0

Protocol and capabilities

Server
stopful 2.49.0
MCP protocol negotiated in check
2025-11-25
Tools
1 advertised
Resources
1 advertised
Resource templates
0 advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {},
  "resources": {},
  "extensions": {
    "io.modelcontextprotocol/ui": {
      "mimeTypes": [
        "text/html;profile=mcp-app"
      ]
    }
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Stopful renders a planned multi-stop driving route as an interactive map. It provides one tool, plot_trip, which takes the stops in driving order — each a real place name, its number of nights, and its decimal lat/lon where known — and returns a link to the map, plus an inline map in clients that support one. Stops sent without coordinates are geocoded from their names, and coordinates that land far from where the name resolves are reported in the result. The map shows per-leg drive times and distances, and an estimate of what the trip will cost before anything is booked, and can be edited by the traveller: dragging a pin moves a stop, dragging the route adds one, and those changes are saved to the trip. Each result carries the trip's `id`; a later call that includes that `id` applies to the same trip, keeping its link and the traveller's map edits, while a call without one creates a separate trip. There is no planning or text generation on this server — it renders the itinerary it is given.

Tools

1 advertised

plot_trip
Advertised by the server during the last successful anonymous check.
Advertised destructiveOpen-world

Creates a road trip on Stopful, or updates one plotted earlier, and returns an interactive map of it — adding, removing, reordering or renaming stops, changing nights, dates or party size, and redrawing the route. Renders a multi-stop driving route as an interactive map and returns a link to it, plus an inline map in clients that support one. Takes a list of stops in driving order, each with a place name, optional decimal lat/lon and number of nights; a stop without coordinates is geocoded from its name, and coordinates that fall far from where the name resolves are reported back in the result. Accepts an optional trip name, start date, party size, and a day-by-day itinerary that becomes a day view over the map. The map shows per-leg drive times and distances and an estimate of what the trip will cost before anything is booked, and is editable: the traveller can drag a pin to move a stop or drag the route to add one, and their changes are saved to the trip. Covers road trips and other multi-stop routes; a single destination or a route with no stops to draw has nothing to render. The result contains the map link, the trip as structured data, and the trip's `id` (see the `id` parameter for updating an existing trip).

plot_trip input schema
{
  "type": "object",
  "required": [
    "stops"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "Id of a trip returned by an earlier call. SEND IT whenever this call is about a trip already plotted in this conversation — showing one of its days, fixing a stop, changing the route. WITHOUT `id` a separate new trip is created and the traveller is left with a pile of near-duplicate maps instead of one they can follow. With it the map link stays the same and their own edits survive. To change an existing trip, send its `id` together with the COMPLETE new `stops` and `days`; there are no partial updates. The map link then stays the same, and edits the traveller made on the map survive. Stops: a re-sent stop keeps the position they dragged it to, stops or waypoints they added on the map but absent from `stops` are kept, and a stop that was there before and is now omitted is removed. Activities: a re-sent one keeps its identity and the place, price and booking it carries on the map, which this tool does not report and so cannot be sent back; an activity the traveller added themselves is kept whether or not it appears here; one this tool showed earlier and now omitted is removed. Without an `id` a separate new trip is created and none of that carries over. Sending `id` with no `stops` is also accepted and changes nothing — it returns the trip exactly as saved."
    },
    "days": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "title"
              ],
              "properties": {
                "lat": {
                  "type": "number",
                  "description": "Optional decimal latitude of the activity. Supplying lat and lon puts a numbered pin on the map for it; without them the activity is still listed under its day, just not placed. Send them for a real place, and leave them out for something that isn't one ('Pack a picnic', 'Drive south')."
                },
                "lon": {
                  "type": "number",
                  "description": "Optional decimal longitude of the activity."
                },
                "note": {
                  "type": "string",
                  "description": "Optional one-line detail."
                },
                "time": {
                  "type": "string",
                  "description": "Optional time or part of day, e.g. '9:00 AM' or 'Morning'."
                },
                "title": {
                  "type": "string",
                  "description": "The place or activity, e.g. 'Altstadt Salzburg'. Every outing from a base belongs here — this is the only place it should appear, never also in `stops`."
                }
              }
            },
            "description": "The day's plan, in order."
          },
          "title": {
            "type": "string",
            "description": "Short day title, e.g. 'Over the Alps to Innsbruck'."
          },
          "summary": {
            "type": "string",
            "description": "Optional one-line what-to-expect for the day."
          }
        }
      },
      "description": "The day-by-day itinerary: one entry per calendar day of the trip, in order, each with a title and the day's activities. It renders as a tappable day view over the map, and selecting a day highlights that day's driving; the activities also become the trip's itinerary in the full Stopful planner, where the traveller can edit them. Send it when planning a trip, or when rewriting the itinerary — the day titles replace the saved ones wholesale, there are no partial day updates, while the activities under them are reconciled with what is saved rather than overwritten (see `id`). On a call that only changes the route of an existing trip, leaving `days` out keeps the saved itinerary untouched, which is usually what is wanted; the result echoes whatever is saved either way."
    },
    "name": {
      "type": "string",
      "description": "Optional trip name (e.g. 'Lisbon → Pyrenees'). Defaults to first → last stop."
    },
    "stops": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Optional. The id this stop was given in an earlier result. Echoing it back identifies that exact stop even if you rename it, so the traveller's placement and edits follow the right stop. Leave it out for a stop you are adding."
          },
          "lat": {
            "type": "number",
            "description": "Optional decimal latitude. On a NEW stop, supplying it is faster and more exact than geocoding the name. On a stop that already exists (one you are sending an `id` for), leave lat/lon out unless you mean to move it — the stop keeps wherever it currently sits, including anywhere the traveller has dragged it. Coordinates sent for an existing stop are an instruction to move it there."
          },
          "lon": {
            "type": "number",
            "description": "Optional decimal longitude. Same as lat: give it for a new stop, and leave it out for an existing one unless the point of the call is to move that stop."
          },
          "sub": {
            "type": "string",
            "description": "Optional short qualifier for disambiguation (e.g. region or country)."
          },
          "name": {
            "type": "string",
            "description": "Real, searchable place name — a town, city or famous landmark (e.g. 'Sirmione', 'Colosseum'). A vague or descriptive phrase may fail to geocode."
          },
          "nights": {
            "type": "integer",
            "description": "Optional. Nights at this stop: 0 = a pass-through on the drive (somewhere the route goes through without staying), 1 or more = overnight. Defaults to 1 when absent. A place visited from a base and returned from is an activity under `days`, not a 0-night stop."
          },
          "arriveBy": {
            "enum": [
              "drive",
              "flight",
              "train",
              "bus"
            ],
            "type": "string",
            "description": "Optional. How the traveller reaches this stop from the previous one. Defaults to a road leg (drive) when absent; 'flight' marks a real flight leg between two stops."
          }
        }
      },
      "description": "The route in driving order, 1 to 40 entries (the first is the origin). Required for a normal call — the only call that omits it is `id` on its own, which re-reads a saved trip unchanged. Each entry needs `name`; every other field is optional. A stop is a place the traveller sleeps, or a place on the road between two such places. An outing made from a base — a waterpark, a market, a cave, a viewpoint visited from the hotel and returned from the same day — is NOT a stop: put it under `days` as that day's activity, and do not repeat the base between outings. Outings sent as stops are moved under their day."
    },
    "guests": {
      "type": "object",
      "properties": {
        "adults": {
          "type": "integer",
          "description": "Number of adults (default 2)."
        },
        "children": {
          "type": "integer",
          "description": "How many children, for when their ages aren't known. Ignored when `childrenAges` is given."
        },
        "childrenAges": {
          "type": "array",
          "items": {
            "type": "integer"
          },
          "description": "Age of each child, e.g. [7, 11]. Preferred over `children`, because hotel and ticket pricing depends on the ages."
        }
      },
      "description": "Optional party size."
    },
    "focusDay": {
      "type": "integer",
      "description": "Optional. Open the map on this day of the itinerary, 1 = the first day. The map shows ONE DAY at a time: the chosen day's stops, activities and driving, with the rest of the route greyed. Use it WITH `id` to answer 'show me day 3' about a trip already plotted — that gives the traveller one map they can page through instead of a new map per day. Omitted, the map opens on the day the trip is actually on (day 1 before it starts), and the traveller can switch day or ask for the whole route from the map itself."
    },
    "startDate": {
      "type": "string",
      "description": "Optional trip start as ISO yyyy-mm-dd."
    }
  }
}
plot_trip output schema
{
  "type": "object",
  "required": [
    "url",
    "trip"
  ],
  "properties": {
    "id": {
      "type": "string",
      "description": "The trip's id. Pass it back as the `id` argument to apply a later change to this same trip."
    },
    "url": {
      "type": "string",
      "description": "Link that opens this trip on the map at stopful.com."
    },
    "days": {
      "type": "array",
      "items": {
        "type": "object"
      },
      "description": "The day-by-day itinerary. Activities may include ones the traveller added in the planner rather than through this tool."
    },
    "trip": {
      "type": "object",
      "required": [
        "name",
        "stops"
      ],
      "properties": {
        "id": {
          "type": "string",
          "description": "The trip's id — the same value as the top-level `id`."
        },
        "name": {
          "type": "string"
        },
        "items": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "cat": {
                "type": "string",
                "description": "Kind of activity, e.g. activity or sight."
              },
              "day": {
                "type": "integer",
                "description": "Day offset within that stop's stay, 0 = the arrival day."
              },
              "geo": {
                "type": "string",
                "description": "How the pin was placed: agent (as sent, then checked), auto (looked up from the name), ticket (from a matched ticket), fixed (corrected)."
              },
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              },
              "name": {
                "type": "string"
              },
              "note": {
                "type": "string"
              },
              "time": {
                "type": "string"
              },
              "stopId": {
                "type": "string",
                "description": "Id of the stop the activity belongs to."
              }
            }
          },
          "description": "The trip's activities, each tied to a stop and a day, including ones the traveller added in the planner."
        },
        "stops": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "name",
              "lat",
              "lon"
            ],
            "properties": {
              "id": {
                "type": "string",
                "description": "Stable id for this stop. Send it back as the stop's `id` when updating the trip, so this stop is recognised even under a different name."
              },
              "lat": {
                "type": "number"
              },
              "lon": {
                "type": "number"
              },
              "sub": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "nights": {
                "type": "integer"
              },
              "arriveBy": {
                "type": "string"
              },
              "waypoint": {
                "type": "boolean",
                "description": "True for a shaping point the traveller dragged out of the route line, not a destination."
              }
            }
          },
          "description": "Final stops in driving order, each with resolved coordinates."
        },
        "guests": {
          "type": "object",
          "properties": {
            "adults": {
              "type": "integer"
            },
            "childrenAges": {
              "type": "array",
              "items": {
                "type": "integer"
              }
            }
          }
        },
        "startDate": {
          "type": "string",
          "description": "ISO yyyy-mm-dd, when one was given."
        },
        "maxKmPerDay": {
          "type": "number"
        }
      },
      "description": "The trip as rendered, after geocoding and (on a continuation) merging the traveller's own map edits."
    },
    "assumed": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Stops placed from their name alone because no coordinates were given — approximate pins."
    },
    "focusDay": {
      "type": "integer",
      "description": "Set when the call asked for one day: the map opens on that day of the itinerary, 1 = the first day."
    },
    "notFound": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Stops that were requested but could not be placed, so they are absent from the map and the route."
    },
    "warnings": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "km": {
            "type": "number"
          },
          "lat": {
            "type": "number"
          },
          "lon": {
            "type": "number"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "description": "Stops whose supplied coordinates are far from where their name resolves, with the distance and the resolved position."
    },
    "unchanged": {
      "type": "boolean",
      "description": "True when the call only read the saved trip and changed nothing — an `id` sent with no `stops`."
    },
    "keptByTraveller": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "description": "Stops left where the traveller placed them on the map, so the coordinates supplied in this call were not applied."
    }
  }
}
plot_trip annotations
{
  "title": "Plot a road trip on a map",
  "readOnlyHint": false,
  "openWorldHint": true,
  "idempotentHint": false,
  "destructiveHint": true
}

Prompts

Not advertised

No prompt definitions available.

Resources

1 advertised

Stopful trip map

Advertised by the server during the last successful anonymous check.

Interactive road-trip map: numbered stops, colour-coded drive legs with times and distances, and a day view. The traveller can change the trip from this map — dragging a pin moves a stop, dragging the route adds one — and those changes are saved to the trip and kept when it is next updated. The map also publishes its current state to the host's model context.

Stopful trip map URI
ui://stopful/map-5.7.0.html

MIME type: text/html;profile=mcp-app

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
629 ms
95th percentile latency
629 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 629 ms · metadata changed · 1 tool(s), 1 resource(s), 0 resource template(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http stopful-mcp https://stopful.com/api/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add stopful-mcp --url https://stopful.com/api/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable stopful-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "stopful-mcp": {
      "url": "https://stopful.com/api/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "stopful-mcp": {
      "type": "streamableHttp",
      "url": "https://stopful.com/api/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start stopful-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "stopful-mcp": {
      "type": "http",
      "url": "https://stopful.com/api/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http stopful-mcp https://stopful.com/api/mcp

Claude Desktop

Open Claude Settings > Connectors > Add custom connector, enter this server URL and enable it for the conversation. This is a manual remote-connector handoff, not a desktop JSON configuration.
Claude Desktop configuration
https://stopful.com/api/mcp

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "stopful-mcp": {
      "serverUrl": "https://stopful.com/api/mcp"
    }
  }
}

Let your agent find its tools

Find servers like Stopful MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Stopful MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing