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 分