ClaudeからPhotoshopを操作できます。macOSに標準で入っているosascriptコマンドでPhotoshopにAppleScriptを送り、その中でExtendScript(.jsx)を実行させる方法です。MCPサーバーは要りません。
デザイン作業のうち、同じ型で数を出す仕事は退屈です。名刺を100人分、バナーをサイズ違いで10本、書籍の帯だけを差し替えて5案。手で作れば作れますが、時間がかかるうえに人為的なミスが混ざります。かといってPhotoshopを自動化しようとすると、公式ドキュメントは英語のPDFで、しかも最新版が2020年で止まっています。
この記事では、実際にPhotoshop 2026を動かしながら、接続の最小手順から写真加工・画像合成・印刷用データの生成・同一レイアウトの量産までを順に確認します。あわせて、公式ドキュメントに載っていない2つのつまずきどころと、DOMで扱えない機能の回避方法も扱います。デザインツールをAIから操作する話としては、FigmaとClaude Codeを連携する方法 が公式MCPを使う側の事例です。
ClaudeからPhotoshopを操作する仕組み
経路は3段です。Claudeがターミナルでosascriptを実行し、osascriptがAppleScriptとしてPhotoshopに命令を送り、Photoshopがその中身のExtendScriptを実行します。

鍵になるのはPhotoshopのdo javascriptコマンドです。Adobe公式の『Adobe Photoshop AppleScript Scripting Reference』によると、このコマンドは「実行するJavaScriptのコードまたはファイル(.jsまたは.jsx)」を受け取り、with argumentsで引数リストを、show debuggerでデバッガの表示タイミングを指定できます。
つまりPhotoshopは「AppleScriptを受け取る口」を持っていて、その口からJavaScriptを流し込めるということです。GUIをクリックして操作するわけではないので、画面の解像度やウィンドウ位置に影響されません。
注意点がひとつあります。アプリケーション名はバージョンまで含めて正確に書く必要があります。「Adobe Photoshop 2026」であって「Photoshop」ではありません。複数バージョンを入れている場合は、狙ったほうを名指しすることになります。
接続する — 3ステップで疎通を確認する
いきなり複雑なスクリプトを送ると、失敗したときに原因の切り分けができません。読み取りだけの命令から始めます。
1. バージョンを取得して疎通を確認する
Photoshopを起動した状態で、ターミナルから次を実行します。
osascript -e 'tell application "Adobe Photoshop 2026" to get version' バージョン番号が返ればつながっています。初回はmacOSが自動化の許可を求めるダイアログを出すので、許可してください。ここで止まる場合は、システム設定のプライバシーとセキュリティにある「オートメーション」の項目を確認します。
2. 開いているドキュメントを調べる
次に、状態を読み取るだけの命令を送ります。書き込みを伴わないので安全です。
tell application "Adobe Photoshop 2026"
set n to count of documents
if n is 0 then
return "開いているドキュメント: 0件"
else
set out to ""
repeat with d in documents
set out to out & (name of d) & " / " & (width of d as string) & "x" & (height of d as string) & return
end repeat
return out
end if
end tell3. ExtendScriptのファイルを送り込む
ここからが本番です。.jsxファイルを書いて、その中身を文字列としてPhotoshopに渡します。ファイルパスを直接渡す方法もありますが、シェルでcatして文字列で渡すほうが確実でした。
set scriptText to (do shell script "cat " & quoted form of "/path/to/script.jsx")
tell application "Adobe Photoshop 2026"
activate
do javascript scriptText
end tellExtendScript側は、最後に評価した式の値が戻り値になります。処理のログを配列に貯めてjoinして返すと、ターミナル側で結果を受け取れます。この戻り値が、あとで効いてきます。
app.displayDialogs = DialogModes.NO;
var log = [];
var doc = app.documents.add(1500, 500, 72, "banner",
NewDocumentMode.RGB, DocumentFill.WHITE);
log.push("created: " + doc.width.value + "x" + doc.height.value);
log.join("\n");写真を加工する — 元レイヤーを残したまま色を作る
まずは1枚の写真に色を作ります。カーブでコントラストを立て、カラーバランスでシャドウを寒色・ハイライトを暖色へ振り、周辺光量を落とします。

