Fortrip

io.github.FortripEngineering由 GitHub 账号 FortripEngineering 发布v1.4.13更新于 Nov 7, 2026

Search hotels worldwide, compare live room rates, book and pay securely, and cancel bookings.

已验证Streamable HTTP可网页运行位置与生活商业与电商

概览

AI 生成的概览

Fortrip 让助手通过 Fortrip 账号搜索全球酒店、比较实时房型报价,并完成预订、付款和取消。

可以做什么
  • 按目的地、酒店名称或地标搜索酒店,并给出参考价格
  • 查看单家酒店的详情、政策、设施与房型
  • 获取最多 15 家酒店、同一入住行程的实时可订报价
  • 预订某个报价,生成待付款订单及付款链接
  • 列出并查看用户订单,支付订单或按条款取消订单
  • 获取灵活日期、预算、分段住宿或多目的地的规划指引
适用场景
适合让助手代为查找和比较酒店、查询特定日期和入住人数的真实可订价格,并在对话中完成预订、付款或取消的场合。
运行要求
可使用托管的远程端点,或本地 npm 包以桥接方式运行,后者需要 Node.js 22 或更高版本。需要 Fortrip 账号并在浏览器中通过 OAuth 登录,无需配置 API 密钥或令牌。需要能访问 Fortrip 的 API。
安装前请注意
预订会产生真实订单,付款会实际扣款;服务器要求助手先展示总价、行程与条款并取得用户明确确认。付款链接任何人持有即可付款,只能发给该用户本人。切勿提供银行卡信息,付款仅通过 Stripe 完成。取消可能产生违约金。住客姓名与入住信息会发送给 Fortrip 和酒店。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Fortrip,将其添加到工作区。
  2. 连接 所需的 凭据。它们会被私密存储,绝不会在公开页面上展示。
  3. 为需要使用其工具的对话启用该服务。

Web executable,通过 Streamable HTTP。 远程服务在工作区中配置后即可从网页运行时运行。

交给智能体安装

把这段提示词粘贴到 Claude Code、Codex、Cursor 等智能体,或 SourceWeft 对话中。智能体会读取该服务器的安装指南,先向你展示发布者、端点和登录方式,经你同意后再把它添加到你的客户端。密钥不会写进任何命令。

阅读 https://sourceweft.com/mcp/io.github.FortripEngineering%2Ffortrip/install.md,并按其中说明把这个 MCP 服务器接入我的客户端。修改任何配置之前,先告诉我它的发布者、端点和登录方式,等我确认。不要把 API 密钥写进命令或我会提交的文件;问我密钥应从哪里获取。

查看智能体所依据的安装指南

其他 MCP 客户端

把它添加到你客户端的 mcpServers 配置中。 客户端会在首次连接时完成 OAuth 登录。

{
  "mcpServers": {
    "fortrip": {
      "type": "http",
      "url": "https://mcp.fortrip.ai/"
    }
  }
}

README

Fortrip

Fortrip's public agent surface in one repository: the fortrip command-line tool for hotel search, live offers and orders, the contract that every agent surface shares, and the store listings that point agents at Fortrip. Hotels only for now.

Everything here talks to Fortrip API v1, https://api.fortrip.ai/v1, or to the hosted MCP server, https://mcp.fortrip.ai/, as a Fortrip user who has signed in with OAuth in the browser.

