Skip to main content

Release QA protocol

This protocol is the pre-release manual QA pass for Glyphs MCP. Run it before publishing a new public version, especially after changes to the plug-in bundle, command registration, Glyphs API compatibility, installer, tool profiles, tool schemas, or mutation helpers.

The goal is not to prove every implementation branch. Unit tests do that. The goal is to verify that the most common real workflows still work in realistic conditions: a large production .glyphs file, real Glyphs UI state, a real MCP client, the Streamable HTTP endpoint, and both supported app lines.

Release gate

A release is ready only when all required checks pass in both:

RuntimeRequired result
Glyphs 3.5.xAll required checks pass or have documented, accepted Glyphs-3-only limitations.
Glyphs 4.xAll required checks pass or have documented, accepted Glyphs-4-only limitations.

Release blockers:

  • Server cannot start, restart, or stop from Edit -> Glyphs MCP Server.
  • Client cannot connect to http://127.0.0.1:9680/mcp/.
  • get_server_info, list_open_fonts, or get_selected_font_and_master fails in normal use.
  • The advertised version, code hash, runtime ID, tool profile, or resource path is clearly wrong.
  • A read-only tool mutates the font.
  • A mutation tool changes the font without confirm=true when it is expected to be confirm-gated.
  • A dry run mutates the font.
  • A common workflow crashes Glyphs, hangs the server, corrupts the font, or returns unbounded output.
  • Glyphs 3.5 and Glyphs 4 expose materially different tool schemas without a documented compatibility reason.

Automated per-tool release gate

Every public MCP tool must have an entry in src/glyphs-mcp/tests/test_release_gate_tool_coverage.py.

That gate records:

  • The test owner or accepted live-smoke requirement.
  • Whether the tool is read-only, edits the font, runs code, writes files, opens UI, saves files, or exposes server docs.
  • The undo-risk level.
  • A short live smoke prompt that can be reused in Glyphs 3.5 and Glyphs 4.

The automated gate must pass before any release candidate:

python3 -m unittest src/glyphs-mcp/tests/test_release_gate_tool_coverage.py
python3 -m unittest discover -s src/glyphs-mcp/tests

Release blockers from this gate:

  • A registered MCP tool is missing from the release-gate manifest.
  • A stale manifest entry names a tool that is no longer registered.
  • A tool entry has no owner test, no live smoke prompt, or no undo-risk classification.
  • An editing or code-execution tool is not classified for undo risk.
  • Any executable Python resource calls glyph.beginUndo() or glyph.endUndo().

The last rule is intentional. Live Glyphs 4 QA showed that MCP-driven batch edits can trigger Glyphs' "There was a problem with undo" recovery dialog when glyph undo groups are used around larger script edits. Mutation helpers should use documented layer/font update APIs and small scoped writes instead.

Test assets

Use at least one large real font file. A good release candidate font has:

  • 1,000+ glyphs.
  • 2+ masters, ideally with roman and italic or weight/width variation.
  • Multiple instances.
  • Real kerning groups and several thousand kerning pairs.
  • OpenType features, including at least one ssXX feature.
  • Components, anchors, annotations if available, guides, bracket layers if available, and non-exporting glyphs.
  • A mix of simple and complex outlines.

Never run destructive tests on the only copy of a production file.

Prepare these files:

FilePurpose
LargeFont-release-baseline.glyphsUntouched reference copy.
LargeFont-release-glyphs35.glyphsGlyphs 3.5 working copy.
LargeFont-release-glyphs4.glyphsGlyphs 4 working copy.

For each working copy, record:

  • File path.
  • File size.
  • Glyph count.
  • Master count and master names.
  • Instance count.
  • Kerning pair count for the main master.
  • Glyphs app version and build number.
  • Glyphs MCP version being tested.
  • Git commit or release tag.

Environment matrix

Run the same test script against each row:

