Skip to main content
Version: v2 · 2.4.0

The v2 tools

Glyphs MCP 2.4.0 provides twenty-one data/action tools plus the optional show_edit_workflow display tool, including save-free open_edit_tab and duplicate_document. Desktop build 71, coordinated sidecar/bridge product version 2.4.0, lean interface revision 1 and bridge protocol 1 identify different parts of the release. The negotiated MCP transport version is separate. Inspect get_status for independent component identities and advertised capabilities; see version and identity.

ToolArgumentsPurpose
get_statusNoneRead sidecar, worker and bridge availability plus negotiated read/write/job capabilities.
list_tasksoptional cursor (0), limit (50)Read open workflows, blockers and paginated closed history without native polling, execution or action tokens.
list_documentsNoneIdentify open documents and their paths, saved state, edit generation and native isCurrent (true/false/null when unavailable).
duplicate_documentdocument_id, idempotency_key, optional family_nameCopy the current font, including unsaved edits, into an independent unsaved document. Requires document.duplicate.v1; never saves either font.
create_documentfamily_name, idempotency_key, optional units_per_em (1000)Create and open an unsaved font with one Regular master and instance. Requires document.create.v1.
open_documentpath, idempotency_keyOpen an existing absolute local .glyphs file or .glyphspackage folder. Reuse an already-open source without reloading or saving; requires document.open.v1.
import_documentpath, idempotency_keyImport a local UFO, OTF or TTF without saving its source; requires document.import.v1.
activate_documentdocument_idShow the exact live font window and bring Glyphs to the front; requires document.activate.v1.
open_edit_tabdocument_id, idempotency_key, optional text, glyphs, master_idOpen and activate a blank, text or exact-glyph Edit tab without saving, including unsaved fonts; requires document.edit-tab.open.v1.
close_documentdocument_id, idempotency_key, optional unsaved_changes (default refuse), destinationClose with explicit save/discard handling and pending-job protection; requires document.close.v1.
read_entitiesdocument_id, entities, fieldsRead at most 100 explicit targets per call.
start_jobdocument_id, kind, optional delta, glyphs, optionsPrepare a supported job without applying it; the negotiated route may use detached native targets or the external worker.
compare_fontsbaseline_files, candidate_files, optional optionsCompare explicit compiled TTFs through the optional Diffenator runtime, returning a file-bound job without a live document.
get_jobjob_id, optional include_previewRead current status, summary, report, sample and error details.
apply_jobjob_id, optional include_preview, approved_overwritesApply a mutation result as a reversible live change.
accept_jobjob_id, optional destination, include_previewPersist an applied mutation, or publish verified export or comparison artifacts to a new directory.
discard_jobjob_id, optional include_previewCancel preparation, discard a proposal or restore current affected live targets.
save_documentdocument_id, optional destination, retry_checkpoint_job_idSave an explicitly identified unowned document.
start_edit_workflowdocument_id, kind, idempotency_key, optional mode, delta, glyphs, options, auto_keep (default true)Retain a requested edit; successful edit cards may Keep after 30 seconds. Honor “wait for my answer” with auto_keep=false.
get_edit_workflowworkflow_id, optional include_review (default false)Read authoritative state, revision, linked job evidence, offered actions and text guidance without opening a card.
show_edit_workflowworkflow_id, optional include_review (default false)Open an optional card for useful visual review, reports, live controls or recovery. Reuse it across stages; do not open cards for routine chat outcomes.
respond_edit_workflowworkflow_id, expected_revision, action_token, optional destination, approved_overwrites, automatic (card only, default false)Execute one offered action with duplicate protection. wait_for_answer disables automatic Keep; automatic=true permits only Keep after success.

Find an open task​

list_tasks(cursor=0, limit=50) returns all unresolved entries on every page, with closed history paginated separately. It correlates workflow and job IDs and reports blockers and known origins. The inventory is passive: it does not execute, save, close, or poll a native operation and does not mint action tokens. Use a retained workflow ID with get_edit_workflow for fresh state, revision and offered choices; use get_job for unmatched job diagnostics. Resume an abandoned workflow through its existing lifecycle rather than rerunning a script.

Create a font​

create_document requires font-creation authorization and no existing document or saved baseline. It opens a new font with the requested family name, one Regular master, one Regular instance and no initial glyphs. family_name and the idempotency key are 1–255 characters without control characters. units_per_em is an integer from 16 to 16384, defaulting to 1000. Native default axes and master metrics are retained. Templates and configurable masters/axes are not supported.

The result contains id, familyName, unitsPerEm, path, dirty, generation, masterIds, instanceIds, creationId and bridgeSessionId. Retain id as the document binding for reads, edits and saving. Creation never saves; under save authorization, use save_document(document_id=id, destination=...) with a new absolute .glyphs or .glyphspackage path. Existing edit-job saved-baseline requirements still apply to this new document.

Use a fresh idempotency key for each intended font. After a lost response, retry the same key and arguments: the bridge retains the outcome and the sidecar journals the request before dispatch. Different arguments give creation_conflict. Closing the created font gives document_not_found on retry, without creating a replacement. After a bridge restart, creation_outcome_unknown requires reconciling the original font; a new key must not be used merely to bypass an uncertain result. A partial native failure reports creation_failed and, when discoverable, its documentId; it never silently closes the font or repeats construction. The bridge retains at most 1024 creation keys per session, then refuses new keys without evicting old ones.

Open a font​

open_document(path, idempotency_key) opens a native local source in Glyphs 4, with no existing document or saved baseline required. Paths must be absolute and point to a .glyphs file or .glyphspackage folder. Symlinks resolve to their canonical source. URLs and binary/UFO imports are outside this operation.

