mcpnavi.jpは、MCPサービスをまとめた日本語のディレクトリサイトだ。MCPとは、ChatGPTやClaudeのようなAIエージェントを外部のデータやツールにつなぐ仕組みのことだ。私たちは2026年9月25日にmcpnavi.jpを公開した。MCPサービスの運営者から、掲載してほしいという依頼が来ることがある。これまで、依頼の窓口はmailto:リンクだけだった。

メールでも依頼は届く。しかし、受付の窓口としては使いにくい。送信者は必要な情報を書き忘れるし、届いたメールは他のメールに埋もれる。決まった項目を入力してもらうフォームがほしかった。

問題は、mcpnavi.jpが静的サイトであることだ。Djangoで全ページをファイルとして生成し、GitHub Pagesで配信している。POSTを受け取るサーバーはない。だから、フォームのバックエンドは他のサービスに任せる必要がある。

選んだのはFiraFormだ。クアラルンプールのTaming Techが作っていて、運営者は私の友人のIszuddin Ismailだ。彼については最後にもう少し書く。これは利害関係の開示であって、選んだ理由ではない。役に立たないフォームを公開サイトに置くつもりはない。

FiraFormとは

FiraFormはフォームバックエンドだ。自分のHTMLでフォームを作り、actionをFiraFormのURLに向ける。

<form action="https://a.firaform.com/api/f/<form-id>" method="post">
  <input type="text" name="service_name" required>
  <input type="email" name="email" required>
  <button type="submit">Send</button>
</form>

送信内容はFiraFormが保存する。メール通知やWebhookの呼び出しもでき、送信後は指定したサンクスページにブラウザを移動させる。サーバー側のコードは一行も書かなくてよい。

機能一覧には、AIスパム検出(仕組みは後述)、ドメインホワイトリスト、CSVエクスポート、送信内容のAPI、決済などが並ぶ。無料プランがあり、有料プランは月5ドルからだ。

フォームとフィールドの設定

ダッシュボードでは、各フィールドに型を設定できる。Text、Email、URL、Number、Checkboxなどがそろっている。型があるので、FiraFormは送信を受け付ける前に値をチェックできる。掲載依頼では、これが大事だ。サービスの公式URLはURLでなければならないし、連絡先はメールアドレスでなければならない。

私たちの掲載依頼フォームは19項目あり、3つのセクションに分かれている。サービスの情報、提供者の情報、依頼を送る人の情報だ。料金や会社情報などは、値そのものではなく、その値が載っている公開ページのURLを聞く。そのページは、mcpnaviの担当者が読んで確認する。

Honeypotフィールド

FiraFormにはHoneypotというフィールド型がある。HTMLに人間には見えない入力欄を置いておく。ボットはすべての欄を埋めるので、この欄にも値を入れる。この欄に値が入った送信を、FiraFormは破棄する。

例えば、この欄にfax_numberというよくありそうなパラメーター名をつけたとする。ボットが喜んで埋めそうな名前だ。

<input type="text" name="fax_number" value=""
       style="display:none" tabindex="-1" autocomplete="off" aria-hidden="true">

知っておきたい点が2つある。

  • type="hidden"は使わない。hidden型の入力欄を飛ばすボットは多い。CSSで隠したtext型のほうが、多くのボットを捕まえられる。tabindex="-1"でキーボード操作の移動先から外し、aria-hidden="true"でスクリーンリーダーにも読み上げさせない。
  • 破棄は通知なしで行われる。ボットもサンクスページに到達するので、弾かれたことに気づかない。ただし、こちらからは見分けられる。FiraFormは送信を保存したときだけ、サンクスページのURLにsubmission_idを付ける。

Seleniumを使い、実際のブラウザで本番サイトをテストした。人が入力した送信にはsubmission_idが付いた。同じフォームで、スクリプトからfax_numberを埋めた送信には付かなかった。期待どおりの動きだ。

AIの出番はスパム判定

Honeypotが捕まえられるのは、すべての欄を埋めるボットだけだ。フォームを正しく埋めて広告を送ってくる人間や、もっと賢いスクリプトは止められない。そこで、FiraFormはAIを使う。

機能一覧に「AIスパム検出」とあっても、それだけでは何をしているのかわからない。そこで、Iszuddinに仕組みを聞いた。返ってきた答えは「大したことはしていない」だった。AIの仕事はスパム判定の1つだけだ。

  1. FiraFormは、送信内容から個人情報を取り除く。
  2. 残りの内容を、Cloudflare Workers AI上のモデルに送る。
  3. モデルがスパムと判断した送信は、ダッシュボードの「Submissions」タブの隣にある「Spam」タブに移される。