Using Fortrip

  • From a shell, or an agent with one: npx -y fortrip <command>. People start with npx -y fortrip login. Agents run npx -y fortrip guide and sign in through the loginUrl a command prints when it exits 3. The CLI's README has the commands, sign-in and exit codes.

  • In an MCP client: connect the hosted server https://mcp.fortrip.ai/ if the client supports remote servers with OAuth, or run the local bridge, npx -y fortrip mcp. The CLI's README has the config for Claude Desktop, Cursor, Codex and Claude Code. The Claude and OpenAI plugins use the hosted server.

  • In Claude Code, as a plugin (the hosted server plus Fortrip's skill):

    /plugin marketplace add FortripEngineering/fortrip/plugin install fortrip@fortrip
  • As a skill, for agents that can run shell commands but don't have Fortrip's MCP tools:

    bash
    npx skills add FortripEngineering/fortrip --skill fortrip-cli

Layout

PathWhat it isHow it's published
packages/contractThe contract of Fortrip API v1: limits, paths, request and response schemas, error codes, field rules, the specs of the MCP tools and CLI commands, and the agent guide, written once and rendered for the MCP server, the Claude skill and the CLInpm: @fortrip/agent-contract, from a v* tag on main, together with fortrip
packages/cliThe fortrip CLI: hotel search, live offers, orders and payment links from a shell, the local MCP bridge (fortrip mcp) and the agent guide (fortrip guide)npm: fortrip, from a v* tag on main, together with @fortrip/agent-contract
plugins/claudeClaude plugin (hosted MCP server + static skill, generated)Submitted in the developer portal; .claude-plugin/marketplace.json at the root lets Claude Code add this repo as a marketplace
plugins/openaiChatGPT / Codex pluginZip uploaded in the OpenAI portal
plugins/registryMCP Registry entryThe Publish Fortrip to MCP Registry workflow
skills/fortrip-cliSkill for agents with a shell (generated)npx skills add FortripEngineering/fortrip --skill fortrip-cli reads it from main
scriptsRepo-wide scripts: generate.ts renders the skills, validate.mjs checks the listings

Development

Node.js 22 or newer and pnpm 11.

bash
pnpm installpnpm test             # tests of every package, and of the scriptspnpm generate         # renders the skills from the contractpnpm generate --check # fails with a diff when a generated file is stalepnpm build && pnpm validate   # checks the store listings

pnpm typecheck and pnpm build run for every package too. CI runs typecheck, tests and the build on Node.js 22 and 24, and on 24 also pnpm generate --check, pnpm validate and claude plugin validate --strict on ., plugins/claude and skills.

Generated files

The skills (SKILL.md and references/planning.md in plugins/claude/skills/fortrip and skills/fortrip-cli) and the contract's src/guide-data.generated.ts are generated. Never edit them: change the guide's fragments in packages/contract/guide/*.md (or a skill's front matter in scripts/generate.ts), run pnpm generate and commit the result. The fragments say which surface states what, and the Claude skill, the MCP instructions and the CLI's guide all render from them, so they cannot drift apart.

Publishing

Claude plugin

plugins/claude is a plugin with two parts:

  • .mcp.json points at the hosted MCP server. On claude.ai and in Cowork it shows up on the plugin's Connectors tab, where users sign in with their Fortrip account (OAuth); Claude Code connects to it directly.
  • skills/fortrip/ is the generated skill that tells Claude how to use the tools: call order, occupancy per room, prices and currency, confirming before the payment link, and errors. references/planning.md covers flexible dates, budgets, split stays and several destinations.

The skill is self-contained: the directory doesn't allow a plugin to fetch instructions at runtime (Software Directory Policy §2F), and its validator blocks unpinned npx launchers, so the CLI skill stays outside plugins/claude. pnpm validate fails if a file under it mentions npx, fortrip@latest or get_planning_guide.

Submit the plugins/claude folder in the developer portal. Check it with:

bash
claude plugin validate --strict plugins/claudeclaude plugin validate --strict .

The second command checks .claude-plugin/marketplace.json at the repository root. It lists plugins/claude as a plugin, which lets npx skills add find the plugin's skill next to skills/fortrip-cli, and lets Claude Code add this repository as a marketplace.

Claude Code keeps users on the version in plugin.json, so bump it whenever anything in plugins/claude changes, regenerated skill text included: patch for wording, minor for new behaviour or components, major for changes that break how people use it. The directory takes the listing icon from the first submission only.

CLI skill

skills/fortrip-cli is the CLI's guide as a static skill: the agent reads the commands, call order, sign-in and what to show the user before a payment link from the skill itself. It needs Node.js 22 or newer and isn't part of the Claude directory plugin.

bash
npx skills add FortripEngineering/fortrip --skill fortrip-cli

OpenAI plugin

plugins/openai/plugin.json holds the listing copy and review test cases, and mcp.json the hosted server URL. mcp.json follows the Agent Plugins schema (type: streamable-http), while plugins/claude/.mcp.json follows Claude's (type: http); both point at the same server. Build the upload:

bash
cd plugins/openai && rm -f ../../fortrip-openai.zip && zip -rX ../../fortrip-openai.zip plugin.json mcp.json assets

Upload the zip in the OpenAI portal. The portal verifies the domain through https://mcp.fortrip.ai/.well-known/openai-apps-challenge, served by mcp.fortrip.ai.

MCP Registry

plugins/registry/server.json lists Fortrip as io.github.FortripEngineering/fortrip, with two ways to connect: the hosted server (remotes) and the npm package fortrip, run as fortrip mcp (packages; the mcpName in packages/cli/package.json must equal the entry's name). The entry is published by its own workflow, Publish Fortrip to MCP Registry (.github/workflows/publish-mcp-registry.yml), which runs only from main and waits for a human to approve the mcp-registry environment. To publish a change, bump version (a version can't go down), set packages[].version to the CLI version on npm, merge to main and run the workflow. After a CLI release, run it once npm has the new version.

To deprecate an old version, or make it active again, run Set an MCP Registry version's status (.github/workflows/mcp-registry-status.yml) with the version, the status and, when deprecating, a message (the publisher takes none for active). It has the same main-only, approval-gated setup. A personal mcp-publisher login github can't do this: it only gets io.github.<user>/*, so the org namespace comes through this repository's OIDC identity.

The Registry binds the server URL to one name, so a rename means deleting the old entry before publishing the new one.

Releasing

fortrip and @fortrip/agent-contract are released together, at the same version.

  1. In a PR, set version to the new X.Y.Z in both packages/cli/package.json and packages/contract/package.json (npm version X.Y.Z --no-git-tag-version in each package's directory), set packages[].version in plugins/registry/server.json to it too, and run pnpm generate and commit what it changes. Merge the PR into main once CI passes.

  2. Tag the merge commit on main and push the tag:

    bash
    git checkout main && git pullgit tag vX.Y.Z && git push origin vX.Y.Z
  3. A human approves the npm-publish environment. The workflow publish.yml then publishes both packages to npm through trusted publishing (npm's OIDC sign-in from GitHub Actions, so no npm token is stored in GitHub), which gives them provenance.

  4. Once npm has the new version, run the MCP Registry workflow (above).

The workflow refuses a tag that isn't vX.Y.Z or vX.Y.Z-rc.N, that differs from the version in either package.json, or that isn't on main. Its build job runs typecheck, tests and the build and packs both tarballs; its publish job, in the npm-publish environment, publishes those tarballs (@fortrip/agent-contract first) without installing or building anything. A release candidate, tagged vX.Y.Z-rc.N, is published under the next dist-tag, so npm install doesn't pick it up. Rerunning the publish job after a partial publish skips what is already on npm.

Docs

packages/docs is the Fumadocs site behind docs.fortrip.ai.

bash
pnpm --filter ./packages/docs dev   # local dev serverpnpm --filter ./packages/docs test  # its tests

Production docs follow the live server, not main. A docs deploy is a docs-* tag, and the order is:

  1. Tag vX.Y.Z (npm, see Releasing above).
  2. Ship the server release that pins that contract to production.
  3. Push the tag docs-X.Y.Z.
  4. A human approves the docs-production environment. The workflow deploy-docs.yml then builds and deploys to Vercel.

The workflow refuses to deploy unless all of these hold at the tagged commit:

  • the contract's version is a plain X.Y.Z, not an rc;
  • packages/contract/src is unchanged since the tag vX.Y.Z of that version;
  • the changelog has the version's entry, dated, and no entry says "Not released yet.".

The tag name itself is free-form. So once main has moved on from the live release (contract changes, or a new undated changelog entry), a docs deploy can't go out from main: cut a short branch from the live v* tag, make the docs-only fix there, tag docs-X.Y.Z-<n> on that branch and push the tag, and also merge the fix to main. A docs-only fix needs no npm release and no changelog entry.

Before tagging a stable release, replace the version's "Not released yet." with "Released YYYY-MM-DD." in packages/docs/content/docs/changelog.mdx, or the changelog gate fails.

One-time setup, by the owner:

  • A Vercel project with Root Directory packages/docs, framework Next.js, "Include files outside the root directory" left on (the build reads packages/contract and packages/cli/package.json) and preview deployments on. packages/docs/vercel.json turns off Git deployments of main: production would otherwise follow main and skip the approval. Other branches still get previews. On the first real run, confirm a push to main creates no production deployment. Importing the project deploys main once anyway; the first docs-* deploy replaces it.
  • The docs.fortrip.ai domain on that project, and Vercel Analytics enabled.
  • The GitHub environment docs-production, with the same required reviewers as npm-publish ("Prevent self-review" off, as there, so whoever pushes the tag can approve), deployment branches and tags limited to the tag pattern docs-*, and the secrets VERCEL_TOKEN, VERCEL_ORG_ID and VERCEL_PROJECT_ID.
  • On the first deploy, confirm vercel build succeeds without VERCEL_TOKEN: the workflow gives the token only to vercel pull and vercel deploy, since the build runs the dependencies' code.

License

Apache License 2.0. Copyright 2026 FortripAI, Inc. (see NOTICE). Releases before 2.0.0 were published under the MIT License. This license does not grant access to the hosted Fortrip service, hotel inventory, or third-party data.

Trademarks

"Fortrip" and the Fortrip logo are trademarks of FortripAI, Inc. The Apache License 2.0 does not grant permission to use the trade names, trademarks, service marks or product names of FortripAI, Inc., except as required for reasonable and customary use in describing the origin of the software and reproducing the content of the NOTICE file (section 6).

来源:README.md,提交 b6aeab9

工具

9

get_planning_guide

Planning guide

Returns Fortrip's planning guide for a request that is more than one hotel for fixed dates (flexible dates, a budget, split stays, several destinations, "find me the best"): what to ask the user, which results count as evidence, how to rank and how to present options.

无需输入参数。

search_hotels

Search hotels

Find hotels by destination and/or text, with an indicative per-room-night price when a stay is given. destination is a place_name with its countryCode, a place or locality by placeId from an earlier answer, a point, or a country. A name several places bear answers destination_ambiguous with candidates: ask the user, then search the chosen placeId. text matches hotel names, brands and areas word for word. Indicative prices are estimates and not bookable: get_hotel_offers returns what the user pays. A search too broad to answer is refused with search_too_broad and its localities: pass one back as { kind: "locality", placeId }, never by name. The answer states the scope, the counts and notes; a next page is the cursor and, optionally, limit.

输入参数sortstaytextlimitcursorlocalefilterspricingdestinationdisplayCurrency

get_hotel

Hotel details

One hotel's catalogue facts: names, address, location, time zone, check-in and check-out times, the full description, every policy and the amenities, and its rooms with include: "rooms". "Not listed" is not "no". No prices or availability: get_hotel_offers has those.

输入参数localehotelId必填include

get_hotel_offers

Live offers

Live, bookable offers for up to 15 hotels and one stay. Every room in stay.rooms must have the same party: for different parties, call once per party. Each offer has the room, the meals included, price.total in USD for the whole stay and every room (what is charged), fees payable at the property, the cancellation terms with times in UTC and the hotel's time zone, the confirmation mode, and an offerId for book_hotel that expires at expiresAt. Hotels with nothing to offer are listed under unavailable with the reason. Up to limitPerHotel offers per hotel, default 10, chosen for a spread of choices in this order while room remains: the cheapest offer, the cheapest that can be cancelled free, the cheapest with breakfast, the cheapest of each other room, then the rest by price; listed cheapest first.

输入参数stay必填localefiltershotelIds必填limitPerHoteldisplayCurrency

book_hotel

Create order

Order one offer from get_hotel_offers. The answer is an unpaid order with a payment link: status awaiting_payment, the total, the stay and its terms, and payment.url, a Fortrip page to pay on. Nothing is charged until the order is paid, on that page or with pay_order (or Alipay/WeChat Pay); anyone holding the link can pay. Send the contact and, for each room of the offer, at least one guest. If the price or terms changed since the offer, the answer is offer_changed with every change and a new offer: show the changes and ask the user again before ordering it. Sending the same details again returns the same order. An offer is ordered once: for another order of the stay (another party, or a corrected guest name), get offers again and order the new offer; when it replaces an unpaid order, tell the user to pay only the new link, as the earlier one expires unpaid. To cancel an order, use cancel_order; for changes, or a cancellation cancel_order can't make, the user emails [email protected] with the order's reference.

输入参数items必填contact必填

get_order

Get order

One of the user's orders by orderId: its status, the payment status and, while it awaits payment, payment.url; the stay and its terms; and the hotel's confirmation number once there is one. Check it after the user pays. An order awaiting payment whose payment.url is null had its link unreadable this time: read it again.

输入参数localeorderId必填

list_orders

List orders

The user's hotel orders, newest first, including those made on the Fortrip website. Pass nextCursor back as cursor for the next page.

输入参数limitcursorlocale

cancel_order

Cancel order

Cancel one of the user's orders by orderId under its cancellation terms, when its cancel.available is true: the hotel booking is cancelled and cancel.refund, what was paid less cancel.penalty, is refunded. Before you call cancel_order, show the user the order's stay (hotel, dates), cancel.penalty and cancel.refund (amount and currency), then end your reply by asking them to confirm the cancellation, and wait for a clear yes. Pass maximumPenalty as the number in cancel.penalty.amount ("120.00" → 120). Never state a refund the order did not return. If the penalty is now higher than maximumPenalty, the answer is cancellation_changed and nothing changes: read the order again and ask the user again. Answers the order: status cancelling, then cancelled once the hotel cancels and the refund is issued; poll get_order. If the hotel refuses, the order goes back to confirmed with cancel.reason refused. Calling it again for the same order answers the order as it is.

输入参数orderId必填maximumPenalty必填

pay_order

Pay order

Pay an order without its link, by shared payment token or Alipay/WeChat Pay (method alipay_wechat_pay). Token: only if payment.handlers isn't empty and you can request one from the user's Link wallet (US and Canadian accounts); never ask the user for one. First show the user the USD total, the stay and every term (meals, cancellation, fees payable at the property), ask them to confirm and wait for a clear yes. Token: request it with config.business_profile and payment.credentialLimits; pass it with price.total. Wallet: pass method, price.total; show payment.wallets (WeChat code as an image if you can, both links); they pay once by either; a code lasts 120 minutes; then the WeChat link opens a new one, or call again. Pay one way only: payment.url or pay_order. On success payment.url stops working; on failure it stays open. Then poll get_order. payment_total_changed: confirm details.total, retry. payment_credential_invalid: new token. service_unavailable: retry. Else payment.url. A token pays one order only.

输入参数total必填methodorderId必填paymentsharedPaymentToken

版本历史

10
  1. v1.4.13最新Nov 6, 2026
  2. v1.4.12Nov 5, 2026
  3. v1.4.10Nov 4, 2026
  4. v1.4.8Nov 4, 2026
  5. v1.4.7Nov 4, 2026
  6. v1.4.4Nov 3, 2026
  7. v1.4.2Oct 29, 2026
  8. v1.4.0Oct 26, 2026
  9. v1.3.0Oct 25, 2026
  10. v1.0.3Oct 21, 2026

评分

已满足适用检查项的 100%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通必需25 分
  • 已声明认证方式必需10 分
  • 已列出工具15 分
  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分