| name | mcp-patterns |
| description | Load MCP development patterns and best practices for building tools with the @umbraco-cms/mcp-server-sdk. Use when starting tool development or needing pattern reference. |
MCP Development Patterns
This skill loads comprehensive patterns for building MCP tools using the @umbraco-cms/mcp-server-sdk.
When to Use
Use this skill when:
- Starting to create new MCP tools
- Needing a reference for tool patterns
- Understanding how to use the toolkit helpers
- Setting up a new MCP server project
Core Architecture
Tool Definition Pattern
All tools follow this structure:
import {
withStandardDecorators,
executeGetApiCall,
executeVoidApiCall,
createToolResult,
CAPTURE_RAW_HTTP_RESPONSE,
ToolDefinition,
} from "@umbraco-cms/mcp-server-sdk";
const MyTool = {
name: "tool-name",
description: "What the tool does",
inputSchema: zodSchema.shape,
outputSchema: responseSchema,
slices: ["read"],
annotations: {
readOnlyHint: true,
destructiveHint: true,
idempotentHint: true,
},
handler: async (params) => {
},
} satisfies ToolDefinition<...>;
export default withStandardDecorators(MyTool);
API Client Configuration
Configure once at server startup:
import { configureApiClient } from "@umbraco-cms/mcp-server-sdk";
import { getMyAPI } from "./api/generated/myApi.js";
configureApiClient(() => getMyAPI());
Helper Functions
executeGetApiCall
For GET operations that return data:
return executeGetApiCall<ReturnType<Client["method"]>, Client>(
(client) => client.method(params, CAPTURE_RAW_HTTP_RESPONSE)
);
executeVoidApiCall
For DELETE/PUT operations that return void:
return executeVoidApiCall<Client>(
(client) => client.deleteItem(id, CAPTURE_RAW_HTTP_RESPONSE)
);
createToolResult / createToolResultError
For custom responses (typically CREATE operations):
if (response.status === 201) {
return createToolResult({ success: true, id: extractedId });
} else {
return createToolResultError(response.data);
}
Slices (Tool Categorization)
Tools are categorized by operation type:
read - GET operations
create - POST create operations
update - PUT operations
delete - DELETE operations
search - Search/filter operations
Annotations Reference
| Operation | readOnlyHint | destructiveHint | idempotentHint |
|---|
| GET | ✅ | ❌ | ❌ |
| DELETE | ❌ | ✅ | ❌ |
| POST | ❌ | ❌ | ❌ |
| PUT | ❌ | ❌ | ✅ |
Important: DELETE is NOT idempotent (2nd call returns 404).
Schema Flattening for Nested Reference Objects
Umbraco APIs wrap references to other entities in a nested object — most commonly
parent: { id } or parent: { path }. Never expose that shape to the LLM: accept a flat
scalar (parentId, path) and rebuild the nested object inside the handler. See
references/schema-flattening.md for the full pattern, the when-to-apply matrix, and the rules.
Project Structure
src/
├── api/
│ ├── client.ts # API client with mock support
│ ├── openapi.yaml # OpenAPI spec
│ └── generated/ # Orval-generated client + Zod schemas
├── tools/
│ └── {entity}/
│ ├── get/
│ ├── post/
│ ├── put/
│ ├── delete/
│ └── index.ts # Collection export
└── index.ts # MCP server entry
Orval Code Generation
Generate client and Zod schemas from OpenAPI:
npm run generate
Configuration in orval.config.ts:
- Generates TypeScript client (configurable via
client option)
- Generates Zod schemas for validation
- Uses custom mutator for authentication
Best Practices
- Use Zod schemas from generated code for type safety
- Hide UUID generation - create internally, don't expect LLM to provide
- Clear descriptions - tools should be self-documenting
- Consistent naming - follow
{action}-{entity} pattern
- Error handling -
withStandardDecorators handles errors automatically
- Flatten nested reference objects - accept
parentId / path, rebuild parent: { id } /
parent: { path } in the handler (see Schema Flattening above, or references/schema-flattening.md for the full pattern)