この判定はキューを通して行うので、フォームの送信処理を止めない。Iszuddinによると、送信を速く保つことに力を入れているという。訪問者は、モデルの応答を待たずにサンクスページを見られる。

ここがHoneypotとの違いだ。Honeypotに引っかかった送信は破棄され、保存されない。一方、モデルがスパムと判断した送信は、別のタブに移るだけだ。モデルが判断を誤っても、その送信はSpamタブで確認できる。

私はこの作りが気に入っている。理由は3つある。

  • AIの仕事が絞られている。 送信を2つのタブに振り分けるだけだ。文章を書いたり、内容を変えたり、返信したりはしない。
  • モデルに渡す前に個人情報を取り除いている。 問い合わせフォームには名前やメールアドレスが集まり、その管理責任はサイト運営者にある。日本では今月、個人情報の漏えいが相次いでいる。ユーザーのデータを受け取るサービスが1つ増えるたびに、漏えいの可能性がある場所も1つ増える。モデルに渡す個人データを減らす設計は、最初からそうあるべきものだ。
  • 訪問者に負担がかからない。 判定はキューで後から行われる。だから、送信の速さはAIを使わないフォームと変わらない。

2言語なら2つのフォーム

mcpnavi.jpは、日本語版が/、英語版が/en/にある。フォームも両方の言語で用意している。最初は、1つのFiraFormフォームで両方を受けるつもりだった。しかし、これは2つの理由でうまくいかない。

  1. 選択肢はラベルのまま送られる。 カテゴリのプルダウンは、日本語ページでは「開発ツール」、英語ページでは「Developer tools」を送る。1つのフォームに混ざると、データが読みにくくなる。
  2. 1つのフォームに設定できるサンクスページは1つだけだ。 日本語のサンクスページと英語のサンクスページは別のページだ。FiraFormは送信に含まれる_nextフィールドを無視し、常にフォームの設定画面で指定したURLを使う。

そのため、同じフィールドを持つフォームが2つ必要になった。最初は、ダッシュボードで日本語版を作ってからコピーしようと考えた。しかし、コピー機能はない。 Iszuddinに聞くと、コピーボタンよりも良い答えが返ってきた。

いちばん良かった点 ―HTMLがフォームを定義する―

FiraFormでは、フィールドを先に定義しなくてよい。空のフォームを作り、自分のHTMLから1件送信する。すると、FiraFormは届いた入力欄ごとにフィールドを作る。私たちのフォームはリポジトリ内のJSONファイルで管理していて、Djangoがそれを両言語でレンダリングしている。各言語のページから1件ずつテスト送信しただけで、FiraFormのフォームが2つできた。フィールド名も両方で一致した。

これには感心した。私は、ダッシュボードが正で、HTMLをそれに合わせるものだと思い込んでいた。2言語分のダッシュボードを手作業でそろえ続けるのは面倒で、間違いも起きやすい。実際はHTMLが正で、FiraFormがそれに合わせる。開発者の仕事の進め方に合っている。フォームはバージョン管理の中にあり、バックエンドがそれに合わせてくれる。

注意点が2つある。

  • 初回の送信では、すべての欄に値を入れる。 FiraFormがフィールドを作るのは、値が入って届いた入力欄だけだ。初回に空のまま送った欄のフィールドは作られない。
  • 自動で作られたフィールドは、すべてText型になる。 正しい型には、後からダッシュボードで変更する。

次にほしい機能

2つ目の点が、私からの唯一の要望だ。HTMLには、各入力欄の型がすでに書いてある。type="email"もtype="url"もマークアップの中にある。FiraFormが初回の送信でtype属性を読み取ってくれれば、追加の作業なしでフォームが完成する。せめて、EmailとURLだけでも認識してほしい。今は1項目ずつ手作業で型を変えている。しかも、言語ごとに1回ずつ、合計2回だ。

時間を取られた設定