CaseGlyphs appPython modeTool profileMCP clientRequired
G35-editGlyphs 3.5.xGlyphs Python or configured external runtimeEditCodex or ClaudeYes
G35-readonlyGlyphs 3.5.xSame as G35-editRead-onlySame clientYes
G4-editGlyphs 4.xGlyphs Python or configured external runtimeEditCodex or ClaudeYes
G4-readonlyGlyphs 4.xSame as G4-editRead-onlySame clientYes
G35-secondary-clientGlyphs 3.5.xSame as G35-editEditOther supported clientRecommended
G4-secondary-clientGlyphs 4.xSame as G4-editEditOther supported clientRecommended

Use the same release artifact for both app versions whenever possible. If Glyphs 3.5 and Glyphs 4 require different install paths or artifacts, record the exact artifact used for each.

Preflight

For each app version:

  1. Quit all Glyphs versions.
  2. Install the release candidate plug-in and dependencies using the same path users will use: signed installer app, terminal installer, or manual plug-in install.
  3. Start only the app version under test.
  4. Open the matching large-font working copy.
  5. Start the server from Edit -> Glyphs MCP Server.
  6. Confirm the status panel shows the expected version, profile, endpoint, and docs URL.
  7. Connect the MCP client to:
http://127.0.0.1:9680/mcp/
  1. Reconnect the MCP client after every profile change.
  2. Keep the Macro Panel visible during at least one pass and capture any repeated tracebacks.

Pass criteria:

  • Server starts without traceback.
  • Port 9680 is reachable locally.
  • Browser access to /mcp/ returns discovery JSON or an expected MCP transport response.
  • MCP client can list tools.
  • The large font remains open and responsive.

Transport and schema checks

Run once in each required matrix case before workflow testing.

IDActionExpected result
T1Connect a real MCP client to /mcp/ using Streamable HTTP.Client initializes without transport warnings.
T2Inspect request/response headers if the client exposes them.Session handling is stable; any returned Mcp-Session-Id is preserved by the client.
T3Call tools/list or use the client's tool inventory view.Tool names, descriptions, and schemas load without JSON schema errors.
T4Compare tools/list in Read-only and Edit profiles.Read-only hides mutation and execution tools; Edit exposes the full surface.
T5Compare the observed Edit profile tool list with the current command set.Missing, renamed, or extra tools are intentional and documented.
T6Call one tool with malformed JSON arguments.Error is structured and bounded; server remains usable.
T7Call one tool with a valid but semantically invalid argument, such as a bad font_index.Error clearly identifies the invalid input.

Smoke tests

Run first in every matrix case.

IDActionExpected result
S1Call get_server_info.Returns version, runtime ID, code hash, resource path, Glyphs reachability, open-font count, and no error.
S2Call list_open_fonts.Shows the large font with a stable font_index, family name, file path if saved, and expected glyph/master counts.
S3Call get_font_masters for the large font.Returns all masters with stable IDs, names, metrics, and no missing active master.
S4Call get_font_instances.Returns instances without crashing on interpolation data.
S5Select 3-8 glyphs in Glyphs, then call get_selected_font_and_master.Response matches the active font, active master, and current UI selection.
S6Switch the active master in Glyphs and repeat S5.Active master changes in the response.
S7Stop and restart the server from Glyphs UI, then reconnect.New calls succeed; stale client sessions fail gracefully or reconnect cleanly.
S8Switch between Read-only and Edit profiles, reconnecting each time.tools/list changes as expected; hidden mutation tools are unavailable in Read-only.

Read-only inspection

Run in both Read-only and Edit profiles. The font must not be marked dirty by these checks.

