@@ -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+
5786const getPrompt = ( currentUrl ) => `Read from ${ currentUrl } so I can ask questions about it.` ;
5887const 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+
164277const 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