The result contains id, path, familyName, dirty, generation, alreadyOpen, openId and bridgeSessionId. Reuse id for subsequent reads, edits and saving. An already-open font is returned without reloading, discarding unsaved edits or explicitly changing window focus. Open never saves or closes another font. Its authorization does not authorize subsequent edits or saving.

Retain a fresh key per intended Open and retry identical arguments after a lost response. Sidecar journals and bridge outcomes prevent duplicate openings. A changed path gives opening_conflict; a closed result gives document_not_found without reopening. After a bridge restart, opening_outcome_unknown requires reconciling the original document. A failed native call may include its documentId; inspect it without replaying Open. The bridge retains up to 1024 Open keys per session and refuses additional keys without evicting earlier ones.

Import a font​

import_document(path, idempotency_key) imports an absolute local .ufo folder, .otf file or .ttf file through Glyphs' native importer. Native sources use open_document. Import needs no open font, saved baseline or script, and never changes import preferences, saves, exports or overwrites its source.

The result binds the actual returned font by id, even when it is pathless. It includes sourcePath, sourceFormat, importId, bridgeSessionId, alreadyOpen, requiresSaveAs, viewOnlyUntilSaveAs, nativeSourceFormats and warnings. Compiled fonts are view-only until Save As to a new native source; decompilation can lose hinting or OpenType tables. Re-export may differ from the original. Saving needs separate authorization and a new .glyphs or .glyphspackage destination; source fingerprints verify that Save As preserves the original. Imported sources do not become edit-job baselines until saved in a native format.

Retry the same key and path after a lost response. Conflicts give importing_conflict; closed results give document_not_found without reopening. A bridge restart or unreadable record gives importing_outcome_unknown and requires reconciliation. Partial failures remain open and are never retried implicitly. Up to 1024 import outcomes are retained per bridge session.

Activate or close a font​

activate_document(document_id) shows the exact live window and requests that Glyphs come to the front. It returns the fresh document state, windowShown and activationRequested; isCurrent reports the observed font selection. Repeated activation is safe. Closed IDs fail without reopening or selecting another font.

close_document(document_id, idempotency_key, unsaved_changes="refuse") closes only that font. Default refuse leaves unsaved or unknown changes open and returns unsaved_changes_required. Choose save under save authorization, with an explicit new destination for pathless/imported fonts. The existing native save, source-hash verification and optional checkpoint complete before Close. Failed saves/checkpoints leave the font open. Choose discard only when the user has explicitly authorized losing all unsaved edits in this font. Cancel means make no tool call. A clean font can close without saving.

Preparing, ready, active, applied or uncertain jobs block Close. Resolve them through their existing Keep/Save/Discard/recovery lifecycle; Close does not resolve jobs. Registration and Close serialize across sidecar transports, and native guards recheck ownership, path, dirty state and generation immediately before closing. There is no modal save prompt.

The result includes id, path, closed, discardedUnsavedChanges, closeId, bridgeSessionId and, for Save-and-Close, saveReceipt. Retain one key and its options per intended Close. Lost responses return the original outcome without another save or close, even after the font has closed. Changed options give closing_conflict; later edits give stale_document and require renewed review. Restarted bridges, unreadable records or unobserved native outcomes require reconciliation. Retrying an uncertain native Close only observes its result; it never repeats the native call. Up to 1024 outcomes are retained per session.

Conversation workflow​

get_status.workflowCapabilities advertises edit.workflow.v1. The three workflow tools wrap existing services; saved, clean source prerequisites remain intact for typed edits. Native scripts use a current-state memory recovery copy without Save. They support every advertised mutation kind, including dimensions_edit, and exclude compilation and export.

Use apply for requested edits and preview for preview-only requests. The coordinator validates scope, capabilities and worker availability before offering a prerequisite save. It binds the request to an explicit document ID. Ordinary typed complete results apply automatically; warnings, skipped/unavailable targets and required overwrites retain report review. Application never saves.

The shared resource is ui://glyphs-mcp/edit-workflow-v1.html, with MIME type text/html;profile=mcp-app. Tool _meta.ui.resourceUri and standard host capabilities control rendering. The resource bundles its code and styles and uses only the host's MCP tool bridge. Every response also returns useful text and structured data. See Editing in the conversation.

The assistant maps ordinary-language replies to server-issued actions; users never need IDs, tokens or JSON. A token is scoped to one workflow revision and action. Repeating a consumed token with identical arguments returns current state without another save/edit; conflicting arguments or stale choices fail. Retry start_edit_workflow with the same idempotency key and unchanged request.

Save and continue saves existing work once, then starts the retained request. It does not authorize saving the resulting edit. Save As uses an absolute new destination and preserves non-overwrite checks. Manual save is followed by Check and continue. Previous applied work must be explicitly saved/discarded; one follow-up request can wait per document. Closing a card does not cancel work.

Small workflow records live beside jobs. After restart they report interruption and reconcile existing operations on request, without replaying a save or edit. Uncertain execution offers outcome checking rather than a blind retry. Native source/target guards, Undo, discard and recovery remain authoritative.

Dimensions reference notes​

With master.dimensions.read.v1, request dimensions on 1–4 exact master selectors. Each response has items, total, returned, and complete. Entries contain key, label, script, present, numeric value, exact storedValue, valid, and editable. These are sidebar reference notes, not outlines, hinting stems or measurements computed from glyphs. Reads require no Save and do not create metadata. Zero is set; malformed data is not blank. Unknown fields are preserved and read-only. The native catalog has 60 fields.