IDActionExpected result
R1Call get_font_glyphs with the large font.Completes in acceptable time and returns glyph names, export flags, categories, and show links or unavailable reasons.
R2Call get_glyph_details for A, n, O, one accented composite, and one non-exporting glyph.Returns layers, components, anchors, paths, and metadata without schema errors.
R3Call get_glyph_components for composite glyphs.Component names, transforms, and target layers are understandable.
R4Call get_glyph_paths for simple and complex glyphs.Returns valid path JSON and bounded output.
R5Select nodes in Glyphs and call get_selected_nodes.Reports selected nodes with layer, path, node index, and per-master mapping when available.
R6Call get_selected_glyphs after changing selection in Glyphs.Response tracks the UI selection and includes show links where possible.
R7Call list_style_sets.Returns stylistic sets, source/replacement glyph groups, and group show links for alternates.
R8Call get_font_kerning for the main master.Returns kerning data without timeout, broken group names, or excessive client-side truncation.
R9Call review_collinear_handles on one glyph with curves.Returns findings or a clean result without mutation.
R10Call render_glyph_review_image for 6-12 glyphs with metrics, sidebearings, bounds, nodes, handles, anchors, and guides overlays.Client receives a visible image or base64 fallback; Glyphs does not become dirty.
R11Click or open a returned showHttpUrl or showMarkdown link.Glyphs focuses the expected glyph/layer, or the response explains why the link is unavailable.
R12Call docs_search for GSLayer bounds, then docs_get for the best result.Returns focused bundled docs without registering all pages.
R13Call resources/list if the client exposes it.Core resources are available without flooding the client with every docs page by default.

Spacing workflow

Run in Edit profile only. Use 10-30 glyphs, including H, O, n, o, sidebearing-sensitive punctuation, and one composite.

IDActionExpected result
SP1Call review_spacing on the selected glyphs or explicit glyph names.Returns suggestions, skipped items, measurement bands, and no mutation.
SP2Call apply_spacing with dry_run=true and conservative clamps.Returns proposed deltas and changed-count preview; font is not dirty because of the dry run.
SP3Call set_spacing_params with a small test parameter change on the working copy.Updates only the intended font/master custom parameters and does not save automatically.
SP4Call set_spacing_guides to add guides to 2-3 test glyphs.Guides appear in Glyphs and are scoped to target glyphs/layers.
SP5Call set_spacing_guides again to clear the guides.Test guides are removed without deleting unrelated guides.
SP6After explicit human approval, call apply_spacing with confirm=true on 1-3 expendable glyphs.Only approved glyph/layer metrics change; changes are visible in Glyphs and undoable where Glyphs supports it.

Pass criteria:

  • Dry-run and review calls do not mutate.
  • Confirmed apply is scoped and predictable.
  • No auto-save occurs.
  • Glyphs 3.5 and Glyphs 4 produce equivalent results within expected API differences.

Kerning workflow

Run in Edit profile. Use the main kerning master and a file with real kerning groups.

IDActionExpected result
K1Call generate_kerning_tab with a moderate limit.Glyphs opens a proof tab with relevant strings; server response includes counts and skipped conditions.
K2Call review_kerning_bumper with include_existing=true, scan_mode="two_pass", and bounded limits.Returns collisions/near-misses or a clean result without mutation.
K3Call apply_kerning_bumper with dry_run=true.Returns proposed glyph-glyph exceptions; no kerning changes are written.
K4After explicit human approval, call apply_kerning_bumper with confirm=true on a small limited set.Only approved kerning exceptions change.
K5Call set_kerning_pair on one disposable pair, then call get_font_kerning.Pair appears with the expected value.
K6Remove or restore the disposable pair with set_kerning_pair.Pair is removed/restored and no unrelated kerning changes occur.

Pass criteria:

  • Large kerning data does not hang the client.
  • Group and glyph exceptions resolve consistently.
  • Confirmed mutation does not affect unrelated pairs.

Outline and glyph editing workflow

Run in Edit profile on disposable glyphs or a copied working file. In Glyphs 4, run each mutating row as a separate smoke step; do not chain create, path replacement, and delete into one batch. Pause after each mutation and stop immediately if Glyphs shows an undo recovery dialog.

