Skip to content

Commit 1c287d9

Browse files
committed
feat: Add Cursor & VSCode integration
1 parent a1c3aab commit 1c287d9

File tree

1 file changed

+125
-3
lines changed
  • apify-docs-theme/src/theme/LLMButtons

1 file changed

+125
-3
lines changed

apify-docs-theme/src/theme/LLMButtons/index.jsx

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ const DROPDOWN_OPTIONS = [
3131
Icon: MarkdownIcon,
3232
value: 'viewAsMarkdown',
3333
},
34+
{
35+
label: 'Copy MCP server',
36+
description: 'Copy Apify MCP configuration',
37+
showExternalIcon: false,
38+
Icon: CopyIcon,
39+
value: 'copyMcpServer',
40+
},
41+
{
42+
label: 'Connect to Cursor',
43+
description: 'Open MCP configurator for Cursor',
44+
showExternalIcon: true,
45+
Icon: ExternalLinkIcon,
46+
value: 'connectCursor',
47+
},
48+
{
49+
label: 'Connect to VS Code',
50+
description: 'Open MCP configurator for VS Code',
51+
showExternalIcon: true,
52+
Icon: ExternalLinkIcon,
53+
value: 'connectVsCode',
54+
},
3455
{
3556
label: 'Open in ChatGPT',
3657
description: 'Ask questions about this page',
@@ -54,6 +75,14 @@ const DROPDOWN_OPTIONS = [
5475
},
5576
];
5677

78+
const MCP_CONFIG_JSON = `{
79+
"mcpServers": {
80+
"apify": {
81+
"url": "https://mcp.apify.com/?tools=docs"
82+
}
83+
}
84+
}`;
85+
5786
const getPrompt = (currentUrl) => `Read from ${currentUrl} so I can ask questions about it.`;
5887
const getMarkdownUrl = (currentUrl) => {
5988
const url = new URL(currentUrl);
@@ -161,6 +190,90 @@ const onCopyAsMarkdownClick = async ({ setCopyingStatus }) => {
161190
}
162191
};
163192

193+
const onCopyMcpServerClick = async () => {
194+
if (window.analytics) {
195+
window.analytics.track('Clicked', {
196+
app: 'docs',
197+
button_text: 'Copy MCP server',
198+
element: 'llm-buttons.copyMcpServer',
199+
});
200+
}
201+
202+
try {
203+
await navigator.clipboard.writeText(MCP_CONFIG_JSON);
204+
} catch (error) {
205+
console.error('Failed to copy MCP configuration:', error);
206+
}
207+
};
208+
209+
const openApifyMcpConfigurator = (integration) => {
210+
try {
211+
window.open(`https://mcp.apify.com/?integration=${integration}`, '_blank');
212+
} catch (error) {
213+
console.error('Error opening fallback URL:', error);
214+
}
215+
};
216+
217+
const openMcpIntegration = async (integration, mcpUrl) => {
218+
// Try to open the app directly using URL scheme
219+
let appUrl;
220+
if (integration === 'cursor') {
221+
// Cursor deeplink format:
222+
// cursor://anysphere.cursor-deeplink/mcp/install?name=$NAME&config=$BASE64_JSON
223+
const cursorConfig = {
224+
url: mcpUrl,
225+
};
226+
const encodedConfig = btoa(JSON.stringify(cursorConfig));
227+
appUrl = `cursor://anysphere.cursor-deeplink/mcp/install?name=apify&config=${encodeURIComponent(encodedConfig)}`;
228+
} else if (integration === 'vscode') {
229+
// VS Code deeplink format: vscode:mcp/install?<url-encoded-json>
230+
// JSON structure: {"name":"Apify","type":"http","url":"<mcpUrl>"}
231+
const mcpConfig = {
232+
name: 'Apify',
233+
type: 'http',
234+
url: mcpUrl,
235+
};
236+
const encodedConfig = encodeURIComponent(JSON.stringify(mcpConfig));
237+
appUrl = `vscode:mcp/install?${encodedConfig}`;
238+
}
239+
240+
if (appUrl) {
241+
try {
242+
window.open(appUrl, '_blank');
243+
} catch {
244+
// If deeplink fails, fallback to web configurator
245+
openApifyMcpConfigurator(integration);
246+
}
247+
} else {
248+
// Fallback to web configurator
249+
openApifyMcpConfigurator(integration);
250+
}
251+
};
252+
253+
const onConnectCursorClick = () => {
254+
if (window.analytics) {
255+
window.analytics.track('Clicked', {
256+
app: 'docs',
257+
button_text: 'Connect to Cursor',
258+
element: 'llm-buttons.connectCursor',
259+
});
260+
}
261+
262+
openMcpIntegration('cursor', 'https://mcp.apify.com/?tools=docs');
263+
};
264+
265+
const onConnectVsCodeClick = () => {
266+
if (window.analytics) {
267+
window.analytics.track('Clicked', {
268+
app: 'docs',
269+
button_text: 'Connect to VS Code',
270+
element: 'llm-buttons.connectVsCode',
271+
});
272+
}
273+
274+
openMcpIntegration('vscode', 'https://mcp.apify.com/?tools=docs');
275+
};
276+
164277
const onViewAsMarkdownClick = () => {
165278
if (window.analytics) {
166279
window.analytics.track('Clicked', {
@@ -257,6 +370,15 @@ export default function LLMButtons({ isApiReferencePage = false }) {
257370
case 'viewAsMarkdown':
258371
onViewAsMarkdownClick();
259372
break;
373+
case 'copyMcpServer':
374+
onCopyMcpServerClick();
375+
break;
376+
case 'connectCursor':
377+
onConnectCursorClick();
378+
break;
379+
case 'connectVsCode':
380+
onConnectVsCodeClick();
381+
break;
260382
case 'openInChatGPT':
261383
onOpenInChatGPTClick();
262384
break;
@@ -277,9 +399,9 @@ export default function LLMButtons({ isApiReferencePage = false }) {
277399
[styles.llmMenuApiReferencePage]: isApiReferencePage,
278400
})}
279401
onMenuOpen={(isOpen) => chevronIconRef.current?.classList.toggle(
280-
styles.chevronIconOpen,
281-
isOpen,
282-
)
402+
styles.chevronIconOpen,
403+
isOpen,
404+
)
283405
}
284406
components={{
285407
MenuBase: (props) => (

0 commit comments

Comments
 (0)