元の画像を潰さないよう、レイヤーを複製してから加工します。
var doc = app.open(new File("/path/to/photo.webp"));
doc.activeLayer.name = "original";
var graded = doc.activeLayer.duplicate();
graded.name = "graded";
doc.activeLayer = graded;
// S字カーブでコントラストを立てる
graded.adjustCurves([[0,0],[64,52],[128,130],[192,206],[255,255]]);
// シャドウを寒色へ、ハイライトを暖色へ
graded.adjustColorBalance([-8,0,16], [0,0,2], [12,3,-14], true);
graded.applyUnSharpMask(45, 1.2, 3);adjustCurvesとadjustColorBalanceはそのレイヤーのピクセルを直接書き換えます。調整レイヤーのような非破壊の適用ではありません。だから元レイヤーを複製して残しておく手順が要ります。この違いは後の節でもう一度触れます。
周辺光量落ちは、ぼかしを効かせた選択範囲を反転して黒で塗ると作れます。ここでひとつ落とし穴があります。
var vig = doc.artLayers.add();
// 選択範囲を画面外へ大きく張り出させる。
// キャンバス内で囲うと、ぼかしが左右と下の内側にも効いて中途半端に明るくなる
doc.selection.select([[-600, 1500], [2722, 1500], [2722, 3576], [-600, 3576]],
SelectionType.REPLACE, 200, true);
doc.selection.fill(black);
doc.selection.deselect();
vig.opacity = 42;ぼかしは選択範囲のすべての辺に効きます。キャンバスぴったりに選択すると、意図していない辺までぼけて色が抜けます。画面外へ大きくはみ出させておくと、見せたい辺のぼかしだけが残ります。
画像を合成する — 別ファイルのレイヤーを持ち込む
複数の写真を1枚にまとめます。別ドキュメントで開いた画像を、レイヤーごと目的のドキュメントへ複製する形です。

var src = app.open(new File(path));
src.flatten(); // 統合してから使う
src.resizeImage(UnitValue(w, "px"), UnitValue(h, "px"), 72,
ResampleMethod.BICUBICSHARPER);
src.artLayers[0].duplicate(doc, ElementPlacement.PLACEATBEGINNING);
src.close(SaveOptions.DONOTSAVECHANGES);
app.activeDocument = doc;
var placed = doc.layers[0];
var b = placed.bounds; // [left, top, right, bottom]
placed.translate(cx - (b[0].value + b[2].value) / 2,
cy - (b[1].value + b[3].value) / 2);
placed.rotate(angle, AnchorPosition.MIDDLECENTER);duplicate()したレイヤーは、元の座標を引き継いだ位置に落ちます。狙った場所へ動かすには、boundsで現在位置を読み、中心の差分をtranslate()する必要があります。
すでに開いているファイルを開き直すと事故る
筆者はここで1回失敗しました。合成に使う画像を先の節でグレーディングしていて、そのドキュメントがPhotoshopに開いたままだったのです。
この状態でapp.open()に同じファイルパスを渡すと、Photoshopは新しく開き直さず編集中のドキュメントを返します。結果、artLayers[0]が意図した写真ではなく最上位のビネットレイヤーになり、合成結果に半透明のグレーの板が貼りつきました。さらにresizeImage()が編集中のドキュメントを縮小してしまいました。
対処は2つです。素材を別ディレクトリへコピーしてから開くことと、開いた直後にflatten()することです。前者で編集中ドキュメントとの衝突を避け、後者でレイヤー構成に依存しなくなります。
印刷用データを作る — dpiと塗り足しを数値で持つ
スクリプトの強みが出るのが印刷物です。解像度・塗り足し・ガイドといった、手作業では設定を忘れがちな項目を数値として持てます。