With master.dimensions.edit.v1, dimensions_edit accepts options.changes: 1–100 entries containing exact master, key, and numeric value (null to clear). No delta or glyphs. Editing uses the existing saved-clean-source workflow. The complete report lists every master/field, exact before/after state and fill, overwrite, clear, or no-op classification.

Fill unset fields using task-established values without confirmation. Before replacing or clearing an existing value, show the exact proposal and obtain explicit approval in the conversation. Pass the report's requiredOverwrites entries unchanged as apply_job.approved_overwrites; each contains master, key, before, and after, with present and exact stored value in each state. The sidecar and bridge reject mismatched or missing approval entries. This is assistant-reported conversational approval, not authenticated consent.

Mixed batches wait for overwrite approval before any application. If a change is declined, prepare a reduced job. Stale values require a new proposal and fresh approval for any changed overwrite. Application never saves; document-level Undo/Redo and whole-job discard preserve exact metadata and originally absent containers. Save separately only when authorized. Writes are qualified for Glyphs 4.1 build 4107; other builds retain reads without advertising edits.

Save-free Edit tabs​

open_edit_tab(document_id, idempotency_key, text=None, glyphs=None, master_id=None) opens and activates a new tab in the exact live document, including unsaved fonts. It requires document.edit-tab.open.v1, no script, baseline or edit workflow. Supply proof text (0–4096 Unicode characters, native /glyphName syntax) or 1–100 exact glyph names in order, including repeats; omit both for a blank tab. An optional exact master ID selects the displayed master; otherwise retain the font's current master. Missing glyph/master layers reject before any tab opens.

The result includes documentId, session-local tabId, tabOpenId, bridgeSessionId, masterId, created and activationRequested, plus fresh native document state. Existing tabs remain open. No font save or geometry edit occurs; native UI state can affect the dirty flag and is never reset by the tool.

Use a fresh key for each intended tab. Identical retries return the original receipt, without creating or reactivating another tab. Changed arguments return edit_tab_conflict; a closed tab returns edit_tab_closed. Bridge restarts, unreadable journals and attempted native failures return edit_tab_outcome_unknown and require outcome reconciliation. Pending jobs owning the document block the action. Tab listing, editing, closing and special layers remain outside the tool.

Native tab opening is qualified on Glyphs 4.1.1 build 4108 with disposable imported and pathless fonts: blank/text/glyph-list tabs, retries, visible proof, unchanged outlines/metrics and no font save. Multiple-master selection and callback failures are source-tested; this editor qualification used one master. The headless runtime does not advertise the action.

Unsaved demo copies and current-state script recovery​

duplicate_document(document_id, idempotency_key, family_name=None) creates an independent pathless copy of the exact live font, including unsaved contents. Optional family renaming affects only the copy. Require document.duplicate.v1. Neither font is saved. Identical retries reuse the original copy; conflicts, closed results, bridge restarts and partial native failures are reconciled without creating another font. Pending source jobs must be resolved first.

Save-free scripting requires script.recovery.snapshot.v1 and preparation version 3. Native qualification on Glyphs 4.1.1 build 4108 covers pathless Run, pre-existing unsaved edits, independent copies, intentional failure and whole-font recovery of outlines, widths, feature source, user data and master metadata. Saved-path restoration and duplication preserve the source bytes and observed pre-run dirty state. Clean pathless restoration is native-tested; clean saved restoration is source-tested. Recovery is session-local and excludes external assets and other documents. See the repository's qualification record at src/bridge/script-snapshot-qualification.json for the exact tested and installed development identities; this candidate has not been published.

Explicit reads​

Discover the intended font once with list_documents; retain its document_id for this MCP connection and reuse it for fresh reads of the same font. Rediscover after document_not_found or explicit target change, never for an unrelated input error. Never substitute another open font. Reconsider current/frontmost intent when the prior target may no longer be intended.

entities is a list of selectors. fields applies to each requested entity, so group compatible entity kinds in a call.

KindSelectorAvailable fields
Glyph{"kind":"glyph","id":"H"}name, unicode, category, subCategory, export
Layer{"kind":"layer","glyph":"H","id":"<layer ID>"}id, name, width, vertWidth, vertOrigin, leftMetricsKey, rightMetricsKey, widthMetricsKey, bounds, outlineHash
Master{"kind":"master","id":"<master ID>"}id, name; with master.properties.v1: ascender, capHeight, xHeight, descender, italicAngle, axes
Master page{"kind":"masters","limit":100}; sole selectorSame requested master fields; see pagination and axis limits below
Glyph inventory{"kind":"glyphs"}; optional limit, opaque cursorname
Layer inventory{"kind":"layers","glyph":"a"}; optional limit, opaque cursorid, name, associatedMasterId, isMasterLayer, isSpecialLayer, isBraceLayer, isBracketLayer
Context{"kind":"context"}; optional glyphLimit with selectedGlyphsview, master, selectedGlyphs
Selection{"kind":"selection"}; optional nodeLimit only with nodesglyph, layer, selectedNodeCount, selectedAnchorCount, selectedComponentCount, selectedGuideCount, selectedOtherCount, optional nodes
Path inventory{"kind":"paths","glyph":"y","layer":"<layer ID>"}; optional limit, opaque cursoritems only; summaries include path/shape indices, state, counts, bounds and pathHash
Path nodes{"kind":"path","glyph":"y","layer":"<layer ID>","index":0}; optional limit, opaque cursornodes only; normalized raw node records and pathHash
Segment{"kind":"segment","glyph":"y","layer":"<layer ID>","path":0,"endNode":4}type, startNode, endNode, controlNodes, points, length, pathHash
Kerning{"kind":"kerning","master":"<master ID>","direction":"LTR","left":"A","right":"V"}value only; null means absent.
Feature-block inventory{"kind":"feature_blocks","blockType":"feature","limit":100}; sole selectoritems only; blockType is prefix, class or feature.
Feature block{"kind":"feature_block","blockType":"feature","id":"<persistent ID>"}id, name, code, automatic, disabled, canBeAutomated, notes, labels, errors, errorType, errorTooltip, filePath
Instance inventory{"kind":"instances","limit":100}; sole selectoritems only; persistent ID, name, static/variable type, export flag and outline format.
Instance{"kind":"instance","id":"<persistent ID>"}id, name, type, exports, outlineFormat, axisValues

