Skip to content
Draft
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: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ GOOGLE_TAG_MANAGER_ID=GTM-PTLT3GH
POSTHOG_API_HOST=https://directus.io/ingest
POSTHOG_API_KEY=phc_secret_key_here
NUXT_PUBLIC_SITE_URL=https://directus.io
# Optional. Fine-grained PAT, public repos read-only. Required for code search and raises GitHub raw rate limits.
# GITHUB_TOKEN=github_pat_...
172 changes: 107 additions & 65 deletions app/components/CopyDocButton.vue
Original file line number Diff line number Diff line change
@@ -1,88 +1,120 @@
<script setup lang="ts">
import type { ContentCollectionItem } from '@nuxt/content';
import { useClipboard } from '@vueuse/core';
import { withBase } from 'ufo';
import {
chatGptPromptUrl,
claudePromptUrl,
MCP_IDES,
mcpDeeplinkPath,
mcpServerUrl,
} from '~/utils/agent-deeplinks';

const props = defineProps<{
page: ContentCollectionItem;
}>();

interface CopyMenuItem {
label: string;
icon: string;
description?: string;
external?: boolean;
onSelect: () => void | Promise<void>;
}

const isCopied = ref(false);
const isMcpUrlCopied = ref(false);
const isMarkdownLinkCopied = ref(false);

const items = [
{
label: 'Copy Page',
description: 'Copy page as Markdown for LLMs',
icon: 'material-symbols:content-copy-outline',
onSelect: () => copyPage(),
},
{
label: 'View as Markdown',
description: 'View this page as plain text',
icon: 'material-symbols:markdown-outline',
onSelect: () => {
navigateTo(
`https://github.com/directus/docs/raw/refs/heads/main/content/${props.page?.stem}.md`,
{
open: {
target: '_blank',
},
},
);
},
},
{
label: 'Open in ChatGPT',
icon: 'i-simple-icons:openai',
target: '_blank',
onSelect() {
navigateTo(
`https://chatgpt.com/?hints=search&q=${encodeURIComponent(`Read ${window.location.href} so I can ask questions about it.`)}`,
{
open: {
target: '_blank',
},
},
);
},
},
{
label: 'Open in Claude',
icon: 'i-simple-icons:anthropic',
target: '_blank',
onSelect() {
navigateTo(
`https://claude.ai/new?q=${encodeURIComponent(`Read ${window.location.href} so I can ask questions about it.`)}`,
{
open: {
target: '_blank',
},
},
);
},
},
];
const baseURL = useRuntimeConfig().app.baseURL;
const markdownUrl = computed(() => withBase(`${props.page?.path ?? ''}.md`, baseURL));
const { copy } = useClipboard();

