friday-mcp-v2 3.0.4 → 3.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/mcp-server.js +102 -253
- package/package.json +1 -1
package/dist/mcp-server.js
CHANGED
|
@@ -249,7 +249,7 @@ function checkRevisionMismatch(expectedRevision, currentState, mode, postId, ses
|
|
|
249
249
|
: '';
|
|
250
250
|
const text = `❌ REVISION_MISMATCH: expected ${expectedRevision}, actual ${currentRevision}` +
|
|
251
251
|
snapshotLine +
|
|
252
|
-
`\n→ get_article_structure
|
|
252
|
+
`\n→ Re-fetch via get_article_structure.`;
|
|
253
253
|
return { content: [{ type: "text", text }], isError: true };
|
|
254
254
|
}
|
|
255
255
|
|
|
@@ -729,24 +729,6 @@ const targetSchema = {
|
|
|
729
729
|
type: "boolean",
|
|
730
730
|
description: "Target user-selected block. Editor mode only.",
|
|
731
731
|
},
|
|
732
|
-
index: {
|
|
733
|
-
type: "number",
|
|
734
|
-
description: "Block index (0-based, flattened).",
|
|
735
|
-
},
|
|
736
|
-
indices: {
|
|
737
|
-
type: "array",
|
|
738
|
-
items: { type: "number" },
|
|
739
|
-
description: "Multiple indices (0-based).",
|
|
740
|
-
},
|
|
741
|
-
range: {
|
|
742
|
-
type: "object",
|
|
743
|
-
properties: {
|
|
744
|
-
start: { type: "number", description: "Start (inclusive)." },
|
|
745
|
-
end: { type: "number", description: "End (inclusive)." },
|
|
746
|
-
},
|
|
747
|
-
required: ["start", "end"],
|
|
748
|
-
description: "Index range.",
|
|
749
|
-
},
|
|
750
732
|
section: {
|
|
751
733
|
type: "string",
|
|
752
734
|
description: "Section by heading text (partial match).",
|
|
@@ -782,7 +764,7 @@ const targetSchema = {
|
|
|
782
764
|
description: "Multiple block refs from snapshot. Requires snapshotId.",
|
|
783
765
|
},
|
|
784
766
|
},
|
|
785
|
-
description: "Target: one of selected/
|
|
767
|
+
description: "Target: one of selected/ref/refs/heading/section/blockType + optional filters (contains/nth). Requires snapshotId when using ref/refs.",
|
|
786
768
|
};
|
|
787
769
|
|
|
788
770
|
const targetSchemaNoSelected = {
|
|
@@ -790,7 +772,7 @@ const targetSchemaNoSelected = {
|
|
|
790
772
|
properties: { ...targetSchema.properties },
|
|
791
773
|
};
|
|
792
774
|
delete targetSchemaNoSelected.properties.selected;
|
|
793
|
-
targetSchemaNoSelected.description = "Target: one of
|
|
775
|
+
targetSchemaNoSelected.description = "Target: one of ref/refs/heading/section/blockType + optional filters (contains/nth). Requires snapshotId when using ref/refs.";
|
|
794
776
|
|
|
795
777
|
const insertSchema = {
|
|
796
778
|
type: "object",
|
|
@@ -815,9 +797,6 @@ function normalizeTarget(target) {
|
|
|
815
797
|
// --- 排他バリデーション ---
|
|
816
798
|
const primaries = [
|
|
817
799
|
target.selected && 'selected',
|
|
818
|
-
target.index !== undefined && 'index',
|
|
819
|
-
target.indices && 'indices',
|
|
820
|
-
target.range && 'range',
|
|
821
800
|
target.heading && 'heading',
|
|
822
801
|
target.ref && 'ref',
|
|
823
802
|
target.refs && 'refs',
|
|
@@ -830,19 +809,6 @@ function normalizeTarget(target) {
|
|
|
830
809
|
if (target.ref) result._ref = target.ref;
|
|
831
810
|
if (target.refs) result._refs = target.refs;
|
|
832
811
|
if (target.selected) result.target = "selected";
|
|
833
|
-
if (target.index !== undefined) result.index = target.index;
|
|
834
|
-
if (target.indices) result.indices = target.indices;
|
|
835
|
-
if (target.range) {
|
|
836
|
-
const s = target.range.start;
|
|
837
|
-
const e = target.range.end;
|
|
838
|
-
if (s === undefined || s === null || e === undefined || e === null ||
|
|
839
|
-
typeof s !== "number" || typeof e !== "number" ||
|
|
840
|
-
!Number.isInteger(s) || !Number.isInteger(e) || s < 0 || e < 0) {
|
|
841
|
-
throw new Error("range.start と range.end は 0 以上の整数で両方指定してください。");
|
|
842
|
-
}
|
|
843
|
-
result.startIndex = s;
|
|
844
|
-
result.endIndex = e;
|
|
845
|
-
}
|
|
846
812
|
if (target.section) result.section = target.section;
|
|
847
813
|
if (target.blockType) result.blockType = target.blockType;
|
|
848
814
|
if (target.nth !== undefined) result.typeIndex = target.nth;
|
|
@@ -931,15 +897,15 @@ function resolveRefFromState(snapshotId, ref, mode, sessionId, postId, currentSt
|
|
|
931
897
|
// 1. snapshot 取得
|
|
932
898
|
const snap = snapshotCache.get(snapshotId);
|
|
933
899
|
if (!snap) {
|
|
934
|
-
throw new Error(`snapshot "${snapshotId}" が見つかりません(期限切れまたは無効)。get_article_structure
|
|
900
|
+
throw new Error(`snapshot "${snapshotId}" が見つかりません(期限切れまたは無効)。Re-fetch via get_article_structure.`);
|
|
935
901
|
}
|
|
936
902
|
|
|
937
903
|
// 2. 状態ソース束縛チェック
|
|
938
904
|
if (snap.mode !== mode) {
|
|
939
|
-
throw new Error(`snapshot は ${snap.mode} モードで取得されましたが、現在は ${mode} モードです。get_article_structure
|
|
905
|
+
throw new Error(`snapshot は ${snap.mode} モードで取得されましたが、現在は ${mode} モードです。Re-fetch via get_article_structure.`);
|
|
940
906
|
}
|
|
941
907
|
if (mode === 'editor' && snap.sessionId && sessionId && snap.sessionId !== sessionId) {
|
|
942
|
-
throw new Error(`snapshot は sessionId "${snap.sessionId}" で取得されましたが、現在の sessionId は "${sessionId}" です。get_article_structure
|
|
908
|
+
throw new Error(`snapshot は sessionId "${snap.sessionId}" で取得されましたが、現在の sessionId は "${sessionId}" です。Re-fetch via get_article_structure.`);
|
|
943
909
|
}
|
|
944
910
|
if (snap.postId !== postId) {
|
|
945
911
|
throw new Error(`snapshot は postId ${snap.postId} 用ですが、postId ${postId} に対して使用されています。`);
|
|
@@ -978,14 +944,14 @@ function resolveRefFromState(snapshotId, ref, mode, sessionId, postId, currentSt
|
|
|
978
944
|
if (refined.length === 1) return refined[0].index;
|
|
979
945
|
throw new Error(
|
|
980
946
|
`ref "${ref}" (type: ${snapBlock.type}) の解決先が ${refined.length > 0 ? refined.length : candidates.length} 件見つかりました。` +
|
|
981
|
-
`一意に特定できません。get_article_structure
|
|
947
|
+
`一意に特定できません。Re-fetch via get_article_structure.`
|
|
982
948
|
);
|
|
983
949
|
}
|
|
984
950
|
|
|
985
951
|
// 5c. 一致 0件 → エラー
|
|
986
952
|
throw new Error(
|
|
987
953
|
`ref "${ref}" (${snapBlock.type}, fingerprint: ${snapBlock.fingerprint}) に一致するブロックが見つかりません。` +
|
|
988
|
-
`構造が大幅に変更された可能性があります。get_article_structure
|
|
954
|
+
`構造が大幅に変更された可能性があります。Re-fetch via get_article_structure.`
|
|
989
955
|
);
|
|
990
956
|
}
|
|
991
957
|
|
|
@@ -1033,7 +999,7 @@ async function resolveRefsAndCheckRevision({
|
|
|
1033
999
|
if (!snapshotId) {
|
|
1034
1000
|
if (_debug) console.error(`[SNAPSHOT] resolveRefs: ref指定あり but snapshotId missing → error`);
|
|
1035
1001
|
return { error: {
|
|
1036
|
-
content: [{ type: "text", text: "❌ ref/refs
|
|
1002
|
+
content: [{ type: "text", text: "❌ snapshotId required for ref/refs. Get it from get_article_structure or any prior write response." }],
|
|
1037
1003
|
isError: true,
|
|
1038
1004
|
}};
|
|
1039
1005
|
}
|
|
@@ -1365,13 +1331,13 @@ function appendRefChangesToText(text, changeInfo) {
|
|
|
1365
1331
|
.join(', ');
|
|
1366
1332
|
break;
|
|
1367
1333
|
case 'updated':
|
|
1368
|
-
out += `\nupdated: [${changeInfo.updatedRefs.join(', ')}] (
|
|
1334
|
+
out += `\nupdated: [${changeInfo.updatedRefs.join(', ')}] (reuse this snapshotId for next operation)`;
|
|
1369
1335
|
break;
|
|
1370
1336
|
case 'expanded':
|
|
1371
1337
|
out += `\nexpanded: ${changeInfo.expanded.oldRef} \u2192 [${changeInfo.expanded.newRefs.join(', ')}]`;
|
|
1372
1338
|
break;
|
|
1373
1339
|
case 'duplicated':
|
|
1374
|
-
out += `\nduplicated: ${changeInfo.sourceRef} \u2192 ${changeInfo.newRef} (source:[${changeInfo.sourceIndex}], new:[${changeInfo.newIndex}]) (
|
|
1340
|
+
out += `\nduplicated: ${changeInfo.sourceRef} \u2192 ${changeInfo.newRef} (source:[${changeInfo.sourceIndex}], new:[${changeInfo.newIndex}]) (reuse this snapshotId for next operation)`;
|
|
1375
1341
|
break;
|
|
1376
1342
|
}
|
|
1377
1343
|
|
|
@@ -2089,40 +2055,33 @@ const tools = [
|
|
|
2089
2055
|
},
|
|
2090
2056
|
{
|
|
2091
2057
|
name: "delete_block",
|
|
2092
|
-
description: "Delete block(s) by
|
|
2058
|
+
description: "Delete block(s) by ref or selection.",
|
|
2093
2059
|
inputSchema: {
|
|
2094
2060
|
type: "object",
|
|
2095
2061
|
properties: {
|
|
2096
2062
|
postId: postIdParam,
|
|
2097
2063
|
site: siteParam,
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
snapshotId: { type: "string", description: "Snapshot ID from get_article_structure. Required when using ref/refs." },
|
|
2102
|
-
ref: { type: "string", description: "Block ref from snapshot (exclusive with index/indices)." },
|
|
2103
|
-
refs: { type: "array", items: { type: "string" }, description: "Multiple block refs (exclusive with index/indices)." },
|
|
2064
|
+
snapshotId: { type: "string", description: "Snapshot ID (from get_article_structure or any write response). Required." },
|
|
2065
|
+
ref: { type: "string", description: "Block ref from snapshot." },
|
|
2066
|
+
refs: { type: "array", items: { type: "string" }, description: "Multiple block refs from snapshot." },
|
|
2104
2067
|
expectedRevision: { type: "string", description: "Revision from snapshot. Rejects if structure changed." },
|
|
2105
2068
|
},
|
|
2106
2069
|
},
|
|
2107
2070
|
},
|
|
2108
2071
|
{
|
|
2109
2072
|
name: "move_block",
|
|
2110
|
-
description: "Move block(s). Use
|
|
2073
|
+
description: "Move block(s). Use fromRef + beforeRef or afterRef. Requires snapshotId.",
|
|
2111
2074
|
inputSchema: {
|
|
2112
2075
|
type: "object",
|
|
2113
2076
|
properties: {
|
|
2114
2077
|
postId: postIdParam,
|
|
2115
2078
|
site: siteParam,
|
|
2116
|
-
from: { type: "number", description: "Source top-level index" },
|
|
2117
|
-
to: { type: "number", description: "Target position" },
|
|
2118
|
-
fromFlat: { type: "number", description: "Source flattened index" },
|
|
2119
|
-
toFlat: { type: "number", description: "Target flattened position" },
|
|
2120
2079
|
count: { type: "integer", description: "Consecutive count (default: 1)" },
|
|
2121
|
-
snapshotId: { type: "string", description: "Snapshot ID from get_article_structure
|
|
2080
|
+
snapshotId: { type: "string", description: "Snapshot ID (from get_article_structure or any write response). Required." },
|
|
2122
2081
|
expectedRevision: { type: "string", description: "Revision from snapshot. Rejects if structure changed." },
|
|
2123
|
-
fromRef: { type: "string", description: "Source block ref
|
|
2124
|
-
beforeRef: { type: "string", description: "Move before this ref (exclusive with
|
|
2125
|
-
afterRef: { type: "string", description: "Move after this ref (exclusive with
|
|
2082
|
+
fromRef: { type: "string", description: "Source block ref." },
|
|
2083
|
+
beforeRef: { type: "string", description: "Move before this ref (exclusive with afterRef)." },
|
|
2084
|
+
afterRef: { type: "string", description: "Move after this ref (exclusive with beforeRef)." },
|
|
2126
2085
|
},
|
|
2127
2086
|
},
|
|
2128
2087
|
},
|
|
@@ -2152,15 +2111,14 @@ const tools = [
|
|
|
2152
2111
|
},
|
|
2153
2112
|
{
|
|
2154
2113
|
name: "duplicate_block",
|
|
2155
|
-
description: "Duplicate block by
|
|
2114
|
+
description: "Duplicate block by ref or selection.",
|
|
2156
2115
|
inputSchema: {
|
|
2157
2116
|
type: "object",
|
|
2158
2117
|
properties: {
|
|
2159
2118
|
postId: postIdParam,
|
|
2160
2119
|
site: siteParam,
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
ref: { type: "string", description: "Block ref from snapshot (exclusive with index)." },
|
|
2120
|
+
snapshotId: { type: "string", description: "Snapshot ID (from get_article_structure or any write response). Required." },
|
|
2121
|
+
ref: { type: "string", description: "Block ref from snapshot." },
|
|
2164
2122
|
expectedRevision: { type: "string", description: "Revision from snapshot. Rejects if structure changed." },
|
|
2165
2123
|
},
|
|
2166
2124
|
},
|
|
@@ -2244,7 +2202,7 @@ const tools = [
|
|
|
2244
2202
|
},
|
|
2245
2203
|
{
|
|
2246
2204
|
name: "insert_block",
|
|
2247
|
-
description: "Insert Gutenberg HTML.
|
|
2205
|
+
description: "Insert Gutenberg HTML. Use beforeRef/afterRef for positioning. Omit both to append to end.",
|
|
2248
2206
|
inputSchema: {
|
|
2249
2207
|
type: "object",
|
|
2250
2208
|
properties: {
|
|
@@ -2252,12 +2210,10 @@ const tools = [
|
|
|
2252
2210
|
site: siteParam,
|
|
2253
2211
|
rawHTML: { type: "string", description: "HTML to insert (exclusive with filePath)" },
|
|
2254
2212
|
filePath: { type: "string", description: "Local file path (exclusive with rawHTML)" },
|
|
2255
|
-
|
|
2256
|
-
position: { type: "string", enum: ["before", "after"], description: "Insert relative to index: 'before' (default) or 'after'. Requires index." },
|
|
2257
|
-
snapshotId: { type: "string", description: "Snapshot ID. Required when using beforeRef/afterRef." },
|
|
2213
|
+
snapshotId: { type: "string", description: "Snapshot ID (from get_article_structure or any write response). Required when using beforeRef/afterRef. Omit to append to end." },
|
|
2258
2214
|
expectedRevision: { type: "string", description: "Revision from snapshot. Rejects if structure changed." },
|
|
2259
|
-
beforeRef: { type: "string", description: "Insert before this ref
|
|
2260
|
-
afterRef: { type: "string", description: "Insert after this ref
|
|
2215
|
+
beforeRef: { type: "string", description: "Insert before this ref." },
|
|
2216
|
+
afterRef: { type: "string", description: "Insert after this ref." },
|
|
2261
2217
|
},
|
|
2262
2218
|
},
|
|
2263
2219
|
},
|
|
@@ -2295,7 +2251,7 @@ const tools = [
|
|
|
2295
2251
|
site: siteParam,
|
|
2296
2252
|
snapshotId: {
|
|
2297
2253
|
type: "string",
|
|
2298
|
-
description: "Snapshot ID from get_article_structure. Required when using ref/refs in target.",
|
|
2254
|
+
description: "Snapshot ID (from get_article_structure or any write response). Required when using ref/refs in target.",
|
|
2299
2255
|
},
|
|
2300
2256
|
expectedRevision: {
|
|
2301
2257
|
type: "string",
|
|
@@ -2380,16 +2336,12 @@ const tools = [
|
|
|
2380
2336
|
},
|
|
2381
2337
|
{
|
|
2382
2338
|
name: "table_operations",
|
|
2383
|
-
description: "Table operations (get/update/add/delete rows/columns/cells).
|
|
2339
|
+
description: "Table operations (get/update/add/delete rows/columns/cells). Requires ref+snapshotId to identify the table block.",
|
|
2384
2340
|
inputSchema: {
|
|
2385
2341
|
type: "object",
|
|
2386
2342
|
properties: {
|
|
2387
2343
|
postId: postIdParam,
|
|
2388
2344
|
site: siteParam,
|
|
2389
|
-
index: {
|
|
2390
|
-
type: "number",
|
|
2391
|
-
description: "Table index (0-based)",
|
|
2392
|
-
},
|
|
2393
2345
|
action: {
|
|
2394
2346
|
type: "string",
|
|
2395
2347
|
enum: ["get_structure", "update_cell", "add_row", "delete_row", "add_column", "delete_column", "move_row", "move_column", "update_row", "update_column"],
|
|
@@ -2416,8 +2368,8 @@ const tools = [
|
|
|
2416
2368
|
items: { type: "string" },
|
|
2417
2369
|
description: "add_row: new cells, add_column: init values, update_row/column: replacements. Omit for empty.",
|
|
2418
2370
|
},
|
|
2419
|
-
snapshotId: { type: "string", description: "Snapshot ID from get_article_structure
|
|
2420
|
-
ref: { type: "string", description: "
|
|
2371
|
+
snapshotId: { type: "string", description: "Snapshot ID (from get_article_structure or any write response). Required." },
|
|
2372
|
+
ref: { type: "string", description: "Table block ref from snapshot." },
|
|
2421
2373
|
expectedRevision: { type: "string", description: "Revision from snapshot. Rejects if structure changed." },
|
|
2422
2374
|
},
|
|
2423
2375
|
required: ["action"],
|
|
@@ -2533,7 +2485,7 @@ async function handleUpdateBlocksTool(args, toolName) {
|
|
|
2533
2485
|
}
|
|
2534
2486
|
if (!snapshotId) {
|
|
2535
2487
|
return {
|
|
2536
|
-
content: [{ type: "text", text: `❌ operations
|
|
2488
|
+
content: [{ type: "text", text: `❌ snapshotId required for operations. Get it from get_article_structure or any prior write response.` }],
|
|
2537
2489
|
isError: true,
|
|
2538
2490
|
};
|
|
2539
2491
|
}
|
|
@@ -3418,9 +3370,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3418
3370
|
if (_guard) return _guard;
|
|
3419
3371
|
}
|
|
3420
3372
|
|
|
3421
|
-
// ref
|
|
3422
|
-
if (
|
|
3423
|
-
return { content: [{ type: "text", text: "❌ ref
|
|
3373
|
+
// ref または refs が必須(選択ブロック削除はeditorコマンド経由で処理)
|
|
3374
|
+
if (!args?.ref && !args?.refs) {
|
|
3375
|
+
return { content: [{ type: "text", text: "❌ ref or refs required, along with snapshotId." }], isError: true };
|
|
3424
3376
|
}
|
|
3425
3377
|
|
|
3426
3378
|
// ref 解決 + revision チェック
|
|
@@ -3432,20 +3384,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3432
3384
|
if (resolved.error) return resolved.error;
|
|
3433
3385
|
const _preState = resolved.currentState || null; // Phase 2: 差分計算用
|
|
3434
3386
|
|
|
3435
|
-
let
|
|
3436
|
-
|
|
3437
|
-
|
|
3387
|
+
let index = resolved.index;
|
|
3388
|
+
let indices = resolved.indices;
|
|
3389
|
+
const count = (index !== undefined) ? 1 : undefined;
|
|
3438
3390
|
|
|
3439
|
-
// Phase 3: 入力 ref
|
|
3391
|
+
// Phase 3: 入力 ref を保持
|
|
3440
3392
|
const _inputRefs = args?.refs ? [...new Set(args.refs)]
|
|
3441
|
-
:
|
|
3393
|
+
: args?.ref ? [args.ref]
|
|
3442
3394
|
: null;
|
|
3443
3395
|
|
|
3444
|
-
// indices と index/count の排他バリデーション
|
|
3445
|
-
if (indices !== undefined && (index !== undefined || count !== undefined)) {
|
|
3446
|
-
return { content: [{ type: "text", text: "❌ indices と index/count は同時に指定できません。" }], isError: true };
|
|
3447
|
-
}
|
|
3448
|
-
|
|
3449
3396
|
// indices モード(非連続一括削除)
|
|
3450
3397
|
if (indices !== undefined) {
|
|
3451
3398
|
if (!Array.isArray(indices) || indices.length === 0) {
|
|
@@ -3490,10 +3437,10 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3490
3437
|
return { content: [{ type: "text", text: appendSnapshotToTextLegacy(_msg, _snap) }] };
|
|
3491
3438
|
}
|
|
3492
3439
|
|
|
3493
|
-
//
|
|
3440
|
+
// 単一 ref モード(ref → index 解決済み)
|
|
3494
3441
|
if (mode === 'headless') {
|
|
3495
3442
|
if (index === undefined) {
|
|
3496
|
-
return { content: [{ type: "text", text: "❌
|
|
3443
|
+
return { content: [{ type: "text", text: "❌ ref resolution failed. Check snapshotId and ref." }], isError: true };
|
|
3497
3444
|
}
|
|
3498
3445
|
try {
|
|
3499
3446
|
const result = await client.headlessDelete(postId, index, count || 1);
|
|
@@ -3544,8 +3491,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3544
3491
|
if (_guard) return _guard;
|
|
3545
3492
|
}
|
|
3546
3493
|
|
|
3547
|
-
|
|
3548
|
-
let { fromFlat, toFlat } = args;
|
|
3494
|
+
let fromFlat, toFlat;
|
|
3549
3495
|
const count = args.count ?? 1;
|
|
3550
3496
|
|
|
3551
3497
|
// count バリデーション
|
|
@@ -3553,70 +3499,41 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3553
3499
|
return { content: [{ type: "text", text: "❌ count は1以上の整数を指定してください。" }], isError: true };
|
|
3554
3500
|
}
|
|
3555
3501
|
|
|
3556
|
-
//
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
const hasRef = args?.fromRef !== undefined || args?.beforeRef !== undefined || args?.afterRef !== undefined;
|
|
3560
|
-
const _modeCount = [hasTopLevel, hasFlat, hasRef].filter(Boolean).length;
|
|
3561
|
-
|
|
3562
|
-
if (_modeCount > 1) {
|
|
3563
|
-
return { content: [{ type: "text", text: "❌ from/to, fromFlat/toFlat, ref(fromRef/beforeRef/afterRef) は同時に指定できません。" }], isError: true };
|
|
3502
|
+
// fromRef + beforeRef/afterRef 必須チェック
|
|
3503
|
+
if (!args.fromRef) {
|
|
3504
|
+
return { content: [{ type: "text", text: "❌ fromRef is required." }], isError: true };
|
|
3564
3505
|
}
|
|
3565
|
-
if (
|
|
3566
|
-
return { content: [{ type: "text", text: "❌
|
|
3506
|
+
if (!args.beforeRef && !args.afterRef) {
|
|
3507
|
+
return { content: [{ type: "text", text: "❌ beforeRef or afterRef is required." }], isError: true };
|
|
3567
3508
|
}
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
const { currentState, error: _revError } = await acquireFreshState({
|
|
3574
|
-
expectedRevision: args.expectedRevision,
|
|
3575
|
-
mode, client, postId, sessionId: _sessionId, siteName: _siteName,
|
|
3576
|
-
});
|
|
3577
|
-
if (_revError) return _revError;
|
|
3578
|
-
_preState = currentState || null;
|
|
3509
|
+
if (args.beforeRef && args.afterRef) {
|
|
3510
|
+
return { content: [{ type: "text", text: "❌ beforeRef と afterRef は同時に指定できません。" }], isError: true };
|
|
3511
|
+
}
|
|
3512
|
+
if (!args.snapshotId) {
|
|
3513
|
+
return { content: [{ type: "text", text: "❌ snapshotId required. Get it from get_article_structure or any prior write response." }], isError: true };
|
|
3579
3514
|
}
|
|
3580
3515
|
|
|
3581
|
-
//
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
if (args.beforeRef && args.afterRef) {
|
|
3590
|
-
return { content: [{ type: "text", text: "❌ beforeRef と afterRef は同時に指定できません。" }], isError: true };
|
|
3591
|
-
}
|
|
3592
|
-
if (!args.snapshotId) {
|
|
3593
|
-
return { content: [{ type: "text", text: "❌ ref を使用するには snapshotId が必要です。get_article_structure で取得してください。" }], isError: true };
|
|
3594
|
-
}
|
|
3595
|
-
|
|
3596
|
-
// acquireFreshState で 1 回だけ state 取得 + revision チェック
|
|
3597
|
-
const { currentState, error: _stateError } = await acquireFreshState({
|
|
3598
|
-
expectedRevision: args.expectedRevision,
|
|
3599
|
-
mode, client, postId, sessionId: _sessionId, siteName: _siteName,
|
|
3600
|
-
});
|
|
3601
|
-
if (_stateError) return _stateError;
|
|
3602
|
-
_preState = currentState || null; // Phase 2: 差分計算用
|
|
3603
|
-
|
|
3604
|
-
// ref 解決(同じ currentState で 2 つの ref を解決)
|
|
3605
|
-
try {
|
|
3606
|
-
fromFlat = resolveRefFromState(args.snapshotId, args.fromRef, mode, _sessionId, postId, currentState);
|
|
3607
|
-
const destRef = args.beforeRef || args.afterRef;
|
|
3608
|
-
const resolvedDest = resolveRefFromState(args.snapshotId, destRef, mode, _sessionId, postId, currentState);
|
|
3609
|
-
// 位置計算: beforeRef → そのまま, afterRef → +1
|
|
3610
|
-
toFlat = args.beforeRef ? resolvedDest : resolvedDest + 1;
|
|
3611
|
-
} catch (e) {
|
|
3612
|
-
return { content: [{ type: "text", text: `❌ ref 解決エラー: ${e.message}` }], isError: true };
|
|
3613
|
-
}
|
|
3516
|
+
// acquireFreshState で state 取得 + revision チェック
|
|
3517
|
+
let _preState = null;
|
|
3518
|
+
const { currentState, error: _stateError } = await acquireFreshState({
|
|
3519
|
+
expectedRevision: args.expectedRevision,
|
|
3520
|
+
mode, client, postId, sessionId: _sessionId, siteName: _siteName,
|
|
3521
|
+
});
|
|
3522
|
+
if (_stateError) return _stateError;
|
|
3523
|
+
_preState = currentState || null;
|
|
3614
3524
|
|
|
3615
|
-
|
|
3525
|
+
// ref 解決(同じ currentState で 2 つの ref を解決)
|
|
3526
|
+
try {
|
|
3527
|
+
fromFlat = resolveRefFromState(args.snapshotId, args.fromRef, mode, _sessionId, postId, currentState);
|
|
3528
|
+
const destRef = args.beforeRef || args.afterRef;
|
|
3529
|
+
const resolvedDest = resolveRefFromState(args.snapshotId, destRef, mode, _sessionId, postId, currentState);
|
|
3530
|
+
toFlat = args.beforeRef ? resolvedDest : resolvedDest + 1;
|
|
3531
|
+
} catch (e) {
|
|
3532
|
+
return { content: [{ type: "text", text: `❌ ref 解決エラー: ${e.message}` }], isError: true };
|
|
3616
3533
|
}
|
|
3617
3534
|
|
|
3618
|
-
//
|
|
3619
|
-
const _inputRefs = (
|
|
3535
|
+
// 入力 ref 保持(count <= 1 のみ差分化)
|
|
3536
|
+
const _inputRefs = (count <= 1) ? [args.fromRef] : null;
|
|
3620
3537
|
|
|
3621
3538
|
// 移動結果のレスポンス生成ヘルパー
|
|
3622
3539
|
const moveMsg = (moved) => {
|
|
@@ -3628,56 +3545,21 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3628
3545
|
: `✅ ブロック移動 (${typeLabel})${_mt_mb}`;
|
|
3629
3546
|
};
|
|
3630
3547
|
|
|
3631
|
-
//
|
|
3632
|
-
if (
|
|
3633
|
-
|
|
3634
|
-
return { content: [{ type: "text", text: "❌ fromFlat と toFlat の両方を指定してください。" }], isError: true };
|
|
3635
|
-
}
|
|
3636
|
-
if (fromFlat === toFlat || toFlat === fromFlat + count) {
|
|
3637
|
-
return { content: [{ type: "text", text: `✅ 移動不要(同じ位置)${_mt_mb}` }] };
|
|
3638
|
-
}
|
|
3639
|
-
|
|
3640
|
-
if (mode === 'headless') {
|
|
3641
|
-
try {
|
|
3642
|
-
const result = await client.headlessMoveFlat(postId, fromFlat, toFlat, count);
|
|
3643
|
-
const _snap = await buildResponseSnapshot(mode, client, postId, _sessionId, _siteName, result);
|
|
3644
|
-
const _msg = moveMsg(result.moved);
|
|
3645
|
-
if (_inputRefs) {
|
|
3646
|
-
const changeInfo = buildChangeInfoFromResult('moved', _snap, result, _preState, { inputRefs: _inputRefs });
|
|
3647
|
-
if (changeInfo) return { content: [{ type: "text", text: appendRefChangesToText(_msg, changeInfo) }] };
|
|
3648
|
-
}
|
|
3649
|
-
return { content: [{ type: "text", text: appendSnapshotToTextLegacy(_msg, _snap) }] };
|
|
3650
|
-
} catch (e) {
|
|
3651
|
-
const formatted = formatHeadlessConflictError(e);
|
|
3652
|
-
if (formatted) return formatted;
|
|
3653
|
-
throw e;
|
|
3654
|
-
}
|
|
3655
|
-
}
|
|
3656
|
-
|
|
3657
|
-
const result = await client.sendEditorCommand("move_block", { fromFlat, toFlat, count }, 90000, _postId, _sessionId);
|
|
3658
|
-
if (!result || result.timeout)
|
|
3659
|
-
return timeoutResponse(name, client, args?.site);
|
|
3660
|
-
if (!result.success)
|
|
3661
|
-
return errorResponse(name, result.error, args?.site);
|
|
3662
|
-
const _snap = await buildResponseSnapshot(mode, client, postId, _sessionId, _siteName, result);
|
|
3663
|
-
const _msg = moveMsg(result.moved);
|
|
3664
|
-
if (_inputRefs) {
|
|
3665
|
-
const changeInfo = buildChangeInfoFromResult('moved', _snap, result, _preState, { inputRefs: _inputRefs });
|
|
3666
|
-
if (changeInfo) return { content: [{ type: "text", text: appendRefChangesToText(_msg, changeInfo) }] };
|
|
3667
|
-
}
|
|
3668
|
-
return { content: [{ type: "text", text: appendSnapshotToTextLegacy(_msg, _snap) }] };
|
|
3669
|
-
}
|
|
3670
|
-
|
|
3671
|
-
// 既存モード(from/to トップレベル)
|
|
3672
|
-
if (from === undefined || to === undefined) {
|
|
3673
|
-
return { content: [{ type: "text", text: "❌ from と to の両方を指定してください" }], isError: true };
|
|
3548
|
+
// 同位置チェック
|
|
3549
|
+
if (fromFlat === toFlat || toFlat === fromFlat + count) {
|
|
3550
|
+
return { content: [{ type: "text", text: `✅ 移動不要(同じ位置)${_mt_mb}` }] };
|
|
3674
3551
|
}
|
|
3675
3552
|
|
|
3676
3553
|
if (mode === 'headless') {
|
|
3677
3554
|
try {
|
|
3678
|
-
const result = await client.
|
|
3555
|
+
const result = await client.headlessMoveFlat(postId, fromFlat, toFlat, count);
|
|
3679
3556
|
const _snap = await buildResponseSnapshot(mode, client, postId, _sessionId, _siteName, result);
|
|
3680
|
-
|
|
3557
|
+
const _msg = moveMsg(result.moved);
|
|
3558
|
+
if (_inputRefs) {
|
|
3559
|
+
const changeInfo = buildChangeInfoFromResult('moved', _snap, result, _preState, { inputRefs: _inputRefs });
|
|
3560
|
+
if (changeInfo) return { content: [{ type: "text", text: appendRefChangesToText(_msg, changeInfo) }] };
|
|
3561
|
+
}
|
|
3562
|
+
return { content: [{ type: "text", text: appendSnapshotToTextLegacy(_msg, _snap) }] };
|
|
3681
3563
|
} catch (e) {
|
|
3682
3564
|
const formatted = formatHeadlessConflictError(e);
|
|
3683
3565
|
if (formatted) return formatted;
|
|
@@ -3685,13 +3567,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3685
3567
|
}
|
|
3686
3568
|
}
|
|
3687
3569
|
|
|
3688
|
-
const result = await client.sendEditorCommand("move_block", {
|
|
3570
|
+
const result = await client.sendEditorCommand("move_block", { fromFlat, toFlat, count }, 90000, _postId, _sessionId);
|
|
3689
3571
|
if (!result || result.timeout)
|
|
3690
3572
|
return timeoutResponse(name, client, args?.site);
|
|
3691
3573
|
if (!result.success)
|
|
3692
3574
|
return errorResponse(name, result.error, args?.site);
|
|
3693
3575
|
const _snap = await buildResponseSnapshot(mode, client, postId, _sessionId, _siteName, result);
|
|
3694
|
-
|
|
3576
|
+
const _msg = moveMsg(result.moved);
|
|
3577
|
+
if (_inputRefs) {
|
|
3578
|
+
const changeInfo = buildChangeInfoFromResult('moved', _snap, result, _preState, { inputRefs: _inputRefs });
|
|
3579
|
+
if (changeInfo) return { content: [{ type: "text", text: appendRefChangesToText(_msg, changeInfo) }] };
|
|
3580
|
+
}
|
|
3581
|
+
return { content: [{ type: "text", text: appendSnapshotToTextLegacy(_msg, _snap) }] };
|
|
3695
3582
|
}
|
|
3696
3583
|
|
|
3697
3584
|
case "undo": {
|
|
@@ -3738,9 +3625,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3738
3625
|
const _mt_dup = process.env.FRIDAY_DEBUG === '1' ? `\n[DEBUG] mode=${mode}` : '';
|
|
3739
3626
|
const _siteName = siteName || args?.site || 'default';
|
|
3740
3627
|
|
|
3741
|
-
// ref
|
|
3742
|
-
if (args?.ref
|
|
3743
|
-
return { content: [{ type: "text", text: "❌ ref
|
|
3628
|
+
// ref 必須チェック
|
|
3629
|
+
if (!args?.ref) {
|
|
3630
|
+
return { content: [{ type: "text", text: "❌ ref and snapshotId are required." }], isError: true };
|
|
3744
3631
|
}
|
|
3745
3632
|
|
|
3746
3633
|
// ref 解決 + revision チェック
|
|
@@ -3750,18 +3637,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3750
3637
|
mode, client, postId, sessionId: _sessionId, siteName: _siteName,
|
|
3751
3638
|
});
|
|
3752
3639
|
if (resolved.error) return resolved.error;
|
|
3753
|
-
const _preState = resolved.currentState || null;
|
|
3640
|
+
const _preState = resolved.currentState || null;
|
|
3754
3641
|
|
|
3755
|
-
const index = resolved.index
|
|
3756
|
-
|
|
3757
|
-
// Phase 3: 入力 ref を保持(index 経路は差分化しない)
|
|
3758
|
-
const _inputRef = args?.ref || null;
|
|
3642
|
+
const index = resolved.index;
|
|
3643
|
+
const _inputRef = args.ref;
|
|
3759
3644
|
|
|
3760
3645
|
if (mode === 'headless') {
|
|
3761
3646
|
const _guard = await guardHeadlessConflict(postId, client, name);
|
|
3762
3647
|
if (_guard) return _guard;
|
|
3763
3648
|
if (index === undefined) {
|
|
3764
|
-
return { content: [{ type: "text", text: "❌
|
|
3649
|
+
return { content: [{ type: "text", text: "❌ ref resolution failed. Check snapshotId and ref." }], isError: true };
|
|
3765
3650
|
}
|
|
3766
3651
|
try {
|
|
3767
3652
|
const result = await client.headlessDuplicate(postId, index);
|
|
@@ -3995,38 +3880,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
3995
3880
|
}
|
|
3996
3881
|
|
|
3997
3882
|
case "insert_block": {
|
|
3998
|
-
let { rawHTML, filePath
|
|
3999
|
-
|
|
4000
|
-
// parentIndex は廃止済み(v3.0.0 Phase 5)— position を使用
|
|
4001
|
-
if (args?.parentIndex !== undefined) {
|
|
4002
|
-
return { content: [{ type: "text", text: "❌ parentIndex は廃止されました。代わりに index + position ('before'/'after') を使用してください。" }], isError: true };
|
|
4003
|
-
}
|
|
3883
|
+
let { rawHTML, filePath } = (args || {});
|
|
4004
3884
|
|
|
4005
3885
|
// 排他チェック
|
|
4006
3886
|
if (rawHTML && filePath) {
|
|
4007
3887
|
return { content: [{ type: "text", text: "❌ rawHTML と filePath は同時に指定できません。どちらか一方を指定してください。" }], isError: true };
|
|
4008
3888
|
}
|
|
4009
3889
|
|
|
4010
|
-
// Phase 4b: beforeRef/afterRef と index/position の排他チェック
|
|
4011
3890
|
const _hasRefPosition = args?.beforeRef !== undefined || args?.afterRef !== undefined;
|
|
4012
|
-
const _hasIndexPosition = index !== undefined || position !== undefined;
|
|
4013
|
-
if (_hasRefPosition && _hasIndexPosition) {
|
|
4014
|
-
return { content: [{ type: "text", text: "❌ beforeRef/afterRef と index/position は同時に指定できません。" }], isError: true };
|
|
4015
|
-
}
|
|
4016
3891
|
if (args?.beforeRef && args?.afterRef) {
|
|
4017
3892
|
return { content: [{ type: "text", text: "❌ beforeRef と afterRef は同時に指定できません。" }], isError: true };
|
|
4018
3893
|
}
|
|
4019
3894
|
|
|
4020
|
-
// position 指定時は index 必須(ref モードでないとき)
|
|
4021
|
-
if (!_hasRefPosition && position !== undefined && index === undefined) {
|
|
4022
|
-
return { content: [{ type: "text", text: "❌ position を指定する場合は index も指定してください。末尾追加は index を省略してください。" }], isError: true };
|
|
4023
|
-
}
|
|
4024
|
-
|
|
4025
|
-
// position バリデーション
|
|
4026
|
-
if (position !== undefined && position !== "before" && position !== "after") {
|
|
4027
|
-
return { content: [{ type: "text", text: `❌ position は 'before' または 'after' を指定してください: ${position}` }], isError: true };
|
|
4028
|
-
}
|
|
4029
|
-
|
|
4030
3895
|
// filePath → rawHTML 解決
|
|
4031
3896
|
if (filePath) {
|
|
4032
3897
|
try { rawHTML = readHTMLFromFile(filePath).html; }
|
|
@@ -4036,13 +3901,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4036
3901
|
return { content: [{ type: "text", text: "❌ rawHTML または filePath を指定してください。Gutenberg HTML 形式のブロックマークアップが必要です。" }], isError: true };
|
|
4037
3902
|
}
|
|
4038
3903
|
|
|
4039
|
-
// index バリデーション(現行 Editor 互換: 整数 >= 0)— ref モードでないとき
|
|
4040
|
-
if (!_hasRefPosition && index !== undefined && (!Number.isInteger(index) || index < 0)) {
|
|
4041
|
-
return { content: [{ type: "text", text: `❌ Invalid index: ${index}` }], isError: true };
|
|
4042
|
-
}
|
|
4043
|
-
|
|
4044
3904
|
// update_blocks コードパスに委譲
|
|
4045
|
-
// expectedRevision は全モードで委譲(ref/index/append 問わず)
|
|
4046
3905
|
const delegatedArgs = {
|
|
4047
3906
|
postId: args.postId,
|
|
4048
3907
|
site: args.site,
|
|
@@ -4058,11 +3917,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4058
3917
|
delegatedArgs.insert = { position: args.beforeRef ? 'before' : 'after' };
|
|
4059
3918
|
delegatedArgs.snapshotId = args.snapshotId;
|
|
4060
3919
|
delegatedArgs.appendToEnd = false;
|
|
4061
|
-
} else if (index !== undefined) {
|
|
4062
|
-
// 既存: index モード
|
|
4063
|
-
delegatedArgs.target = { index };
|
|
4064
|
-
delegatedArgs.insert = { position: position ?? 'before' };
|
|
4065
|
-
delegatedArgs.appendToEnd = false;
|
|
4066
3920
|
} else {
|
|
4067
3921
|
// 末尾追加
|
|
4068
3922
|
delegatedArgs.insert = { position: 'before' };
|
|
@@ -4234,9 +4088,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4234
4088
|
return { content: [{ type: "text", text: "❌ action は必須です" }], isError: true };
|
|
4235
4089
|
}
|
|
4236
4090
|
|
|
4237
|
-
// ref
|
|
4238
|
-
if (args?.ref
|
|
4239
|
-
return { content: [{ type: "text", text: "❌ ref
|
|
4091
|
+
// ref 必須チェック
|
|
4092
|
+
if (!args?.ref) {
|
|
4093
|
+
return { content: [{ type: "text", text: "❌ ref and snapshotId are required." }], isError: true };
|
|
4240
4094
|
}
|
|
4241
4095
|
|
|
4242
4096
|
// ref 解決 + revision チェック
|
|
@@ -4246,11 +4100,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4246
4100
|
mode, client, postId, sessionId: _sessionId, siteName: _siteName,
|
|
4247
4101
|
});
|
|
4248
4102
|
if (resolved.error) return resolved.error;
|
|
4249
|
-
const _preState = resolved.currentState || null;
|
|
4103
|
+
const _preState = resolved.currentState || null;
|
|
4250
4104
|
|
|
4251
|
-
const index = resolved.index
|
|
4105
|
+
const index = resolved.index;
|
|
4252
4106
|
if (index === undefined) {
|
|
4253
|
-
return { content: [{ type: "text", text: "❌
|
|
4107
|
+
return { content: [{ type: "text", text: "❌ ref resolution failed. Check snapshotId and ref." }], isError: true };
|
|
4254
4108
|
}
|
|
4255
4109
|
|
|
4256
4110
|
const tableParams = {
|
|
@@ -4539,9 +4393,6 @@ function _detectTargetType(args) {
|
|
|
4539
4393
|
if (t) {
|
|
4540
4394
|
if (t.ref) return 'ref';
|
|
4541
4395
|
if (t.refs) return 'refs';
|
|
4542
|
-
if (t.index !== undefined) return 'index';
|
|
4543
|
-
if (t.indices) return 'indices';
|
|
4544
|
-
if (t.range) return 'range';
|
|
4545
4396
|
if (t.section) return 'section';
|
|
4546
4397
|
if (t.heading) return 'heading';
|
|
4547
4398
|
if (t.blockType) return 'blockType';
|
|
@@ -4549,8 +4400,6 @@ function _detectTargetType(args) {
|
|
|
4549
4400
|
}
|
|
4550
4401
|
if (args.ref || args.beforeRef || args.afterRef || args.fromRef) return 'ref';
|
|
4551
4402
|
if (args.refs) return 'refs';
|
|
4552
|
-
if (args.index !== undefined || args.fromFlat !== undefined || args.from !== undefined) return 'index';
|
|
4553
|
-
if (args.indices) return 'indices';
|
|
4554
4403
|
return null;
|
|
4555
4404
|
}
|
|
4556
4405
|
|