Resolve IDs from the document or selection data; do not guess a layer ID. Kerning selectors address stored keys. Supported directions are LTR, RTL and vertical; collision preparation is LTR only.

layer.read.exact.v1 in get_status.bridge.readCapabilities advertises exact layer selection and native id = layerId read-back. Supply both a nonempty glyph name and exact native layer ID; omitted/wrong-type values return invalid_request, and missing targets or name aliases return target_not_found. Special/backup layers can be read by their own explicit IDs; their names are not selectors. If a required capability is unavailable, the private installation needs updating: update the bridge, sidecar and skills together, reload and verify fresh status. Do not substitute workflows for earlier private builds. Stored layer metrics keys and optional vertical values do not resolve glyph-level inheritance. Native bounds preserve fractions; outlineHash is an opaque guard, not a geometry payload. See the shipped glyphs/references/layer-reads.md for field limits and measurement interpretation.

For master discovery, require masters.list.v1 in get_status → bridge.readCapabilities. The page's values.items contains native IDs/names, total gives the current count, and complete: false requires another call with nextCursor passed unchanged as the selector's cursor. The integer limit is 1–100. A page cannot be mixed with other selectors. Reads work on dirty and unsaved documents without saving. stale_master_cursor requires restarting without a cursor after a document, count or page-boundary change. Pages are live reads, not an atomic snapshot; avoid editing between pages and re-enumerate after observed edits.

master.read.exact.v1 advertises strict explicit master selection: require a nonempty native ID, never a name alias. IDs need not be UUIDs, and names can repeat. Require these capabilities on the current private build; if absent, update the bridge, sidecar and skills together. Protocol 1 alone does not establish support.

master.properties.v1 adds requested fractional native master defaults and optional axes to exact and paged reads. These are not per-layer or export metrics; reading the italic angle does not slant outlines. Axes return native order, exact axisId, tag, name, index, internalValue and externalValue, inside items with total, returned and complete. Native null positions have explicit unavailable locations and complete:false; missing/unreadable native getters return unsupported_read. No mapping is calculated. At most 32 axes per master and 256 master-axis items per request; narrow the master subset/page or omit axes if invalid_request reports this bound. Unrequested fields remain absent. See glyphs/references/master-reads.md for precision and targeting rules.

For glyph-name discovery, require negotiated glyphs.list.v1. One glyph-page selector returns native-order items, total, returned, complete and an opaque nextCursor. The integer limit is 1–100, default 100; name is initially the only field. Pass the cursor unchanged until complete. On stale_glyph_cursor or any observed edit, discard partial results and restart. Document/count/change- signal/dirty/boundary guards are bounded, not an atomic multi-call snapshot. Known names need no inventory. See glyphs/references/glyph-discovery.md.

For document context, require negotiated document.context.v1. Use one context selector and request view, master and/or selectedGlyphs. View is font, edit or unavailable; master is native id/name or null. Selected glyph names follow native font.selection or font.selectedLayers order, bounded by an integer glyphLimit of 1–100 (default 100). Native selection can collapse repeated Edit View occurrences. total, returned, limit, complete and source make coverage explicit; missing native evidence includes an unavailable reason. Incomplete evidence is not a complete edit scope. No node details are implicit. See glyphs/references/context-reads.md. This capability also advertises the native isCurrent marker in list_documents; retained IDs never retarget.

For selection, require negotiated selection.context.v1. Request the compact counts explicitly; smaller projections remain valid. Add nodes only when coordinates are needed, with one selection entity in the entire request. nodeLimit defaults to 64 and accepts integers 1–256. Nodes return total, returned, limit, complete and items; truncated evidence is never complete. Indices use zero-based layer.paths (excluding components) and path.nodes (including off-curves). Null glyph/layer and nodes:null mean no active Edit View. See the shipped glyphs/references/selection-reads.md for exact semantics.

Path geometry requires negotiated paths.list.v1 and path.geometry.v1. With outline.background.read.v1, exact layer, paths, path and segment selectors also accept surface:"background"; omission means foreground. IDs always identify the owning foreground layer. Missing backgrounds are rejected without creating one. Paging cursors are bound to the selected surface. Path pages contain at most 100 summaries; node pages contain at most 256 nodes. Pass opaque cursors unchanged. A cursor guards document generation, dirty state, target, count and exact path geometry; stale_path_cursor requires restarting without it. Reads work for dirty and unsaved fonts. pathHash is an opaque exact path guard. Segment endNode is an on-curve raw node index; line, cubic and quadratic geometry is normalized, while malformed or unknown segment types are rejected. These live pages are bounded, not an atomic snapshot.

