Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 Matthew-Wise
Copyright (c) 2025-present Umbraco

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Umbraco MCP ![GitHub License](https://img.shields.io/github/license/matthew-wise/umbraco-mcp?style=plastic&link=https%3A%2F%2Fgithub.com%2FMatthew-Wise%2Fumbraco-mcp%3Ftab%3DMIT-1-ov-file%23readme)
# Umbraco MCP ![GitHub License](https://img.shields.io/github/license/umbraco/Umbraco-CMS-MCP-Dev?style=plastic&link=https%3A%2F%2Fgithub.com%2Fumbraco%2FUmbraco-CMS-MCP-Dev%2Fblob%2Fmain%2FLICENSE)

An MCP (Model Context Protocol) server for [Umbraco CMS](https://umbraco.com/)
it provides access to key parts of the Management API enabling you to do back office tasks with your agent.
Expand Down Expand Up @@ -34,7 +34,7 @@ Once you have this information head back into Claude desktop app and head to Set
"mcpServers": {
"umbraco-mcp": {
"command": "npx",
"args": ["@umbraco-mcp/umbraco-mcp-cms@alpha"],
"args": ["@umbraco-cms/mcp-dev@beta"],
"env": {
"NODE_TLS_REJECT_UNAUTHORIZED": "0",
"UMBRACO_CLIENT_ID": "umbraco-back-office-mcp",
Expand Down Expand Up @@ -62,7 +62,7 @@ Restart Claude and try it out with a simple prompt such as `Tell me the GUID of
Use the Claude Code CLI to add the Umbraco MCP server:

```bash
claude mcp add umbraco-mcp npx @umbraco-mcp/umbraco-mcp-cms@alpha
claude mcp add umbraco-mcp npx @umbraco-cms/mcp-dev@beta
```

Or configure environment variables and scope:
Expand All @@ -71,7 +71,7 @@ Or configure environment variables and scope:
npm install -g @anthropic-ai/claude-code

# Add with environment variables
claude mcp add umbraco-mcp --env UMBRACO_CLIENT_ID="your-id" --env UMBRACO_CLIENT_SECRET="your-secret" --env UMBRACO_BASE_URL="https://your-domain.com" --env NODE_TLS_REJECT_UNAUTHORIZED="0" --env UMBRACO_INCLUDE_TOOL_COLLECTIONS="culture,document,media" -- npx @umbraco-mcp/umbraco-mcp-cms@alpha
claude mcp add umbraco-mcp --env UMBRACO_CLIENT_ID="your-id" --env UMBRACO_CLIENT_SECRET="your-secret" --env UMBRACO_BASE_URL="https://your-domain.com" --env NODE_TLS_REJECT_UNAUTHORIZED="0" --env UMBRACO_INCLUDE_TOOL_COLLECTIONS="culture,document,media" -- npx @umbraco-cms/mcp-dev@beta

# Verify installation
claude mcp list
Expand Down Expand Up @@ -99,7 +99,7 @@ Follow the MCP [install guide](https://code.visualstudio.com/docs/copilot/custom
"umbraco-mcp": {
"type": "stdio",
"command": "npx",
"args": ["@umbraco-mcp/umbraco-mcp-cms@alpha"],
"args": ["@umbraco-cms/mcp-dev@beta"],
"env": {
"UMBRACO_CLIENT_ID": "<API user name>",
"UMBRACO_CLIENT_SECRET": "<API client secret>",
Expand Down Expand Up @@ -127,7 +127,7 @@ Add the following to the config file and update the env variables.
"mcpServers": {
"umbraco-mcp": {
"command": "npx",
"args": ["@umbraco-mcp/umbraco-mcp-cms@alpha"],
"args": ["@umbraco-cms/mcp-dev@beta"],
"env": {
"UMBRACO_CLIENT_ID": "<API user name>",
"UMBRACO_CLIENT_SECRET": "<API client secret>",
Expand Down
23 changes: 20 additions & 3 deletions build/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,25 @@ stages:
# Setup temp npm project to load in defaults from the local .npmrc
npm init -y

# Find the first .tgz file in the current directory and publish it
# Find the first .tgz file in the current directory
files=( ./*.tgz )
npm publish "${files[0]}"
displayName: Push to npm

# Extract version from package.json in the tarball
tar -tf "${files[0]}" | grep package/package.json | head -1 | xargs tar -xf "${files[0]}" --strip-components=1
version=$(node -p "require('./package.json').version")

# Determine tag based on version
if [[ "${version}" == *"alpha"* ]]; then
tag="alpha"
elif [[ "${version}" == *"beta"* ]]; then
tag="beta"
elif [[ "${version}" == *"rc"* ]]; then
tag="rc"
else
tag="latest"
fi

echo "Publishing version ${version} with tag ${tag}"
npm publish "${files[0]}" --tag $tag --access public
displayName: Push to npm with dynamic tagging
workingDirectory: $(Pipeline.Workspace)/npm-package
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@umbraco-mcp/umbraco-mcp-cms",
"version": "0.1.0-alpha.7",
"name": "@umbraco-cms/mcp-dev",
"version": "16.0.0-beta.1",
"type": "module",
"description": "A model context protocol (MCP) server for Umbraco CMS",
"main": "index.js",
Expand Down Expand Up @@ -30,7 +30,7 @@
],
"repository": {
"type": "git",
"url": "git+https://github.com/Matthew-Wise/umbraco-mcp.git"
"url": "git+https://github.com/umbraco/Umbraco-CMS-MCP-Dev"
},
"keywords": [
"Umbraco",
Expand All @@ -43,9 +43,9 @@
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/Matthew-Wise/umbraco-mcp/issues"
"url": "https://github.com/umbraco/Umbraco-CMS-MCP-Dev/issues"
},
"homepage": "https://github.com/Matthew-Wise/umbraco-mcp#readme",
"homepage": "https://github.com/umbraco/Umbraco-CMS-MCP-Dev#readme",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.9.0",
"@types/uuid": "^10.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,49 @@ describe("create-document", () => {
};
expect(norm).toMatchSnapshot();
});

it("should create a document with specific cultures", async () => {
// Create document with specific cultures
const docModel = {
documentTypeId: ROOT_DOCUMENT_TYPE_ID,
name: TEST_DOCUMENT_NAME,
cultures: ["en-US", "da-DK"],
values: [],
};

const result = await CreateDocumentTool().handler(docModel, {
signal: new AbortController().signal,
});

expect(result).toMatchSnapshot();

const item = await DocumentTestHelper.findDocument(TEST_DOCUMENT_NAME);
expect(item).toBeDefined();
// Should have variants for both cultures
expect(item!.variants).toHaveLength(2);
const cultures = item!.variants.map(v => v.culture).sort();
expect(cultures).toEqual(["da-DK", "en-US"]);
});

it("should create a document with empty cultures array (null culture)", async () => {
// Create document with empty cultures array - should behave like original (null culture)
const docModel = {
documentTypeId: ROOT_DOCUMENT_TYPE_ID,
name: TEST_DOCUMENT_NAME,
cultures: [],
values: [],
};

const result = await CreateDocumentTool().handler(docModel, {
signal: new AbortController().signal,
});

expect(result).toMatchSnapshot();

const item = await DocumentTestHelper.findDocument(TEST_DOCUMENT_NAME);
expect(item).toBeDefined();
// Should have single variant with null culture (original behavior)
expect(item!.variants).toHaveLength(1);
expect(item!.variants[0].culture).toBeNull();
});
});
32 changes: 24 additions & 8 deletions src/umb-management-api/tools/document/post/create-document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const createDocumentSchema = z.object({
documentTypeId: z.string().uuid("Must be a valid document type type UUID"),
parentId: z.string().uuid("Must be a valid document UUID").optional(),
name: z.string(),
cultures: z.array(z.string()).optional().describe("Array of culture codes. If not provided or empty array, will create single variant with null culture."),
values: z
.array(
z.object({
Expand All @@ -25,7 +26,10 @@ const createDocumentSchema = z.object({

const CreateDocumentTool = CreateUmbracoTool(
"create-document",
`Creates a document,
`Creates a document with support for multiple cultures.

If cultures parameter is provided, a variant will be created for each culture code.
If cultures parameter is not provided or is an empty array, will create a single variant with null culture (original behavior).

Always follow these requirements when creating documents exactly, do not deviate in any way.

Expand Down Expand Up @@ -633,6 +637,24 @@ const CreateDocumentTool = CreateUmbracoTool(

const documentId = uuidv4();

// Determine cultures to use
let culturesToUse: (string | null)[] = [];

if (model.cultures === undefined || model.cultures.length === 0) {
// If cultures not provided or empty array, use original behavior (null culture)
culturesToUse = [null];
} else {
// Use provided cultures
culturesToUse = model.cultures;
}

// Create variants for each culture
const variants = culturesToUse.map(culture => ({
culture,
name: model.name,
segment: null,
}));

const payload: CreateDocumentRequestModel = {
id: documentId,
documentType: {
Expand All @@ -645,13 +667,7 @@ const CreateDocumentTool = CreateUmbracoTool(
: undefined,
template: null,
values: model.values,
variants: [
{
culture: null,
name: model.name,
segment: null,
},
],
variants,
};

const response = await client.postDocument(payload);
Expand Down
Loading