async function copyPage() {
function openExternal(url: string) {
navigateTo(url, { open: { target: '_blank' } });
}

async function copyWithFlag(text: string, flag: Ref<boolean>) {
try {
const value = (props.page?.rawbody ?? '').replace(/\\n/g, '\n');
await useClipboard().copy(value);
isCopied.value = true;
await copy(text);
}
catch {
try {
await navigator.clipboard.writeText(text);
}
catch {
return;
}
}
flag.value = true;
setTimeout(() => {
flag.value = false;
}, 2000);
}

setTimeout(() => {
isCopied.value = false;
}, 2000);
async function copyPage() {
let value: string;
try {
value = await $fetch<string>(markdownUrl.value, { responseType: 'text' });
}
catch {
// Silently fail if copy doesn't work
value = (props.page?.rawbody ?? '').replace(/\\n/g, '\n');
}
await copyWithFlag(value, isCopied);
}

const copyMarkdownLink = () => copyWithFlag(`${window.location.origin}${markdownUrl.value}`, isMarkdownLinkCopied);
const copyMcpUrl = () => copyWithFlag(mcpServerUrl(window.location.origin, baseURL), isMcpUrlCopied);

const items = computed<CopyMenuItem[][]>(() => [
[
{
label: isMarkdownLinkCopied.value ? 'Copied Markdown link!' : 'Copy Markdown link',
icon: 'material-symbols:link',
onSelect: () => copyMarkdownLink(),
},
{
label: 'View as Markdown',
icon: 'material-symbols:markdown-outline',
external: true,
onSelect: () => openExternal(markdownUrl.value),
},
{
label: 'Open in ChatGPT',
icon: 'i-simple-icons:openai',
external: true,
onSelect: () => openExternal(chatGptPromptUrl(`Read ${window.location.href} so I can ask questions about it.`)),
},
{
label: 'Open in Claude',
icon: 'i-simple-icons:anthropic',
external: true,
onSelect: () => openExternal(claudePromptUrl(`Read ${window.location.href} so I can ask questions about it.`)),
},
],
[
{
label: isMcpUrlCopied.value ? 'Copied MCP URL!' : 'Copy MCP server URL',
description: 'Use with any MCP-compatible client',
icon: 'material-symbols:link',
onSelect: () => copyMcpUrl(),
},
...MCP_IDES.map(ide => ({
label: ide.label,
icon: ide.icon,
external: true,
onSelect: () => openExternal(mcpDeeplinkPath(baseURL, ide.id)),
})),
],
]);
</script>

<template>
<UFieldGroup>
<UButton
color="neutral"
variant="outline"
size="sm"
variant="soft"
leading-icon="material-symbols:content-copy-outline"
:label="isCopied ? 'Copied!' : 'Copy page'"
@click="copyPage"
Expand All @@ -93,8 +125,9 @@ async function copyPage() {
:content="{ side: 'bottom', align: 'end' }"
>
<UButton
size="sm"
variant="soft"
color="neutral"
variant="outline"
icon="material-symbols:keyboard-arrow-down"
/>
<template #item="{ item }">
Expand All @@ -104,8 +137,17 @@ async function copyPage() {
class="text-lg"
/>
<div>
<p>{{ item.label }}</p>
<p class="text-xs text-muted">
<p>
{{ item.label }}<UIcon
v-if="item.external"
name="i-lucide-arrow-up-right"
class="ml-0.5 size-3 text-muted align-text-top"
/>
</p>
<p
v-if="item.description"
class="text-xs text-muted"
>
{{ item.description }}
</p>
</div>
Expand Down
34 changes: 34 additions & 0 deletions app/utils/agent-deeplinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// IDE deeplink helpers for the docs MCP server and agent prompt copy actions.
// Toolkit-supported IDEs are sourced from @nuxtjs/mcp-toolkit's deeplink route.

export type McpIde = 'cursor' | 'vscode';

export interface McpIdeOption {
id: McpIde;
label: string;
icon: string;
}

export const MCP_IDES: McpIdeOption[] = [
{ id: 'cursor', label: 'Add to Cursor', icon: 'i-simple-icons:cursor' },
{ id: 'vscode', label: 'Add to VS Code', icon: 'i-simple-icons:visualstudiocode' },
];

export function mcpDeeplinkPath(baseURL: string, ide?: McpIde) {
const base = baseURL.replace(/\/$/, '');
const path = `${base}/mcp/deeplink`;
return ide ? `${path}?ide=${ide}` : path;
}

export function mcpServerUrl(origin: string, baseURL: string) {
const base = baseURL.replace(/\/$/, '');
return `${origin}${base}/mcp`;
}

export function chatGptPromptUrl(prompt: string) {
return `https://chatgpt.com/?hints=search&q=${encodeURIComponent(prompt)}`;
}

export function claudePromptUrl(prompt: string) {
return `https://claude.ai/new?q=${encodeURIComponent(prompt)}`;
}
4 changes: 3 additions & 1 deletion content/guides/11.ai/2.mcp/0.index.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
stableId: 91f4d2fc-286d-47c0-8942-68af505ce679
title: Overview
title: Directus MCP
description: Connect AI assistants directly to your Directus instance. Let Claude, ChatGPT, and other AI tools manage your content without manual copy-pasting.
navigation:
title: Overview
---

<!-- TODO: Add video
Expand Down
91 changes: 91 additions & 0 deletions content/mcp-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
stableId: 4b6990b6-8399-4386-890d-a8eda0e0b8b9
title: Docs MCP Server
description: Connect Claude, Cursor, VS Code, or any MCP-compatible client directly to the Directus documentation.
---

You landed here because you opened the Directus docs MCP server in a browser. The server itself speaks the [Model Context Protocol](https://modelcontextprotocol.io/). Connect a compatible AI client to it and your assistant can search and read these docs as a live tool.

::callout{icon="material-symbols:info" color="info"}
This is the **docs MCP server**, which exposes this documentation site to AI clients. If you want to connect AI tools to your own Directus instance to manage content and schema, see the [Directus product MCP guide](/guides/ai/mcp) instead.
::

## Server URL

```
https://directus.io/docs/mcp
```

Transport: streamable HTTP. No authentication required.

## Install in one click

::card-group

:::card{title="Add to Cursor" icon="i-simple-icons:cursor" to="/mcp/deeplink" target="_blank"}
Opens Cursor and registers the docs MCP server.
:::

:::card{title="Add to VS Code" icon="i-simple-icons:visualstudiocode" to="/mcp/deeplink?ide=vscode" target="_blank"}
Opens VS Code and registers the docs MCP server.
:::

::

## Manual setup

For clients without a one-click installer, add the server to your MCP config.

### Claude Code

```bash
claude mcp add --transport http directus-docs https://directus.io/docs/mcp
```

### Claude Desktop

Edit `claude_desktop_config.json`:

```json
{
"mcpServers": {
"directus-docs": {
"type": "http",
"url": "https://directus.io/docs/mcp"
}
}
}
```

### Codex / Windsurf / other HTTP-MCP clients

Point them at `https://directus.io/docs/mcp` with transport set to `http` (sometimes called `streamable-http`).

## Available tools

| Tool | Inputs | Use it for |
|---|---|---|
| `list-docs` | `pathPrefix?`, `limit?` | Discover available docs pages when you do not know exact paths. |
| `get-doc` | `path` | Fetch a single page's full markdown by path (e.g. `/getting-started/overview`). |
| `search-docs` | `query`, `section?`, `framework?`, `limit?` | Full-text search across the docs. Returns ranked results with title, snippet, and section. |
| `search-directus-code` | `query`, `repo?`, `language?`, `path?`, `limit?` | Search source code across allowlisted Directus GitHub repos. |
| `get-directus-file` | `repo?`, `path`, `ref?`, `offset?`, `bytes?` | Fetch source files from allowlisted Directus GitHub repos. |

## Try it

After connecting, ask your AI client questions like:

- "Search the Directus docs for OAuth setup."
- "Get the docs page on configuring the SDK in Nuxt."
- "List all guides in the data-model section."
- "How do I create a flow in Directus? Use the docs MCP."

The assistant should call `search-docs` or `list-docs`, then `get-doc` to read the relevant page.

## Troubleshooting

- **Connection refused or 404** - confirm the URL is exactly `https://directus.io/docs/mcp` (note the `/docs` prefix).
- **Tools not showing up** - restart your MCP client after adding the server. Some clients only load tools at startup.
- **Search returns nothing** - `search-docs` is case-insensitive but does keyword match. Try fewer or broader terms.

Found a bug? Report it at [github.com/directus/docs/issues](https://github.com/directus/docs/issues).
Loading