どれも欠陥ではない。しかし、どれにも時間を取られた。始める前に知っておくとよい。

  • Referrer-Policyヘッダーに注意する。 本番での初回テストは「Unauthorized domain」というエラーで失敗し、リクエストのoriginは"null"になっていた。DjangoのReferrer-Policyの初期値はsame-originだ。この設定では、ブラウザが他のサイトへのPOSTでOrigin: nullを送る。そのため、FiraFormのドメインホワイトリストが照合できない。ブラウザ自体の初期値であるSECURE_REFERRER_POLICY = "strict-origin-when-cross-origin"に変えると解決した。GitHub Pagesはこのヘッダーを送らないので、本番環境では問題がなかった。原因は開発サーバーだった。
  • ホワイトリストには127.0.0.1ではなくlocalhostを登録する。 開発サーバーからテストするときは、localhostを登録し、http://localhost:<ポート>/でサイトを開く。ポート番号は何でもよい。
  • Textフィールドの「Allowed Characters」を確認する。 最初に作った英語版フォームは、送信を拒否した。一部のTextフィールドで、この設定のチェックが入っていたからだ。この設定は、.、@、/、括弧などの記号をブロックする。どれも、サービス名、URLに似た概要、インストールコマンドには含まれる文字だ。必要がなければ、チェックを外しておく。

コミュニティについて

FiraFormの作り手はIszuddin Ismail氏だ(以下「Iszuddin」)。彼の会社のTaming Techは、マレーシアと東南アジアで、企業や個人向けのLaravel研修も行っている。2024年8月には、Taming Techがクアラルンプールの研修センターで私が話したミートアップの会場を提供してくれた。

マレーシアの開発者の多くは、彼を何よりもJOMLAUNCHの人として知っている。彼はJOMLAUNCHを立ち上げ、2015年から2022年まで運営した。JOMLAUNCHは、JOMWEBのデモデイとして始まった。JOMWEBは、マレーシアのプログラマー・開発者・システム管理者が集まるFacebookグループだ。Vulcan Postによる2015年10月の開催レポートによると、商用・個人・オープンソース・コミュニティの4部門で、17のプロジェクトが発表された。当時、グループのメンバーは11,000人いた。

考え方は単純だ。メンバーが何かを作り、互いに見せ合う。JOMLAUNCHは、年月とともに2つの方向に広がった。1つはJOMLAUNCH ASIAで、ブートストラッパー、インディーハッカー、ソロ開発者のためのカンファレンスだ。いずれも、自己資金でインターネット上のソフトウェア事業を作る人たちを指す。もう1つはJomLaunch Youthだ。Malaysia Techlympicsは、マレーシア科学技術革新省(MOSTI)のプログラムだ。2022年、JomWebはそのコーポレートパートナーとして、JomLaunch Youthを開催した。IoT、モバイルアプリ、Webアプリの無料トラックを用意した。MOSTIのプレスリリース(科学技術・イノベーション省、マレー語)によると、50以上の大学・専門学校から、18歳から30歳の参加者が1,000人以上集まった。閉会式は2022年10月22日に、クアラルンプールのWorld Trade Centreで行われた。同じ会場では10月19日から22日までJOMLAUNCH 2022が開かれ、私たちLaLoka Labsもブースを出し、講演を行った。

PHPの分野では、Iszuddinは『24 Jam Laravel』を書いている。本人によれば、マレー語で書かれた初の本格的なLaravel入門書だ。この本で、マレーシアの開発者は母語でLaravelを学べるようになった。

こうした経歴は、FiraFormの作り方にも表れていると思う。FiraFormは、1つの仕事だけをする小さな道具だ。作ったのは、毎日PHPを書き、自分のプロダクトを作って公開する人たちの中で長年過ごしてきた人だ。FiraFormはダッシュボードからではなく、開発者のHTMLから始まる。開発者なら、そう設計するはずだ。

まとめ

FiraFormのおかげで、mcpnavi.jpはmailto:リンクから、入力チェックとスパム対策のあるフォームに変わった。日本語と英語の2言語に対応し、自前のサーバーは使っていない。かかった時間は半日だ。

Honeypotは単純なボットを捕まえ、AIフィルタはそれをすり抜けたスパムを別のタブに移す。サンクスページへの移動も正しく動き、送信内容は見やすい表で届く。

いちばん良かったのは、自分のHTMLからフォームが作られる点だ。おかげで、多言語対応の作業が言語ごとに1件のテスト送信で済んだ。同じ送信で入力欄の型も読み取ってくれれば、設定は1回で終わる。

静的サイトを運営していて、サーバーを持たずにフォームを置きたいなら、FiraFormを試してみてほしい。私たちのフォームはこちらです。