Stored kerning reads use exactly fields:["value"] and 1–100 explicit selectors. Use glyph names or established stored @MMK_ group keys, not native glyph IDs. null means no entry for the exact keys/master/direction; zero is stored and fractions are retained. This does not compute effective class/exception kerning. Dirty and unsaved fonts need no Save or job for inspection. One bad selector rejects the request. See glyphs/references/kerning-reads.md for recovery.

Feature-source reads require features.read.v1. Feature pages are live, native-order pages of one block type with opaque cursors and a 1–100 limit. Restart after stale_feature_cursor. Exact selectors use persistent IDs, never display names. Instance reads similarly require instances.read.v1; pages use opaque cursors and restart after stale_instance_cursor. Exact instance IDs feed export jobs. Both reads work on dirty documents without a Save.

Example read_entities arguments:

{
"document_id": "<document ID>",
"entities": [{"kind": "selection"}],
"fields": ["glyph", "layer", "selectedNodeCount"]
}

Supported job kinds​

kindScope and options
width_deltaFinite, nonzero delta; optional glyphs; no options. Adjusts widths across the selected glyphs' layers.
spacingOptional glyphs and master IDs, references, width mode and sampling options. Spacing guide.
kerning_collisionExplicit options.pairs, optional master IDs and sampling options; no glyphs. Kerning guide.
kerning_editkerning.edit.exact.v1; 1–100 options.edits with op: set/remove, explicit master and direction, glyph/group left and right objects. Only set accepts finite value; zero is stored. Native preparation and selective recovery; no worker or collision analysis.
start_nodesExplicit glyphs, contour index and master/reference selection. Correspondence guide.
slantOptional glyphs, angle, pivot, master IDs and straight-stem preservation. Slant guide.
outline_editTyped, ordered path operations on ordinary masters or explicit layer IDs; requires bridge outline.edit.v1.
native_actionOne closed Glyphs command over exact layer/glyph/font targets; requires bridge native.action.v1 and the action in nativeActions.
feature_compileClosed saved-source or live-editor compiler diagnostic; requires the corresponding feature.compile.*.v1 capability.
font_exportOne exact static/variable instance exported to private staging and structurally verified; requires export and verification capabilities.

With kerning.proof.v1, read_entities accepts one kerning_proof selector with exact master, direction, requested languages, optional limit (1–100) and unchanged continuation cursor, using fields:["pairs"]. Mapping visits at most 256 glyphs per read; candidate pages scan at most 256 pairs. Empty pages can be incomplete. Primary Unicode ambiguities are skipped. Results include existing group/exception coverage and unshaped proof strings, with pinned MIT provenance. They are pairs to inspect, without numeric recommendations.

For example, the mutation fields for increasing H's layer widths by 8.125 units are shown below. Pass them with the explicit document_id to start_job, or with document_id, mode, and an idempotency_key to start_edit_workflow.

{"kind": "width_delta", "delta": 8.125, "glyphs": ["H"]}

Only width_delta accepts delta. The other jobs use options. outline_edit selects glyphs inside options.targets, not the top-level glyphs argument. A supplied glyph list must contain unique nonempty names; use the workflow's narrower limits where specified. Missing or incompatible targets are reported, not silently substituted.

outline_edit is advertised in jobKinds only when the live bridge advertises outline.edit.v1 in writeCapabilities. Each target supplies glyph, referenceLayer, path hash guards, an ordered operations list and either layers:{"scope":"all_masters"} or explicit IDs. all_masters excludes brace, bracket and other special layers. Add surface:"background" to a target to edit its owning layers’ existing backgrounds; this requires outline.background.edit.v1. The reference guard and all operations then address that same surface. Foreground is the default. Both surfaces support the same path operations and Undo/discard lifecycle; background images are outside this contract. Background topology is excluded from foreground interpolation checks, and report rows identify their surface. Operations are split_segment, update_nodes, remove_node, delete_nodes, reverse_path, set_start_node, set_closed, add_path and delete_path; subsequent indices refer to the evolving path. remove_node requires outline.remove-node.v1, targets one on-curve raw index, and uses Glyphs' native keep-shape operation. Its report lists the exact removed raw indices and surviving nodes adjusted by Glyphs. delete_nodes remains raw topology surgery and produces a contour-geometry risk warning. Arc length is the split default and path_time is explicit. The closed contract allows 1–32 unique split fractions, 256 total operations, 4,096 expanded layer changes and 4,096 nodes per path. Coordinates must be finite. Locked, malformed, degenerate, unsupported and hint-referenced destructive targets are rejected. compatibilityPolicy:"preserve" is the default; allow_incompatible requires an explicit user request and is highlighted in the report.

{
"document_id": "<document ID>",
"kind": "outline_edit",
"options": {
"compatibilityPolicy": "preserve",
"targets": [{
"glyph": "y", "layers": {"scope": "all_masters"},
"referenceLayer": "<Thin Extended layer ID>",
"guards": [{"path": 0, "hash": "sha256:0000000000000000000000000000000000000000000000000000000000000000"}],
"operations": [{"op": "split_segment", "path": 0,
"startNode": 3, "endNode": 4, "fractions": [0.10, 0.25, 0.82],
"measure": "arc_length"}]
}]
}
}

Native shape-preserving removal is explicit:

{"op":"remove_node","path":0,"node":25}

Preparation resolves this to the same operation plus ascending removedNodes. Multiple removals are separate ordered operations so every later index addresses the evolving path.

Closed native actions​

native_action is advertised only when the running bridge reports native.action.v1 and at least one recognized action. get_status.nativeActions is the sorted live catalog; unknown bridge values are filtered. Check both the job kind and the requested action immediately before preparation. A missing action is unavailable on that Glyphs build and cannot be replaced with a menu name, selector, method name or Python payload.

Each job contains exactly one action. Layer actions accept 1–100 explicit glyph targets, each with layers:{"scope":"all_masters"} or layers:{"scope":"ids","ids":[...]}. all_masters excludes brace, bracket, other special and background layers. Exact IDs can select stored special layers. Expansion is capped at 4,096 layers. Glyph actions use targets:[{"glyph":"A"}]; font actions forbid targets. Automatic block updates use targets:[{"blockType":"feature","id":"<persistent ID>"}] and require native automatic plus canBeAutomated evidence.

{
"document_id": "<document ID>",
"kind": "native_action",
"options": {
"action": "update_metrics",
"targets": [{
"glyph": "h",
"layers": {"scope": "all_masters"}
}]
}
}

The closed catalog is:

ActionScopeFixed behavior
update_metricslayersyncMetrics(); never updateMetrics()
correct_path_directionlayercorrectPathDirection()
round_coordinateslayerroundCoordinates()
add_extremeslayeraddNodesAtExtremes(force, false); optional boolean arguments.force, default false
cleanup_pathslayercleanUpPaths()
remove_overlaplayerremoveOverlap(false); never selection-dependent
add_missing_anchorslayeraddMissingAnchors()
align_componentslayerQualified immediate component alignment
decompose_componentslayerdecomposeComponents()
decompose_cornerslayerdecomposeCorners()
make_componentslayermakeComponents()
reinterpolatelayerreinterpolate()
connect_open_pathslayerconnectAllOpenPaths()
swap_foreground_backgroundlayer pairswapForegroundWithBackground()
update_glyph_infoglyphupdateGlyphInfo(false); never renames
update_featuresfontupdateFeatures()
update_automatic_feature_blockfeature blockNative update() on one exact automatic, automatable prefix/class/feature

All other actions accept an empty/omitted arguments object. Top-level delta and glyphs, unknown fields, arbitrary selectors and selection-sensitive variants are rejected. Compiler-only work uses the separate feature_compile diagnostic job; binary generation uses the separate font_export artifact job. Generic menu invocation remains outside the contract.

Most actions run on a private saved snapshot in the worker. Glyph colors may prepare on detached target copies inside Glyphs without a worker or live edits. It hashes an action-specific Glyphs property-list projection and reports exact targets, changed/no-op counts, warnings, before/after summaries and the full report path. Encoded state is limited to 8 MiB per target and 64 MiB per job. The live bridge checks the prepared before hash, retains a bounded native snapshot, registers exact native Undo/Redo, invokes only the cataloged selector, checks the after hash, and immediately restores on exception or readback mismatch. Layer/glyph actions use glyph history; update_features uses document history. Native snapshots never cross the bridge or become a general object API.

Feature compilation​

feature_compile is advertised when either feature.compile.saved.v1 or feature.compile.live.v1 appears in jobCapabilities. Options contain only mode, defaulting to saved:

{"document_id":"<document ID>","kind":"feature_compile","options":{"mode":"saved"}}

Saved mode opens the exact saved snapshot in the external Glyphs worker, calls GSFont.compileFeatures(), interprets its explicit success/error result and verifies that full persisted feature state did not change. Live mode performs the call on Glyphs' main thread against the clean open document, retains a deep feature snapshot, and restores on a persisted-state mismatch. Both modes use the common saved-clean start guard and never save.

The terminal resultKind is diagnostic and status is completed. Compiler rejection is represented as report.success:false with normalized native and block diagnostics; it is not a worker failure. Diagnostic jobs cannot be applied, accepted or discarded. Compilation establishes compiler acceptance on that host, not exported shaping behavior.

Verified font export​

font_export is advertised only with font.verify.tables.v1 and a static or variable export capability. Select one exact persistent instanceId; format is otf or ttf; containers are 1–3 unique values from plain, woff, woff2. Web containers require font.export.web.v1. Typed generation flags are autoHint, removeOverlap, useSubroutines, useProductionNames and decomposeSmartComponents. Variable export fixes overlap removal to false; TTF forbids subroutines.

Optional verification.shapingCases contains at most 32 feature-on/off expectations with a four-character tag and optional unaffected control, direction, script, language and variation coordinates. Any case requires font.verify.shaping.v1.

{
"document_id":"<document ID>",
"kind":"font_export",
"options":{
"instanceId":"<persistent ID>",
"format":"otf",
"containers":["plain","woff2"],
"verification":{"shapingCases":[]}
}
}

The external worker calls typed GSInstance.generate() against the saved snapshot, never saves it, and keeps outputs in job-private staging. fontTools must parse every file and verify required tables, outline type, variable state and container. HarfBuzz verifies requested shaping samples. The exact manifest allows 1–3 regular files, at most 512 MiB each and 1 GiB total.

The ready resultKind is artifact; apply_job is invalid. discard_job deletes private staging. Authorized publication calls accept_job with a new absolute destination directory. The sidecar rechecks the clean source and every staged hash, fsyncs a private sibling directory and publishes it by atomic rename without overwrite. A receipt binds destination, source hash and manifest. Interrupted publication is reconciled from the exact destination; it is never blindly replayed.

Results and lifecycle​

Tool results use the existing envelope: {"ok":true,"data":...} or {"ok":false,"error":...}. Inspect the returned data rather than treating transport success as job success.