A5(148×210mm)を350dpiで作り、塗り足しを3mm付けると2122×2976ピクセルになります。ガイドも同時に引けます。
var W = 2122, H = 2976, BLEED = 41; // 3mm ≒ 41px @350dpi
var doc = app.documents.add(W, H, 350, "cover",
NewDocumentMode.RGB, DocumentFill.WHITE);
doc.guides.add(Direction.VERTICAL, BLEED);
doc.guides.add(Direction.VERTICAL, W - BLEED);
doc.guides.add(Direction.HORIZONTAL, BLEED);
doc.guides.add(Direction.HORIZONTAL, H - BLEED);グラデーションマップが無いので、ブレンドモードで作る
写真を2色に圧縮するデュオトーンは、通常グラデーションマップで作ります。しかしDOMにその機能はありません。ブレンドモードの組み合わせで等価な結果を作れます。
- 写真を
desaturate()でグレースケールにする - ハイライト側の色(金)を乗算で重ねる
- シャドウ側の色(紺)をスクリーンで重ねる
- どちらも
grouped = trueで写真にクリッピングする
photo.desaturate();
var gold = doc.artLayers.add();
doc.selection.selectAll(); doc.selection.fill(hex("E0B15E")); doc.selection.deselect();
gold.blendMode = BlendMode.MULTIPLY;
gold.grouped = true;
var navy = doc.artLayers.add();
doc.selection.selectAll(); doc.selection.fill(hex("101E38")); doc.selection.deselect();
navy.blendMode = BlendMode.SCREEN;
navy.grouped = true;乗算で黒側を金に寄せ、スクリーンで暗部を紺に持ち上げる。結果としてシャドウが紺、ハイライトが金になり、グラデーションマップとほぼ同じ絵になります。
スクリプトでは解決できない問題もあります。今回使った写真は608×1080しかなく、A5フルブリードだと3.49倍に拡大しています。デュオトーンで粗が隠れているだけで、実案件なら入稿できません。解像度不足はコードでは埋められないので、素材の段階で確保する必要があります。
同じ型で量産する — 色だけを差し替える
ここがスクリプト化の本命です。レイアウトを固定し、色や文言だけを配列で回します。

各パネルはベースの矩形レイヤーを敷き、そこに写真とフィルターをクリッピングしています。クリッピングを使うと、写真をどれだけ大きく置いてもパネルの幅で切り取られます。
この状態から色だけを差し替えると、案出しが一瞬で終わります。

