pumperly-mcp
你将获得
类型 | 用途 | MCP URI / 工具 ID |
资源 | 只读浏览配置、统计信息和汇率 |
|
工具 | 查找站点、计算路线和地理编码位置 |
|
所有功能均通过单个 JSON-RPC 端点 (/mcp) 暴露。
LLM / 智能体可以:initialize -> readResource -> listTools -> callTool ……以此类推。
Related MCP server: Foundry MCP Server
快速开始 (Docker Compose)
services:
pumperly-mcp:
image: drumsergio/pumperly-mcp:latest
ports:
- "127.0.0.1:8080:8080"
environment:
- PUMPERLY_URL=https://pumperly.com安全提示: HTTP 传输默认监听
127.0.0.1:8080。如果需要将其暴露在网络上,请将其置于带有身份验证的反向代理之后。
通过 npm 安装 (stdio 传输)
npx pumperly-mcp或者全局安装:
npm install -g pumperly-mcp
pumperly-mcp这将从 GitHub Releases 下载适用于你平台的预构建 Go 二进制文件,并使用 stdio 传输运行它。需要至少一个 已发布的版本。
本地构建
git clone https://github.com/GeiserX/pumperly-mcp
cd pumperly-mcp
# (optional) create .env from the sample
cp .env.example .env && $EDITOR .env
go run ./cmd/server配置
变量 | 默认值 | 描述 |
|
| Pumperly 实例 URL(末尾不带 /) |
|
| HTTP 监听地址(Docker 设置为 |
| (空 = HTTP) | 设置为 |
将它们放入 .env 文件(参考 .env.example)或在环境变量中设置。
测试
已使用 Inspector 进行测试,目前完全可用。在提交 PR 之前,请确保此 MCP 服务器通过该媒介表现良好。
客户端 LLM 的配置示例
{
"schema_version": "v1",
"name_for_human": "Pumperly-MCP",
"name_for_model": "pumperly_mcp",
"description_for_human": "Query real-time fuel prices, find stations, plan routes, and geocode locations via Pumperly.",
"description_for_model": "Interact with a Pumperly instance that aggregates fuel station data. First call initialize, then reuse the returned session id in header \"Mcp-Session-Id\" for every other call. Use readResource to fetch URIs that begin with pumperly://. Use listTools to discover available actions and callTool to execute them.",
"auth": { "type": "none" },
"api": {
"type": "jsonrpc-mcp",
"url": "http://localhost:8080/mcp",
"init_method": "initialize",
"session_header": "Mcp-Session-Id"
},
"logo_url": "https://pumperly.com/logo.png",
"contact_email": "acsdesk@protonmail.com",
"legal_info_url": "https://github.com/GeiserX/pumperly-mcp/blob/main/LICENSE"
}鸣谢
Pumperly -- 实时油价聚合
MCP-GO -- 现代 MCP 实现
GoReleaser -- 轻松实现多架构发布
维护者
贡献
欢迎参与!提交 Issue 或提交 PR。
Pumperly-MCP 遵循 Contributor Covenant 行为准则。
GeiserX 的其他 MCP 服务器
cashpilot-mcp — 被动收入监控
duplicacy-mcp — 备份健康状况监控
genieacs-mcp — TR-069 设备管理
lynxprompt-mcp — AI 配置蓝图
telegram-archive-mcp — Telegram 消息归档
相关项目
项目 | 描述 |
开源燃油和电动汽车路线规划器,提供实时价格 | |
Pumperly 燃油和电动汽车路线规划器的官方 Android 应用 | |
Pumperly 燃油和电动汽车充电价格的 Home Assistant 自定义集成 | |
用于 Pumperly 燃油和电动汽车充电数据的 n8n 社区节点 |
Available Tools
6 toolscalculate_routeCalculate a driving routeARead-onlyIdempotent
Calculate a driving route and return its distance (km), driving time (minutes) and bounding box, with up to 2 alternatives when there are no waypoints. Ends can be place names, "lat,lon" text or coordinates. To find fuel along the way, call cheapest_fuel_on_route instead: it routes and searches in one call. Set include_geometry only to pass the line to find_route_stations; it can be up to 2000 points.
| Name | Required | Description | Default |
|---|---|---|---|
| origin | No | Start as a place name ("Madrid") or "lat,lon" text ("40.4168,-3.7038"). Or give origin_lat and origin_lon. | |
| dest_lat | No | End latitude, decimal degrees. | |
| dest_lon | No | End longitude, decimal degrees. | |
| waypoints | No | Optional stops in order, as [lon, lat] pairs (longitude first), at most 5. Example: [[-1.13, 40.34]]. | |
| origin_lat | No | Start latitude, decimal degrees. | |
| origin_lon | No | Start longitude, decimal degrees. | |
| destination | No | End as a place name ("Valencia") or "lat,lon" text. Or give dest_lat and dest_lon. | |
| include_geometry | No | Include each route's GeoJSON LineString ([lon, lat] points). Default false. |
Output Schema
| Name | Required | Description |
|---|---|---|
| origin | Yes | |
| routes | Yes | |
| destination | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent and non-destructive, so the safety profile is covered. The description still adds real behavioral detail beyond them: alternatives are capped at 2 and only appear when there are no waypoints, and the geometry payload can reach 2000 points. It stops short of stating any rate or size limits on the route call itself.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three tight sentences, front-loaded with what the tool returns, then the alternative, then the parameter caveat. No filler, and each clause carries a decision-relevant fact.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Eight optional parameters and an output schema mean the schema and output do most of the structural work; the description covers the non-obvious behavioral facts (alternatives rule, geometry cost, sibling routing) that the structured data cannot express. Nothing an agent needs before calling it is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine interpretation: the interaction between waypoints and the availability of alternatives, and the fact that include_geometry exists solely to feed find_route_stations. The place-name vs. 'lat,lon' vs. coordinate-pair input forms are already in the schema and are only lightly echoed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (calculate a driving route) plus exactly what comes back: distance in km, driving time in minutes, and a bounding box. It also distinguishes itself from cheapest_fuel_on_route and find_route_stations, so an agent can select it without opening sibling schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit alternative with its trigger condition: 'To find fuel along the way, call cheapest_fuel_on_route instead: it routes and searches in one call.' It also constrains include_geometry to the single case where the line is fed to find_route_stations, which prevents a costly misuse.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
cheapest_fuel_on_routeCheapest fuel along a tripARead-onlyIdempotent
Plan a drive and find where to fill up: routes from origin to destination, then lists the stations within corridor_km of the route, cheapest first, with each one's km from the start. Ends can be place names ("Madrid", "Valencia"), "lat,lon" text or coordinates. Prices are per litre (per kg for CNG, LNG and H2, per kWh for EV) in local currency; cross-border trips mix currencies, see pumperly://exchange-rates. Uses two of the API's 30 route searches per minute.
| Name | Required | Description | Default |
|---|---|---|---|
| fuel | Yes | Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units. | |
| limit | No | How many stations to return, 1 to 50. Default 10. | |
| origin | No | Start as a place name ("Madrid") or "lat,lon" text ("40.4168,-3.7038"). Or give origin_lat and origin_lon. | |
| sort_by | No | price (cheapest first, default) or route (in driving order). | price |
| dest_lat | No | End latitude, decimal degrees. | |
| dest_lon | No | End longitude, decimal degrees. | |
| origin_lat | No | Start latitude, decimal degrees. | |
| origin_lon | No | Start longitude, decimal degrees. | |
| corridor_km | No | How far off the route a station may be, in km, 0.5 to 50. Default 2 (a short detour). | |
| destination | No | End as a place name ("Valencia") or "lat,lon" text. Or give dest_lat and dest_lon. |
Output Schema
| Name | Required | Description |
|---|---|---|
| origin | Yes | |
| stations | Yes | |
| destination | Yes | |
| distance_km | Yes | |
| duration_min | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/openWorld, so the bar is lower, yet the description adds real operational context: the call consumes two of the API's 30 route searches per minute, prices are in local currency and cross-border trips mix currencies (pointing to pumperly://exchange-rates). Units differ by fuel (per litre/kg/kWh), which an agent cannot infer from the enum alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences with the core action front-loaded before caveats about units, currency, and rate limits. Every sentence carries information; slight density in the pricing sentence but no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists so return values need no explanation, and the description covers the remaining unknowns an agent needs: routing behavior, corridor semantics, sorting default, unit/currency handling, and quota cost. Sibling differentiation is the only omission.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so every parameter is already documented; the description only restates the accepted end formats that origin/destination already explain. It adds no default/resolution behavior (e.g., which wins if both origin and origin_lat are supplied) beyond the schema, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource: plans a route from origin to destination and returns stations within corridor_km sorted cheapest-first with distance from start. That is far more precise than the title. However, it never distinguishes itself from the very similar sibling find_route_stations, so an agent comparing the two must open both schemas to choose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by 'Plan a drive and find where to fill up', and the accepted end formats (place name, 'lat,lon', or lat/lon params) guide invocation. But there is no explicit when-to-use/when-not guidance and no mention of calculate_route or find_route_stations as alternatives, even though they overlap heavily.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_nearest_stationsFind nearest fuel or EV stationsARead-onlyIdempotent
Find the fuel stations or EV chargers closest to a point, with current prices. Give the point as lat and lon, or as a place name in near (geocoded for you). sort_by=distance (default) returns the nearest; sort_by=price returns the cheapest among the 50 nearest within the radius. Prices are per litre (per kg for CNG, LNG and H2, per kWh for EV) in the station's local currency. Covers about 40 countries, mostly in Europe; pumperly://stats lists them.
| Name | Required | Description | Default |
|---|---|---|---|
| lat | No | Latitude of the search point in decimal degrees, e.g. 40.4168. Give with lon, or use near instead. | |
| lon | No | Longitude of the search point in decimal degrees, e.g. -3.7038. Give with lat, or use near instead. | |
| fuel | Yes | Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units. | |
| near | No | Place name or address to search around, e.g. "Atocha, Madrid". Used when lat and lon are not given. | |
| limit | No | How many stations to return, 1 to 50. Default 5. | |
| sort_by | No | distance (nearest first, default) or price (cheapest first). | distance |
| radius_km | No | Search radius in km, 0.5 to 100. Default 10. |
Output Schema
| Name | Required | Description |
|---|---|---|
| fuel | Yes | |
| unit | Yes | What prices are per: L, kg or kWh |
| found | Yes | |
| center | No | |
| prices | Yes | |
| sort_by | Yes | |
| returned | Yes | |
| stations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Goes well past the annotations (which already declare read-only, idempotent, non-destructive, open-world) by disclosing the key hidden behavior: sort_by=price only considers the 50 nearest stations inside the radius, so it is not a global cheapest search. It also gives price units per fuel family, local-currency pricing, and the ~40-country coverage with a pointer to pumperly://stats.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what the tool returns, then the input options, then the sort semantics and pricing units. Five dense sentences, each carrying distinct information with no restatement of the title or schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter, enum-heavy, open-world search tool with an output schema already covering return shape, the description supplies everything else an agent needs: input-mode choice, sort trade-offs and its 50-station cap, price units and currency, and geographic coverage limits.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds genuine meaning the schema does not: the price-sort candidate cap of 50 nearest within radius, and the per-unit pricing basis (per litre, per kg for CNG/LNG/H2, per kWh for EV). It reinforces rather than replaces the schema's lat/lon vs near and limit descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Find the fuel stations or EV chargers closest to a point') plus the value-add of current prices. The point-proximity scope inherently separates it from route-based siblings (calculate_route, find_route_stations, cheapest_fuel_on_route) and from get_stations_in_area.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explains the two ways to specify a search point (lat/lon vs a geocoded `near` name) and when sort_by=price is the right choice. It never names the sibling tools or says when to prefer them (e.g. route-based search instead), so it stops short of full when/when-not guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
find_route_stationsFuel or EV stations along a route lineARead-onlyIdempotent
List the fuel stations or EV chargers within corridor_km of a route line you already have, cheapest first by default, with each station's km along the route. Most callers want cheapest_fuel_on_route, which takes place names and does the routing too. Prices are per litre (per kg for CNG, LNG and H2, per kWh for EV) in local currency. The API allows 30 route searches per minute.
| Name | Required | Description | Default |
|---|---|---|---|
| fuel | Yes | Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units. | |
| limit | No | How many stations to return, 1 to 50. Default 10. | |
| sort_by | No | price (cheapest first, default) or route (in driving order from the start). | price |
| geometry | Yes | GeoJSON LineString of the route: {"type":"LineString","coordinates":[[lon,lat],...]}, longitude first, 2 to 2000 points. calculate_route returns one with include_geometry=true. | |
| corridor_km | No | How far from the route a station may be, in km, 0.5 to 50. Default 5. |
Output Schema
| Name | Required | Description |
|---|---|---|
| fuel | Yes | |
| unit | Yes | What prices are per: L, kg or kWh |
| found | Yes | |
| center | No | |
| prices | Yes | |
| sort_by | Yes | |
| returned | Yes | |
| stations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover readOnly/idempotent/openWorld, and the description adds context they cannot: the 30 route searches per minute rate limit and the price unit conventions (per litre, per kg for CNG/LNG/H2, per kWh for EV). These are behavioral facts an agent needs and cannot infer from the structured fields.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences, each carrying distinct information: scope, alternative, units, rate limit. The key constraint and the sibling redirect are front-loaded, and nothing is repeated from the title or schema verbatim.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
An output schema exists, so return values need not be described. For a 5-parameter tool with nested geometry, the description covers the prerequisite input, the alternative tool, unit semantics, and throughput limits — nothing an agent needs to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3; the description goes beyond it by spelling out the price-unit mapping per fuel family, which the schema only gestures at via a resource reference. It adds less on geometry/corridor_km/limit, which the schema already documents thoroughly.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (list fuel stations / EV chargers), plus the scoping constraint (within corridor_km of a route line you already have), the default ordering, and the per-station 'km along route' output. This is enough to tell it apart from cheapest_fuel_on_route and get_stations_in_area without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes most callers to the sibling: 'Most callers want cheapest_fuel_on_route, which takes place names and does the routing too.' Combined with 'a route line you already have', it states both the prerequisite for using this tool and the condition that selects the alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
geocodeGeocode a place nameARead-onlyIdempotent
Turn a place name or address into coordinates (lat, lon), best match first. The station and route tools already accept place names, so call this only to pick between ambiguous places or to get coordinates for a bbox. Give lat and lon to prefer results near a point.
| Name | Required | Description | Default |
|---|---|---|---|
| lat | No | Optional latitude to prefer nearby results. Give with lon. | |
| lon | No | Optional longitude to prefer nearby results. Give with lat. | |
| limit | No | How many matches to return, 1 to 10. Default 5. | |
| query | Yes | City, address or point of interest, e.g. "Puerta del Sol, Madrid". At most 200 characters. |
Output Schema
| Name | Required | Description |
|---|---|---|
| query | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, openWorld and non-destructive, so the safety profile is covered. The description adds valuable behavioral context — results are ranked best-match-first and lat/lon biases ranking toward a point — but says nothing about result limits, error behavior, or no-match outcomes.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three compact sentences, front-loaded with the core transformation, then the routing guidance, then the optional refinement. Every sentence carries distinct information with no repetition of the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only lookup with an output schema, full schema coverage, and rich annotations, the description is complete enough to call correctly: what it returns, how to bias results, and when to prefer siblings are all covered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters are already documented in the schema, including the lat/lon pairing rule and the 1-10 limit. The description only echoes the proximity behavior, adding no syntax or format detail beyond the schema. Baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (place name/address → coordinates) plus the ordering rule ('best match first'). It also distinguishes itself from the station and route siblings by noting they accept place names directly, so an agent can tell when this tool is not needed.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly gives both when-not ('the station and route tools already accept place names') and when-to ('pick between ambiguous places or get coordinates for a bbox'). The lat/lon proximity hint adds a second invocation context. Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_stations_in_areaFuel or EV stations in an areaARead-onlyIdempotent
List the fuel stations or EV chargers inside a bounding box with current prices, cheapest first by default, plus the min, median and max price per currency. Use it for "cheapest diesel in this town". For a point and radius use find_nearest_stations; along a trip use cheapest_fuel_on_route. Prices are per litre (per kg for CNG, LNG and H2, per kWh for EV) in local currency. Keep the box city-sized: a whole-country box can exceed the 8 MB answer cap.
| Name | Required | Description | Default |
|---|---|---|---|
| bbox | Yes | Bounding box as "minLon,minLat,maxLon,maxLat" in decimal degrees, longitude first. Example for central Madrid: "-3.75,40.38,-3.65,40.45". | |
| fuel | Yes | Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units. | |
| limit | No | How many stations to return, 1 to 100. Default 20. | |
| sort_by | No | price (cheapest first, default) or distance (from the centre of the box). | price |
Output Schema
| Name | Required | Description |
|---|---|---|
| fuel | Yes | |
| unit | Yes | What prices are per: L, kg or kWh |
| found | Yes | |
| center | No | |
| prices | Yes | |
| sort_by | Yes | |
| returned | Yes | |
| stations | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly/idempotent/non-destructive, and the description still adds real behavioral content: default sort order, the aggregate min/median/max per currency returned alongside results, the price unit per fuel family (litre, kg for CNG/LNG/H2, kWh for EV), and the 8 MB answer cap with the box-size advice that avoids it. The size cap is a non-obvious failure mode disclosed up front.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads what the tool returns, then routing alternatives, then unit semantics, then the size constraint — each sentence carries distinct information with no repetition of schema or annotations.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Output schema exists so return values need no explanation, yet the description still flags the aggregate fields and the cap that affects result completeness. For a 4-parameter read tool with full schema coverage, nothing an agent needs in order to call it correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description earns more by explaining that prices are quoted per litre, per kg for CNG/LNG/H2 and per kWh for EV, which is unit semantics the schema only points to indirectly. It does not restate the bbox format or limit bounds, which the schema already handles well.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('List the fuel stations or EV chargers inside a bounding box') and immediately names the two nearest siblings (find_nearest_stations, cheapest_fuel_on_route) with the geometric condition that selects each. An agent can route between all three without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit when-to-use ('cheapest diesel in this town'), plus explicit alternatives for the two adjacent cases: point-and-radius and along-a-trip. It also gives a when-not constraint ('keep the box city-sized'). Nothing is left to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.2.1- Changed
calculate_route22 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / dest_lat / descriptionPrevious value: -"Destination latitude"New value: +"End latitude, decimal degrees." - added
Input schema / properties / dest_lat / maximumAdded value: +90 - added
Input schema / properties / dest_lat / minimumAdded value: +-90 - changed
Input schema / properties / dest_lon / descriptionPrevious value: -"Destination longitude"New value: +"End longitude, decimal degrees." - added
Input schema / properties / dest_lon / maximumAdded value: +180 - added
Input schema / properties / dest_lon / minimumAdded value: +-180 - added
Input schema / properties / destinationAdded value: +{ + "description": "End as a place name (\"Valencia\") or \"lat,lon\" text. Or give dest_lat and dest_lon.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / include_geometryAdded value: +{ + "default": false, + "description": "Include each route's GeoJSON LineString ([lon, lat] points). Default false.", + "type": "boolean" +} - added
Input schema / properties / originAdded value: +{ + "description": "Start as a place name (\"Madrid\") or \"lat,lon\" text (\"40.4168,-3.7038\"). Or give origin_lat and origin_lon.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - changed
Input schema / properties / origin_lat / descriptionPrevious value: -"Origin latitude"New value: +"Start latitude, decimal degrees." - added
Input schema / properties / origin_lat / maximumAdded value: +90 - added
Input schema / properties / origin_lat / minimumAdded value: +-90 - changed
Input schema / properties / origin_lon / descriptionPrevious value: -"Origin longitude"New value: +"Start longitude, decimal degrees." - added
Input schema / properties / origin_lon / maximumAdded value: +180 - added
Input schema / properties / origin_lon / minimumAdded value: +-180 - changed
Input schema / properties / waypoints / descriptionPrevious value: -"Optional JSON array of waypoints as [[lon,lat],...]"New value: +"Optional stops in order, as [lon, lat] pairs (longitude first), at most 5. Example: [[-1.13, 40.34]]." - added
Input schema / properties / waypoints / itemsAdded value: +{ + "items": { + "type": "number" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" +} - added
Input schema / properties / waypoints / maxItemsAdded value: +5 - changed
Input schema / properties / waypoints / typePrevious value: -"string"New value: +"array" - changed
Input schema / requiredPrevious value: -[ - "origin_lon", - "origin_lat", - "dest_lon", - "dest_lat" -]New value: +[] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "destination": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "origin": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "routes": { + "items": { + "additionalProperties": false, + "properties": { + "bbox": { + "items": { + "type": "number" + }, + "type": [ + "null", + "array" + ] + }, + "distance_km": { + "type": "number" + }, + "duration_min": { + "type": "number" + }, + "geometry": { + "additionalProperties": false, + "properties": { + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "type": [ + "null", + "array" + ] + }, + "type": [ + "null", + "array" + ] + }, + "type": { + "enum": [ + "LineString" + ], + "type": "string" + } + }, + "required": [ + "type", + "coordinates" + ], + "type": "object" + }, + "points": { + "description": "Number of points in the route line", + "type": "integer" + } + }, + "required": [ + "distance_km", + "duration_min", + "points" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "origin", + "destination", + "routes" + ], + "type": "object" +}
- Added
cheapest_fuel_on_route - Changed
find_nearest_stations22 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / fuel / descriptionPrevious value: -"Fuel type code (e.g. B7, E5, E10, E85, LPG, CNG, LNG, EV)"New value: +"Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units." - added
Input schema / properties / fuel / enumAdded value: +[ + "B7", + "B7_PREMIUM", + "B10", + "B_AGRICULTURAL", + "HVO", + "E5", + "E5_PREMIUM", + "E10", + "E5_98", + "E98_E10", + "LPG", + "CNG", + "LNG", + "H2", + "EV", + "ADBLUE" +] - changed
Input schema / properties / lat / descriptionPrevious value: -"Latitude of the search centre"New value: +"Latitude of the search point in decimal degrees, e.g. 40.4168. Give with lon, or use near instead." - added
Input schema / properties / lat / maximumAdded value: +90 - added
Input schema / properties / lat / minimumAdded value: +-90 - added
Input schema / properties / limit / defaultAdded value: +5 - changed
Input schema / properties / limit / descriptionPrevious value: -"Maximum number of results (default 5)"New value: +"How many stations to return, 1 to 50. Default 5." - added
Input schema / properties / limit / maximumAdded value: +50 - added
Input schema / properties / limit / minimumAdded value: +1 - changed
Input schema / properties / limit / typePrevious value: -"number"New value: +"integer" - changed
Input schema / properties / lon / descriptionPrevious value: -"Longitude of the search centre"New value: +"Longitude of the search point in decimal degrees, e.g. -3.7038. Give with lat, or use near instead." - added
Input schema / properties / lon / maximumAdded value: +180 - added
Input schema / properties / lon / minimumAdded value: +-180 - added
Input schema / properties / nearAdded value: +{ + "description": "Place name or address to search around, e.g. \"Atocha, Madrid\". Used when lat and lon are not given.", + "maxLength": 200, + "minLength": 1, + "type": "string" +} - added
Input schema / properties / radius_km / defaultAdded value: +10 - changed
Input schema / properties / radius_km / descriptionPrevious value: -"Search radius in kilometres (default 10)"New value: +"Search radius in km, 0.5 to 100. Default 10." - added
Input schema / properties / radius_km / maximumAdded value: +100 - added
Input schema / properties / radius_km / minimumAdded value: +0.5 - added
Input schema / properties / sort_byAdded value: +{ + "default": "distance", + "description": "distance (nearest first, default) or price (cheapest first).", + "enum": [ + "distance", + "price" + ], + "type": "string" +} - changed
Input schema / requiredPrevious value: -[ - "lat", - "lon", - "fuel" -]New value: +[ + "fuel" +] - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "center": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": [ + "null", + "object" + ] + }, + "found": { + "type": "integer" + }, + "fuel": { + "type": "string" + }, + "prices": { + "items": { + "additionalProperties": false, + "properties": { + "count": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "max": { + "type": "number" + }, + "median": { + "type": "number" + }, + "min": { + "type": "number" + } + }, + "required": [ + "currency", + "count", + "min", + "median", + "max" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "returned": { + "type": "integer" + }, + "sort_by": { + "enum": [ + "price", + "distance", + "route" + ], + "type": "string" + }, + "stations": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "brand": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "description": "ISO 3166-1 alpha-2 country code", + "type": "string" + }, + "currency": { + "description": "ISO 4217 currency of price", + "type": "string" + }, + "distance_km": { + "description": "Straight-line distance from the search point, km", + "type": [ + "null", + "number" + ] + }, + "ev_fees": { + "additionalProperties": true, + "type": "object" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "power_kw": { + "description": "EV only: maximum charging power, kW", + "type": [ + "null", + "number" + ] + }, + "price": { + "description": "Price per unit in currency", + "type": [ + "null", + "number" + ] + }, + "reported_at": { + "description": "When the price was last reported (RFC 3339)", + "type": "string" + }, + "route_km": { + "description": "Distance along the route from its start, km", + "type": [ + "null", + "number" + ] + }, + "unit": { + "description": "What the price is per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "unit": { + "description": "What prices are per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "fuel", + "unit", + "sort_by", + "found", + "returned", + "prices", + "stations" + ], + "type": "object" +}
- Changed
find_route_stations13 fields changed- added
Input schema / additionalPropertiesAdded value: +false - added
Input schema / properties / corridor_km / defaultAdded value: +5 - changed
Input schema / properties / corridor_km / descriptionPrevious value: -"Corridor width in kilometres (default 5)"New value: +"How far from the route a station may be, in km, 0.5 to 50. Default 5." - added
Input schema / properties / corridor_km / maximumAdded value: +50 - added
Input schema / properties / corridor_km / minimumAdded value: +0.5 - changed
Input schema / properties / fuel / descriptionPrevious value: -"Fuel type code (e.g. B7, E5, E10, E85, LPG, CNG, LNG, EV)"New value: +"Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units." - added
Input schema / properties / fuel / enumAdded value: +[ + "B7", + "B7_PREMIUM", + "B10", + "B_AGRICULTURAL", + "HVO", + "E5", + "E5_PREMIUM", + "E10", + "E5_98", + "E98_E10", + "LPG", + "CNG", + "LNG", + "H2", + "EV", + "ADBLUE" +] - changed
Input schema / properties / geometry / descriptionPrevious value: -"GeoJSON LineString as a JSON string, e.g. {\"type\":\"LineString\",\"coordinates\":[[lon,lat],...]}"New value: +"GeoJSON LineString of the route: {\"type\":\"LineString\",\"coordinates\":[[lon,lat],...]}, longitude first, 2 to 2000 points. calculate_route returns one with include_geometry=true." - added
Input schema / properties / geometry / propertiesAdded value: +{ + "coordinates": { + "items": { + "items": { + "type": "number" + }, + "maxItems": 2, + "minItems": 2, + "type": "array" + }, + "maxItems": 2000, + "minItems": 2, + "type": "array" + }, + "type": { + "enum": [ + "LineString" + ], + "type": "string" + } +} - changed
Input schema / properties / geometry / typePrevious value: -"string"New value: +"object" - added
Input schema / properties / limitAdded value: +{ + "default": 10, + "description": "How many stations to return, 1 to 50. Default 10.", + "maximum": 50, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / sort_byAdded value: +{ + "default": "price", + "description": "price (cheapest first, default) or route (in driving order from the start).", + "enum": [ + "price", + "route" + ], + "type": "string" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "center": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": [ + "null", + "object" + ] + }, + "found": { + "type": "integer" + }, + "fuel": { + "type": "string" + }, + "prices": { + "items": { + "additionalProperties": false, + "properties": { + "count": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "max": { + "type": "number" + }, + "median": { + "type": "number" + }, + "min": { + "type": "number" + } + }, + "required": [ + "currency", + "count", + "min", + "median", + "max" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "returned": { + "type": "integer" + }, + "sort_by": { + "enum": [ + "price", + "distance", + "route" + ], + "type": "string" + }, + "stations": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "brand": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "description": "ISO 3166-1 alpha-2 country code", + "type": "string" + }, + "currency": { + "description": "ISO 4217 currency of price", + "type": "string" + }, + "distance_km": { + "description": "Straight-line distance from the search point, km", + "type": [ + "null", + "number" + ] + }, + "ev_fees": { + "additionalProperties": true, + "type": "object" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "power_kw": { + "description": "EV only: maximum charging power, kW", + "type": [ + "null", + "number" + ] + }, + "price": { + "description": "Price per unit in currency", + "type": [ + "null", + "number" + ] + }, + "reported_at": { + "description": "When the price was last reported (RFC 3339)", + "type": "string" + }, + "route_km": { + "description": "Distance along the route from its start, km", + "type": [ + "null", + "number" + ] + }, + "unit": { + "description": "What the price is per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "unit": { + "description": "What prices are per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "fuel", + "unit", + "sort_by", + "found", + "returned", + "prices", + "stations" + ], + "type": "object" +}
- Changed
geocode12 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / lat / descriptionPrevious value: -"Optional latitude for geographic biasing"New value: +"Optional latitude to prefer nearby results. Give with lon." - added
Input schema / properties / lat / maximumAdded value: +90 - added
Input schema / properties / lat / minimumAdded value: +-90 - added
Input schema / properties / limitAdded value: +{ + "default": 5, + "description": "How many matches to return, 1 to 10. Default 5.", + "maximum": 10, + "minimum": 1, + "type": "integer" +} - changed
Input schema / properties / lon / descriptionPrevious value: -"Optional longitude for geographic biasing"New value: +"Optional longitude to prefer nearby results. Give with lat." - added
Input schema / properties / lon / maximumAdded value: +180 - added
Input schema / properties / lon / minimumAdded value: +-180 - changed
Input schema / properties / query / descriptionPrevious value: -"Search query (e.g. city name, address, POI)"New value: +"City, address or point of interest, e.g. \"Puerta del Sol, Madrid\". At most 200 characters." - added
Input schema / properties / query / maxLengthAdded value: +200 - added
Input schema / properties / query / minLengthAdded value: +1 - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "query": { + "type": "string" + }, + "results": { + "items": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + } + }, + "required": [ + "query", + "results" + ], + "type": "object" +}
- Changed
get_stations_in_area8 fields changed- added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / bbox / descriptionPrevious value: -"Bounding box as \"minLon,minLat,maxLon,maxLat\""New value: +"Bounding box as \"minLon,minLat,maxLon,maxLat\" in decimal degrees, longitude first. Example for central Madrid: \"-3.75,40.38,-3.65,40.45\"." - added
Input schema / properties / bbox / patternAdded value: +"^\\s*-?\\d+(\\.\\d+)?\\s*,\\s*-?\\d+(\\.\\d+)?\\s*,\\s*-?\\d+(\\.\\d+)?\\s*,\\s*-?\\d+(\\.\\d+)?\\s*$" - changed
Input schema / properties / fuel / descriptionPrevious value: -"Fuel type code (e.g. B7, E5, E10, E85, LPG, CNG, LNG, EV)"New value: +"Fuel code. Common ones: B7 = diesel, E5 = petrol 95, E10 = petrol 95 E10, E5_98 = petrol 98, LPG = autogas, EV = charging. The pumperly://fuel-types resource lists all 16 with labels and price units." - added
Input schema / properties / fuel / enumAdded value: +[ + "B7", + "B7_PREMIUM", + "B10", + "B_AGRICULTURAL", + "HVO", + "E5", + "E5_PREMIUM", + "E10", + "E5_98", + "E98_E10", + "LPG", + "CNG", + "LNG", + "H2", + "EV", + "ADBLUE" +] - added
Input schema / properties / limitAdded value: +{ + "default": 20, + "description": "How many stations to return, 1 to 100. Default 20.", + "maximum": 100, + "minimum": 1, + "type": "integer" +} - added
Input schema / properties / sort_byAdded value: +{ + "default": "price", + "description": "price (cheapest first, default) or distance (from the centre of the box).", + "enum": [ + "price", + "distance" + ], + "type": "string" +} - changed
Output schema / (root)Previous value: -nullNew value: +{ + "additionalProperties": false, + "properties": { + "center": { + "additionalProperties": false, + "properties": { + "city": { + "type": "string" + }, + "country": { + "type": "string" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "state": { + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": [ + "null", + "object" + ] + }, + "found": { + "type": "integer" + }, + "fuel": { + "type": "string" + }, + "prices": { + "items": { + "additionalProperties": false, + "properties": { + "count": { + "type": "integer" + }, + "currency": { + "type": "string" + }, + "max": { + "type": "number" + }, + "median": { + "type": "number" + }, + "min": { + "type": "number" + } + }, + "required": [ + "currency", + "count", + "min", + "median", + "max" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "returned": { + "type": "integer" + }, + "sort_by": { + "enum": [ + "price", + "distance", + "route" + ], + "type": "string" + }, + "stations": { + "items": { + "additionalProperties": false, + "properties": { + "address": { + "type": "string" + }, + "brand": { + "type": "string" + }, + "city": { + "type": "string" + }, + "country": { + "description": "ISO 3166-1 alpha-2 country code", + "type": "string" + }, + "currency": { + "description": "ISO 4217 currency of price", + "type": "string" + }, + "distance_km": { + "description": "Straight-line distance from the search point, km", + "type": [ + "null", + "number" + ] + }, + "ev_fees": { + "additionalProperties": true, + "type": "object" + }, + "lat": { + "type": "number" + }, + "lon": { + "type": "number" + }, + "name": { + "type": "string" + }, + "power_kw": { + "description": "EV only: maximum charging power, kW", + "type": [ + "null", + "number" + ] + }, + "price": { + "description": "Price per unit in currency", + "type": [ + "null", + "number" + ] + }, + "reported_at": { + "description": "When the price was last reported (RFC 3339)", + "type": "string" + }, + "route_km": { + "description": "Distance along the route from its start, km", + "type": [ + "null", + "number" + ] + }, + "unit": { + "description": "What the price is per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "name", + "lat", + "lon" + ], + "type": "object" + }, + "type": [ + "null", + "array" + ] + }, + "unit": { + "description": "What prices are per: L, kg or kWh", + "type": "string" + } + }, + "required": [ + "fuel", + "unit", + "sort_by", + "found", + "returned", + "prices", + "stations" + ], + "type": "object" +}
5 tool updates
v0.1.0- First observed
calculate_route - First observed
find_nearest_stations - First observed
find_route_stations - First observed
geocode - First observed
get_stations_in_area
TDQS
Scored across 6 tools
Each tool targets a distinct query shape: route geometry, route+fuel, point-radius, route-corridor, geocoding, or bounding box. The main overlap is between cheapest_fuel_on_route and find_route_stations, but the descriptions explicitly steer callers to the right one.
All names use snake_case and are mostly verb_noun or find_* patterns. Minor deviations exist: cheapest_fuel_on_route has no leading verb, and the set mixes calculate/find/get/geocode rather than one retrieval verb, but the conventions remain readable.
Six tools is well-scoped for a fuel-station and routing server, with each tool covering a meaningful query mode. There is no obvious filler or redundancy that should be removed.
The surface covers the main workflows: geocoding, route calculation, nearest stations, area search, and route-corridor fuel search. A dedicated station-detail or station-by-ID tool is missing, but most practical agent workflows can be composed from the existing tools.
Maintenance
Related MCP Connectors
Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.
An MCP server that provides an API to LLMs to manage their JumpCloud resources.
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
Related MCP Servers
- AlicenseBqualityDmaintenanceAn MCP server that provides LLMs access to other LLMs444 npm81MIT
- AlicenseNot gradedqualityFmaintenanceA lightweight, fast MCP server that provides onchain capabilities for the LLMs and Agents.62 npm255MIT
- AlicenseNot gradedqualityDmaintenanceEnables LLMs to interact with any REST API that has an OpenAPI specification by providing a lightweight MCP server that translates between natural language and API calls.MIT
- FlicenseNot gradedqualityDmaintenanceLocal MCP server that exposes fixed tools for GPT, Claude, and Gemini while routing to any OpenAI-compatible chat completions backend with independent configuration per target.1-