A job starts as preparing, then becomes ready, completed, failed or moves through cancelling to cancelled. Applying a ready job moves through applying to its reported outcome. Accepting an applied job moves through accepting to terminal accepted; an invoked but unverified native save becomes terminal accept_uncertain. Discarding an applied job moves through discarding. Use get_job until the operation is terminal; inspect errors and recovery details even when preparation initially succeeded. resultKind distinguishes mutation, terminal diagnostic and publishable artifact results.

include_preview defaults to true on get_job, apply_job, accept_job and discard_job. Set it to false to omit top-level sample and report.sample; the response explicitly returns previewIncluded:false. Counts, report metadata, progress, errors and operation state remain. Read the full report once before application and use the compact projection for subsequent polling.

get_job includes a compact sample and, where supplied, the path to the full external report. One glyph can produce several changes. A ready proposal with no changes is not evidence that a new spacing result was applied.

All preparation currently requires a saved, clean baseline. Applying mutation jobs is always save-free. accept_job accepts the reviewed result; native Undo/Redo use per-glyph history before acceptance. Discard restores the entire job only where its affected targets remain current. See Safety and recovery.

For uncertain execution, retain the job ID and reconcile it with get_job. Do not automatically submit a replacement. Job IDs belong to the current service session, not a durable cross-restart queue.

Verified saving​

accept_job requires an applied job. Without destination, it saves to the document's current path. With a destination, it performs Save As to a new absolute .glyphs or .glyphspackage path. Before saving, the bridge validates the stored resolved targets against the live document in bounded chunks. This does not exclude unrelated edits: acceptance saves the entire document.

save_document performs the same verified Save or Save As for an explicitly identified document only when no job for it is applying, applied, accepting or discarding. A pathless document requires Save As. Save As never overwrites an existing destination, follows an unsafe symlink or collides with another open document. Neither tool uses a save dialog, font index or frontmost-document inference. Calling either tool is the authorization to save; there is no separate confirm or reason argument.

The persistence receipt reports the save and document/job identities, Save or Save As mode, old and new paths, dirty state, native success, source hashes before and after, whether the source changed since job preparation, and verification:"native_and_source_hash". Save As additionally verifies that the original source hash did not change. A prior manual Save is recorded as a changed source where applicable, but accept_job invokes native Save again. Receipt hashes describe the persisted source; they do not prove that unrelated live edits outside a job's targets were absent.

Preflight errors happen before the native selector and leave an applied job retryable and discardable. After selector invocation, any exception, false result, path/dirty mismatch or source-hash failure is uncertain and must not be replayed automatically. An accepted job is idempotent and cannot be discarded. Git diff and commit remain outside Glyphs MCP; use the receipt path and hash in that separate workflow.

Precision and boundaries​

Spacing preparation and reference comparison use an inclusive absolute 0.001 font-unit tolerance. Width changes are evaluated independently of negligible translations. Native write verification, recovery, Undo/Redo, topology and source hashes remain exact.

Native Python uses kind="python_script" through start_edit_workflow with capability script.native.v1, without adding tools. Dedicated master replacement, Unicode allocation and untyped repair endpoints remain absent. Typed path editing uses bounded outline_edit schemas; complete-target Glyphs commands use the negotiated closed native_action catalog. Compilation and export are closed job kinds over typed arguments, not generic compiler/export endpoints. None is a generic selector or object protocol. The companions add native views, not extra MCP tools; the data/action count is twenty-one; optional card display adds one tool.

Kerning discovery​

read_entities advertises kerning.groups.v1 for explicitly requested native left/right/top/bottom kerning group and key properties on named glyphs. kerning.pairs.v1 adds one kerning_pairs selector for an exact master and LTR/RTL/vertical direction. Request left, right and/or value, with optional raw leftKey/rightKey filters. Pages return at most 100 stored pairs and scan at most 256 groups/entries, including misses. total is null (unavailable); follow nextCursor until complete, even after an empty page. Sides retain raw keys and native glyph-name resolution, including explicit unresolved IDs. Pages are live, not atomic; restart after an observed edit or stale_kerning_cursor. These are reads only: no effective-kerning engine, job or Save. Missing private capabilities require a coordinated bridge/sidecar/skills update. These capabilities reuse the twenty-one data/action tools; card display is separate.

Native scripts​

Use python_script with source, JSON params, entrypoint="per_target"|"script", targets, and optional intended-effect summary (500 characters). executionMode and recovery are retired and rejected. Target selectors are explicit glyph/layer/ surface entries or all/named glyphs in one exact master; background IDs identify owning foreground layers. Whole scripts may use an empty target list; callbacks require eligible surfaces. Native scripts have no fixed surface-count ceiling. The complete internal request is limited to 4 MiB, including the resolved manifest; source and parameters retain their 128 KiB and 64 KiB bounds. Large selectors resolve in cancellable main-thread chunks. Oversize fails before execution, without silently truncating the requested scope. Typed limits remain unchanged. Use a coordinated bridge/sidecar installation for this preparation protocol; never replace a rejected manifest with a whole-font loop to evade it.

A requested font task authorizes in-scope Run without a separate code review or human click. Write-only/review-only requests do not execute; writing includes code review, while explicit previews wait. Preparation and polling never run module code. Require script.recovery.snapshot.v1 and preparation version 3. Dirty, clean and pathless fonts run without Save. A detached current-state copy is captured immediately before execution; capture failure prevents execution.

get_edit_workflow defaults include_review=false for compact progress and action state. Set it true for optional View details/Show script. Scripts run on Glyphs' main thread; callbacks yield between batches. Cancellation stops remaining calls, not the current invocation. Successful execution does not verify intended changes.

