Skip to content

MCP Tools Reference

VMark exposes four composite MCP tools to AI assistants: session, workspace, document, and workflow. Together they cover 14 actions — the read/write spine plus the file/window lifecycle plus CST-safe edits for GitHub Actions YAML.

The previous 12-tool / 76-action surface was pruned because in-document formatting tools (bold, headings, tables, etc.) duplicate work that AI agents already do trivially via Markdown round-trip. See the MCP pruning plan for the full rationale.

Recommended Workflow

  1. Call session.get_state once to see open windows, tabs, and per-tab {filePath, dirty, revision, kind}.
  2. For Markdown: document.read → reason → document.write (passing expected_revision for safe concurrency).
  3. For GitHub Actions YAML (kind: "yaml-workflow"): workflow.apply_patch for CST-safe edits that preserve comments and anchors; workflow.validate for actionlint diagnostics.
  4. File operations (open, save, close, switch tabs) live on workspace.

Mermaid Diagrams

When using AI to generate Mermaid via MCP, consider installing the mermaid-validator MCP server — it catches syntax errors using the same Mermaid v11 parsers before diagrams reach your document.


session

One-shot orientation. Discover every window, every tab, and the server's capabilities in a single call.

get_state

No arguments.

Returns {windows, capabilities}:

json
{
  "windows": [
    {
      "label": "main",
      "focused": true,
      "tabs": [
        {
          "id": "tab-1",
          "filePath": "/path/to/notes.md",
          "title": "notes",
          "dirty": false,
          "revision": "rev-x7Q3aB1F",
          "kind": "markdown"
        },
        {
          "id": "tab-2",
          "filePath": "/repo/.github/workflows/ci.yml",
          "title": "ci",
          "dirty": true,
          "revision": "rev-x7Q3aB1F",
          "kind": "yaml-workflow"
        }
      ]
    }
  ],
  "capabilities": {
    "version": "<vmark-mcp-server version>",
    "supportedKinds": ["markdown", "yaml-workflow"],
    "mcpProtocol": "0.1.0"
  }
}

The kind discriminator tells you whether to use document.write (for markdown) or workflow.apply_patch (for yaml-workflow) on that tab.


workspace

File and window lifecycle. Nothing in-document.

new

Create a new untitled tab.

ParameterTypeRequiredDescription
kindstringNo"markdown" (default) or "yaml-workflow"
windowLabelstringNoTarget window; defaults to focused

Returns {tabId}.

open

Open a file from disk.

ParameterTypeRequired
filePathstringYes
windowLabelstringNo

Returns {tabId}.

save

Save a tab to its existing path.

ParameterTypeRequired
tabIdstringNo (defaults to focused)

Returns {filePath, revision}.

save_as

Save a tab to a new path.

ParameterTypeRequired
tabIdstringNo
filePathstringYes

Returns {revision}.

close

Close a tab. Refuses to discard unsaved work without force.

ParameterTypeRequired
tabIdstringYes
forcebooleanNo

Returns {closed: true} on success, {closed: false, reason: "DIRTY"} if the tab is dirty and force was not supplied.

switch_tab

Activate a tab.

ParameterTypeRequired
tabIdstringYes

focus_window

Focus a window.

ParameterTypeRequired
windowLabelstringYes

document

Read, write, transform. The spine of the surface.

read

ParameterTypeRequired
tabIdstringNo (defaults to focused)

Returns {content, revision, filePath, kind, dirty}. Always read before writing — the revision token must accompany the next write.

write

Replace full document content.

ParameterTypeRequiredDescription
tabIdstringNoTarget tab (defaults to focused)
contentstringYesNew full content
expected_revisionstringNoRevision token from the most recent read

If expected_revision is supplied and the document has changed since that read, the response is a STALE structured-error envelope with the current revision; re-read and retry.

json
// success
{ "revision": "rev-newAfterWrite" }

// stale
{ "error": "STALE", "message": "Document has changed since the last read", "current_revision": "rev-currentNow" }

transform

Apply a deterministic rewrite. Currently supports CJK-specific transforms (full-width ↔ ASCII punctuation conversion, CJK ↔ Latin spacing).

ParameterTypeRequiredDescription
tabIdstringNoTarget tab
kindstringYes"cjk-format", "cjk-spacing", or "cjk-punctuation"
expected_revisionstringNoConcurrency token

cjk-format applies the user's CJK formatting settings end-to-end. cjk-spacing inserts single spaces between CJK characters and adjacent Latin/digits. cjk-punctuation converts ASCII punctuation that sits beside CJK characters to its full-width form.

Returns {revision}.


workflow

actionlint validation and CST-safe surgical edits for GitHub Actions workflow YAML. Available only for tabs whose kind is "yaml-workflow".

document.read / document.write work on every tab — including workflow YAML

The workflow tool is not a substitute for the read/write spine. For a workflow tab, you can:

  • document.read to get the raw YAML text (with all comments)
  • document.write to replace it wholesale (whatever string you send is stored verbatim — comments preserved if you include them)
  • workflow.apply_patch when you want the server itself to guarantee that comments, anchors, and key order survive a partial edit

Use apply_patch when changing one field and leaving everything else untouched (the server can't drop comments it doesn't change). Use document.write when you're rewriting wholesale or generating a new workflow from scratch.

apply_patch

Apply an array of IRPatch objects. Patches are dispatched through VMark's CST-aware mutators, which preserve comments, anchors, and key order. Raw document.write to a YAML file would lose them.

ParameterTypeRequired
tabIdstringNo
patchesIRPatch[]Yes
expected_revisionstringNo

IRPatch is a discriminated union (kind field). Supported kinds:

kindEffect
workflow.setSet top-level fields ({path, value}) — name, env.X, etc.
job.setSet a field on a job ({jobId, path, value})
step.setSet a field on a step ({jobId, stepIndex, path, value})
with.setSet a key in a step's with: block ({jobId, stepIndex, key, value})
with.removeRemove a key from a step's with: block
needs.add / needs.removeAdd or remove a job ID from needs:
trigger.setFiltersReplace a trigger filter array — branches, paths, types, etc. ({event, filter, value: string[]})

Returns {revision} on success or a structured STALE / INVALID_PATCH / NOT_WORKFLOW error envelope.

validate

Run actionlint over the workflow YAML.

ParameterTypeRequired
tabIdstringNo

Returns {ok, diagnostics, binaryAvailable}. Each diagnostic carries {line, col, message, severity}. binaryAvailable: false means actionlint is not installed locally; install via Homebrew or upstream releases.


Errors

Two error shapes appear:

Domain errors — set success: false and return a JSON-encoded envelope in error:

json
{ "error": "STALE", "message": "...", "current_revision": "rev-..." }

Argument-shape errors — for missing/invalid required arguments (e.g., document.write without a content field), error is a plain string describing the problem. The structured envelope is reserved for domain-level conditions.

CodeSurfaced asMeaning
STALEenvelopeexpected_revision did not match; re-read and retry
INVALID_PATCHenvelopeworkflow.apply_patch received a malformed patches array
INVALID_TABenvelopetabId could not be resolved
INVALID_PATHenvelopeworkspace.open received a filePath that could not be read
NOT_WORKFLOWenvelopeworkflow.* was called on a non-YAML-workflow tab
READ_ONLYenvelopeA mutation was attempted on a read-only document
INTERNALenvelopeUnexpected handler error
(plain string)stringRequired argument missing or wrong type