IDActionExpected result
E1Call create_glyph for a test glyph such as gmcp.test.Glyph appears in Glyphs with expected defaults.
E2Call update_glyph_properties on gmcp.test.Unicode/category/export/color or other targeted property changes correctly.
E3Call update_glyph_metrics on gmcp.test.Width and sidebearings update only on intended layers.
E4Call copy_glyph from a simple source glyph to gmcp.test.Outlines/components copy as requested.
E5Call add_component_to_glyph on gmcp.test.Component appears with expected transform.
E6Call add_anchor_to_glyph on gmcp.test.Anchor appears on the target layer.
E7Call get_glyph_paths, modify a small path fixture, then call set_glyph_paths on gmcp.test.Paths are replaced only on the target layer and remain valid in Glyphs.
E8Select eligible nodes and call add_corner_to_all_masters with a known _corner.* name.Corner hints are added across intended masters only, or the tool returns a clear precondition error.
E9Call apply_collinear_handles_smooth with dry_run=true.Reports proposed smooth flags and does not mutate.
E10After explicit approval, call apply_collinear_handles_smooth with confirm=true on gmcp.test.Smooth flags update only where reported.
E11Call delete_glyph for gmcp.test.Test glyph is removed and references are not left in a broken state.

Pass criteria:

  • All edit tools target the requested glyph/layer/master.
  • Error messages are clear for invalid glyph names, missing layers, bad path JSON, and incompatible selections.
  • The app remains responsive after path replacement and corner operations.

Annotation workflow

Run in Edit profile on 1-2 disposable glyphs.

IDActionExpected result
A1Call get_glyph_annotations.Existing annotations are listed or an empty list is returned.
A2Call add_glyph_annotation.Native Glyphs annotation appears and MCP ownership metadata is stored.
A3Call add_glyph_annotation_group for a small linked note/shape group.Group appears and is returned by get_glyph_annotation_groups.
A4Call update_glyph_annotation on the created annotation.Text, position, or style changes exactly as requested.
A5Call delete_glyph_annotation on the created annotation.Only that annotation is removed.
A6Call clear_glyph_annotations with default MCP-managed scope.MCP-managed test annotations are removed; unrelated manual annotations remain.
A7On a copied file only, call clear_glyph_annotations with scope="all" for the test layer.All annotations on that layer are removed and no other layer is affected.

Italic and compensated tuning workflow

Run only on copied working files. Prefer a multi-master font where roman/italic or weight masters are compatible enough for meaningful checks.

IDActionExpected result
I1Call set_master_italic_angle with dry_run=true.Reports intended master and angle without changing Font Info.
I2After approval, call set_master_italic_angle with confirm=true on the working copy.Only the intended master's Metrics italicAngle changes.
I3Call review_italic_first_pass for 3-5 glyphs.Reports copy/slant plan, skips unsafe glyphs, and does not mutate.
I4Call apply_italic_first_pass with dry_run=true.Returns the same proposed scope without mutation.
I5After approval, call apply_italic_first_pass with confirm=true on disposable glyphs/layers.Creates expected backups if configured, updates only targets, and remains visually plausible.
C1Call review_master_stem_metrics.Reports existing or missing stem metrics for relevant masters.
C2Call set_master_stem_metrics with dry_run=true.Reports intended custom-parameter changes without mutation.
C3Call measure_stem_ratio between two masters.Returns numeric ratio or clear measurement failure.
C4Call review_compensated_tuning on one compatible glyph.Returns set_glyph_paths-compatible JSON and does not mutate.
C5Call apply_compensated_tuning with dry_run=true on 1-3 glyphs.Reports proposed transforms and backup behavior without mutation.
C6After approval, call apply_compensated_tuning with confirm=true on disposable glyphs.Only approved glyphs change; backups are created when requested.

Pass criteria:

  • Source angle convention is consistent in both app versions.
  • Tools reject incompatible layers with clear errors rather than producing broken outlines.
  • Confirmed changes are reviewable visually with render_glyph_review_image.

Export and save workflow

Run on copied working files only.

IDActionExpected result
X1Call ExportDesignspaceAndUFO to a temporary output directory with decompose_smart_components=false and decompose_smart_corners=false.Designspace and UFOs are written with structured logs and no silent failures.
X2Inspect exported file count and error log.All expected masters/instances are present or documented as skipped.
X3Call save_font with no path after a small approved test mutation.Active working copy is saved; no other open font is saved.
X4Call save_font with a new temporary path.Save-as path is created and the active document state is clear.

