Skip to content

Build an UpDoc MCP server #129

Description

@deanleigh

Implements Route B from planning/AI_DRIVEN_IMPORT_ROUTES.md. Design and field notes are on #106; this issue is the build.

Why now

Three things became true after the design was written, and all three shrink the work:

Design assumed Actually available
Write the server from scratch @umbraco-cms/create-umbraco-mcp-server scaffolds it
Reimplement Management API calls in TypeScript Chain to @umbraco-cms/mcp-dev and delegate
Roughly 1-2 weeks Smaller — most of that estimate was plumbing now provided

So UpDoc's server only has to own extract, transform, apply mapping. Everything Umbraco-shaped — scaffold, create, save — is delegated to the developer server via chaining.

Decisions

npm package, not an RCL. An MCP server is a Node process the host spawns, not something loaded into the ASP.NET pipeline. Confirmed by the reference implementation.

In this repo, at mcp/. Umbraco split their editor and developer servers because they are separate products with separate release cycles. UpDoc's server is part of UpDoc, and the tool contract should version with the code it drives. mcp/ rather than src/UpDoc.Mcp/ because src/ holds .NET projects, and MCP_DOCS_SERVER.md already proposed mcp/.

One package for now. The docs server in MCP_DOCS_SERVER.md was never built, so there is nothing to merge or keep apart. Splitting later is easy; maintaining two from the start is not.

Reference implementation

umbraco/Umbraco-CMS-MCP-Editor — Phil's recommendation, and the pattern to follow.

Package umbraco-mcp-editor-cms: ESM, bin entry, ships dist only, Node >= 22. Versioned 18.0.0-beta.3, tracking the Umbraco major — the same idea as UpDoc's NuGet scheme.

Its dependencies show the chaining:

"@umbraco-cms/mcp-dev": "^18.0.1"
"@umbraco-cms/mcp-server-sdk": "^1.0.0-beta.31"
"@umbraco-cms/mcp-hosted": "^1.0.0-beta.31"

It builds for two targets from one codebase: stdio locally, and a Cloudflare Worker with OAuth. Testing runs in three layers — unit, LLM evals, and hosted E2E.

Chaining

Three patterns, per the SDK docs. Two are useful here:

  • Delegation — call an Umbraco tool from inside a handler, invisible to the AI. This is how the import gets its scaffold and creates the document.
  • Composite — orchestrate several chained calls in one tool, so an import is a single invocation rather than several LLM round-trips.

Proxying is not wanted. Re-exposing Umbraco's tools duplicates what is already there when both servers run side by side.

Increments

Each is independently useful. Stopping after any of them leaves something that works.

  • Scaffold and one trivial tool. updoc-list-workflows. Proves the server connects and is discoverable.
  • Read-only import. updoc-import with dryRun: true — returns the extraction, creates nothing. No write risk, and per the field notes on AI-driven import: let AI sessions run Create from Source (endpoint / UpDoc MCP server) #106 this is the highest-value single flag.
  • Write. The same tool with dryRun: false, creating the document through delegation.
  • Warnings. warnings[] naming fields that fell back to blueprint defaults. Comes from a real failure: Organiser Name silently took the blueprint placeholder while every other organiser field extracted fine.

Carry this rule into every write

update-document and raw PUT /document/{id} do full replacement — every top-level field omitted is set to null. This has broken the site three times (115 group tours in July 2026, TTM5086 in June, Sights media in April).

Delegating to a chained tool does not exempt anything. A composite tool that calls the destructive endpoint is exactly as destructive as calling it directly. Use update-document-properties and update-block-property.

Worth raising with Umbraco HQ: this is a general hazard of the MCP surface, not specific to UpDoc.

Not in scope

The end-of-import tasks on a tour — Skybrud redirect, member custom property, page permissions — are outside UpDoc entirely. Public access is reachable through existing Umbraco MCP tools; the other two are not, and no UpDoc server would help with them.

Refs #106, #41.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions