amplesscms

article

MCP 経由で記事を書く

ampless の MCP HTTP トランスポート、トークンと認可境界、利用できるツール、curl で叩く例をまとめました。この記事自体も MCP 経由で更新しています。

ampless には MCP (Model Context Protocol) サーバーが同梱されています。管理 UI、公開サイト、MCP は同じ AppSync スキーマを使いますが、MCP Lambda だけは IAM (SigV4) で接続します。AI エージェントに記事作成や更新を任せたいときは、この経路を使います。

トランスポート

MCP のトランスポートは HTTP に集約しています。Lambda Function URL に Bearer トークンを付けて POST する形です。stdio は廃止しました。

ワイヤフォーマットは JSON-RPC 2.0 です。initialize / tools/list / tools/call の三つだけを自前で扱う、かなり小さな実装にしています。

MCP クライアント
  ↓ HTTPS POST + Authorization: Bearer amk_...
Lambda Function URL
  ↓ SHA-256 ハッシュ → McpToken テーブルを GetItem(admin 専用モデル)
  ↓ 一致 + 失効/期限切れでなければ通過
ツールディスパッチ(@ampless/mcp-server/tools)
  ├── AppSync を SigV4 (IAM) で叩く
  └── S3 を Lambda 実行ロールで叩く

トークンの仕組み

トークンは、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 は専用ツールに一本化している

ツール一覧

いま登録されているツールは 11 個です。

ツール 説明
list_posts ステータスフィルタとページネーション付きで、投稿一覧を返す
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 レコードを作成
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> を開き、表示を目で確認する

この記事だけでなく、ホームから辿れるほかの紹介記事もこの流れで入れました。書き手として AI エージェントを使いつつ、構成や判断はサイト運用者側に残す、という分担です。

editor の信頼モデル

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

MCP トークンは、前述のとおり実質 admin 相当です。通常の editor より信頼境界をもう一段厳しく見てください。トークンを渡す相手は、admin と同じくらい信頼できる人物か、同等の制約を置いた運用ルールの中に閉じるのが安全です。

サニタイズが必要な運用に向けては、opt-in のサニタイズプラグイン(DOMPurify ベース)を予定しています。

関連記事

MCP 経由で記事を書く