Execute-code workflow

Run in Edit profile. These checks verify real-world scripting without using it for tasks that have dedicated tools.

IDActionExpected result
EC1Call execute_code with a small read-only snippet that returns Glyphs version and active family name.Captures stdout/stderr and returns the last expression as configured.
EC2Call execute_code with capture_output=false.Call completes and large output is not returned through MCP.
EC3Call execute_code with max_output_chars and a deliberately long print.Output is bounded and clearly truncated.
EC4Call execute_code_with_context for a selected glyph/layer read-only snippet.Injected font, glyph, and layer helpers point to the expected targets.
EC5Call execute_code with snippet_only=true.Returns a Macro Panel snippet without executing it.
EC6Call an invalid snippet.Error envelope is structured, bounded, and does not kill the server.
EC7Attempt a snippet containing exit() or sys.exit() on a copied file.Server returns an error or safe failure and remains usable afterward.

Pass criteria:

  • Execution tools do not leak unbounded output.
  • Errors stay structured and do not poison later calls.
  • Dedicated tools remain preferred in documentation and agent prompts.

Security and local access checks

Run at least once per release candidate, preferably in Glyphs 4 unless auth/origin code changed.

IDActionExpected result
SEC1Start without GLYPHS_MCP_AUTH_TOKEN.Local client connects normally.
SEC2Start with GLYPHS_MCP_AUTH_TOKEN set.Requests without token fail; requests with Authorization: Bearer <token> succeed.
SEC3Test mcp-auth-token header if the client/tooling supports custom headers.Valid token succeeds.
SEC4Start with restrictive GLYPHS_MCP_ALLOWED_ORIGINS.Allowed local origins work; disallowed origins fail when an Origin header is present.
SEC5Confirm localhost binding.Server is not unexpectedly exposed beyond local host.

Installer and packaging checks

Run on a clean or near-clean macOS user account when possible.

IDActionExpected result
P1Install with the signed installer app.Plug-in, dependencies, client configs, and optional skills install without manual file edits.
P2Install into Glyphs 3 explicitly.Plug-in lands in the Glyphs 3-compatible location and starts in Glyphs 3.5.
P3Install into Glyphs 4 explicitly or by the terminal installer default.Plug-in lands in the Glyphs 4-compatible location and starts in Glyphs 4.
P4Re-run installer over an existing install.Upgrade path is clear and does not duplicate plug-ins or client entries.
P5Install skills for Codex and Claude where selected.Skills appear after client reload and reference the expected MCP endpoint/workflows.
P6Uninstall or overwrite manually if that path is documented for users.Leaves no stale duplicate bundle that Glyphs can load accidentally.

Performance and resilience

Measure on the large font in both Glyphs 3.5 and Glyphs 4.

CheckTarget
Server startStarts in less than 10 seconds on a normal development machine.
get_server_infoLess than 2 seconds.
list_open_fontsLess than 3 seconds.
get_font_glyphs on large fontLess than 15 seconds, or documented if the font is unusually large.
get_font_kerning on main masterLess than 20 seconds with bounded response size acceptable to the client.
render_glyph_review_image for 12 glyphsLess than 20 seconds and image is visible.
review_kerning_bumper with bounded limitsCompletes without freezing Glyphs; record elapsed time.
Repeated calls20 mixed read calls in a row do not leak obvious memory, crash, or progressively slow down.

Resilience checks:

  • Close the active font and call list_open_fonts.
  • Call a glyph-specific tool with no open font.
  • Call a tool with an invalid font_index.
  • Call a tool with a missing glyph name.
  • Call a mutating tool in Read-only profile.
  • Interrupt or cancel a client request if the client supports cancellation.
  • Restart the MCP client while the Glyphs server remains running.

Expected result: clear errors, no crash, and subsequent valid calls still work.

Cross-version comparison

After the two required app passes, compare the result logs.

