` components that have matching titles.
+
+```mdx
+
+
+```javascript example.js
+const greeting = "Hello, world!";
+console.log(greeting);
+```
+
+```python example.py
+greeting = "Hello, world!"
+print(greeting)
+```
+
+
+```
+
+For dropdown style instead of tabs:
+
+```mdx
+
+ ...code blocks...
+
+```
+
+## Expandables
+
+Show/hide nested properties. Primarily used in API documentation.
+
+```mdx
+
+ Unique identifier.
+ Display name.
+
+```
+
+Props:
+- `title` (string): Toggle label.
+- `defaultOpen` (boolean, default: false): Initially expanded.
+
+## Fields
+
+Document API parameters and response structures.
+
+### ParamField
+
+```mdx
+
+ Maximum number of results to return.
+
+
+
+ User email address.
+
+
+
+ Bearer token for authentication.
+
+```
+
+Props:
+- First parameter format: `query.name`, `path.name`, `body.name`, or `header.name`.
+- `type` (string): `number`, `string`, `boolean`, `object`. Append `[]` for arrays.
+- `required` (boolean): Mark as required.
+- `deprecated` (boolean): Mark as deprecated.
+- `default` (any): Default value.
+- `placeholder` (string): Playground input placeholder.
+
+### ResponseField
+
+```mdx
+
+ Unique user identifier.
+
+
+
+
+ Record ID.
+ Current status.
+
+
+```
+
+Props:
+- `name` (string, required): Field name.
+- `type` (string, required): Field type.
+- `required` (boolean): Required indicator.
+- `deprecated` (boolean): Deprecation flag.
+- `default` (string): Default value.
+- `pre` (string[]): Labels rendered before the field name.
+- `post` (string[]): Labels rendered after the field name.
+
+## Request and response examples
+
+Display code examples in the right sidebar on API pages.
+
+```mdx
+
+
+```bash cURL
+curl --request POST \
+ --url https://api.example.com/users \
+ --header 'Authorization: Bearer TOKEN'
+```
+
+```python Python
+import requests
+response = requests.post(
+ "https://api.example.com/users",
+ headers={"Authorization": "Bearer TOKEN"}
+)
+```
+
+
+
+
+
+```json 200
+{
+ "id": "usr_123",
+ "status": "active"
+}
+```
+
+
+```
+
+## Frames
+
+Styled container for images with optional captions.
+
+```mdx
+
+
+
+```
+
+Props:
+- `caption` (string): Text below image. Supports Markdown.
+- `hint` (string): Text above image.
+
+## Icons
+
+Display icons inline.
+
+```mdx
+
+
+Text with inline icon.
+```
+
+Props:
+- `icon` (string, required): Icon name, URL, or file path.
+- `iconType` (string): Font Awesome style.
+- `size` (number): Pixel size.
+- `color` (string): Hex color.
+
+## Tooltips
+
+Hover-triggered contextual help.
+
+```mdx
+
+ API
+ requests are sent over HTTPS.
+```
+
+Props:
+- `tip` (string, required): Tooltip text.
+- `headline` (string): Text above tip.
+- `cta` (string): Call-to-action link text.
+- `href` (string): Link URL (required if using `cta`).
+
+## Badge
+
+Inline labels and status indicators.
+
+```mdx
+
+ Active
+
+```
+
+Props:
+- `color` (string, default: "gray"): `gray`, `blue`, `green`, `yellow`, `orange`, `red`, `purple`, `white`, `surface`.
+- `size` (string, default: "md"): `xs`, `sm`, `md`, `lg`.
+- `shape` (string, default: "rounded"): `rounded`, `pill`.
+- `icon` (string): Icon name.
+- `stroke` (boolean): Outline style instead of filled.
+- `disabled` (boolean): Reduced opacity.
+
+## Tree
+
+Display hierarchical file/folder structures.
+
+```mdx
+
+
+
+
+
+
+
+
+
+
+```
+
+Tree.Folder props:
+- `name` (string, required): Folder name.
+- `defaultOpen` (boolean, default: false): Expanded by default.
+- `openable` (boolean, default: true): Can expand/collapse.
+
+Tree.File props:
+- `name` (string, required): File name.
+
+## Mermaid diagrams
+
+Use mermaid code blocks for flowcharts, sequence diagrams, and more.
+
+````mdx
+```mermaid
+flowchart LR
+ A[Start] --> B{Decision}
+ B -->|Yes| C[Action]
+ B -->|No| D[Other action]
+```
+````
+
+## Panel
+
+Customize right sidebar content, replacing the table of contents.
+
+```mdx
+
+ Custom sidebar content goes here.
+
+```
+
+## Prompt
+
+Display copyable AI prompts.
+
+```mdx
+
+You are a technical writer. Generate a README for a Node.js project
+that includes installation, usage, and contributing sections.
+
+```
+
+Props:
+- `description` (string, required): Card header. Supports Markdown.
+- `actions` (array, default: ["copy"]): `"copy"`, `"cursor"`.
+- `icon` (string): Icon name.
+
+## Color
+
+Display color palettes with click-to-copy.
+
+```mdx
+
+
+
+
+
+```
+
+Table variant with rows:
+
+```mdx
+
+
+
+
+
+
+```
+
+## Tiles
+
+Visual preview cards, typically used in grid layouts.
+
+```mdx
+
+
+
+
+
+```
+
+Props:
+- `href` (string, required): Link destination.
+- `title` (string): Tile title.
+- `description` (string): Short description.
+
+## Update
+
+Display changelog entries and release notes.
+
+```mdx
+
+ ## What's new
+
+ - Added dark mode support
+ - Improved search performance
+
+```
+
+Props:
+- `label` (string, required): Date or version identifier.
+- `description` (string): Version or release name.
+- `tags` (string[]): Filterable tags.
+- `rss` (object): Custom RSS entry with `title` and `description`.
+
+## Visibility
+
+Show different content to humans (web UI) versus AI agents (Markdown output). Content marked `for="humans"` renders on the site but is excluded from `.md` URLs; content marked `for="agents"` is hidden on the site but included in Markdown output.
+
+```mdx
+
+ Click the **Get started** button in the top-right corner.
+
+
+
+ To create an account, call `POST /v1/accounts` with a valid email.
+
+```
+
+Props:
+- `for` (string, required): `"humans"` or `"agents"`.
+
+## View
+
+Language/framework-specific content sections that switch with a multi-view dropdown.
+
+```mdx
+
+ ```javascript
+ console.log("Hello from JavaScript!");
+ ```
+
+
+
+ ```python
+ print("Hello from Python!")
+ ```
+
+```
+
+Props:
+- `title` (string, required): View selector label.
+- `icon` (string): Icon name.
diff --git a/agent-context/context/skills/mintlify/reference/configuration.md b/agent-context/context/skills/mintlify/reference/configuration.md
new file mode 100644
index 0000000000..b062ca1be3
--- /dev/null
+++ b/agent-context/context/skills/mintlify/reference/configuration.md
@@ -0,0 +1,591 @@
+# Configuration reference
+
+Full docs.json settings, snippets, hidden pages, and custom CSS/JS.
+
+## docs.json
+
+The `docs.json` file controls the entire site. Required fields: `theme`, `name`, `colors.primary`, and `navigation`.
+
+### Splitting configuration with `$ref`
+
+Use `$ref` at any level of `docs.json` to load configuration from another JSON file. Useful for splitting large configs or sharing navigation across deployments.
+
+```json
+{
+ "$schema": "https://mintlify.com/docs.json",
+ "theme": "mint",
+ "name": "Your Docs",
+ "colors": { "primary": "#3B82F6" },
+ "navigation": {
+ "$ref": "./navigation.json"
+ }
+}
+```
+
+Rules:
+- `$ref` must be a relative path to a `.json` file.
+- When `$ref` resolves to an object, sibling keys in the same block take precedence over matching keys in the referenced file.
+- When `$ref` resolves to a non-object (e.g., an array), sibling keys are ignored.
+- Referenced files can contain their own `$ref` entries, resolved relative to that file.
+- Paths must stay within the project root. Circular references cause a build error.
+
+```json
+{
+ "$schema": "https://mintlify.com/docs.json",
+ "theme": "mint",
+ "name": "Your Docs",
+ "colors": {
+ "primary": "#3B82F6"
+ },
+ "navigation": {
+ "groups": [
+ {
+ "group": "Getting started",
+ "pages": ["index", "quickstart"]
+ }
+ ]
+ }
+}
+```
+
+## Complete frontmatter fields
+
+The SKILL.md file lists common frontmatter fields. Here is the complete set. All fields are optional; if `title` is omitted, Mintlify generates one from the file path (dashes and underscores become spaces, first letter capitalized).
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `title` | string | Page title in navigation and browser tabs. Auto-generated from the path if omitted. |
+| `description` | string | Brief description for SEO. Displays under the title. |
+| `sidebarTitle` | string | Short title for sidebar navigation. |
+| `icon` | string | Lucide, Font Awesome, or Tabler icon name. Also accepts a URL or file path. |
+| `iconType` | string | Font Awesome icon style: `regular`, `solid`, `light`, `thin`, `sharp-solid`, `duotone`, `brands`. |
+| `tag` | string | Label next to page title in sidebar (e.g., "NEW"). |
+| `hidden` | boolean | Remove from sidebar. Page still accessible by URL. Do not set to `false`; remove the field entirely to make a page visible again. |
+| `noindex` | boolean | Exclude from site search, sitemaps, search engine indexing, and AI assistant context. Still visible in navigation. |
+| `searchable` | boolean | Defaults to `true`. Set `false` to exclude the page from site search and AI assistant context while keeping it indexable externally and visible in navigation. |
+| `boost` | number | Multiply the page's in-product search ranking. Values above 1 prioritize, between 0 and 1 de-prioritize. No effect when `searchable: false`. |
+| `deprecated` | boolean | Show a "deprecated" label next to the page title. |
+| `hideFooterPagination` | boolean | Hide the previous/next navigation links at the bottom of the page. |
+| `related` | array or boolean | Related pages shown in the **Related topics** section, or `false` to hide it. Requires the Related pages add-on. |
+| `hideApiMarker` | boolean | Hide the HTTP method badge next to the page title in the sidebar. |
+| `contextual` | object | Override the site-wide contextual menu (`options`, `display`) for this page. `options: []` disables it. |
+| `groups` | string[] | Restrict the page to users in specific groups. Requires authentication configured first. |
+| `mode` | string | Page layout: `default`, `wide`, `custom`, `frame`, `center`. |
+| `keywords` | array | Search terms for internal search and SEO. |
+| `api` | string | API endpoint for interactive playground (e.g., `"POST /users"`). |
+| `openapi` | string | OpenAPI endpoint reference (e.g., `"GET /endpoint"`). |
+| `url` | string | External URL. Makes the nav entry link externally. |
+| `timestamp` | boolean | Override global timestamp setting for this page. |
+
+Any other key is accepted as custom frontmatter (e.g. `product: "API"`).
+
+## Page modes
+
+Control page layout with the `mode` frontmatter field.
+
+```yaml
+# Default: standard layout with sidebar and table of contents
+---
+title: "Page title"
+---
+
+# Wide: hides table of contents for extra horizontal space
+---
+title: "Page title"
+mode: "wide"
+---
+
+# Custom: blank canvas, only top navbar visible
+---
+title: "Page title"
+mode: "custom"
+---
+
+# Frame: like custom but keeps sidebar (Aspen, Almond, and Luma themes only)
+---
+title: "Page title"
+mode: "frame"
+---
+
+# Center: removes sidebar and TOC, centers content (Mint and Linden themes only)
+---
+title: "Page title"
+mode: "center"
+---
+```
+
+## Theme
+
+One of: `mint`, `maple`, `palm`, `willow`, `linden`, `almond`, `aspen`, `sequoia`, `luma`.
+
+| Theme | Character |
+|-------|-----------|
+| `mint` | Classic, time-tested |
+| `maple` | Modern, clean, good for AI/SaaS |
+| `palm` | Sophisticated, fintech-focused |
+| `willow` | Stripped-back, minimal |
+| `linden` | Retro terminal, monospace |
+| `almond` | Card-based, minimalist |
+| `aspen` | Modern, supports complex navigation |
+| `sequoia` | Minimal, elegant, large-scale content |
+| `luma` | Clean, minimal design for polished documentation |
+
+## Colors
+
+```json
+"colors": {
+ "primary": "#3B82F6",
+ "light": "#F8FAFC",
+ "dark": "#0F172A"
+}
+```
+
+- `primary` (required): Main color, generally for emphasis in light mode.
+- `light`: Color for emphasis in dark mode.
+- `dark`: Color for buttons and hover states.
+
+All values must be hex codes starting with `#`.
+
+## Logo
+
+```json
+"logo": {
+ "light": "/logo/light.svg",
+ "dark": "/logo/dark.svg",
+ "href": "https://example.com"
+}
+```
+
+## Favicon
+
+Single file or light/dark variants:
+
+```json
+"favicon": "/favicon.ico"
+```
+
+```json
+"favicon": {
+ "light": "/favicon.png",
+ "dark": "/favicon-dark.png"
+}
+```
+
+## Icons
+
+```json
+"icons": {
+ "library": "lucide"
+}
+```
+
+Options: `"fontawesome"` (default), `"lucide"`, or `"tabler"`. You can only use one library per project. Individual icons can still use URLs or file paths regardless of this setting.
+
+## Fonts
+
+```json
+"fonts": {
+ "family": "Inter"
+}
+```
+
+Google Fonts load automatically by family name. For custom fonts:
+
+```json
+"fonts": {
+ "family": "CustomFont",
+ "source": "/fonts/CustomFont.woff2",
+ "format": "woff2",
+ "weight": 400,
+ "heading": {
+ "family": "HeadingFont",
+ "weight": 700
+ },
+ "body": {
+ "family": "BodyFont",
+ "weight": 400
+ }
+}
+```
+
+## Appearance
+
+```json
+"appearance": {
+ "default": "system",
+ "strict": false
+}
+```
+
+- `default`: `"system"`, `"light"`, or `"dark"`.
+- `strict`: Set `true` to hide the light/dark mode toggle.
+
+## Background
+
+```json
+"background": {
+ "image": {
+ "light": "/bg-light.svg",
+ "dark": "/bg-dark.svg"
+ },
+ "decoration": "gradient",
+ "color": {
+ "light": "#FFFFFF",
+ "dark": "#000000"
+ }
+}
+```
+
+- `decoration`: `"gradient"`, `"grid"`, or `"windows"`.
+
+## Styling
+
+```json
+"styling": {
+ "eyebrows": "breadcrumbs",
+ "latex": true,
+ "codeblocks": {
+ "theme": {
+ "light": "github-light",
+ "dark": "github-dark"
+ }
+ }
+}
+```
+
+- `eyebrows`: `"section"` (default) or `"breadcrumbs"`.
+- `latex`: Override automatic LaTeX detection.
+- `codeblocks`: `"system"` (default), `"dark"`, a Shiki theme name, or an object with `light`/`dark` themes.
+
+## Navbar
+
+```json
+"navbar": {
+ "links": [
+ {
+ "label": "Community",
+ "href": "https://example.com/community"
+ },
+ {
+ "type": "github",
+ "href": "https://github.com/example/repo"
+ }
+ ],
+ "primary": {
+ "type": "button",
+ "label": "Get Started",
+ "href": "https://example.com/start"
+ }
+}
+```
+
+Link types: omit `type` for standard text link, `"github"` for repo with star count, `"discord"` for server with online count.
+
+Primary button types: `"button"`, `"github"`, `"discord"`.
+
+## Footer
+
+```json
+"footer": {
+ "socials": {
+ "x": "https://x.com/example",
+ "github": "https://github.com/example",
+ "linkedin": "https://linkedin.com/company/example"
+ },
+ "links": [
+ {
+ "header": "Resources",
+ "items": [
+ { "label": "Blog", "href": "https://example.com/blog" }
+ ]
+ }
+ ]
+}
+```
+
+Valid social keys: `x`, `website`, `facebook`, `youtube`, `discord`, `slack`, `github`, `linkedin`, `instagram`, `hacker-news`, `medium`, `telegram`, `bluesky`, `threads`, `reddit`, `podcast`.
+
+## Banner
+
+```json
+"banner": {
+ "content": "Version 2.0 is live! [Learn more](/changelog)",
+ "dismissible": true,
+ "type": "info",
+ "color": {
+ "light": "#7C3AED",
+ "dark": "#5B21B6"
+ }
+}
+```
+
+- `content` (required): Supports basic Markdown (links, bold, italic). Custom components are not supported.
+- `dismissible`: Show a close button. Stays hidden for a user until content changes. Default: `false`.
+- `type`: Background style. `"info"` (primary color, default), `"warning"` (amber), `"critical"` (red).
+- `color`: Custom background hex. Overrides `type`. Object with `light` and `dark` keys, or a single hex string. Banner text is white — choose a dark enough background.
+
+Language-specific banners can be set inside the `navigation.languages` entries.
+
+## Variables
+
+Global content variables substituted at build time using `{{variableName}}` syntax in MDX files.
+
+```json
+"variables": {
+ "apiVersion": "v2",
+ "baseUrl": "https://api.example.com"
+}
+```
+
+Keys must be alphanumeric with hyphens only. Values are plain strings. Use in any `.mdx` file:
+
+```mdx
+The current API version is {{apiVersion}}.
+```
+
+## Redirects
+
+```json
+"redirects": [
+ {
+ "source": "/old-page",
+ "destination": "/new-page",
+ "permanent": true
+ }
+]
+```
+
+## Metadata
+
+```json
+"metadata": {
+ "timestamp": true
+}
+```
+
+Shows "Last modified on [date]" on all pages. Override per-page with `timestamp` frontmatter.
+
+## Interaction
+
+```json
+"interaction": {
+ "drilldown": false
+}
+```
+
+Controls whether clicking a navigation group navigates to its first page (`true`) or only expands/collapses (`false`).
+
+## SEO
+
+```json
+"seo": {
+ "metatags": {
+ "canonical": "https://docs.example.com",
+ "og:locale": "en_US"
+ },
+ "indexing": "navigable"
+}
+```
+
+- `indexing`: `"navigable"` (only nav pages) or `"all"` (every page including hidden).
+
+## Search
+
+```json
+"search": {
+ "prompt": "Search documentation..."
+}
+```
+
+## Contextual menu
+
+```json
+"contextual": {
+ "options": ["copy", "chatgpt", "claude", "cursor", "vscode"],
+ "display": "header"
+}
+```
+
+- `options` (required): First item is the default action. Built-in values: `"assistant"`, `"copy"`, `"view"`, `"chatgpt"`, `"claude"`, `"perplexity"`, `"grok"`, `"aistudio"`, `"devin"`, `"windsurf"`, `"mcp"`, `"add-mcp"`, `"cursor"`, `"vscode"`, `"devin-mcp"`. Custom objects accepted with `title`, `description`, `icon`, and `href` fields.
+- `display`: Where to show the menu. `"header"` (default) or `"toc"`.
+
+## Thumbnails
+
+```json
+"thumbnails": {
+ "appearance": "light",
+ "background": "/images/thumbnail-bg.svg",
+ "fonts": {
+ "family": "Inter"
+ }
+}
+```
+
+## Error handling
+
+```json
+"errors": {
+ "404": {
+ "redirect": true,
+ "title": "Page not found",
+ "description": "This page doesn't exist."
+ }
+}
+```
+
+## API configuration
+
+```json
+"api": {
+ "openapi": "openapi.json",
+ "playground": {
+ "display": "interactive",
+ "proxy": true
+ },
+ "examples": {
+ "languages": ["bash", "javascript", "python"],
+ "defaults": "all",
+ "prefill": false,
+ "autogenerate": true
+ },
+ "mdx": {
+ "server": "https://api.example.com",
+ "auth": {
+ "method": "bearer"
+ }
+ }
+}
+```
+
+- `openapi`: Single file, array, or object with `source` and `directory`.
+- `asyncapi`: Same format as `openapi` for AsyncAPI specs.
+- `playground.display`: `"interactive"`, `"simple"`, `"none"`, or `"auth"`.
+- `playground.proxy`: Route requests through Mintlify's proxy. Default: `true`.
+- `playground.credentials`: Include cookies and auth headers for cross-origin requests when proxy is `false`. Default: `false`.
+- `params.expanded`: Expand all parameters by default. `"all"` or `"closed"` (default).
+- `params.post`: OpenAPI schema field keys to surface as pills next to parameter names.
+- `url`: Set to `"full"` to always show the full base URL (default: only shown when multiple base URLs exist).
+- `examples.languages`: `bash`, `go`, `java`, `javascript`, `node`, `php`, `powershell`, `python`, `ruby`, `swift`.
+- `examples.defaults`: `"required"` or `"all"` (include optional params).
+- `examples.prefill`: Pre-fill playground fields with spec example values. Default: `false`.
+- `examples.autogenerate`: Generate code samples from API specs. Default: `true`.
+- `mdx.auth.method`: `"bearer"`, `"basic"`, `"key"`, `"cobo"`.
+
+## Integrations
+
+```json
+"integrations": {
+ "ga4": { "measurementId": "G-XXXXXXXXXX" },
+ "gtm": { "tagId": "GTM-XXXXX" },
+ "posthog": { "apiKey": "phc_xxx", "apiHost": "https://app.posthog.com" },
+ "amplitude": { "apiKey": "xxx" },
+ "mixpanel": { "projectToken": "xxx" },
+ "segment": { "key": "xxx" },
+ "clarity": { "projectId": "xxx" },
+ "fathom": { "siteId": "xxx" },
+ "hotjar": { "hjid": "xxx", "hjsv": "xxx" },
+ "logrocket": { "appId": "xxx" },
+ "heap": { "appId": "xxx" },
+ "pirsch": { "id": "xxx" },
+ "plausible": { "domain": "xxx", "server": "optional" },
+ "hightouch": { "writeKey": "xxx", "apiHost": "optional" },
+ "clearbit": { "publicApiKey": "xxx" },
+ "intercom": { "appId": "xxx" },
+ "frontchat": { "snippetId": "xxx" },
+ "telemetry": { "enabled": true },
+ "cookies": { "key": "consent_key", "value": "accepted" }
+}
+```
+
+## Reusable snippets
+
+Store reusable content in the `/snippets/` directory.
+
+### MDX snippets
+
+```mdx
+
+Before you begin, make sure you have:
+- Node.js 18+
+- A Mintlify account
+```
+
+Import in any page:
+
+```mdx
+import Prerequisites from "/snippets/prerequisites.mdx";
+
+
+```
+
+### JSX components
+
+```jsx
+// snippets/counter.jsx
+export const Counter = () => {
+ const [count, setCount] = useState(0);
+ return (
+
+
+ {count}
+
+
+ );
+};
+```
+
+Import in any page using the root-relative path to the file:
+
+```mdx
+import { Counter } from "/snippets/counter.jsx";
+
+
+```
+
+JSX components can live in any directory, not just `/snippets/`. Nested imports between snippet files are not supported.
+
+## Hidden pages
+
+Set `hidden: true` in frontmatter to remove from sidebar. Page remains accessible by URL.
+
+```yaml
+---
+title: "Internal reference"
+hidden: true
+---
+```
+
+Or omit the page from `docs.json` navigation entirely.
+
+## .mintignore
+
+Exclude files completely from the published docs. Place `.mintignore` in the docs root. Uses `.gitignore` syntax.
+
+```
+drafts/
+*.draft.mdx
+private-notes.md
+**/internal/**
+!important.mdx
+```
+
+Files in `.mintignore` are not published, not indexed, and not accessible by URL.
+
+## Custom CSS and JavaScript
+
+### CSS
+
+Add `.css` files to your repository. Class names become available in all MDX files.
+
+```css
+/* styles.css */
+#navbar {
+ background: #fffff2;
+}
+```
+
+Built-in Tailwind CSS v3 classes are available. Arbitrary values (e.g., `w-[350px]`) are not supported — use inline `style` instead.
+
+### JavaScript
+
+Any `.js` file in the content directory is included globally on all pages.
diff --git a/agent-context/context/skills/mintlify/reference/navigation.md b/agent-context/context/skills/mintlify/reference/navigation.md
new file mode 100644
index 0000000000..ff44a1a236
--- /dev/null
+++ b/agent-context/context/skills/mintlify/reference/navigation.md
@@ -0,0 +1,352 @@
+# Navigation reference
+
+All navigation patterns for the `navigation` property in `docs.json`.
+
+## Pages
+
+Flat list of pages with no grouping.
+
+```json
+{
+ "navigation": {
+ "pages": ["index", "quickstart", "guides/example"]
+ }
+}
+```
+
+## Groups
+
+```json
+{
+ "navigation": {
+ "groups": [
+ {
+ "group": "Getting started",
+ "icon": "rocket",
+ "pages": ["index", "quickstart"]
+ },
+ {
+ "group": "Guides",
+ "icon": "book-open",
+ "tag": "NEW",
+ "pages": [
+ "guides/overview",
+ {
+ "group": "Advanced",
+ "expanded": false,
+ "pages": ["guides/advanced/config", "guides/advanced/deploy"]
+ }
+ ]
+ }
+ ]
+ }
+}
+```
+
+Group properties:
+- `group` (required): Section title.
+- `pages` (required): Array of page paths or nested groups.
+- `icon`: Icon name.
+- `tag`: Label displayed next to group name.
+- `root`: Page that opens when clicking the group title.
+- `expanded`: Default open state for nested groups (`true`/`false`). Top-level groups are always expanded.
+- `directory`: When the group has a `root` page, render a listing of child pages below the root page content. Values: `"none"` (default), `"accordion"` (collapsible list), `"card"` (horizontal cards). Inherits recursively; descendants can override.
+- `boost`: Numeric multiplier for in-product search ranking of every page in the group. Use values `> 1` to prioritize, `0–1` to de-prioritize.
+
+## Tabs
+
+```json
+{
+ "navigation": {
+ "tabs": [
+ {
+ "tab": "Documentation",
+ "icon": "book-open",
+ "groups": [
+ {
+ "group": "Getting started",
+ "pages": ["index", "quickstart"]
+ }
+ ]
+ },
+ {
+ "tab": "API reference",
+ "icon": "square-terminal",
+ "pages": ["api/overview", "api/endpoints"]
+ },
+ {
+ "tab": "Blog",
+ "icon": "newspaper",
+ "href": "https://example.com/blog"
+ }
+ ]
+ }
+}
+```
+
+### Menus (within tabs)
+
+```json
+{
+ "tab": "Developer tools",
+ "menu": [
+ {
+ "item": "API reference",
+ "icon": "rocket",
+ "groups": [
+ {
+ "group": "Endpoints",
+ "pages": ["api/get", "api/post"]
+ }
+ ]
+ },
+ {
+ "item": "SDKs",
+ "icon": "code",
+ "description": "Client libraries",
+ "pages": ["sdk/javascript", "sdk/python"]
+ }
+ ]
+}
+```
+
+## Anchors
+
+```json
+{
+ "navigation": {
+ "anchors": [
+ {
+ "anchor": "Documentation",
+ "icon": "book-open",
+ "groups": [
+ {
+ "group": "Getting started",
+ "pages": ["quickstart", "tutorial"]
+ }
+ ]
+ },
+ {
+ "anchor": "Blog",
+ "href": "https://example.com/blog"
+ }
+ ]
+ }
+}
+```
+
+### Global anchors
+
+Appear on all pages regardless of active section:
+
+```json
+{
+ "navigation": {
+ "global": {
+ "anchors": [
+ {
+ "anchor": "Changelog",
+ "icon": "list",
+ "href": "/changelog"
+ }
+ ]
+ },
+ "tabs": [...]
+ }
+}
+```
+
+## Global navigation
+
+`navigation.global` supports tabs, anchors, dropdowns, languages, versions, and products that appear on all pages regardless of active section. Useful for persistent switchers and cross-cutting links.
+
+```json
+{
+ "navigation": {
+ "global": {
+ "tabs": [
+ { "tab": "API", "href": "/api-reference", "icon": "square-terminal" }
+ ],
+ "anchors": [
+ { "anchor": "Changelog", "icon": "list", "href": "/changelog" }
+ ],
+ "languages": [
+ { "language": "en", "default": true },
+ { "language": "es" }
+ ],
+ "versions": [
+ { "version": "v2", "default": true },
+ { "version": "v1" }
+ ],
+ "products": [
+ { "product": "Core API", "icon": "server" },
+ { "product": "Mobile SDK", "icon": "smartphone" }
+ ]
+ }
+ }
+}
+```
+
+Global element properties:
+- `global.tabs`: Each entry requires `tab` (string) and `href`. Optional: `icon`, `iconType`, `hidden`.
+- `global.anchors`: Each entry requires `anchor` (string) and `href`. Optional: `icon`, `iconType`, `color.light`, `color.dark`, `hidden`.
+- `global.dropdowns`: Each entry requires `dropdown` (string) and `href`. Optional: `icon`, `iconType`, `hidden`.
+- `global.languages`: Each entry requires `language` (code string). Optional: `default`, `hidden`, `href`.
+- `global.versions`: Each entry requires `version` (string). Optional: `default`, `hidden`, `href`.
+- `global.products`: Each entry requires `product` (string). Optional: `description`, `icon`, `iconType`.
+
+## Dropdowns
+
+```json
+{
+ "navigation": {
+ "dropdowns": [
+ {
+ "dropdown": "Documentation",
+ "icon": "book-open",
+ "groups": [
+ {
+ "group": "Getting started",
+ "pages": ["index", "quickstart"]
+ }
+ ]
+ },
+ {
+ "dropdown": "API reference",
+ "icon": "square-terminal",
+ "pages": ["api/overview"]
+ }
+ ]
+ }
+}
+```
+
+## Products
+
+```json
+{
+ "navigation": {
+ "products": [
+ {
+ "product": "Core API",
+ "description": "Core API documentation",
+ "icon": "server",
+ "tabs": [
+ {
+ "tab": "Documentation",
+ "groups": [
+ { "group": "Getting started", "pages": ["core/quickstart"] }
+ ]
+ }
+ ]
+ },
+ {
+ "product": "Mobile SDK",
+ "icon": "smartphone",
+ "pages": ["mobile/overview"]
+ }
+ ]
+ }
+}
+```
+
+## Versions
+
+```json
+{
+ "navigation": {
+ "versions": [
+ {
+ "version": "2.0.0",
+ "default": true,
+ "tag": "Latest",
+ "groups": [
+ { "group": "Getting started", "pages": ["v2/overview", "v2/quickstart"] }
+ ]
+ },
+ {
+ "version": "1.0.0",
+ "tag": "Deprecated",
+ "groups": [
+ { "group": "Getting started", "pages": ["v1/overview", "v1/quickstart"] }
+ ]
+ }
+ ]
+ }
+}
+```
+
+Version properties:
+- `version` (required): Version label shown in the selector.
+- `default`: Set `true` to make this the default version (otherwise the first entry is the default).
+- `tag`: Badge label displayed in the version selector dropdown (e.g., `"Latest"`, `"Recommended"`, `"Beta"`).
+
+## Languages
+
+```json
+{
+ "navigation": {
+ "languages": [
+ {
+ "language": "en",
+ "groups": [
+ { "group": "Getting started", "pages": ["en/overview", "en/quickstart"] }
+ ]
+ },
+ {
+ "language": "es",
+ "groups": [
+ { "group": "Comenzando", "pages": ["es/overview", "es/quickstart"] }
+ ]
+ }
+ ]
+ }
+}
+```
+
+Each language entry can include its own `banner`, `footer`, and `navbar` configuration overrides.
+
+## OpenAPI in navigation
+
+```json
+{
+ "navigation": {
+ "groups": [
+ {
+ "group": "API reference",
+ "openapi": "/path/to/openapi.json",
+ "pages": [
+ "overview",
+ "GET /users",
+ "POST /users",
+ {
+ "group": "Products",
+ "openapi": "/path/to/openapi-v2.json",
+ "pages": ["GET /products", "POST /products"]
+ }
+ ]
+ }
+ ]
+ }
+}
+```
+
+When you add `openapi` to a navigation element without specifying pages, Mintlify auto-generates pages for all endpoints.
+
+## Choosing a navigation pattern
+
+| Pattern | When to use |
+|---------|-------------|
+| Groups | Default. Single audience, straightforward hierarchy. |
+| Tabs | Distinct sections with different audiences or content types. |
+| Anchors | Persistent section links at sidebar top. |
+| Dropdowns | Multiple sections users switch between. |
+| Products | Multi-product company with separate docs per product. |
+| Versions | Multiple API/product versions. |
+| Languages | Localized content. |
+
+Navigation elements can nest within each other. Common combinations:
+- Tabs containing groups
+- Products containing tabs
+- Versions containing tabs
+- Anchors containing groups
diff --git a/agent-context/context/skills/mintlify/reference/site-discovery.md b/agent-context/context/skills/mintlify/reference/site-discovery.md
new file mode 100644
index 0000000000..bac809a625
--- /dev/null
+++ b/agent-context/context/skills/mintlify/reference/site-discovery.md
@@ -0,0 +1,29 @@
+# Product discovery for new documentation sites
+
+Use this workflow when creating a new site or replacing substantial placeholder content. Skip it for targeted edits to an established site.
+
+## Build a product brief
+
+Inspect the repository, supplied URLs, existing pages, and attachments first. Determine what they already establish about:
+
+- What the product helps people accomplish
+- Who the primary documentation reader is and what brings them to the docs
+- The first 1–3 tasks that reader must complete
+- Why the product was built or chosen over the current approach
+
+Ask one round of up to four questions for important gaps that only the user can answer. Make each question specific to the source material. When the interface supports choices, suggest 2–4 plausible answers derived from the sources and allow a custom answer.
+
+Do not ask about facts you can verify yourself. Infer presentation choices such as theme, page grouping, and component usage unless choosing incorrectly would waste substantial work.
+
+If the user does not answer, state or record reasonable assumptions and continue. Do not repeat the questions later in generation.
+
+## Use the brief
+
+Synthesize answers and verified facts into a concise internal brief:
+
+- `description`: the outcome the product creates
+- `primaryAudience`: the main reader and their context
+- `jobsToBeDone`: the critical tasks the docs must enable
+- `motivation`: the problem, differentiation, or reason the product exists
+
+Use the brief to prioritize the homepage, introduction, quickstart, navigation, and examples. Preserve the user's terminology. Do not copy the brief mechanically onto every page.
diff --git a/agent-context/package.json b/agent-context/package.json
new file mode 100644
index 0000000000..eda1e3bb5a
--- /dev/null
+++ b/agent-context/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "@mintlify/agent-context",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "engines": {
+ "node": ">=22"
+ },
+ "scripts": {
+ "build": "node scripts/build.mjs",
+ "check": "node scripts/check.mjs",
+ "status": "node scripts/status.mjs",
+ "test": "node --test"
+ }
+}
diff --git a/agent-context/scripts/build.mjs b/agent-context/scripts/build.mjs
new file mode 100644
index 0000000000..a0fb9ce03b
--- /dev/null
+++ b/agent-context/scripts/build.mjs
@@ -0,0 +1,12 @@
+import path from 'node:path';
+import { buildAll, repositoryRoot } from './lib.mjs';
+
+const selectedIds = process.argv.slice(2);
+const results = await buildAll({
+ outputRoot: path.join(repositoryRoot, 'dist'),
+ selectedIds,
+});
+
+for (const { provenance } of results) {
+ console.log(`${provenance.target}: ${provenance.sourceCommit}`);
+}
diff --git a/agent-context/scripts/check.mjs b/agent-context/scripts/check.mjs
new file mode 100644
index 0000000000..d6fa77974b
--- /dev/null
+++ b/agent-context/scripts/check.mjs
@@ -0,0 +1,36 @@
+import assert from 'node:assert/strict';
+import { mkdtemp, readFile, rm } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import path from 'node:path';
+import { buildAll } from './lib.mjs';
+
+const outputRoot = await mkdtemp(path.join(tmpdir(), 'mintlify-agent-context-'));
+
+try {
+ const results = await buildAll({ outputRoot });
+ assert.equal(results.length, 3);
+
+ const sharedFiles = [
+ 'api-docs.md',
+ 'cli.md',
+ 'components.md',
+ 'configuration.md',
+ 'navigation.md',
+ 'site-discovery.md',
+ ];
+ for (const file of sharedFiles) {
+ const contents = await Promise.all(
+ results.map(({ provenance }) =>
+ readFile(
+ path.join(outputRoot, provenance.target, 'skills', 'mintlify', 'reference', file),
+ 'utf8',
+ ),
+ ),
+ );
+ assert.ok(contents.every((content) => content === contents[0]), `${file} drifted`);
+ }
+
+ console.log('All generated targets are valid and shared references are identical.');
+} finally {
+ await rm(outputRoot, { recursive: true, force: true });
+}
diff --git a/agent-context/scripts/lib.mjs b/agent-context/scripts/lib.mjs
new file mode 100644
index 0000000000..8bcd2a8e6f
--- /dev/null
+++ b/agent-context/scripts/lib.mjs
@@ -0,0 +1,154 @@
+import { execFileSync } from 'node:child_process';
+import { cp, mkdir, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const scriptsDirectory = path.dirname(fileURLToPath(import.meta.url));
+export const repositoryRoot = path.resolve(scriptsDirectory, '..');
+
+const contextDirectory = path.join(repositoryRoot, 'context', 'skills', 'mintlify');
+const mcpServersPath = path.join(repositoryRoot, 'context', 'mcp-servers.json');
+const targetsDirectory = path.join(repositoryRoot, 'targets');
+
+export async function loadTargets(selectedIds = []) {
+ const entries = (await readdir(targetsDirectory))
+ .filter((entry) => entry.endsWith('.json'))
+ .sort();
+ const targets = await Promise.all(
+ entries.map(async (entry) =>
+ JSON.parse(await readFile(path.join(targetsDirectory, entry), 'utf8')),
+ ),
+ );
+
+ for (const target of targets) {
+ if (
+ typeof target.id !== 'string' ||
+ typeof target.repository !== 'string' ||
+ !['.mcp.json', 'mcp.json'].includes(target.mcpConfigFile) ||
+ !['mcp_servers', 'mcpServers'].includes(target.mcpConfigKey)
+ ) {
+ throw new Error(`Invalid target configuration: ${JSON.stringify(target)}`);
+ }
+ }
+
+ const ids = new Set(targets.map((target) => target.id));
+ for (const id of selectedIds) {
+ if (!ids.has(id)) {
+ throw new Error(`Unknown target: ${id}`);
+ }
+ }
+
+ return selectedIds.length === 0
+ ? targets
+ : targets.filter((target) => selectedIds.includes(target.id));
+}
+
+function markGenerated(skill) {
+ const frontmatterEnd = skill.indexOf('\n---\n', 4);
+ if (frontmatterEnd === -1) {
+ throw new Error('SKILL.md frontmatter is not closed');
+ }
+
+ const insertionPoint = frontmatterEnd + '\n---\n'.length;
+ return `${skill.slice(0, insertionPoint)}\n\n${skill.slice(insertionPoint)}`;
+}
+
+export function sourceCommit() {
+ if (process.env.GITHUB_SHA) {
+ return process.env.GITHUB_SHA;
+ }
+
+ try {
+ const changes = execFileSync('git', ['status', '--porcelain', '--', '.'], {
+ cwd: repositoryRoot,
+ encoding: 'utf8',
+ stdio: ['ignore', 'pipe', 'ignore'],
+ }).trim();
+ if (changes) {
+ return 'working-tree';
+ }
+
+ return execFileSync('git', ['rev-parse', 'HEAD'], {
+ cwd: repositoryRoot,
+ encoding: 'utf8',
+ stdio: ['ignore', 'pipe', 'ignore'],
+ }).trim();
+ } catch {
+ return 'working-tree';
+ }
+}
+
+function validateSkill(skill, target) {
+ if (!skill.startsWith('---\n')) {
+ throw new Error(`${target.id}: SKILL.md must start with YAML frontmatter`);
+ }
+ if (!/^name: mintlify$/m.test(skill) || !/^description: .+$/m.test(skill)) {
+ throw new Error(`${target.id}: SKILL.md requires name and description fields`);
+ }
+ if (/mint analytics/.test(skill)) {
+ throw new Error(`${target.id}: SKILL.md contains retired CLI commands`);
+ }
+}
+
+export async function buildTarget(target, outputRoot) {
+ const targetRoot = path.join(outputRoot, target.id);
+ const skillOutput = path.join(targetRoot, 'skills', 'mintlify');
+ await rm(targetRoot, { recursive: true, force: true });
+ await mkdir(skillOutput, { recursive: true });
+
+ const skillTemplate = await readFile(path.join(contextDirectory, 'SKILL.md'), 'utf8');
+ const skill = markGenerated(skillTemplate);
+ validateSkill(skill, target);
+ await writeFile(path.join(skillOutput, 'SKILL.md'), skill);
+ await cp(path.join(contextDirectory, 'reference'), path.join(skillOutput, 'reference'), {
+ recursive: true,
+ });
+
+ const mcpServers = JSON.parse(await readFile(mcpServersPath, 'utf8'));
+ const mcpConfig = { [target.mcpConfigKey]: mcpServers };
+ await writeFile(
+ path.join(targetRoot, target.mcpConfigFile),
+ `${JSON.stringify(mcpConfig, null, 2)}\n`,
+ );
+
+ const provenance = {
+ schemaVersion: 1,
+ sourceRepository: 'mintlify/docs',
+ sourcePath: 'agent-context',
+ sourceCommit: sourceCommit(),
+ target: target.id,
+ };
+ await writeFile(
+ path.join(targetRoot, '.mintlify-agent-context.json'),
+ `${JSON.stringify(provenance, null, 2)}\n`,
+ );
+
+ return { targetRoot, provenance };
+}
+
+export async function buildAll({ outputRoot, selectedIds = [] } = {}) {
+ const resolvedOutput = outputRoot ?? path.join(repositoryRoot, 'dist');
+ await mkdir(resolvedOutput, { recursive: true });
+ const targets = await loadTargets(selectedIds);
+ return Promise.all(targets.map((target) => buildTarget(target, resolvedOutput)));
+}
+
+export async function copyTargetToRepository(targetId, destination, outputRoot) {
+ const [target] = await loadTargets([targetId]);
+ const sourceRoot = path.join(outputRoot, targetId);
+ const sourceSkill = path.join(sourceRoot, 'skills', 'mintlify');
+ const destinationSkill = path.join(destination, 'skills', 'mintlify');
+
+ await stat(sourceSkill);
+ await rm(destinationSkill, { recursive: true, force: true });
+ await mkdir(path.dirname(destinationSkill), { recursive: true });
+ await cp(sourceSkill, destinationSkill, { recursive: true });
+ await cp(
+ path.join(sourceRoot, target.mcpConfigFile),
+ path.join(destination, target.mcpConfigFile),
+ );
+ await cp(
+ path.join(sourceRoot, '.mintlify-agent-context.json'),
+ path.join(destination, '.mintlify-agent-context.json'),
+ );
+}
diff --git a/agent-context/scripts/status.mjs b/agent-context/scripts/status.mjs
new file mode 100644
index 0000000000..1f71a8bda6
--- /dev/null
+++ b/agent-context/scripts/status.mjs
@@ -0,0 +1,37 @@
+import { readFile } from 'node:fs/promises';
+import path from 'node:path';
+import { loadTargets, repositoryRoot, sourceCommit } from './lib.mjs';
+
+const workspaceRoot = path.resolve(process.argv[2] ?? path.join(repositoryRoot, '..', '..'));
+const targets = await loadTargets();
+const currentSourceCommit = sourceCommit();
+const rows = [];
+
+for (const target of targets) {
+ const repositoryName = target.repository.split('/').at(-1);
+ const pluginRoot = path.join(workspaceRoot, repositoryName);
+
+ try {
+ const lock = JSON.parse(
+ await readFile(path.join(pluginRoot, '.mintlify-agent-context.json'), 'utf8'),
+ );
+ rows.push({
+ target: target.id,
+ repository: target.repository,
+ state: lock.sourceCommit === currentSourceCommit ? 'current' : 'behind',
+ source: lock.sourceCommit,
+ });
+ } catch {
+ rows.push({
+ target: target.id,
+ repository: target.repository,
+ state: 'unavailable',
+ source: '-',
+ });
+ }
+}
+
+console.table(rows);
+if (rows.some((row) => row.state !== 'current')) {
+ process.exitCode = 1;
+}
diff --git a/agent-context/scripts/sync-target.mjs b/agent-context/scripts/sync-target.mjs
new file mode 100644
index 0000000000..a79ce505a0
--- /dev/null
+++ b/agent-context/scripts/sync-target.mjs
@@ -0,0 +1,13 @@
+import path from 'node:path';
+import { buildAll, copyTargetToRepository, repositoryRoot } from './lib.mjs';
+
+const [targetId, destinationArgument] = process.argv.slice(2);
+if (!targetId || !destinationArgument) {
+ throw new Error('Usage: node scripts/sync-target.mjs ');
+}
+
+const destination = path.resolve(process.cwd(), destinationArgument);
+const outputRoot = path.join(repositoryRoot, 'dist');
+await buildAll({ outputRoot, selectedIds: [targetId] });
+await copyTargetToRepository(targetId, destination, outputRoot);
+console.log(`Synced ${targetId} context to ${destination}`);
diff --git a/agent-context/targets/claude.json b/agent-context/targets/claude.json
new file mode 100644
index 0000000000..852b09b3ca
--- /dev/null
+++ b/agent-context/targets/claude.json
@@ -0,0 +1,6 @@
+{
+ "id": "claude",
+ "repository": "mintlify/mintlify-claude-plugin",
+ "mcpConfigFile": ".mcp.json",
+ "mcpConfigKey": "mcpServers"
+}
diff --git a/agent-context/targets/codex.json b/agent-context/targets/codex.json
new file mode 100644
index 0000000000..422c70a0c3
--- /dev/null
+++ b/agent-context/targets/codex.json
@@ -0,0 +1,6 @@
+{
+ "id": "codex",
+ "repository": "mintlify/codex-plugin",
+ "mcpConfigFile": ".mcp.json",
+ "mcpConfigKey": "mcp_servers"
+}
diff --git a/agent-context/targets/cursor.json b/agent-context/targets/cursor.json
new file mode 100644
index 0000000000..ca7fca11ee
--- /dev/null
+++ b/agent-context/targets/cursor.json
@@ -0,0 +1,6 @@
+{
+ "id": "cursor",
+ "repository": "mintlify/cursor-plugin",
+ "mcpConfigFile": "mcp.json",
+ "mcpConfigKey": "mcpServers"
+}
diff --git a/agent-context/test/build.test.mjs b/agent-context/test/build.test.mjs
new file mode 100644
index 0000000000..af2ed4710d
--- /dev/null
+++ b/agent-context/test/build.test.mjs
@@ -0,0 +1,84 @@
+import assert from 'node:assert/strict';
+import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import path from 'node:path';
+import test from 'node:test';
+import { buildAll, copyTargetToRepository } from '../scripts/lib.mjs';
+
+test('builds all client variants from one canonical skill', async () => {
+ const outputRoot = await mkdtemp(path.join(tmpdir(), 'mintlify-agent-context-test-'));
+
+ try {
+ await buildAll({ outputRoot });
+ const codex = await readFile(
+ path.join(outputRoot, 'codex', 'skills', 'mintlify', 'SKILL.md'),
+ 'utf8',
+ );
+ const cursor = await readFile(
+ path.join(outputRoot, 'cursor', 'skills', 'mintlify', 'SKILL.md'),
+ 'utf8',
+ );
+ const claude = await readFile(
+ path.join(outputRoot, 'claude', 'skills', 'mintlify', 'SKILL.md'),
+ 'utf8',
+ );
+ const codexMcp = JSON.parse(
+ await readFile(path.join(outputRoot, 'codex', '.mcp.json'), 'utf8'),
+ );
+ const cursorMcp = JSON.parse(
+ await readFile(path.join(outputRoot, 'cursor', 'mcp.json'), 'utf8'),
+ );
+ const claudeMcp = JSON.parse(
+ await readFile(path.join(outputRoot, 'claude', '.mcp.json'), 'utf8'),
+ );
+
+ assert.equal(cursor, codex);
+ assert.equal(claude, codex);
+ for (const skill of [codex, cursor, claude]) {
+ assert.match(skill, /Generated from mintlify\/docs\/agent-context/);
+ assert.match(skill, /### Mintlify Search/);
+ assert.match(skill, /### Mintlify Admin/);
+ assert.match(skill, /Complete authentication in the browser when prompted/);
+ assert.match(skill, /mint automations/);
+ assert.doesNotMatch(skill, /mint analytics|mint workflow|\{\{/);
+ }
+ assert.deepEqual(codexMcp.mcp_servers, cursorMcp.mcpServers);
+ assert.deepEqual(claudeMcp.mcpServers, cursorMcp.mcpServers);
+ assert.deepEqual(Object.keys(cursorMcp.mcpServers), [
+ 'Mintlify Search',
+ 'Mintlify Admin',
+ ]);
+ } finally {
+ await rm(outputRoot, { recursive: true, force: true });
+ }
+});
+
+test('sync replaces only generated context paths', async () => {
+ const root = await mkdtemp(path.join(tmpdir(), 'mintlify-agent-context-sync-test-'));
+ const outputRoot = path.join(root, 'dist');
+ const destination = path.join(root, 'plugin');
+
+ try {
+ await mkdir(path.join(destination, 'skills', 'mintlify'), { recursive: true });
+ await writeFile(path.join(destination, 'README.md'), 'target-owned\n');
+ await writeFile(path.join(destination, 'skills', 'mintlify', 'stale.md'), 'remove me\n');
+ await writeFile(path.join(destination, '.mcp.json'), '{"stale":true}\n');
+
+ await buildAll({ outputRoot, selectedIds: ['codex'] });
+ await copyTargetToRepository('codex', destination, outputRoot);
+
+ assert.equal(await readFile(path.join(destination, 'README.md'), 'utf8'), 'target-owned\n');
+ await assert.rejects(readFile(path.join(destination, 'skills', 'mintlify', 'stale.md')));
+ assert.match(
+ await readFile(path.join(destination, 'skills', 'mintlify', 'SKILL.md'), 'utf8'),
+ /### Mintlify Search/,
+ );
+ const mcpConfig = JSON.parse(await readFile(path.join(destination, '.mcp.json'), 'utf8'));
+ assert.deepEqual(Object.keys(mcpConfig.mcp_servers), [
+ 'Mintlify Search',
+ 'Mintlify Admin',
+ ]);
+ } finally {
+ await rm(root, { recursive: true, force: true });
+ }
+});