Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
作者提供的说明(英文原文)
Real shortest sea routes between ports: distance, canals/straits, GeoJSON. UN/LOCODE aware.
这三项永远不会合并成一个分数:意见、测量和人气是不同的东西。
我们还没有为它写通俗的介绍。下面是我们从注册中心读取到的事实。
选择你使用的 AI 应用,然后复制设置。
你需要
你用的是哪个应用?
在上方选择你的应用。我们会记住它,下次按钮会显示“安装到 …”。
打开 Claude Desktop → Settings → Developer → Edit Config,把下面的内容粘贴到 claude_desktop_config.json
{
"mcpServers": {
"searoute": {
"command": "npx",
"args": [
"-y",
"@searoute-ts/mcp"
]
}
}
}在终端中运行这条命令
claude mcp add searoute -- npx -y @searoute-ts/mcp粘贴到项目中的 .cursor/mcp.json,或 ~/.cursor/mcp.json
{
"mcpServers": {
"searoute": {
"command": "npx",
"args": [
"-y",
"@searoute-ts/mcp"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
粘贴到项目中的 .vscode/mcp.json
{
"servers": {
"searoute": {
"type": "stdio",
"command": "npx",
"args": [
"-y",
"@searoute-ts/mcp"
]
}
}
}浏览器会先询问是否打开应用,然后应用会显示设置并请你确认。密钥的值会留空,由你自己填写。
这个服务器在你的电脑上运行,而 ChatGPT 只能连接互联网上的服务器。请选择其他应用。
你的选择通过 Cookie 保存在这台设备上,无需账户。
这些设置是根据作者在注册中心的条目生成的。如果 README 的写法不同,请以 README 为准。
它在你的电脑上运行;安装后,工具列表会显示在你的 AI 应用中。
我们检查的是任何人都能看到的信息;我们没有阅读代码。不适用的检查项不计入总数。 · 检查于 2026年11月6日
注册名称已通过 GitHub 账户 mayurrawte 验证,该账户拥有此仓库
我们还没有从 GitHub 读取这个仓库
我们还没有从 GitHub 读取这个仓库
它在你的电脑上运行,没有互联网端点
在你的电脑上运行,改用本地设置
它在你的电脑上运行;我们只能读取互联网端点的工具列表
与注册中心里这个分类中所有活跃的服务器比较。只有当发布者拥有该仓库时,星标数才计入。
它在 1 项适用的基础检查中通过了 1 项。我们没有阅读代码。在添加密钥或令牌之前,请先看看这个服务器要求哪些权限,并使用只具备所需权限的密钥。
注册中心没有列出必需的密钥,不过有些服务器会在首次使用时要求你登录。
它在你的电脑上运行。你的 AI 应用会在需要时启动它,数据不会经过作者的服务器(除非它自己调用在线服务)。
GitHub 仓库最后更新于–,最新版本(0.1.1)于2个月前进入注册中心。
支持 MCP 的应用,例如 Claude Desktop、Claude Code、Cursor 和 VS Code。你的电脑上还需要有 Node.js。
同一分类中有:Spec Driven Development, Remote Desktop Commander, Reelsaw
同一分类中其他作者的服务器,按星标数排序。
如何看卡片:★ GitHub 星标i三大支柱:评价 · 检查 · 使用情况i更新 = 最新版本发布到注册中心的时间i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Drive a self-hosted Reelsaw: turn long videos into vertical clips, edit, re-render and export them.
按原文从作者的仓库复制。PopNx MCP 没有审查或背书其内容。HTML、图片和脚本已被移除。链接会离开本站。
Shortest sea route between any two points on Earth. A TypeScript / JavaScript
library for maritime route planning, port-to-port distance, ETA estimation, and
shipping-lane visualisation — powered by the 2025 Eurostat maritime network.
npm install searoute-ts
import { seaRoute } from 'searoute-ts';
const route = seaRoute([121.5, 31.0], [4.4, 51.9]); // Shanghai → Rotterdam → GeoJSON LineString, ~10,664 nm via Suez Canal
🗺️ [Try the interactive demo](https://mayurrawte.github.io/searoute-ts/) — click two points on a map and see the route, with all options live. (source)
📏 [Port-to-port sea distances](https://mayurrawte.is-a.dev/searoute-ts/routes/) — distance, sailing time and canal passages for common routes between major ports.
Works from plain JavaScript too — the package ships compiled
.jsplus
.d.tsdeclarations. The-tsin the name is for searchability, not a
language requirement.
seaRouteAlternatives returns the baseline plus up to N realistic alternatives.seaRouteMulti for port rotations and itineraries.speedKnots → durationHours.import { seaRoute } from 'searoute-ts';
const route = seaRoute([-74.04, 40.69], [-0.13, 51.5]); // NYC → London // route.properties.length // ≈ 3 362 nm // route.properties.units // 'nauticalmiles'
seaRoute(shanghai, rotterdam, {
units: 'kilometers', speedKnots: 22, }); // → 19 753 km, properties.durationHours ≈ 485 h (≈ 20 days)
seaRoute(shanghai, rotterdam, {
restrictions: ['suez', 'babelmandeb'], }); // → routes via Cape of Good Hope, ~25 800 km
seaRoute(shanghai, newYork, {
vesselDraftMeters: 16, // exceeds Panama's 15.2 m TFW }); // → Panama auto-blocked, route goes via Suez
import 'searoute-ts/ports'; // enables UN/LOCODE strings on the core API
import { seaRoute } from 'searoute-ts';
seaRoute('CNSHA', 'NLRTM'); // Shanghai → Rotterdam seaRoute('CNSHA', [4.4, 51.9]); // mixing a code and coordinates is fine too
The ~1 600-port dataset lives behind the searoute-ts/ports subpath so the core stays lean — importing it registers the resolver. You can also resolve codes yourself:
import { lookupPort, resolvePort } from 'searoute-ts/ports';
lookupPort('SGSIN'); // → { code, name: 'Singapore', country, coordinates: [lon, lat] } resolvePort('SGSIN'); // → [103.85, 1.28]
Unknown codes throw UnknownPortError. See Port codes below for provenance.
Don't want to bundle the ~135 KB dataset? Fetch it at runtime with loadPorts — the analog of loadNetwork. The dataset also ships as a raw dist/ports.json, so jsDelivr/unpkg serve it versioned for free:
import { seaRoute, loadPorts } from 'searoute-ts';
// Pin a version for reproducibility, or use @latest to always get the newest. await loadPorts('https://cdn.jsdelivr.net/npm/searoute-ts@latest/dist/ports.json');
seaRoute('CNSHA', 'NLRTM'); // works — the fetched dataset is now registered
https://cdn.jsdelivr.net/npm/searoute-ts@latest/dist/ports.json # newest https://cdn.jsdelivr.net/npm/searoute-ts@ /dist/ports.json # frozen/immutable
(dist/ports.json ships from the release that adds port codes onward — pin any version at or after it for reproducibility.)
loadPorts registers the fetched dataset (so code strings resolve) and returns it. It uses the global fetch (Node ≥18 / browsers); pass { fetch } to override.
import { seaRouteMulti } from 'searoute-ts';
seaRouteMulti( [shanghai, singapore, mumbai, rotterdam], { units: 'kilometers', returnPassages: true }, ); // → one concatenated LineString, total length, union of passages
import { seaRouteAlternatives } from 'searoute-ts';
const alts = seaRouteAlternatives(shanghai, rotterdam, { k: 4 }); // baseline 19 753 km via Suez // no-malacca 20 759 km // no-suez 25 315 km (via Panama) // no-suez-no-panama 25 845 km (via Cape of Good Hope)
The network is bundled by default, so seaRoute works offline with zero setup. If you'd rather not ship the ~1 MB network (e.g. to trim a browser bundle, or to use an updated network without upgrading the package), fetch it at runtime and pass it via the existing network option:
import { seaRoute, loadNetwork } from 'searoute-ts';
// CORS-enabled, served from GitHub Pages (or point at your own host / a CDN). const network = await loadNetwork('https://mayurrawte.github.io/searoute-ts/marnet.json');
const route = seaRoute(shanghai, rotterdam, { network });
Only the fetch is async — seaRoute itself stays synchronous. loadNetwork uses the global fetch (Node ≥18 and all browsers); pass { fetch } to supply your own. This is purely opt-in; nothing changes if you don't use it.
| Approach | How | Data version | Works offline | Best for |
|---|---|---|---|---|
| Bundled (default) | seaRoute(a, b) — no network | pinned to your installed package | ✅ | Most users; zero config, deterministic |
| Latest via URL | loadNetwork('…/marnet.json') | always the newest hosted | ❌ needs network | Always-current data without upgrading |
| Pinned via CDN | loadNetwork('https://cdn.jsdelivr.net/npm/searoute-ts@2.0.1/…') | frozen (immutable) | ❌ needs network | Reproducible builds |
You choose the version by choosing the URL:
https://cdn.jsdelivr.net/npm/searoute-ts@latest/dist/marnet.json # newest
https://cdn.jsdelivr.net/npm/searoute-ts@2/dist/marnet.json # newest 2.x https://cdn.jsdelivr.net/npm/searoute-ts@2.0.1/dist/marnet.json # frozen
A pinned URL never changes, so your routes stay reproducible. (These standalone-JSON CDN paths land with the package once the network ships as a separate asset — see issue #10; until then, use the GitHub Pages URL.)
For production, prefer a pinned URL (or just the bundled default) so your distances don't shift when the network is updated.
The bundled network is Eurostat's 100 km marnet_plus. Eurostat also publishes finer resolutions, which give more accurate coastal routing and shorter-hop fidelity at the cost of a larger download and slightly slower first-route graph construction. Two moderate resolutions ship as subpath exports so you only pay for them if you import them:
import { DEFAULT_MARNET } from 'searoute-ts/marnet-20km'; // or 'searoute-ts/marnet-50km'
import { seaRoute } from 'searoute-ts';
seaRoute(origin, destination, { network: DEFAULT_MARNET });
Like the bundled default, each variant ships once as a shared dist/data/marnet- .cjs asset that both the CJS and ESM builds load at runtime, so importing a variant doesn't duplicate the network across builds.
| Import | Resolution | Segments | JSON size | gzipped | Coastal accuracy |
|---|---|---|---|---|---|
searoute-ts (bundled default) | 100 km | 9,847 | ~1.3 MB | ~0.18 MB | Baseline — good for global routing |
searoute-ts/marnet-50km | 50 km | 15,498 | ~1.9 MB | ~0.27 MB | Modest step up |
searoute-ts/marnet-20km | 20 km | 29,581 | ~3.6 MB | ~0.51 MB | Noticeably finer coastal hops |
via loadNetwork (see below) | 10 km | 48,301 | ~5.9 MB | ~0.84 MB | High — larger download |
via loadNetwork (see below) | 5 km | 72,478 | ~9.0 MB | ~1.24 MB | Highest — largest download |
The 10 km and 5 km networks are large enough that bundling them would dominate the install, so they are not shipped in the package. Generate them from the Eurostat source with scripts/build-marnet.cjs (the script header documents the GDAL conversion), host the resulting JSON, and load it with loadNetwork — or pass any FeatureCollection to the network option directly.
{
type: 'Feature', geometry: { type: 'LineString', coordinates: [[lon, lat], ...] }, properties: { length: number, // in units, in-water only units: 'nauticalmiles' | 'kilometers' | 'miles' | ..., bbox: [minLon, minLat, maxLon, maxLat], greatCircleLength: number, // haversine between inputs, same units detourRatio: number, // routeKm / greatCircleKm originSnapKm: number, // input → network distance destinationSnapKm: number, durationHours?: number, // if speedKnots set passages?: ('suez' | 'panama' | ...)[], // if returnPassages: true ecaKm?: number, // if emissions + searoute-ts/eca imported ecaFraction?: number, // ecaKm / length (0–1) co2eTonnes?: number, // if emissions + vesselClass/factor } }
length runs between the two snapped network vertices. It does not include the legs from your inputs to the network (originSnapKm, destinationSnapKm, always in km), even with appendOriginDestination: true, which only adds the raw points to the line. For a door-to-door figure, add them yourself:
const { length, originSnapKm, destinationSnapKm } = seaRoute(a, b, { units: 'kilometers' }).properties;
const doorToDoorKm = originSnapKm + length + destinationSnapKm;
On hops shorter than the network resolution (~100 km by default, ~20 km with searoute-ts/marnet-20km), the snapped vertices can sit closer together than the inputs, so length can come out below greatCircleLength (detourRatio < 1). If both inputs snap to the same vertex, `NoRouteErro
只显示了开头部分;其余内容请在仓库中阅读