Record differences in:

  • Tool list and schemas.
  • Server info fields.
  • Font, master, instance, glyph, path, component, and kerning response shapes.
  • Show-link availability.
  • Image rendering output.
  • Error messages.
  • Performance.
  • Dirty-state behavior.
  • Any Glyphs API compatibility warning in the Macro Panel.

Acceptable differences:

  • Glyphs app build numbers.
  • Glyphs API fields that do not exist in one app version.
  • Different internal layer/master IDs for files resaved by different app versions.
  • Minor rendering differences that do not block visual review.

Unacceptable differences:

  • Same tool call succeeds in one app version and crashes or hangs in the other.
  • Mutating calls affect different targets.
  • Dry runs mutate in either app.
  • Tool schemas are missing in one app version without intentional profile or compatibility reason.

Regression prompt pack

Use these prompts in a real MCP client rather than calling only low-level test scripts. They verify the agent-facing experience and common user paths.

Connection

Use Glyphs MCP to verify the current setup.
Call get_server_info, list_open_fonts, and get_selected_font_and_master.
Tell me the app version, MCP version, active font, active master, selected glyphs, and whether this looks ready for editing.
Do not mutate or save.

Large-font inspection

Inspect the active large font with Glyphs MCP.
Call get_font_glyphs, get_font_masters, get_font_instances, list_style_sets, and get_font_kerning for the active master.
Summarize counts, feature coverage, and any errors.
Do not mutate or save.

Visual review

Render a visual review image for A, O, n, o, a composite glyph, and one punctuation glyph.
Use metrics, sidebearings, bounds, nodes, handles, anchors, and guides overlays.
Report concrete visual or data issues only.
Do not mutate or save.

Spacing safe apply

Review spacing for my current selection.
Rules: read first, never auto-save, and do not mutate without a dry run.

1. Call review_spacing.
2. Call apply_spacing with dry_run=true and conservative clamps.
3. Summarize changed glyphs, largest deltas, and skipped items.
4. Stop before confirm=true.

Kerning safe apply

Review kerning collisions and near-misses in the active font/master.
Use bounded limits suitable for a large production font.
Call review_kerning_bumper first, then apply_kerning_bumper with dry_run=true.
Summarize the worst findings and proposed exceptions.
Stop before confirm=true.

Docs and scripting

Search the bundled Glyphs docs for GSLayer bounds, fetch the best result, then run a small read-only execute_code snippet that returns the active Glyphs version and active family name.
Keep output bounded and do not mutate or save.

Test log template

Copy this section into the release notes or QA issue for each candidate.

# Glyphs MCP release QA

- Release candidate:
- Git commit:
- Artifact:
- Tester:
- Date:
- Large font:
- Baseline file:
- Glyphs 3.5 working copy:
- Glyphs 4 working copy:

## Matrix

| Case | App build | Python runtime | Client | Profile | Result | Notes |
| --- | --- | --- | --- | --- | --- | --- |
| G35-edit | | | | Edit | | |
| G35-readonly | | | | Read-only | | |
| G4-edit | | | | Edit | | |
| G4-readonly | | | | Read-only | | |

## Required sections

| Section | Glyphs 3.5 | Glyphs 4 | Notes |
| --- | --- | --- | --- |
| Preflight | | | |
| Smoke tests | | | |
| Read-only inspection | | | |
| Spacing workflow | | | |
| Kerning workflow | | | |
| Outline and glyph editing | | | |
| Annotation workflow | | | |
| Italic and compensated tuning | | | |
| Export and save | | | |
| Execute-code | | | |
| Security/local access | | | |
| Installer/packaging | | | |
| Performance/resilience | | | |
| Cross-version comparison | | | |

## Blockers

- [ ] None

## Accepted limitations

-

## Final decision

- [ ] Ready to publish
- [ ] Not ready

Cleanup

After testing:

  1. Restore or delete all working copies.
  2. Remove temporary export directories.
  3. Remove test client config entries if they point to a development checkout.
  4. Reinstall the public stable version if the machine is used for normal production work.
  5. Archive the test log with the release artifacts.