@stackwright/mcp
v0.7.0
Published
MCP server for Stackwright — exposes content types, page management, and validation as agent tools
Readme
Stackwright MCP Server
The @stackwright/mcp package provides an MCP (Model Context Protocol) server that exposes Stackwright's content types, page management, and validation as agent tools. This enables AI agents and other MCP-compatible clients to programmatically interact with Stackwright projects.
Overview
The MCP server runs as a stdio-based service and provides tools for:
- Content Type Introspection: Discover available content types and their schemas
- Page Management: List, add, and validate pages in a Stackwright project
- Site Management: Read, write, and validate site configuration; list available themes
- Project Management: Get project information and scaffold new projects
- Git Operations: Stage content changes and open pull requests for review
- Visual Rendering: Screenshot pages, capture before/after diffs, verify brand consistency
Prerequisites
- Node.js v18+
- Stackwright monorepo cloned and built (
pnpm install && pnpm buildfrom root)
Running the Server
# From the monorepo root
pnpm stackwright-mcpThe server starts and listens on stdin/stdout for MCP protocol messages.
Available Tools
Content Type Tools
stackwright_get_content_types
List all available Stackwright content types with their fields.
Parameters: None
Returns: Text listing of all content types and sub-types with their fields
Example Usage:
const result = await server.callTool('stackwright_get_content_types', {});
// Returns: CONTENT TYPES (use as keys inside content_items[]):
// main (main)
// label: string
// heading: TextBlock
// textBlocks: TextBlock[]
// media?: MediaItem
// ...Page Tools
stackwright_list_pages
List all pages in a Stackwright project.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright project
Returns: Text listing of pages with slugs and headings
Example Usage:
const result = await server.callTool('stackwright_list_pages', {
projectRoot: '/path/to/project'
});
// Returns: Pages (3):
// about — About Us
// contact — Contact
// team/leadershipstackwright_add_page
Create a new page in a Stackwright project.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright projectslug(string): Page slug (e.g., "about" or "team/leadership")heading(string, optional): Optional heading for the new page
Returns: Text confirmation with created page path
Example Usage:
const result = await server.callTool('stackwright_add_page', {
projectRoot: '/path/to/project',
slug: 'about',
heading: 'About Us'
});
// Returns: Created page "about" at /path/to/project/content/pages/about/content.ymlstackwright_validate_pages
Validate page YAML files against the Stackwright content schema.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright projectslug(string, optional): Validate only this slug; omit to validate all pages
Returns: Text validation result or error messages
Example Usage:
const result = await server.callTool('stackwright_validate_pages', {
projectRoot: '/path/to/project'
});
// Returns: ✓ Validation passed for all pages.
// Or: Validation failed:
// [about] Missing required field: headingSite Tools
stackwright_validate_site
Validate the stackwright.yml site configuration file.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright project
Returns: Text validation result or error messages
Example Usage:
const result = await server.callTool('stackwright_validate_site', {
projectRoot: '/path/to/project'
});
// Returns: ✓ Site config is valid (/path/to/project/stackwright.yml).stackwright_get_site_config
Read the raw YAML content of the stackwright.yml site configuration file.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright project
Returns: Text with the full YAML content of the site configuration
stackwright_write_site_config
Write or update the stackwright.yml site configuration. Validates against the site config Zod schema before writing — invalid YAML is rejected with field-level errors.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright projectcontent(string): The full YAML content for the site config
Returns: Text confirmation of creation or update, or validation error details
Example Usage:
const result = await server.callTool('stackwright_write_site_config', {
projectRoot: '/path/to/project',
content: `title: "My Site"
navigation:
- label: "Home"
href: "/"
appBar:
titleText: "My Site"
footer:
copyright: "© 2026"
`
});
// Returns: Updated site config at /path/to/project/stackwright.ymlstackwright_list_themes
List all built-in Stackwright themes.
Parameters: None
Returns: Text listing of themes with IDs, names, and descriptions
Example Usage:
const result = await server.callTool('stackwright_list_themes', {});
// Returns: Built-in themes (3):
// default — Default Theme
// dark — Dark Theme: Dark mode theme
// light — Light Theme: Light mode themeProject Tools
stackwright_get_project_info
Get information about a Stackwright project.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright project
Returns: Text with project info including package versions, theme, and page count
Example Usage:
const result = await server.callTool('stackwright_get_project_info', {
projectRoot: '/path/to/project'
});
// Returns: Project root: /path/to/project
// Site title: My Site
// Active theme: default
// Pages: 5
// Packages:
// @stackwright/core: 0.1.0-alpha.0
// @stackwright/nextjs: 0.1.0-alpha.0stackwright_scaffold_project
Scaffold a new Stackwright Next.js project.
Parameters:
targetDir(string): Absolute path where the new project should be createdname(string, optional): npm package name for the new projecttitle(string, optional): Site titletheme(string, optional): Theme ID to use
Returns: Text confirmation with scaffolded project details
Example Usage:
const result = await server.callTool('stackwright_scaffold_project', {
targetDir: '/path/to/new-project',
name: 'my-stackwright-site',
title: 'My Site',
theme: 'default'
});
// Returns: Scaffolded project at: /path/to/new-project
// Theme: default
// Sample pages: about, contact, homeGit Operations Tools
stackwright_stage_changes
Stage modified or new Stackwright content files (page YAML, site config, co-located images) for commit. Only content files are staged — arbitrary files like .env or package.json are ignored for safety.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright projectpaths(string[], optional): Specific relative paths to stage (still filtered to allowed content paths)
Returns: Text listing of staged and skipped files
Allowed file patterns (relative to project root):
pages/**/*.{yml,yaml}andcontent/pages/**/*.{yml,yaml}— page contentpages/**/*.{png,jpg,jpeg,gif,svg,webp}andcontent/pages/**/*.{png,jpg,jpeg,gif,svg,webp}— co-located imagesstackwright.{yml,yaml}— site configuration
Example Usage:
const result = await server.callTool('stackwright_stage_changes', {
projectRoot: '/path/to/project'
});
// Returns: Staged 2 file(s):
// + pages/about/content.yml
// + stackwright.yml
//
// Skipped 1 non-content file(s):
// - package.jsonstackwright_open_pr
Validate all staged YAML, commit changes, push to a new branch, and open a GitHub pull request. Requires the GitHub CLI (gh) to be installed and authenticated. Aborts if validation fails — invalid YAML is never committed.
Parameters:
projectRoot(string): Absolute path to the root of the Stackwright projecttitle(string, optional): PR title (auto-generated from file list if omitted)description(string, optional): PR body/description (auto-generated summary if omitted)branchName(string, optional): Custom branch name (default:content/agent-<timestamp>)baseBranch(string, optional): Target branch for the PR (default: repo default branch)draft(boolean, optional): Open as a draft PR (default: false)
Returns: Text with PR URL, branch name, commit hash, and list of committed files
Prerequisites:
- Git repository initialized with a remote named
origin - GitHub CLI installed and authenticated (
gh auth login) - Content changes staged via
stackwright_stage_changes
Example Usage:
const result = await server.callTool('stackwright_open_pr', {
projectRoot: '/path/to/project',
title: 'Add services page',
baseBranch: 'dev'
});
// Returns: Pull request opened: https://github.com/org/repo/pull/42
// Branch: content/agent-20260306-143022
// Commit: a1b2c3d4
// Files committed (1):
// pages/services/content.ymlVisual Rendering Tools
stackwright_check_dev_server
Check if a Stackwright dev server is running and reachable. Call this before using render tools.
Parameters:
baseUrl(string, optional): Base URL to check (default: http://localhost:3000)
Returns: Text confirming server is reachable, or error with instructions to start the dev server
Example Usage:
const result = await server.callTool('stackwright_check_dev_server', {});
// Returns: ✓ Dev server is running at http://localhost:3000. You can now use stackwright_render_page to see your pages.stackwright_render_page
Render a Stackwright page and return a screenshot image. Use this to visually verify how a page looks after writing or editing content. Requires a running dev server.
Parameters:
baseUrl(string, optional): Base URL of the running dev server (default: http://localhost:3000)slug(string): Page slug to render (e.g., "/" for home, "/about", "/pricing")viewport(object, optional):{ width: number, height: number }— viewport size (default: 1280x720). Use 375x667 for mobile.fullPage(boolean, optional): Capture full scrollable page or just the viewport (default: true)format(string, optional): Image format — "png" or "jpeg" (default: "png")
Returns: Text with render metadata plus a PNG/JPEG screenshot image
Example Usage:
const result = await server.callTool('stackwright_render_page', {
slug: '/about',
viewport: { width: 375, height: 667 }
});
// Returns: Rendered "/about" (375x667, 1234ms):
// [image: screenshot of the rendered page]stackwright_render_diff
Capture a "before" screenshot of a page for visual comparison. After capturing, make your YAML changes and call stackwright_render_page to see the "after" state.
Parameters:
baseUrl(string, optional): Base URL of the running dev server (default: http://localhost:3000)slug(string): Page slug to snapshotviewport(object, optional):{ width: number, height: number }— viewport sizefullPage(boolean, optional): Capture full scrollable page (default: true)
Returns: Text with snapshot metadata plus a "before" PNG screenshot
Workflow:
- Call
stackwright_render_diffto capture the current state - Write your YAML changes (
stackwright_write_pageorstackwright_compose_site) - Wait for the dev server to hot-reload
- Call
stackwright_render_pageto see the "after" state - Compare the two images to evaluate your changes
Example Usage:
const result = await server.callTool('stackwright_render_diff', {
slug: '/pricing'
});
// Returns: 📸 "Before" snapshot captured for "/pricing" (1280x720, 987ms).
// [image: screenshot of the current page state]Integration with MCP Clients
The Stackwright MCP server follows the Model Context Protocol (MCP) specification. Any MCP-compatible client can connect to and use these tools.
Example: Full editorial loop (content authoring → PR)
import { McpClient } from '@modelcontextprotocol/sdk/client/mcp.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const client = new McpClient();
await client.connect(new StdioClientTransport());
try {
const projectRoot = '/path/to/project';
// 1. Understand available content types
const contentTypes = await client.callTool('stackwright_get_content_types', {});
// 2. Create a new page
await client.callTool('stackwright_add_page', {
projectRoot,
slug: 'services',
heading: 'Our Services',
});
// 3. Validate
const validation = await client.callTool('stackwright_validate_pages', {
projectRoot,
slug: 'services',
});
if (validation.isError) {
console.error('Validation failed:', validation.content[0].text);
return;
}
// 4. Stage content changes (only content files are staged)
const staged = await client.callTool('stackwright_stage_changes', { projectRoot });
console.log(staged.content[0].text);
// 5. Open a PR for human review
const pr = await client.callTool('stackwright_open_pr', {
projectRoot,
title: 'Add services page',
baseBranch: 'dev',
});
console.log(pr.content[0].text); // PR URL
} finally {
await client.disconnect();
}Best Practices
- Always validate after creating or modifying content — call
stackwright_validate_pagesafterstackwright_add_page. - Use
stackwright_get_content_typesto ground your YAML — the tool returns the live Zod-derived schema, so field names and required/optional status are always current. - Check
isErroron every response — tools signal errors via theisErrorflag rather than throwing, so a successful HTTP-level call can still represent a domain error. - Use absolute paths — all
projectRootandtargetDirparameters must be absolute paths. - Disconnect in a finally block — always call
client.disconnect()to avoid leaving the server process orphaned. - Render after editing — call
stackwright_render_pageafter making content changes to visually verify the result. This catches layout, spacing, and brand consistency issues that schema validation alone cannot detect. - Check the dev server first — always call
stackwright_check_dev_serverbefore any render tool. If the server isn't running, render tools will fail with a clear error.
Development
Building
cd packages/mcp
pnpm buildTesting
cd packages/mcp
pnpm testRunning in Development Mode
cd packages/mcp
pnpm devArchitecture
The MCP server is built on top of the @modelcontextprotocol/sdk and exposes functionality from the @stackwright/cli package as MCP tools. Each tool corresponds to a CLI command but is adapted for programmatic use.
Tool Registration Pattern
server.tool(
'tool_name',
'Tool description',
{
param1: z.string().describe('Description'),
param2: z.number().optional().describe('Optional description'),
},
async ({ param1, param2 }) => {
return { content: [{ type: 'text', text: 'Result' }] };
}
);Error Handling
Tools return structured responses with:
content: Array of content items (text, images, etc.)isError: Boolean flag indicating if the response represents an error
{
"content": [{ "type": "text", "text": "Validation failed:\n [about] Missing required field: heading" }],
"isError": true
}Troubleshooting
Server not responding — ensure the server is running with pnpm stackwright-mcp and that stdin/stdout are properly connected to the client.
Tool not found — verify the tool name is correct (all tool names are prefixed with stackwright_).
Validation failures — check the structured error text for specific field issues; use stackwright_get_content_types to confirm required fields.
Permission errors on file operations — use absolute paths and ensure the server process has write access to the target directory.
Version Compatibility
@modelcontextprotocol/sdk: ^1.27.0@stackwright/cli: Same workspace version- Node.js: ^18.0.0 or later
License
MIT License. See the LICENSE file for details.
Support
- GitHub Issues: https://github.com/Per-Aspera-LLC/stackwright/issues