auto_keep=true on new workflows enables a countdown only in a visible, connected and freshly reconciled successful result card: Keep changes without saving is selected after 30 seconds. Failed, partial, blocked and uncertain outcomes never automatically Keep. Pass auto_keep=false for “wait for my answer”, or dispatch the existing wait_for_answer action. This disables the countdown without cancelling work. Retain that preference until changed. Disable Automatic Keep before waiting on a native question. Silence, Skip and dismissal apply no choice and end no recovery offer; do not reopen the same question automatically. Elapsed time never authorizes Run, Apply, Save, Undo or Restore. Only cards use automatic=true when dispatching the timed Keep; the server rejects that flag for other actions or outcomes. Hidden cards, open Script details and reconnects require a fresh read and countdown. Text-only clients remain manual. Cards display Pause countdown while counting down and Turn off automatic Keep before it starts; both invoke wait_for_answer. Wait for my answer remains the conversational opt-out. Opening View details or More options pauses the countdown; successful background reads preserve time only for an unchanged workflow revision and request.

Portable workflow cards​

Native interaction comes first. The twenty-one data/action tools do not attach UI resources; show_edit_workflow is the additional display-only tool. Open one card only when a visual comparison, interactive inspection, substantial report, meaningful live controls or recovery information adds value, or the user requests it. Use native questions for unresolved decisions and missing information, grouping related independent questions within host limits; use ordinary chat when unavailable. Routine progress, success and errors use native conversation or tool feedback. Reuse an existing card as it refreshes; polling does not create another card. No additional question is needed for an already authorized action.

Visual glyph comparisons and interactive inspection require a suitable renderer or native Glyphs view. The workflow card presents lifecycle state, report details and offered controls; it does not itself provide a dedicated outline viewer.

Cards lead with the font, current status, a request summary of at most 140 characters and known target counts. The filename identifies the document; View details contains the full path, complete request, optional script source/output and diagnostics. Important warnings and recovery consequences remain visible.

Each offered action retains its identifier, token and destination requirement. The optional presentation field groups it as primary, secondary, menu or auto_keep. Cards place less common choices under More options; text responses list main choices first and other options separately. Machine-readable controls retain all valid actions. Clients without presentation metadata can use the original action order.

uiRefreshIntervalMs is a card-only hint: 1500 during processing, 5000 while choices or recovery remain, and 0 for settled outcomes without actions. The existing poll field continues to guide agent polling; an idle card refresh does not require continuous conversational polling. Hidden cards suspend reads and reconcile on return.

An outdated choice hides its old controls and triggers a read of the same workflow. A successful read replaces the obsolete state and clears the stale warning without replaying an action. A failed read offers Check status; destination validation errors remain visible until resolved or superseded. Interactive rendering follows negotiated MCP Apps capabilities, with no client-name-specific branches. Text-only clients reconcile through the same tools; the server cannot rewrite a client's historical messages or host-owned controls. Compatibility in a named client still requires checks in that client.

Restore before run restores the whole pre-run copy, including pre-existing unsaved edits, discards later edits and clears Undo history, returning a fresh document binding. Recovery is held in bridge memory and does not survive Glyphs restarts. Keep or Save ends that offer. Failures can leave partial edits; reload is never automatic. External effects are not restored. Reconcile uncertain outcomes without replaying code.

Unified edit results​

The existing respond_edit_workflow tool offers finish_edit for successful typed results and retains finish_script for scripts. Both Keep without saving and end the workflow recovery offer. Typed Keep preserves native Undo/Redo; selective Undo these changes remains available until Keep or Save. Script recovery uses whole-font Restore before run. Typed blockers offer manual Keep, including jobs without an originating workflow; blockingWorkflowId identifies one when present.

New successful typed cards share the 30-second Keep countdown with scripts. Existing typed records remain manual. Failed or uncertain Wait requests pause the local countdown; no result timer runs for unresolved, partial or blocked outcomes.

The private edit.prepare.native.v1 capability selects bounded native preparation for width deltas, Dimensions, glyph colors and coordinate-only node updates. It adds no public tool or user mode. Saved/clean prerequisites, limits and selective recovery remain unchanged. Other requests retain their existing worker route.

Optional local Git checkpoints​

font.checkpoints.v1 in workflow capabilities adds on-demand checkpoint_history, checkpoint_details, checkpoint_compare and checkpoint_scope selectors to read_entities with fields=["checkpoint"]. Use one selector, 1–100 entries per page, returned cursors, and exact full revisions. These reads do not load history during ordinary polling. Action records distinguish execution, verification and possible manual edits.

The explicit project setting in .glyphs-mcp.json authorizes local checkpoints, including the starting saved baseline, but no extra font Saves or push. After a verified Save, Git failure returns a separate failed checkpoint receipt. Retry with the offered workflow action or retry_checkpoint_job_id; no Save is repeated.

With font.checkpoint-restore.v1, kind=checkpoint_restore and options={revision: FULL_REVISION} use the existing edit workflow. Applying loads that font version in Glyphs, replaces subsequent unsaved edits and clears Undo. It leaves the disk font untouched, returns the fresh document binding and does not save. A later authorized Save records the restoration in a new checkpoint.

Compiled comparisons​

compare_fonts requires font.compare.diffenator.v1 in get_status.comparisonCapabilities. comparisonWorker reports the separate runtime's availability; Glyphs may be closed. Inputs, options, limits, snapshot verification, cancellation and create-only report publication are described in Compare compiled fonts. File-bound comparison jobs cannot be applied and do not use start_job or document save checks.