var variants = [
["A", "17A8A8", "002A2E", "C5D92D", "232A02"],
["B", "2DD4BF", "04302C", "38BDF8", "06243A"],
["C", "FF7A45", "2E0F04", "6C63FF", "0E0A32"]
];
for (var v = 0; v < variants.length; v++) {
var vname = variants[v][0]; // ※ 変数名を name にしてはいけない(後述)
repaint("filt-mult-left", variants[v][1]);
repaint("filt-scr-left", variants[v][2]);
repaint("filt-mult-right", variants[v][3]);
repaint("filt-scr-right", variants[v][4]);
doc.saveAs(new File(dir + "/out-" + vname + ".png"),
new PNGSaveOptions(), true, Extension.LOWERCASE);
}角丸の枠はDOMに無いので、座標を計算する
画面を囲む角丸の白枠も、DOMには角丸の選択範囲がありません。角の円弧を分割して多角形の選択範囲を作れば描けます。
function roundRect(l, t, r, b, rad, seg) {
var pts = [], i, a;
for (i = 0; i <= seg; i++) { a = Math.PI * 1.5 + (Math.PI / 2) * (i / seg);
pts.push([r - rad + Math.cos(a) * rad, t + rad + Math.sin(a) * rad]); }
for (i = 0; i <= seg; i++) { a = (Math.PI / 2) * (i / seg);
pts.push([r - rad + Math.cos(a) * rad, b - rad + Math.sin(a) * rad]); }
for (i = 0; i <= seg; i++) { a = Math.PI / 2 + (Math.PI / 2) * (i / seg);
pts.push([l + rad + Math.cos(a) * rad, b - rad + Math.sin(a) * rad]); }
for (i = 0; i <= seg; i++) { a = Math.PI + (Math.PI / 2) * (i / seg);
pts.push([l + rad + Math.cos(a) * rad, t + rad + Math.sin(a) * rad]); }
return pts;
}
// 外側を白で塗り、内側を消すと「線」になる
doc.selection.select(roundRect(28, 28, W - 28, H - 28, 26, 10)); doc.selection.fill(white);
doc.selection.select(roundRect(31, 31, W - 31, H - 31, 23, 10)); doc.selection.clear();つまずき① 変数名がアプリのプロパティと衝突する
4案を書き出したはずが、ファイルが1つしかできていませんでした。しかもファイル名がout-Adobe Photoshop.pngです。
原因は変数名でした。ExtendScriptをPhotoshopで実行すると、グローバルスコープがアプリケーションオブジェクトそのものになります。トップレベルでvar name = ...と書くと、それはapp.nameへの代入になります。このプロパティは読み取り専用なので、代入は黙って無視され、参照すると「Adobe Photoshop」が返ります。
// ダメな例:トップレベルの name は app.name と衝突する
var name = variants[v][0];
doc.saveAs(new File(dir + "/out-" + name + ".png"), ...); // → out-Adobe Photoshop.png
// 直し方:別の名前にする
var vname = variants[v][0];エラーは出ません。黙って別の値が入るので、書き出したファイルを見るまで気づけませんでした。nameのほかにversionやparentも同じ性質を持ちます。関数の引数として使う分にはローカル変数になるので問題ありませんが、トップレベルでは避けたほうが安全です。
つまずき② ネストした三項演算子が誤って評価される
もっと厄介なものを踏みました。テキストの中央揃えが効かず、すべて右揃えになったのです。コードは次のようになっていました。
var x = (mode === "center") ? (W - w) / 2
: (mode === "right") ? (W - margin - w)
: margin;デバッグ出力を入れるとmodeは確かに"center"で、typeofもstringでした。それでも右揃えの分岐が実行されます。最小のコードに落として再現させたのが次です。
function a(m){ return (m === "center") ? "A" : (m === "right") ? "B" : "C"; }
function b(m){ return (m === "center") ? "A" : ((m === "right") ? "B" : "C"); }
function d(m){ return (m === "center") ? "A" : "X"; }
a("center") + a("right") + a("left") // → "BBC" 期待は "ABC"
b("center") + b("right") + b("left") // → "ABC" 括弧を付けると正常
d("center") + d("right") // → "AX" ネストしなければ正常| 書き方 | 結果(期待は ABC) |
|---|---|
a ? "A" : b ? "B" : "C"(素のネスト) | BBC(誤り) |
a ? "A" : (b ? "B" : "C")(括弧あり) | ABC(正常) |
== に変えてネスト | BBC(誤り) |
| ネストしない三項 | 正常 |
===が壊れているわけではありません。単体の比較は正しくtrueを返します。壊れているのは括弧を付けずにネストした条件演算子の評価です。==でも同じように誤ります。筆者の環境では$.versionが4.5.12、$.buildが82.4でした。
この挙動はAdobeコミュニティのバグ報告フォーラムに2025年7月24日付で投稿があり、true ? a : true ? b : c;がaではなくbを返すと報告されています。修正表明はなく、括弧による回避が案内されるにとどまっています。Adobe公式ドキュメントにこの挙動を記載したものは見つかりませんでした。
回避策は単純です。ネストした三項演算子を使わずif文で書くか、内側を必ず括弧で囲みます。ExtendScriptはES3相当の古いJavaScriptなので、モダンな記法に頼らず素直に書くのが結局は安全でした。
DOMで扱えない機能と、その回避方法
Photoshopのスクリプティングには2つの層があります。オブジェクト指向で書けるDOMと、記録した操作を再生するAction Managerです。Adobe公式の『Adobe Photoshop Scripting Guide』は、Action Managerを「スクリプティングインターフェースからは他の方法でアクセスできないPhotoshopの機能を対象とするスクリプトを記述できるようにするもの」と説明しています。
つまりDOMに無い機能は、Action Manager経由で叩くのが公式の想定です。ただしAction Managerのコードは記述が冗長で、可読性が大きく落ちます。実際にどこまでDOMで足りるのかを整理します。
| 機能 | DOMでの扱い | 取った回避策 |
|---|---|---|
| 調整レイヤーの追加 | 該当APIなし | adjustCurves()等をレイヤーへ直接適用(破壊的) |
| グラデーションマップ | 該当APIなし | 乗算+スクリーンのクリッピングで等価な絵を作る |
| レイヤーマスクの追加 | 追加は不可。既存マスクの濃度・ぼかしは読み書き可 | ぼかした選択範囲+塗りで代用 |
| レイヤースタイル | applyStyle()で名前付きプリセットの適用は可能。効果パラメータの任意指定は不可 | 影は別レイヤーをずらして敷く |
| スマートオブジェクト | open()のasSmartObjectでファイルを開く際は可能。既存レイヤーの変換は不可 | 今回は使わず統合して扱った |
| 角丸の選択範囲 | 該当APIなし | 角の円弧を計算して多角形選択を作る |
筆者は当初、レイヤースタイルとスマートオブジェクトも「DOMでは一切できない」と考えていました。公式リファレンスを読み直すとどちらも部分的にDOMで扱えます。前者はスタイルパネルにあるプリセットを名前で適用でき、後者はファイルを開く時点でスマートオブジェクト化できます。
もうひとつ、書き方に関わる前提があります。Adobe公式のスクリプティングリファレンスは2020年版が最新で、そこで更新が止まっています。実機のPhotoshop 2026とは6メジャーバージョン離れています。リファレンスに載っていないことは「不可能」の証明にはなりません。
書き出して確認するループを組み込む
スクリプトでデザインを組む上で、これが最も重要でした。コードが例外なく通っても、出来上がった絵が崩れていることがあります。
実際に起きたのは、欧文ラベルが版面からはみ出す、罫線が見出しに重なる、行送りの単位を取り違えて2行目が画面外へ飛ぶ、といった事故です。いずれもPhotoshopはエラーを出しません。
テキストレイヤーのboundsを読めば、はみ出しは機械的に検出できます。
var b = t.bounds;
var right = b[2].value;
var flag = "";
if (right > W - MARGIN) { flag = " 版面外"; }
if (right > W - BLEED) { flag = " 仕上がり線を超過"; }
log.push(lname + ": x右端=" + Math.round(right) + flag);この一手間で、目視より確実にはみ出しを拾えます。実際にこのチェックが「欧文ラベルが版面外」「タイトルが仕上がり線を超過」の2件を検出しました。
とはいえ数値で拾えるのは、あらかじめ検査項目を決められるものだけです。「重なって読めない」「バランスが悪い」は書き出した画像を見ないと分かりません。今回も、実機のプロフィールアイコンが本文に重なっていることに気づいたのは、画像を拡大して確認したときでした。書き出して目で見るところまでを1周に含めるのが、結局いちばん早い進め方です。
デザインの良し悪しそのものは、スクリプトでは判断できません。構図やレイアウトの基準は Photoshop・Illustratorで構図を再現する方法 に、バナーの設計原則は Photoshopバナーデザインの基本テクニック に整理しています。
Windowsから操作する場合
ここまではmacOSのosascriptを前提にしてきました。WindowsにAppleScriptはありませんが、COM経由で同じことができます。
Adobe公式の『Adobe Photoshop VBScript Scripting Reference』によると、Windows環境ではCreateObject("Photoshop.Application")でPhotoshopを操作でき、DoJavaScriptおよびDoJavaScriptFileでExtendScriptを実行できます。
| macOS | Windows | |
|---|---|---|
| 入口 | osascript(AppleScript) | COM(VBScript / PowerShell等) |
| 実行コマンド | do javascript(コードとファイルの両方を受ける) | DoJavaScript と DoJavaScriptFile の2つに分かれる |
| デバッガ制御 | show debugger | PsJavaScriptExecutionMode |
送り込む.jsxの中身は共通です。入口だけがOSごとに違う、という構造になっています。
MCPで繋ぐか、直接叩くか
AIエージェントから外部アプリを操作する方法は、いまMCPが主流です。ではなぜ今回はosascriptを使ったのか。答えは単純で、Photoshopに公式のMCPサーバーが用意されていないからです。

