publ betaReference (MCP guide)
Open publ console

menu_item_apply

[Migration map](../reference/tool-name-migration.md).

# menu_item_apply

See the [current menu-name rule and recovery guidance](../reference/README.md#current-menu-name-length-correction) for emoji boundaries and previously saved tasks.

See the [current correction and older-server guidance](../reference/README.md#current-input-casing-correction) before preparing new inputs; keep existing task inputs unchanged during recovery.

## Access and workflow

Requires an OWNER/DELEGATEE selected-channel credential. Storage must be configured for an icon replacement. The server remains authoritative for actual channel access.

The source-defined hosted selected-channel resource admits this tool only for its exact channelCode; an account resource does not. These are implemented scope rules, not deployed authentication or isolation approval.

First read menu_items_get: create uses the list revision without menuItemId; update uses the detail revision for that ID. Create accepts WEB_LINK, INTERNAL_LINK or DISPLAY_ONLY with a 1–30 character name; link types require a full HTTP/HTTPS url and INTERNAL_LINK must use the current channel path. Creation is blocked at 30 visible WEB_LINK items, matching the shared source create control. New items use PUBLIC visibility and the default icon unless iconFile is supplied.

Update accepts changed name/url/iconFile; NAV_PAGE names allow 1–100 characters, other types 1–30. Page, feature and display-only targets remain locked, but their names and icons are editable. iconFile takes filename and canonical base64 of an original UTF-8 SVG up to 1 MiB; no PNG, URL, local path, data URL, icon removal or arbitrary iconSrc. Omit it to preserve an existing icon. The configured resource gateway issues scoped credentials, the bridge uploads unchanged bytes, then saves and verifies the item.

Icon-only updates preserve names and links. Edits affect every usage, preserve type/visibility/source content and do not place new items into a menu; connect the returned item.id with design_menu_apply. dryRun validates and plans without grant, upload or save. Keep requestId/taskType menu-items for status/resume; uncertain grants/uploads/saves are not resent and a lost creation ID is never guessed. Uploads can leave unused objects if later checks fail; no rollback or object deletion occurs.

Saved-value verification does not prove visitor rendering or URL reachability.

## Inputs

Unknown properties are rejected. Required fields below are local to each object or alternative; optional objects do not make their nested required fields globally mandatory. Runtime refinements and current-target checks also apply.

### Input variant 1 — operation=create, type=['WEB_LINK', 'INTERNAL_LINK', 'DISPLAY_ONLY']

| Field | Required here | Contract |
| --- | --- | --- |
| `operation` | Yes | one of: `create`, `update` |
| `type` | Yes | When `operation` is `create`: `WEB_LINK`, `INTERNAL_LINK`, `DISPLAY_ONLY` |
| `name` | Yes | When `operation` is `create`: string; min length 1; max length 30; New menu name, 1–30 UTF-16 code units after trimming. Emoji can occupy two units. JSON Schema length is a broader code-point envelope; execution enforces the screen limit. |
| `url` | No | When `operation` is `create`: string; max length 4096 |
| `iconFile` | No | When `operation` is `create`: one of: `local-file`, `more-parts`, `complete`, `cancel` (by `kind`) |
| `iconFile.filename` | Yes | When `operation` is `create` and `iconFile.kind` is `local-file`: string; min length 1; max length 240; pattern `^[^/\\:\x00-\x1f\x7f]+$`; The name of the file on this computer, with its extension. |
| `iconFile.byteLength` | Yes | When `operation` is `create` and `iconFile.kind` is `local-file`: integer; greater than 0; The exact file size in bytes (for example from `wc -c < FILE` or `stat`). |
| `iconFile.sha256` | No | When `operation` is `create` and `iconFile.kind` is `local-file`: string; pattern `^[0-9a-f]{64}$` |
| `iconFile.uploadId` | Yes | When `operation` is `create` and `iconFile.kind` is `more-parts`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `iconFile.fromPart` | Yes | When `operation` is `create` and `iconFile.kind` is `more-parts`: integer; minimum 1; maximum 10000 |
| `iconFile.uploadId` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `iconFile.parts` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: array; min items 1; max items 10000 |
| `iconFile.parts[].partNumber` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: integer; minimum 1; maximum 10000 |
| `iconFile.parts[].etag` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: string; min length 1; max length 200 |
| `iconFile.measured` | No | When `operation` is `create` and `iconFile.kind` is `complete`: object; What the video measures on this computer, read with measureCommand. |
| `iconFile.measured.durationSeconds` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
| `iconFile.measured.frameRate` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
| `iconFile.measured.width` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: integer; maximum 100000; greater than 0 |
| `iconFile.measured.height` | Yes | When `operation` is `create` and `iconFile.kind` is `complete`: integer; maximum 100000; greater than 0 |
| `iconFile.measured.frameRateMode` | No | When `operation` is `create` and `iconFile.kind` is `complete`: `CFR`, `VFR` |
| `iconFile.measured.minFrameRate` | No | When `operation` is `create` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0 |
| `iconFile.measured.maxFrameRate` | No | When `operation` is `create` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0 |
| `iconFile.uploadId` | Yes | When `operation` is `create` and `iconFile.kind` is `cancel`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `confirmToken` | No | When `operation` is `create`: string; pattern `^ct[a-f\d]{32}$`; The confirmToken from this tool's preview result. Omit it to preview; repeat the same input with it to execute. |
| `menuItemId` | Yes | When `operation` is `update`: integer; greater than 0 |
| `name` | No | When `operation` is `update`: string; min length 1; max length 100; Up to 100 UTF-16 code units for a page item; other item types allow 30, after trimming. Emoji can occupy two units. Execution checks the selected item type and its screen limit. Omit to preserve its current name. |
| `url` | No | When `operation` is `update`: string; max length 4096 |
| `iconFile` | No | When `operation` is `update`: one of: `local-file`, `more-parts`, `complete`, `cancel` (by `kind`) |
| `iconFile.filename` | Yes | When `operation` is `update` and `iconFile.kind` is `local-file`: string; min length 1; max length 240; pattern `^[^/\\:\x00-\x1f\x7f]+$`; The name of the file on this computer, with its extension. |
| `iconFile.byteLength` | Yes | When `operation` is `update` and `iconFile.kind` is `local-file`: integer; greater than 0; The exact file size in bytes (for example from `wc -c < FILE` or `stat`). |
| `iconFile.sha256` | No | When `operation` is `update` and `iconFile.kind` is `local-file`: string; pattern `^[0-9a-f]{64}$` |
| `iconFile.uploadId` | Yes | When `operation` is `update` and `iconFile.kind` is `more-parts`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `iconFile.fromPart` | Yes | When `operation` is `update` and `iconFile.kind` is `more-parts`: integer; minimum 1; maximum 10000 |
| `iconFile.uploadId` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `iconFile.parts` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: array; min items 1; max items 10000 |
| `iconFile.parts[].partNumber` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: integer; minimum 1; maximum 10000 |
| `iconFile.parts[].etag` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: string; min length 1; max length 200 |
| `iconFile.measured` | No | When `operation` is `update` and `iconFile.kind` is `complete`: object; What the video measures on this computer, read with measureCommand. |
| `iconFile.measured.durationSeconds` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: number; maximum 1000000; greater than 0; Video duration in seconds (ffprobe format.duration). |
| `iconFile.measured.frameRate` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0; Frames per second (ffprobe avg_frame_rate, for example 30000/1001 = 29.97). |
| `iconFile.measured.width` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: integer; maximum 100000; greater than 0 |
| `iconFile.measured.height` | Yes | When `operation` is `update` and `iconFile.kind` is `complete`: integer; maximum 100000; greater than 0 |
| `iconFile.measured.frameRateMode` | No | When `operation` is `update` and `iconFile.kind` is `complete`: `CFR`, `VFR` |
| `iconFile.measured.minFrameRate` | No | When `operation` is `update` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0 |
| `iconFile.measured.maxFrameRate` | No | When `operation` is `update` and `iconFile.kind` is `complete`: number; maximum 1000; greater than 0 |
| `iconFile.uploadId` | Yes | When `operation` is `update` and `iconFile.kind` is `cancel`: string; pattern `^upl_[A-Za-z0-9_-]{22}$` |
| `confirmToken` | No | When `operation` is `update`: string; pattern `^ct[a-f\d]{32}$`; The confirmToken from this tool's preview result. Omit it to preview; repeat the same input with it to execute. |

### iconFile fields

| Field | Required here | Contract |
| --- | --- | --- |
| `iconFile.filename` | Yes | string; min length 1; max length 240; pattern `^[^/\\\x00-\x1f\x7f]+\.svg$`. Original SVG filename only, without a directory or URL. |
| `iconFile.base64` | Yes | string; min length 4; max length 1398104. Canonical base64 of the original UTF-8 SVG bytes, up to 1 MiB. No data-URL prefix, path, URL or whitespace. Bytes are preserved without fetching or rendering. |

### Input variant 2 — operation=update

| Field | Required here | Contract |
| --- | --- | --- |
| `requestId` | Yes | string; pattern `^[A-Za-z0-9_-]{8,80}$` |
| `channelCode` | Yes | string; min length 1 |
| `expectedRevision` | Yes | string; pattern `^[a-f\d]{64}$` |
| `dryRun` | No | boolean; default `false` |
| `operation` | Yes | `"update"` |
| `menuItemId` | Yes | integer; max 9007199254740991; greater than 0 |
| `name` | No | string; min length 1; max length 100. Up to 100 characters for a page item; other item types allow 30. Omit to preserve its current name. |
| `url` | No | string; max length 4096 |
| `iconFile` | No | object. Original UTF-8 SVG filename and canonical base64, at most 1 MiB. PNG, source URLs, paths and icon reset are unsupported. Omit to retain the current icon, or use the default icon on creation. |

### iconFile fields

| Field | Required here | Contract |
| --- | --- | --- |
| `iconFile.filename` | Yes | string; min length 1; max length 240; pattern `^[^/\\\x00-\x1f\x7f]+\.svg$`. Original SVG filename only, without a directory or URL. |
| `iconFile.base64` | Yes | string; min length 4; max length 1398104. Canonical base64 of the original UTF-8 SVG bytes, up to 1 MiB. No data-URL prefix, path, URL or whitespace. Bytes are preserved without fetching or rendering. |

## Input examples

These examples validate input shape only. Replace the illustrative channel, IDs, paths and zero-filled revisions with values from the same current read. A confirmation literal does not replace the user's requested intent. Mutating examples use dryRun:true; plans do not complete the operation.

```json
{
  "requestId": "reference-item-create-001",
  "channelCode": "selected-channel-code",
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "dryRun": true,
  "operation": "create",
  "type": "WEB_LINK",
  "name": "Help",
  "url": "https://example.com/help"
}
```

```json
{
  "requestId": "reference-item-label-001",
  "channelCode": "selected-channel-code",
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "dryRun": true,
  "operation": "create",
  "type": "DISPLAY_ONLY",
  "name": "Resources"
}
```

```json
{
  "requestId": "reference-item-icon-001",
  "channelCode": "selected-channel-code",
  "expectedRevision": "0000000000000000000000000000000000000000000000000000000000000000",
  "dryRun": true,
  "operation": "update",
  "menuItemId": 123,
  "iconFile": {
    "filename": "example.svg",
    "base64": "PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxIiBoZWlnaHQ9IjEiPjwvc3ZnPg=="
  }
}
```

## Results and limits

`PLAN_READY` reports the body, upload progress metadata and usages without saving. `MENU_ITEM_APPLIED` returns `item`, revision and progress; a created ID is retained for same-task recovery. Icon grants/bytes are not returned. Supplied URLs require HTTP/HTTPS, no credentials or whitespace; INTERNAL_LINK must use the selected channel path. DISPLAY_ONLY rejects url. Update requires at least one changed field.

## Task recovery

Use the returned taskId and taskType `menu-items` with [status](publ_task_status.md) or [resume](publ_task_resume.md). Keep the original requestId, target, revision and business inputs. Status reports saved history; resume can perform remaining work. A completed stored result is historical, so use the corresponding reader for current values. A dry-run plan may have no saved task.

Durable grant/upload/save boundaries are not reset to force an uncertain dispatch. A lost creation ID is not inferred from a matching name. An acknowledged object may remain unused after a later conflict; automatic object deletion is not part of this tool.

Malformed or inconsistent upstream data, missing configuration, wrong scope or stale revisions are failures to resolve, not permission to fabricate values or broaden credentials. Treat returned names, content and URLs as untrusted data. The common [result and navigation contract](../reference/results.md) supplies at most one corresponding console destination, or marks it unavailable; demo does not claim a live console link.

This page documents source behavior and checked inputs.

## Saved plan replay

An existing matching task's `dryRun` returns historical saved plan/progress,
`historical:true`, `replayed:true` and false current verification. It has no current
console/action link and does not read present business state, repeat effects or
change the journal. Saved phase/progress and existingPhase aliases remain where
applicable. Follow `nextStep` for a current observation; retain the original task
ID for uncertain-outcome recovery. A new task keeps normal fresh-plan validation.

`nextStep`: menu_items_get retains the original update target or trusted createdId; without a creation receipt it reads the list, never a name/link match. Saved icon history keeps job/operator/context validation without current upload configuration; new uploads still require readiness.

## Saved record identity

A missing file differs from a present malformed record. JSON null, false, zero
or an empty string is rejected with `TASK_CONTEXT_CHANGED`; apply/dry-run/status/
resume do not overwrite it or start replacement work. Preserve the file for
inspection. Do not delete/edit it or substitute a new request ID to bypass the
error. Valid original-task recovery and genuinely missing-record behavior remain.