amplesscms

article

MCP 経由で記事を書く

ampless の MCP HTTP トランスポート、トークンと認可境界の考え方、いま並んでいる 14 個のツール、curl で素朴に叩く例。AI エージェントをコエンジニアとして参加させたいときの content side の入口です。

ampless には MCP (Model Context Protocol) サーバが最初から同梱されています。管理 UI、公開サイト、MCP — どれも同じ AppSync スキーマを叩いている構成で、MCP Lambda だけは IAM (SigV4) で接続するかたち。Claude / Cursor / Claude Code のような MCP 対応エージェントをサイトにコエンジニアとして同居させたい場面では、コンテンツ側の入口がこの経路になります(コード編集や npm、git のような作業側はエージェント自身のツールが受け持ちます)。

トランスポート

MCP のトランスポートは、HTTP 一本に集約しています(Lambda Function URL + Bearer トークン)。stdio は廃止しました。ワイヤフォーマットは JSON-RPC 2.0 で、initialize / tools/list / tools/call の三つだけを自前でハンドリングする最小構成になっています。

flowchart TD
  Client["MCP クライアント"] -->|"HTTPS POST<br/>Authorization: Bearer amk_..."| Url["Lambda Function URL"]
  Url --> Token["SHA-256 hash<br/>McpToken GetItem"]
  Token -->|"一致 + 有効"| Dispatch["tool dispatcher<br/>@ampless/mcp-server/tools"]
  Dispatch -->|"SigV4 / IAM"| AppSync["AppSync GraphQL"]
  Dispatch -->|"Lambda execution role"| S3["S3"]

トークンの仕組み

トークンは amk_ というプレフィックスに、base64url のランダム値を付け足した形の文字列です。

  • 発行は管理画面 /admin/mcp-tokens から行います(McpToken は admin 専用モデル)
  • 保管は SHA-256 ハッシュのみ。平文は発行時に 1 回だけ表示します
  • 認証パスは、McpToken テーブルへの GetItem 1 発で終わります。途中で AppSync は経由しません

トークン自体に、ロールは載っていません。実際の認可境界は MCP Lambda の IAM ロール側にあって、スキーマの allow.resource(mcpHandler).to(['query', 'mutate']) から、Post / Page / PostTag / Media に対する admin 相当のアクセス権を受け取っています。つまり トークンを持っていること = admin 相当の CMS アクセス権を持っていること、と見るほうが実態に近い。発行できるのは admin だけですが、配布先を選ぶときも、admin 権限を手渡すのと同じ気持ちで判断する必要があります。

ペイロードと制約

  • Function URL の呼び出しサイズ上限は、base64 展開後でおよそ 6 MB です
  • 大きな静的バンドルは、差分系ツール(upload_static_file / commit_static_post)に分割して投入してください
  • create_post / update_post は、format: 'static' を意図的に弾いています(manifest と S3 のズレを避けるため、static は専用ツールに一本化)

ツール一覧

いま登録されているのは 14 個です。

ツール 説明
list_posts 軽量サマリーで投稿一覧を返す。検索 / ソート / status フィルタ + ページネーション対応
get_post slug / postId で 1 件取得
create_post 投稿を新規作成(format ∈ tiptap / markdown / html、static は拒否)
update_post 投稿を更新
delete_post 投稿を削除し、PostTag 行も合わせてクリーンアップ
upload_media base64 のバイト列を public/media/YYYY/MM/ にアップロードして Media レコードを作成
list_media アップロード済みメディアの一覧
search_media メディアをファイル名や MIME タイプで検索
delete_media S3 オブジェクトと Media レコードをまとめて削除
get_schema CMS のコンテンツスキーマ(static 投稿の注記つき)を返す
upload_static_bundle zip 1 発で送るバンドルアップロード。展開・検証・S3 プレフィックス置換・manifest 上書きを atomic に
upload_static_file public/static/<slug>/ 配下に、1 ファイルずつ差分アップロード
delete_static_file public/static/<slug>/ 配下のファイルを、差分削除
commit_static_post S3 プレフィックスを再スキャンして、Post の manifest を再構築(差分編集後の "save" にあたる操作)

ロール別の制御は持っていません。MCP トークンが発行された時点で、全ツールにアクセスできます。

curl で素朴に叩く

中身は JSON-RPC over HTTP なので、シェルから直接叩けます。投稿一覧を 5 件取得する例を出しておきます。

curl -sS -X POST "$MCP_URL" \
  -H "Authorization: Bearer $MCP_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "list_posts",
      "arguments": { "limit": 5 }
    }
  }'

この紹介サイトのスクリーンショットも、同じ要領で upload_media を叩いて、戻り値の src を本文の Markdown 画像リンクに差し込みました。

エージェントから記事を書くフロー

Claude Code や Claude Desktop のような MCP 対応エージェントから書くなら、コンテンツ側の流れはだいたい次のとおりです。

  1. get_schema でスキーマを確認し、list_posts で既存記事を把握する
  2. upload_media で図版・スクリーンショットをアップロードし、戻り値の URL を本文に埋め込む
  3. create_post(format: markdown / tiptap / html)で投稿する
  4. ブラウザで /<slug> を開いて、表示を目視で確認する

この記事に限らず、ホームから辿れるほかの紹介記事もすべて、この流れで入れました。

コエンジニアとしての参加範囲

MCP が担うのはあくまでも コンテンツ側(投稿、メディア、静的バンドル)です。それ以外 — テーマの React コンポーネントを編集する、cms.config.ts にプラグインを足す、copy-themethemes/my-*/ を切り出す、npm run sandbox で動作確認する、git push で本番反映する — といった作業は、エージェント自身が持っているファイルシステム / シェル / npm / git のツール側でやることになります。

両方が同じプロジェクトディレクトリで動くと、エージェントは「投稿を書く」「テーマを書き換える」「プラグインを試す」を一連の作業として往復できるようになります。書き手だけでなく、サイト全体を一緒に育てるコエンジニアとして振る舞ってもらえる、というのが他の CMS と大きく違うところです。書く方向や判断はサイト運用者側に残しつつ、手を動かす部分はかなり広く渡せます。

editor の信頼モデル

ampless では、Cognito グループの editor を信頼済みプリンシパルとして扱う設計にしています。本文は tiptap / markdown / html のどれであっても、サニタイズせずそのまま保存・配信されます。WordPress の unfiltered_html capability に近い考え方です。

MCP トークンは前述のとおり実質 admin 相当なので、信頼境界はもう一段ぶん厳しく見る必要があります。トークンを渡す相手は、admin と同等に信頼できる人物か、それに見合った運用ルールの中に閉じる、という形で整理するのが安全です。サニタイズが要る運用に向けては、opt-in のサニタイズプラグイン(DOMPurify ベース)を予定しています。

関連記事