判断の順序はこうなります。公式MCPがあるならそれを使うのが最短です。無い場合、そのアプリがスクリプトAPIを持っているなら、osascriptやCOMで直接叩くほうが速い。どちらも無いときに初めて、MCPサーバーを自作する検討に入ります。
公式MCPが用意されているツールの連携手順は Claude CodeのMCP連携ガイド一覧 にまとめています。自作する側の手順は MCPサーバーの作り方 が対になります。
向く仕事と、向かない仕事
実際に一通り作ってみて、線引きははっきりしました。
| 向く | 向かない | |
|---|---|---|
| 仕事の性質 | 同じ型で数を出す/既存ファイルの一括処理 | 1点物のデザイン制作 |
| 具体例 | 名刺の差し込み、バナーのサイズ違い、書き出しの統一、塗り足しや解像度の一括検査 | コンセプトから起こす表紙、写真の緻密なレタッチ |
| 理由 | 同じ手順を正確に繰り返せる。差分がコードとして残る | 試行錯誤の速度は手で動かすほうが速い |
とくに検版の自動化は実利が大きいと感じました。塗り足しの有無、解像度、カラーモード、フォントの埋め込みを全ファイル走査してレポートを出す、といった作業は人がやると必ず見落としが出ます。ここはスクリプトが確実に勝ちます。
まとめ
ClaudeからPhotoshopは操作できます。osascriptでAppleScriptを送り、その中のdo javascriptでExtendScriptを実行する。MCPは不要で、macOSの標準機能だけで完結します。
ただし万能ではありません。DOMには調整レイヤーもグラデーションマップも角丸選択も無く、代替の実装を自分で組む必要があります。ネストした三項演算子は誤って評価され、トップレベルのnameはアプリのプロパティと衝突します。そして何より、書き出して目で見る工程を省くと、崩れたまま出力され続けます。
まず試すなら、Photoshopを開いた状態でosascript -e 'tell application "Adobe Photoshop 2026" to get version'を1行走らせてみてください。バージョン番号が返れば、あとは.jsxを書くだけです。
自社の制作フローに合わせて量産や検版の仕組みを組みたい場合は、実装のご相談も承っています。
よくある質問
Q. ClaudeからPhotoshopを操作するのにMCPサーバーは必要ですか?
必要ありません。macOSに標準で入っているosascriptコマンドでAppleScriptをPhotoshopに送り、その中のdo javascriptでExtendScriptを実行します。Photoshopには公式のMCPサーバーが用意されていないため、スクリプトAPIを直接叩くこの方法が最短です。
Q. ExtendScriptは非推奨になったのではありませんか?
ExtendScript自体は非推奨ではありません。Adobe公式のUXPドキュメントは、UXPを推奨プラットフォームとしつつ「現時点でPhotoshopからExtendScriptのサポートを削除する計画はない」と明記しています。非推奨化されたのは統合開発環境のExtendScript Toolkit(ESTK)とCEPであって、言語ではありません。ただしサポート期限のアナウンスも存在しないため、長期の見通しは立てにくい点に注意してください。
Q. 調整レイヤーやレイヤースタイルはスクリプトで扱えますか?
調整レイヤーの追加はDOMに該当するAPIがなく、Action Manager経由になります。レイヤースタイルはapplyStyle()でスタイルパネルの既存プリセットを名前指定で適用できますが、影の距離や色といった効果パラメータを任意に指定することはできません。レイヤーマスクは新規追加ができない一方、既存マスクの濃度やぼかしは読み書きできます。
Q. Windowsでも同じことができますか?
できます。AppleScriptの代わりにCOMを使います。CreateObject("Photoshop.Application")でPhotoshopに接続し、DoJavaScriptまたはDoJavaScriptFileでExtendScriptを実行します。送り込む.jsxの中身はmacOSと共通で、入口だけが違います。
Q. 生成AI機能や生成塗りつぶしもスクリプトから使えますか?
使えません。スクリプトから扱えるのは、あくまでスクリプティングインターフェースが公開している機能に限られます。生成AI関連の機能はGUI側の操作として提供されており、この方法では呼び出せませんでした。
Q. 画像の解像度が足りない場合もスクリプトで対処できますか?
できません。拡大は元にない情報を作り出せないため、粗さはそのまま残ります。今回もA5の印刷サイズに対して元画像が小さく、3倍以上に拡大した状態でした。仕上がりを担保するには、素材の段階で必要な解像度を確保する必要があります。
Q. スクリプトで作ったデータは、そのままデザイナーに渡せますか?
渡せます。生成したドキュメントはレイヤー構成を保ったままPSDで保存できるため、続きの作業を人が引き取れます。今回の例でも、写真・カラーフィルター・テキストをすべて個別のレイヤーとして残しています。
