Partelisto について
Partelistoは、スペインのバケーションレンタル運営者向けに、予約、ゲストのチェックイン、SES.HOSPEDAJESの状況、利用量を扱うリモートMCPサーバーです。権限を分けた読み取りと書き込みに対応し、ゲストの個人情報はツールの結果に含めません。
主な機能
- 01物件と予約の一覧表示
- 02ゲストフォームとSES.HOSPEDAJESの状況確認
- 03利用量と要対応項目の確認
- 04チェックインリンクの再送
- 05書き込み権限による予約作成
- 06ゲストの個人情報を除外
活用シーン
- 01フォーム未完了の到着予定者を特定
- 02SES.HOSPEDAJESの送信失敗を確認
- 03アカウント利用量とプラン上限を確認
- 04予約作成またはチェックインリンク再送
MCP 対応
HTTPとKeycloak OAuthのBearerトークンを使って、MCPクライアントをPartelistoに接続します。読み取りツールに加え、別途許可が必要な予約作成とチェックインリンクの変更操作を提供します。
設定
リモートサーバーはインストール不要です。クライアントを選んで設定をコピーするか、ワンクリックで導入できます。
https://mcp.partelisto.es/mcpclaude mcp add --transport http targetgrps-partelisto https://mcp.partelisto.es/mcp{"mcpServers":{"targetgrps-partelisto":{"type":"http","url":"https://mcp.partelisto.es/mcp"}}}Cursor で開く{"servers":{"targetgrps-partelisto":{"type":"http","url":"https://mcp.partelisto.es/mcp"}}}VS Code で開く{"mcpServers":{"targetgrps-partelisto":{"type":"http","url":"https://mcp.partelisto.es/mcp"}}}{"mcpServers":{"targetgrps-partelisto":{"serverUrl":"https://mcp.partelisto.es/mcp"}}}コマンドはターミナルで実行し、JSON は各クライアントの MCP 設定に貼り付けて再起動します。認可が必要なサーバーは初回接続時にログインを求めます。
ツール
8| パラメーター | 型 | 必須 | 説明 |
|---|---|---|---|
| checkIn | string | はい | チェックイン日、yyyy-MM-dd。 |
| checkOut | string | はい | チェックアウト日、yyyy-MM-dd。 |
| guestName | string|null | いいえ | ゲスト名、省略可。 |
| guestEmail | string|null | いいえ | ゲストのメールアドレス、省略可。 |
| guestPhone | string|null | いいえ | ゲストの電話番号、省略可。 |
| propertyId | string | はい | list_properties から取得した物件 ID。 |
| templateId | string|null | いいえ | チェックインテンプレート ID。物件に有効な(アーカイブされていない)テンプレートがちょうど 1 つある場合は省略でき、自動的に選択されます。それ以外の場合は必須です。 |
詳細
Partelisto MCP
スペインのバケーションレンタル向けSES.HOSPEDAJESおよびゲストのチェックイン機能を、AIエージェントに提供します。
サインイン済みホストのスペイン国内の宿泊施設について、Claude、ChatGPT、またはMCP互換のAIエージェントが運用上の質問に回答できるリモートMCPサーバーです。未入力のゲストフォームが残っている到着予定、SES.HOSPEDAJES(警察への登録)の送信に失敗した予約、アカウントのプラン上限までの残り状況などを確認できます。また、別途権限を付与すると、ゲストのチェックインリンクを再送できます。このサーバー独自のビジネスロジックは追加しません。すべてのツールは、api-gatewayにすでに存在するクエリまたはミューテーションを薄くラップし、Webアプリとまったく同じ方法でアクセスを制御します。
ツール: list_properties · list_bookings · get_guest_form_status · list_ses_statuses ·
get_usage_summary · get_attention_required · send_guest_checkin_link · create_booking(最後の2つには追加のpartelisto:writeスコープが必要です。それぞれがラップする内容については、下記のツールの表をご覧ください)。
プロンプトの例: 「今日、対応が必要なことは何ですか?」 · 「今日到着予定で、ゲストフォームが未入力のものはどれですか?」 · 「SES.HOSPEDAJESの送信に失敗した予約を表示して。」 · 「9月12日から15日まで、ゲストAna GarcíaのCasa Solの予約を作成して。」 · 「予約Xのチェックインリンクを再送して。」
どのツールもゲストの個人識別情報(メールアドレス、電話番号、パスポート/DNI、生年月日、国籍、書類の内容)を選択または返却することはありません。下記のツール(v1)をご覧ください。
Claude / ChatGPT / Copilot
│ MCP over HTTP, Bearer token from Keycloak OAuth
▼
partelisto-mcp (this service)
│ same GraphQL call the SPA would make, same Bearer token forwarded as-is
▼
api-gateway → backoffice / booking / guestdocs
一般的なサービス構成テンプレートと異なる理由
TargetGrpsの他のサービスはすべてデータを所有しています(MongoDB、テナンシーミドルウェア、ApiServiceBootstrapper)。このサービスはデータを所有しません。Domainレイヤーもデータベースもありません。ゲートウェイの同等サービスではなく、ゲートウェイのクライアントです。プロジェクトの構成では、テンプレートが存在するのと同じテスト容易性を保つため、Application(DTO、固定のGraphQLドキュメント、純粋なレスポンス整形/秘匿化ロジック)とInfrastructure(ゲートウェイHTTPクライアント)を維持していますが、初期化するものがないため、Mongo/マルチテナンシーの初期化処理は省いています。
ツール(v1)
| ツール | スコープ | ラップするもの |
|---|---|---|
list_properties | partelisto:read | properties |
list_bookings | partelisto:read | bookingsPage(skip/takeのみ。現時点ではfilterなし。下記参照) |
get_guest_form_status | partelisto:read | submissionStatus |
list_ses_statuses | partelisto:read | sesSubmissionStatuses |
get_usage_summary | partelisto:read | partelistoUsageInfo |
get_attention_required | partelisto:read | bookingsPage + sesSubmissionStatusesをクライアント側で合成します。新しいクエリはありません。直近50件の予約を調べ、ゲストフォームが未入力の直近/現在の宿泊に加え、SESの送信に失敗した予約も対象にします。 |
send_guest_checkin_link | partelisto:write | sendGuestLinkミューテーション。ゲストにメールを送信し、リンクを更新します |
create_booking | partelisto:write | createBookingミューテーション。物件に有効なテンプレートが1つだけある場合は、templates(propertyId)を使用してtemplateIdを自動解決します。それ以外の場合は、呼び出し元にテンプレートの選択を求めます。チェックインリンクは送信しません。送信するには、別途send_guest_checkin_linkを呼び出してください。 |
これらのツールは、ゲストのメールアドレス、電話番号、パスポート/DNI、生年月日、国籍、書類の内容を選択または返却することはありません。選択される項目はGatewayQueries、DTOにマッピングされる項目はResponseShaperをご覧ください。二重の防御策として、クエリが個人識別情報と思われるフィールドを選択するよう変更された場合、GatewayQueriesTestsによってビルドが失敗します。
list_bookingsでは、現時点でBookingsQuery.BookingFilter(propertyId/日付範囲/ステータス)を公開していません。GraphQLの入力型名はHotChocolateのミューテーション規約によって生成されるため、推測で追加する価値がないと判断しました。ゲートウェイスキーマを直接イントロスペクションできるようになったら追加してください。
認可 — 独立した2つのレイヤー
- スコープ。このサービスの
PartelistoTools.RequireScopeで確認します。ベアラートークンの JWTscopeクレームには、5つの読み取りツールでpartelisto:readが含まれている必要があり、さらにsend_guest_checkin_linkではpartelisto:writeも必要です。トークンはProgram.csの標準的なAddJwtBearerハンドラーによって Keycloak に照らして検証されます(署名、発行者、有効期限)。このサービスは実際に JWT を検証し、未検証のクレームを信頼することはありません。これにより、OAuth の同意画面で「自分のデータを読む」と「自分に代わってメールを送信する」を分けて提示できます。 - 所有権/テナント。Web アプリと同様に、すべての呼び出しで api-gateway が適用します。生のベアラートークンが変更されずに転送され、gateway の
OwnerAccessポリシーによって、そのユーザーが閲覧できるデータが判断されます。有効なpartelisto:writeスコープがあっても、それだけで特定の予約へのアクセス権が付与されるわけではありません。gateway は呼び出し元がその予約を所有しているかどうかも引き続き確認します。
RFC 9728 の保護リソースメタデータは /.well-known/oauth-protected-resource で公開されます。authorization_servers は Keycloak のレルムを指し、両方のスコープが記載されているため、仕様に準拠した MCP クライアントは、人がトークンを貼り付けなくても、トークンの取得方法を検出できます。
Keycloak のセットアップ(完了)
partelisto レルムには、クライアント partelisto-mcp(UUID f5a1cb7f-d6f9-474c-818a-183584dbec30)があります。これはパブリッククライアントで、standardFlowEnabled(authorization_code + PKCE)が有効、consentRequired: true、directAccessGrantsEnabled: true です。2 つのオプションのクライアントスコープ partelisto:read と partelisto:write が割り当てられ、同意画面に表示されます(どちらも display.on.consent.screen: true)。アクセス・トークンのオーディエンスマッパーが 2 つクライアントに設定されています。1 つは partelisto-mcp を追加するもので(このサービスがトークンを受け入れられるようにします)、もう 1 つは api-gateway を追加するものです(同じトークンが変更されずに転送され、ゲートウェイにも受け入れられるようにします。最初のマッパーだけではオーディエンスが拡張されず置き換えられるため、ゲートウェイへの接続が気付かないうちに壊れていました。後でここに別のオーディエンスマッパーを追加する場合は、この点に注意してください)。
登録済みのリダイレクト URI は https://claude.ai/api/mcp/auth_callback と https://chatgpt.com/connector_platform_oauth_redirect です。各クライアントが実際に接続された時点で、Claude Code のローカルコールバックなどを追加してください。クライアントの OAuth フローを完了するには、Keycloak に正確な URI を登録しておく必要があります。
さらに 2 つの修正が必要でした。実際にサインイン済みの Claude.ai セッション(curl ではなくブラウザー)と、実際の(e2e ではない)Partelisto アカウントを使ったテストで初めて判明しました。
partelisto-mcpクライアントでfullScopeAllowedがfalseでした(Admin API 経由で作成されたクライアントに対する Keycloak のデフォルト設定です)。無効になっていると、ユーザーが実際にどのロールを持っているかに関係なく、発行されたトークンのrealm_access.rolesにはoffline_accessしか含まれませんでした。trueに設定して修正しました(partelisto-spaではすでにtrueで、このクライアントも同じ設定になりました)。partelisto-spaではクライアントに直接設定されているoidc-usermodel-realm-role-mapperプロトコルマッパー(名前「realm roles」、クレーム名roles)がありませんでした。TargetGrps.BuildingBlocks.Bootstrapperの JWT 設定ではRoleClaimType = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"が設定されますが、realm_access.rolesがそのクレームタイプにマッピングされることはありません(インストール済み NuGet パッケージを逆コンパイルして確認したところ、OnTokenValidatedハンドラーはクレームをログに記録するだけです)。そのため、ユーザーがどのロールを持っているか、またrealm_accessに何が含まれているかにかかわらず、このマッパーがないクライアントではOwnerAccessなどのRequireRole(...)ポリシーが失敗します。partelisto-spaのマッパー設定をそのままpartelisto-mcpにコピーしました。
実際にサインイン済みの Claude.ai セッションを使って、curl だけではなく、エンドツーエンドで検証しました。カスタムコネクターを追加し、ブラウザーでの OAuth フローと同意フローを最後まで完了しました(両方のスコープが個別に表示されて許可され、2 スコープの設計が正しく表示されることを確認)。list_properties は上記 2 つのバグによって 2 回失敗しましたが、どちらも実環境で修正し、再接続したところ、ゲートウェイ → バックオフィス → GraphQL → このサービス → Claude の全経路を通じて、バックオフィスから実際の回答を受け取れました。これは現在、プロジェクト全体で最も十分に検証された経路です。未検証なのは ChatGPT との接続だけです。
デプロイ済み
https://mcp.partelisto.es で稼働中です。k8s/deployment.yaml を直接適用しています(Helm は使用していません。理由はこのファイルのヘッダーコメントを参照してください)。イメージは ghcr.io/targetgrps/partelisto-mcp、名前空間は targetgrps-microservices です。CI(.github/workflows/build-publish.yml)は main へのプッシュごとにビルド、テスト、プッシュを行います。今後リリースする際は、k8s/deployment.yaml の image: タグを更新して再適用してください。
CI は単独で完結しており、各兄弟サービスの build-publish.yml とは異なり、targetgrps/reusable-workflows は呼び出しません。このリポジトリは非公開であり、このリポジトリは意図的に公開されています(下記「公開について」を参照)。公開リポジトリから非公開リポジトリの再利用可能なワークフローを呼び出すことはできません。GitHub は、そのリポジトリのアクセスレベル設定とは無関係に、ディスパッチ時に「workflow was not found」として拒否します。また、再利用可能なワークフローのその他の機能(npm/nuget クライアントの公開、Mongo イメージ、Slack 通知)はこのサービスには該当しないため、小規模なインラインワークフローを使うのが適切であり、回避策ではありませんでした。GHCR へのログインには secrets.GITHUB_TOKEN ではなく GH_TOKEN_TARGETGRPS も必要でした。初回展開時にパッケージを個人用トークンでプッシュしたため、このリポジトリ自体の Actions ID は「Manage Actions access」リストに登録されていませんでした(これは GHCR の設定で、リモートから修正する REST API はありません)。
docker run/docker compose をローカルで実行しただけでは見つからず、実際にデプロイして初めて判明し、修正したバグが 2 件あります。
dotnet publish --no-build -o /appは、ソースツリーがすでに配置されているのと同じディレクトリに公開していたため、Content 項目(appsettings.json)が暗黙のうちに欠落していました。イメージには設定がまったくなく、起動時にKeycloak:Authority is not configuredでクラッシュしていました。公開先を/outに変更して修正しました。- TLS を終端する ingress の背後で
request.Schemeがhttpと判定され、/.well-known/oauth-protected-resourceが"resource": "http://mcp.partelisto.es"を返していました。UseForwardedHeadersを使って修正しました。
この展開の一環として、このリポジトリの外部でも問題に遭遇し、修正しました。このクラスターの cert-manager 1.18.2 では、新しい TLS 証明書を一切発行できませんでした(ingress-nginx の厳格なパス検証に関する上流のバグです。cert-manager#7791)。cert-manager 名前空間にある cert-manager Deployment の引数に --feature-gates=ACMEHTTP01IngressPathTypeExact=false を追加してパッチを適用しました。これは文書化された回避策で、1.18 より前の動作に戻すものです。この問題はクラスター全体で証明書の発行を妨げており、このサービスだけの問題ではありませんでした。
実際の HTTPS 経由で稼働を確認しました。/healthz、/.well-known/oauth-protected-resource(正しい https:// リソースと両方のスコープが一覧表示されること)、および MCP の initialize ハンドシェイクを検証済みです。
公式 MCP レジストリに登録済み
io.github.TargetGrps/partelisto-mcp は registry.modelcontextprotocol.io で稼働中かつ active です。curl "https://registry.modelcontextprotocol.io/v0.1/servers?search=partelisto-mcp" で確認できます。.github/workflows/publish-mcp-registry.yml により、server.json に変更を含む main へのプッシュごとに、GitHub Actions OIDC を通じて公開されます。ログインもトークンの保存も不要で、手動承認もありません。名前空間は組織名と大文字・小文字まで正確に一致させる必要があります(targetgrps ではなく TargetGrps)。一致しないと、レジストリの OIDC チェックで 403 が返されます。
まだ完了していないこと(手動でのフォローアップ)
- ChatGPT App ディレクトリにはまだ申請していません。 MCP レジストリとは異なり、これには OIDC/CI の手段がありません。OpenAI の申請フローは、開発者アカウントでログインして行う人手による審査プロセスです。稼働環境の
/mcpURL の確認、ドメインの確認、OAuth フロー用の審査担当者の認証情報の提供、テストケースの作成、5~10 営業日の審査への申請が必要です (申請ガイドライン)。これは、組織の OpenAI 開発者アカウントを管理している(または今後管理する)担当者が行う必要があります。アカウントへのログインや新規作成を自動化することはできません。 - ChatGPT からはまだ実際に接続されていません — リダイレクト URI は登録済みですが、Claude.ai とは異なり(上記の Keycloak セクションを参照。エンドツーエンドで検証済み)、このサーバーに対する ChatGPT のコネクター追加フローを完了した人はいません。
partelisto-mcpKeycloak クライアントに登録されているのは、Claude.ai と ChatGPT のリダイレクト URI だけです。ほかのクライアント(Copilot など)が実際に接続されたら、追加してください。
ローカル開発
dotnet build # builds src/*.sln
dotnet test # 8 unit tests, ResponseShaper + GatewayQueries PII guard
dotnet run --project src/TargetGrps.Partelisto.Mcp.Api -- --stdio # MCP over stdin/stdout (Glama introspection)
docker compose build targetgrps-partelisto-mcp
docker compose up -d
curl http://localhost:5207/healthz
curl http://localhost:5207/.well-known/oauth-protected-resource
docker compose down
appsettings.Development.json では、Gateway:BaseUrl が http://localhost:5201 に設定されています。これはワークスペースの Compose スタックにおける api-gateway のポートです(起動方法については、partelisto のローカル開発環境の起動に関するメモを参照してください)。一方、Docker Compose ファイルでは http://host.docker.internal:5201 が使われています。このサービスのコンテナーは、そのスタックの Docker ネットワークに接続されていないためです。
実際の MCP クライアントを介さずにツールを手動で試す
tools/list にトークンは不要です。tools/call には、スコープに partelisto:read/partelisto:write を含む、partelisto-mcp クライアント向けに発行されたアクセストークンが必要です。partelisto-spa のトークン(SPA の開発者ツールからコピーしたものなど)は使えません。RequireScope に拒否されます。このクライアントでは consentRequired: true が設定されているため、パスワードグラントではトークンが発行されません(同意画面を表示するブラウザーがないため)。Keycloak 管理コンソールでこの設定をオフにすれば発行できます。単発のローカルテストには問題ありませんが、その後は設定を元に戻してください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。