@redocly/theme 0.59.0-next.10 → 0.59.0-next.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Buttons/ConnectMCPButton.d.ts +8 -0
- package/lib/components/Buttons/ConnectMCPButton.js +145 -0
- package/lib/components/Buttons/variables.d.ts +1 -0
- package/lib/components/Buttons/variables.js +41 -1
- package/lib/components/PageActions/PageActions.js +4 -1
- package/lib/components/PageActions/variables.js +2 -0
- package/lib/components/Segmented/Segmented.d.ts +1 -8
- package/lib/components/Segmented/Segmented.js +3 -1
- package/lib/core/constants/index.d.ts +1 -0
- package/lib/core/constants/index.js +1 -0
- package/lib/core/constants/mcp.d.ts +1 -0
- package/lib/core/constants/mcp.js +5 -0
- package/lib/core/hooks/index.d.ts +2 -0
- package/lib/core/hooks/index.js +2 -0
- package/lib/core/hooks/use-connect-mcp-button.d.ts +13 -0
- package/lib/core/hooks/use-connect-mcp-button.js +50 -0
- package/lib/core/hooks/use-mcp-config.d.ts +9 -0
- package/lib/core/hooks/use-mcp-config.js +27 -0
- package/lib/core/hooks/use-page-actions.d.ts +1 -1
- package/lib/core/hooks/use-page-actions.js +98 -95
- package/lib/core/openapi/index.d.ts +1 -0
- package/lib/core/styles/global.js +1 -0
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/index.js +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/mcp.d.ts +6 -0
- package/lib/core/types/mcp.js +3 -0
- package/lib/core/types/segmented.d.ts +12 -0
- package/lib/core/types/segmented.js +3 -0
- package/lib/core/utils/index.d.ts +1 -0
- package/lib/core/utils/index.js +1 -0
- package/lib/core/utils/mcp.d.ts +2 -0
- package/lib/core/utils/mcp.js +31 -0
- package/lib/icons/ConnectIcon/ConnectIcon.d.ts +9 -0
- package/lib/icons/ConnectIcon/ConnectIcon.js +17 -0
- package/lib/icons/VSCodeIcon/VSCodeIcon.d.ts +9 -0
- package/lib/icons/VSCodeIcon/VSCodeIcon.js +17 -0
- package/lib/markdoc/components/ConnectMCP/ConnectMCP.d.ts +8 -0
- package/lib/markdoc/components/ConnectMCP/ConnectMCP.js +19 -0
- package/lib/markdoc/components/default.d.ts +1 -0
- package/lib/markdoc/components/default.js +1 -0
- package/lib/markdoc/default.d.ts +6 -0
- package/lib/markdoc/default.js +2 -0
- package/lib/markdoc/tags/connect-mcp.d.ts +2 -0
- package/lib/markdoc/tags/connect-mcp.js +27 -0
- package/package.json +3 -3
- package/src/components/Buttons/ConnectMCPButton.tsx +180 -0
- package/src/components/Buttons/variables.ts +41 -0
- package/src/components/PageActions/PageActions.tsx +5 -1
- package/src/components/PageActions/variables.ts +2 -0
- package/src/components/Segmented/Segmented.tsx +15 -20
- package/src/core/constants/index.ts +1 -0
- package/src/core/constants/mcp.ts +1 -0
- package/src/core/hooks/index.ts +2 -0
- package/src/core/hooks/use-connect-mcp-button.ts +79 -0
- package/src/core/hooks/use-mcp-config.ts +43 -0
- package/src/core/hooks/use-page-actions.ts +148 -126
- package/src/core/openapi/index.ts +1 -0
- package/src/core/styles/global.ts +2 -1
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +9 -0
- package/src/core/types/mcp.ts +8 -0
- package/src/core/types/segmented.ts +14 -0
- package/src/core/utils/index.ts +1 -0
- package/src/core/utils/mcp.ts +34 -0
- package/src/icons/ConnectIcon/ConnectIcon.tsx +27 -0
- package/src/icons/VSCodeIcon/VSCodeIcon.tsx +29 -0
- package/src/markdoc/components/ConnectMCP/ConnectMCP.tsx +28 -0
- package/src/markdoc/components/default.ts +1 -0
- package/src/markdoc/default.ts +2 -0
- package/src/markdoc/tags/connect-mcp.ts +25 -0
|
@@ -1,24 +1,40 @@
|
|
|
1
|
-
import { useMemo } from 'react';
|
|
1
|
+
import { useMemo, useCallback } from 'react';
|
|
2
2
|
|
|
3
3
|
import type { PageProps, UiAccessibleConfig } from '@redocly/config';
|
|
4
|
-
import type { PageAction } from '../types';
|
|
4
|
+
import type { PageAction, MCPClientType, McpConnectionParams } from '../types';
|
|
5
5
|
|
|
6
6
|
import { CopyIcon } from '@redocly/theme/icons/CopyIcon/CopyIcon';
|
|
7
7
|
import { ChatGptIcon } from '@redocly/theme/icons/ChatGptIcon/ChatGptIcon';
|
|
8
8
|
import { ClaudeIcon } from '@redocly/theme/icons/ClaudeIcon/ClaudeIcon';
|
|
9
9
|
import { MarkdownFullIcon } from '@redocly/theme/icons/MarkdownFullIcon/MarkdownFullIcon';
|
|
10
|
+
import { VSCodeIcon } from '@redocly/theme/icons/VSCodeIcon/VSCodeIcon';
|
|
11
|
+
import { CursorIcon } from '@redocly/theme/icons/CursorIcon/CursorIcon';
|
|
10
12
|
|
|
11
13
|
import { useThemeHooks } from './use-theme-hooks';
|
|
12
14
|
import { useThemeConfig } from './use-theme-config';
|
|
15
|
+
import { useMCPConfig } from './use-mcp-config';
|
|
13
16
|
import { ClipboardService } from '../utils/clipboard-service';
|
|
14
17
|
import { IS_BROWSER } from '../utils/dom';
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
const DEFAULT_ENABLED_ACTIONS = [
|
|
18
|
-
|
|
19
|
-
'
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
import { generateMCPDeepLink } from '../utils/mcp';
|
|
19
|
+
|
|
20
|
+
const DEFAULT_ENABLED_ACTIONS = [
|
|
21
|
+
'copy',
|
|
22
|
+
'view',
|
|
23
|
+
'chatgpt',
|
|
24
|
+
'claude',
|
|
25
|
+
'docs-mcp-cursor',
|
|
26
|
+
'docs-mcp-vscode',
|
|
27
|
+
] as const;
|
|
28
|
+
|
|
29
|
+
export type PageActionType =
|
|
30
|
+
| 'copy'
|
|
31
|
+
| 'view'
|
|
32
|
+
| 'chatgpt'
|
|
33
|
+
| 'claude'
|
|
34
|
+
| 'docs-mcp-cursor'
|
|
35
|
+
| 'docs-mcp-vscode'
|
|
36
|
+
| 'mcp-cursor'
|
|
37
|
+
| 'mcp-vscode';
|
|
22
38
|
|
|
23
39
|
export function usePageActions(
|
|
24
40
|
pageSlug: string,
|
|
@@ -27,12 +43,12 @@ export function usePageActions(
|
|
|
27
43
|
): PageAction[] {
|
|
28
44
|
const { useTranslate, usePageData, usePageProps, usePageSharedData } = useThemeHooks();
|
|
29
45
|
const { translate } = useTranslate();
|
|
30
|
-
|
|
31
46
|
const themeConfig = useThemeConfig();
|
|
32
47
|
const pageProps = usePageProps();
|
|
33
48
|
const openApiSharedData = usePageSharedData<
|
|
34
49
|
{ options: { excludeFromSearch: boolean } } | undefined
|
|
35
50
|
>('openAPIDocsStore');
|
|
51
|
+
const mcpConfig = useMCPConfig();
|
|
36
52
|
|
|
37
53
|
const shouldHideAllActions = shouldHidePageActions(
|
|
38
54
|
pageProps,
|
|
@@ -41,148 +57,154 @@ export function usePageActions(
|
|
|
41
57
|
);
|
|
42
58
|
const { isPublic } = usePageData() || {};
|
|
43
59
|
|
|
60
|
+
const createMCPHandler = useCallback(
|
|
61
|
+
(clientType: MCPClientType, requiresMcpUrl: boolean = false) =>
|
|
62
|
+
() => {
|
|
63
|
+
if (requiresMcpUrl && !mcpUrl) return null;
|
|
64
|
+
if (!requiresMcpUrl && (mcpUrl || mcpConfig.isMcpDisabled)) return null;
|
|
65
|
+
|
|
66
|
+
const config = requiresMcpUrl
|
|
67
|
+
? { serverName: mcpConfig.serverName, url: mcpUrl || '' }
|
|
68
|
+
: { serverName: mcpConfig.serverName, url: mcpConfig.serverUrl || '' };
|
|
69
|
+
|
|
70
|
+
return createMCPAction({ clientType, mcpConfig: config, translate });
|
|
71
|
+
},
|
|
72
|
+
[mcpUrl, mcpConfig, translate],
|
|
73
|
+
);
|
|
74
|
+
|
|
44
75
|
const result: PageAction[] = useMemo(() => {
|
|
45
76
|
if (shouldHideAllActions) {
|
|
46
77
|
return [];
|
|
47
78
|
}
|
|
48
79
|
|
|
49
80
|
const origin = IS_BROWSER ? window.location.origin : (globalThis as any)['SSR_HOSTNAME'];
|
|
50
|
-
|
|
51
81
|
const normalizedSlug = pageSlug.startsWith('/') ? pageSlug : '/' + pageSlug;
|
|
52
|
-
|
|
53
82
|
const mdPageUrl = new URL(
|
|
54
83
|
origin + normalizedSlug + (normalizedSlug === '/' ? 'index.html.md' : '.md'),
|
|
55
84
|
).toString();
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
btoa(JSON.stringify(jsonConfig['mcp-server'])),
|
|
79
|
-
);
|
|
80
|
-
return url;
|
|
86
|
+
const actionHandlers: Record<PageActionType, () => PageAction | null> = {
|
|
87
|
+
'docs-mcp-cursor': createMCPHandler('cursor'),
|
|
88
|
+
'docs-mcp-vscode': createMCPHandler('vscode'),
|
|
89
|
+
'mcp-cursor': createMCPHandler('cursor', true),
|
|
90
|
+
'mcp-vscode': createMCPHandler('vscode', true),
|
|
91
|
+
|
|
92
|
+
copy: () => ({
|
|
93
|
+
buttonText: translate('page.actions.copyButtonText', 'Copy'),
|
|
94
|
+
title: translate('page.actions.copyTitle', 'Copy for LLM'),
|
|
95
|
+
description: translate('page.actions.copyDescription', 'Copy page as Markdown for LLMs'),
|
|
96
|
+
iconComponent: CopyIcon,
|
|
97
|
+
onClick: async () => {
|
|
98
|
+
try {
|
|
99
|
+
const result = await fetch(mdPageUrl);
|
|
100
|
+
if (result.status !== 200) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const text = await result.text();
|
|
104
|
+
ClipboardService.copyCustom(text);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(error);
|
|
81
107
|
}
|
|
82
|
-
|
|
83
|
-
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
|
|
111
|
+
view: () => ({
|
|
112
|
+
buttonText: translate('page.actions.viewAsMdButtonText', 'View as Markdown'),
|
|
113
|
+
title: translate('page.actions.viewAsMdTitle', 'View as Markdown'),
|
|
114
|
+
description: translate('page.actions.viewAsMdDescription', 'Open this page as Markdown'),
|
|
115
|
+
iconComponent: MarkdownFullIcon,
|
|
116
|
+
link: mdPageUrl,
|
|
117
|
+
}),
|
|
118
|
+
|
|
119
|
+
chatgpt: () => {
|
|
120
|
+
if (!isPublic) {
|
|
121
|
+
return null;
|
|
84
122
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
'Install MCP server on Cursor',
|
|
98
|
-
),
|
|
99
|
-
iconComponent: CursorIcon,
|
|
100
|
-
onClick: () => {
|
|
101
|
-
try {
|
|
102
|
-
const url = generateMCPConfig(true);
|
|
103
|
-
window.open(url, '_blank');
|
|
104
|
-
} catch (error) {
|
|
105
|
-
console.error(error);
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
};
|
|
109
|
-
case 'copy':
|
|
110
|
-
return {
|
|
111
|
-
buttonText: translate('page.actions.copyButtonText', 'Copy'),
|
|
112
|
-
title: translate('page.actions.copyTitle', 'Copy for LLM'),
|
|
113
|
-
description: translate(
|
|
114
|
-
'page.actions.copyDescription',
|
|
115
|
-
'Copy page as Markdown for LLMs',
|
|
116
|
-
),
|
|
117
|
-
iconComponent: CopyIcon,
|
|
118
|
-
onClick: async () => {
|
|
119
|
-
try {
|
|
120
|
-
const result = await fetch(mdPageUrl);
|
|
121
|
-
if (result.status !== 200) {
|
|
122
|
-
throw new Error('Failed to fetch markdown content');
|
|
123
|
-
}
|
|
124
|
-
const text = await result.text();
|
|
125
|
-
|
|
126
|
-
ClipboardService.copyCustom(text);
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error(error);
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
};
|
|
132
|
-
case 'view':
|
|
133
|
-
return {
|
|
134
|
-
buttonText: translate('page.actions.viewAsMdButtonText', 'View as Markdown'),
|
|
135
|
-
title: translate('page.actions.viewAsMdTitle', 'View as Markdown'),
|
|
136
|
-
description: translate(
|
|
137
|
-
'page.actions.viewAsMdDescription',
|
|
138
|
-
'Open this page as Markdown',
|
|
139
|
-
),
|
|
140
|
-
iconComponent: MarkdownFullIcon,
|
|
141
|
-
link: mdPageUrl,
|
|
142
|
-
};
|
|
143
|
-
case 'chatgpt':
|
|
144
|
-
if (!isPublic) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return {
|
|
149
|
-
buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
|
|
150
|
-
title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
|
|
151
|
-
description: translate(
|
|
152
|
-
'page.actions.chatGptDescription',
|
|
153
|
-
'Get insights from ChatGPT',
|
|
154
|
-
),
|
|
155
|
-
iconComponent: ChatGptIcon,
|
|
156
|
-
link: getExternalAiPromptLink('https://chat.openai.com'),
|
|
157
|
-
};
|
|
158
|
-
case 'claude':
|
|
159
|
-
if (!isPublic) {
|
|
160
|
-
return null;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
|
|
165
|
-
title: translate('page.actions.claudeTitle', 'Open in Claude'),
|
|
166
|
-
description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
|
|
167
|
-
iconComponent: ClaudeIcon,
|
|
168
|
-
link: getExternalAiPromptLink('https://claude.ai/new'),
|
|
169
|
-
};
|
|
123
|
+
return {
|
|
124
|
+
buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
|
|
125
|
+
title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
|
|
126
|
+
description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
|
|
127
|
+
iconComponent: ChatGptIcon,
|
|
128
|
+
link: getExternalAiPromptLink('https://chat.openai.com', mdPageUrl),
|
|
129
|
+
};
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
claude: () => {
|
|
133
|
+
if (!isPublic) {
|
|
134
|
+
return null;
|
|
170
135
|
}
|
|
171
|
-
|
|
172
|
-
|
|
136
|
+
return {
|
|
137
|
+
buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
|
|
138
|
+
title: translate('page.actions.claudeTitle', 'Open in Claude'),
|
|
139
|
+
description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
|
|
140
|
+
iconComponent: ClaudeIcon,
|
|
141
|
+
link: getExternalAiPromptLink('https://claude.ai/new', mdPageUrl),
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (themeConfig.navigation?.actions?.items || actions || DEFAULT_ENABLED_ACTIONS)
|
|
147
|
+
.map((action) => actionHandlers[action]?.())
|
|
148
|
+
.filter((action): action is PageAction => action !== null);
|
|
173
149
|
}, [
|
|
174
150
|
shouldHideAllActions,
|
|
175
151
|
pageSlug,
|
|
176
152
|
themeConfig.navigation?.actions?.items,
|
|
177
153
|
actions,
|
|
178
|
-
mcpUrl,
|
|
179
154
|
translate,
|
|
180
155
|
isPublic,
|
|
156
|
+
createMCPHandler,
|
|
181
157
|
]);
|
|
182
158
|
|
|
183
159
|
return result;
|
|
184
160
|
}
|
|
185
161
|
|
|
162
|
+
function getExternalAiPromptLink(baseUrl: string, mdPageUrl: string): string {
|
|
163
|
+
const externalAiPrompt = `Read ${mdPageUrl} and answer questions based on the content.`;
|
|
164
|
+
const url = new URL(baseUrl);
|
|
165
|
+
url.searchParams.set('q', externalAiPrompt);
|
|
166
|
+
return url.toString();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
type CreateMCPActionParams = {
|
|
170
|
+
clientType: MCPClientType;
|
|
171
|
+
mcpConfig: McpConnectionParams;
|
|
172
|
+
translate: (key: string, defaultValue: string) => string;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
function createMCPAction({ clientType, mcpConfig, translate }: CreateMCPActionParams): PageAction {
|
|
176
|
+
const url = generateMCPDeepLink(clientType, mcpConfig);
|
|
177
|
+
const sharedProps = {
|
|
178
|
+
onClick: () => {
|
|
179
|
+
window.open(url, '_blank');
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
if (clientType === 'cursor') {
|
|
184
|
+
return {
|
|
185
|
+
...sharedProps,
|
|
186
|
+
buttonText: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'),
|
|
187
|
+
title: translate('page.actions.connectMcp.cursor', 'Connect to Cursor'),
|
|
188
|
+
description: translate(
|
|
189
|
+
'page.actions.connectMcp.cursorDescription',
|
|
190
|
+
'Install MCP server on Cursor',
|
|
191
|
+
),
|
|
192
|
+
iconComponent: CursorIcon,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
...sharedProps,
|
|
198
|
+
buttonText: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'),
|
|
199
|
+
title: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'),
|
|
200
|
+
description: translate(
|
|
201
|
+
'page.actions.connectMcp.vscodeDescription',
|
|
202
|
+
'Install MCP server on VS Code',
|
|
203
|
+
),
|
|
204
|
+
iconComponent: VSCodeIcon,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
186
208
|
function shouldHidePageActions(
|
|
187
209
|
pageProps: PageProps,
|
|
188
210
|
themeConfig: UiAccessibleConfig,
|
|
@@ -8,6 +8,7 @@ export type { UserClaims } from '../types/user-claims';
|
|
|
8
8
|
export type { OperationParameter, ParameterHighlight } from '../types/search';
|
|
9
9
|
export type { TFunction, TOptions } from '../types/l10n';
|
|
10
10
|
export type { SelectOption, SelectProps } from '../types/select';
|
|
11
|
+
export type { SegmentedOption, SegmentedProps } from '../types/segmented';
|
|
11
12
|
export { IS_BROWSER } from '../utils/dom';
|
|
12
13
|
export {
|
|
13
14
|
addLeadingSlash,
|
|
@@ -19,7 +19,7 @@ import { checkbox } from '@redocly/theme/icons/CheckboxIcon/variables';
|
|
|
19
19
|
import { admonition } from '@redocly/theme/components/Admonition/variables';
|
|
20
20
|
import { footer } from '@redocly/theme/components/Footer/variables';
|
|
21
21
|
import { button } from '@redocly/theme/components/Button/variables';
|
|
22
|
-
import { aiAssistantButton } from '@redocly/theme/components/Buttons/variables';
|
|
22
|
+
import { aiAssistantButton, connectMCPButton } from '@redocly/theme/components/Buttons/variables';
|
|
23
23
|
import { navbar } from '@redocly/theme/components/Navbar/variables';
|
|
24
24
|
import { search } from '@redocly/theme/components/Search/variables';
|
|
25
25
|
import { menu, mobileMenu } from '@redocly/theme/components/Menu/variables';
|
|
@@ -1243,6 +1243,7 @@ export const styles = css`
|
|
|
1243
1243
|
${breadcrumbs}
|
|
1244
1244
|
${button}
|
|
1245
1245
|
${aiAssistantButton}
|
|
1246
|
+
${connectMCPButton}
|
|
1246
1247
|
${cards}
|
|
1247
1248
|
${catalog}
|
|
1248
1249
|
${catalogClassic}
|
package/src/core/types/index.ts
CHANGED
package/src/core/types/l10n.ts
CHANGED
|
@@ -215,6 +215,13 @@ export type TranslationKey =
|
|
|
215
215
|
| 'page.actions.cursorMcpButtonText'
|
|
216
216
|
| 'page.actions.cursorMcpTitle'
|
|
217
217
|
| 'page.actions.cursorMcpDescription'
|
|
218
|
+
| 'page.actions.connectMcp'
|
|
219
|
+
| 'page.actions.connectMcp.cursor'
|
|
220
|
+
| 'page.actions.connectMcp.cursorDescription'
|
|
221
|
+
| 'page.actions.connectMcp.vscode'
|
|
222
|
+
| 'page.actions.connectMcp.vscodeDescription'
|
|
223
|
+
| 'page.actions.connectMcp.copyConfig'
|
|
224
|
+
| 'page.actions.connectMcp.copyConfigDescription'
|
|
218
225
|
| 'openapi.download.description.title'
|
|
219
226
|
| 'openapi.info.title'
|
|
220
227
|
| 'openapi.info.contact.url'
|
|
@@ -271,6 +278,8 @@ export type TranslationKey =
|
|
|
271
278
|
| 'openapi.noResponseExample'
|
|
272
279
|
| 'openapi.discriminator.searchPlaceholder'
|
|
273
280
|
| 'openapi.discriminator.searchNoResults'
|
|
281
|
+
| 'openapi.discriminator.defaultMapping'
|
|
282
|
+
| 'openapi.discriminator.defaultMappingTooltip'
|
|
274
283
|
| 'openapi.noResponseContent'
|
|
275
284
|
| 'openapi.noRequestPayload'
|
|
276
285
|
| 'openapi.hidePattern'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { SelectOption } from './select';
|
|
3
|
+
|
|
4
|
+
export type SegmentedOption<T> = SelectOption<T> & {
|
|
5
|
+
divider?: React.ReactNode;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type SegmentedProps<T = any> = {
|
|
9
|
+
options: SegmentedOption<T>[];
|
|
10
|
+
value: T;
|
|
11
|
+
onChange: ({ label, value }: SegmentedOption<T>) => void;
|
|
12
|
+
className?: string;
|
|
13
|
+
size?: 'regular' | 'small';
|
|
14
|
+
};
|
package/src/core/utils/index.ts
CHANGED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { MCPClientType, McpConnectionParams } from '../types';
|
|
2
|
+
|
|
3
|
+
function generateCursorMCPDeepLink(config: McpConnectionParams): string {
|
|
4
|
+
const cursorConfig = {
|
|
5
|
+
url: config.url,
|
|
6
|
+
description: 'MCP Server',
|
|
7
|
+
};
|
|
8
|
+
const encodedConfig = btoa(JSON.stringify(cursorConfig));
|
|
9
|
+
return `cursor://anysphere.cursor-deeplink/mcp/install?name=${config.serverName}&config=${encodedConfig}`;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function generateVSCodeMCPDeepLink(config: McpConnectionParams): string {
|
|
13
|
+
const vscodeConfig = {
|
|
14
|
+
name: config.serverName,
|
|
15
|
+
url: config.url,
|
|
16
|
+
type: 'http',
|
|
17
|
+
};
|
|
18
|
+
const encodedConfig = encodeURIComponent(JSON.stringify(vscodeConfig));
|
|
19
|
+
return `vscode:mcp/install?${encodedConfig}`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function generateMCPDeepLink(
|
|
23
|
+
clientType: MCPClientType,
|
|
24
|
+
config: McpConnectionParams,
|
|
25
|
+
): string {
|
|
26
|
+
switch (clientType) {
|
|
27
|
+
case 'cursor':
|
|
28
|
+
return generateCursorMCPDeepLink(config);
|
|
29
|
+
case 'vscode':
|
|
30
|
+
return generateVSCodeMCPDeepLink(config);
|
|
31
|
+
default:
|
|
32
|
+
return generateCursorMCPDeepLink(config);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { IconProps } from '@redocly/theme/icons/types';
|
|
5
|
+
|
|
6
|
+
const Icon = (props: IconProps) => (
|
|
7
|
+
<svg
|
|
8
|
+
width="14"
|
|
9
|
+
height="14"
|
|
10
|
+
viewBox="0 0 14 14"
|
|
11
|
+
fill="none"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
d="M10.5 7.00875C9.74669 7.00947 9.01371 7.25321 8.40999 7.70375L6.29999 5.59375C6.75586 4.99443 7.00185 4.26175 6.99999 3.50875C7.00197 2.72021 6.73759 1.9541 6.24973 1.33459C5.76186 0.715073 5.07908 0.278462 4.31205 0.0955081C3.54502 -0.0874454 2.73868 -0.00601915 2.02371 0.326591C1.30874 0.659201 0.727035 1.2235 0.372873 1.92805C0.0187108 2.63259 -0.087157 3.43608 0.0724264 4.20832C0.23201 4.98055 0.647693 5.67627 1.25211 6.18272C1.85652 6.68917 2.61425 6.97668 3.4025 6.99864C4.19074 7.02061 4.96331 6.77576 5.59499 6.30375L7.69999 8.41375C7.22812 9.04527 6.98326 9.81759 7.00508 10.6056C7.0269 11.3937 7.31412 12.1512 7.82021 12.7557C8.32631 13.3601 9.02164 13.776 9.79358 13.9359C10.5655 14.0959 11.3688 13.9906 12.0735 13.637C12.7781 13.2834 13.3427 12.7024 13.6758 11.9879C14.009 11.2734 14.0912 10.4674 13.9091 9.70036C13.727 8.93334 13.2913 8.25026 12.6726 7.76174C12.0539 7.27323 11.2883 7.00791 10.5 7.00875ZM0.999993 3.50875C0.999993 3.0143 1.14662 2.53095 1.42132 2.11983C1.69602 1.70871 2.08647 1.38827 2.54328 1.19906C3.0001 1.00984 3.50277 0.960328 3.98772 1.05679C4.47267 1.15325 4.91813 1.39136 5.26776 1.74099C5.61739 2.09062 5.85549 2.53608 5.95196 3.02103C6.04842 3.50598 5.99891 4.00865 5.80969 4.46546C5.62047 4.92228 5.30004 5.31272 4.88892 5.58743C4.4778 5.86213 3.99445 6.00875 3.49999 6.00875C2.83695 6.00875 2.20107 5.74536 1.73223 5.27652C1.26339 4.80768 0.999993 4.1718 0.999993 3.50875Z"
|
|
17
|
+
fill="currentColor"
|
|
18
|
+
/>
|
|
19
|
+
</svg>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const ConnectIcon = styled(Icon).attrs(() => ({
|
|
23
|
+
'data-component-name': 'icons/ConnectIcon/ConnectIcon',
|
|
24
|
+
}))<IconProps>`
|
|
25
|
+
height: ${({ size }) => size || '14px'};
|
|
26
|
+
width: ${({ size }) => size || '14px'};
|
|
27
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { IconProps } from '@redocly/theme/icons/types';
|
|
5
|
+
|
|
6
|
+
const Icon = (props: IconProps) => (
|
|
7
|
+
<svg
|
|
8
|
+
width="16"
|
|
9
|
+
height="16"
|
|
10
|
+
viewBox="0 0 100 100"
|
|
11
|
+
fill="none"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
{...props}
|
|
14
|
+
>
|
|
15
|
+
<path
|
|
16
|
+
clipRule="evenodd"
|
|
17
|
+
d="m70.9119 99.3171c1.575.6136 3.3709.5743 4.9606-.1907l20.5883-9.9067c2.1634-1.041 3.5392-3.2305 3.5392-5.6325v-67.1739c0-2.402-1.3757-4.5915-3.5391-5.6325l-20.5884-9.907044c-2.0863-1.003885-4.5279-.757996-6.359.573194-.2615.19016-.5107.40248-.7445.63646l-39.4139 35.95809-17.1679-13.0319c-1.5982-1.2131-3.83357-1.1137-5.31787.2365l-5.5063 5.0088c-1.815582 1.6515-1.817663 4.5078-.0045 6.1621l14.88857 13.5831-14.88857 13.5831c-1.813163 1.6542-1.811082 4.5106.0045 6.1621l5.5063 5.0088c1.4843 1.3502 3.71967 1.4496 5.31787.2364l17.1679-13.0318 39.4139 35.958c.6235.6239 1.3556 1.0937 2.1429 1.4004zm4.1033-72.0182-29.9061 22.7012 29.9061 22.7011z"
|
|
18
|
+
fill="currentColor"
|
|
19
|
+
fillRule="evenodd"
|
|
20
|
+
/>
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
export const VSCodeIcon = styled(Icon).attrs(() => ({
|
|
25
|
+
'data-component-name': 'icons/VSCodeIcon/VSCodeIcon',
|
|
26
|
+
}))<IconProps>`
|
|
27
|
+
height: ${({ size }) => size || '16px'};
|
|
28
|
+
width: ${({ size }) => size || '16px'};
|
|
29
|
+
`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import type { JSX } from 'react';
|
|
4
|
+
import type { MCPOption } from '@redocly/theme/core/types';
|
|
5
|
+
|
|
6
|
+
import { ConnectMCPButton } from '@redocly/theme/components/Buttons/ConnectMCPButton';
|
|
7
|
+
import { useThemeConfig } from '@redocly/theme/core/hooks';
|
|
8
|
+
|
|
9
|
+
export type ConnectMCPProps = {
|
|
10
|
+
placement?: 'top' | 'bottom';
|
|
11
|
+
alignment?: 'start' | 'end';
|
|
12
|
+
options?: MCPOption[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function ConnectMCP({
|
|
16
|
+
placement = 'bottom',
|
|
17
|
+
alignment = 'start',
|
|
18
|
+
options,
|
|
19
|
+
}: ConnectMCPProps): JSX.Element | null {
|
|
20
|
+
const themeConfig = useThemeConfig();
|
|
21
|
+
const isMcpDisabled = themeConfig.mcp?.hide || themeConfig.mcp?.docs?.hide;
|
|
22
|
+
|
|
23
|
+
if (isMcpDisabled) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return <ConnectMCPButton placement={placement} alignment={alignment} options={options} />;
|
|
28
|
+
}
|
|
@@ -20,3 +20,4 @@ export * from '@redocly/theme/markdoc/components/CodeWalkthrough/CodeToggle';
|
|
|
20
20
|
export * from '@redocly/theme/markdoc/components/CodeWalkthrough/CodeContainer';
|
|
21
21
|
export * from '@redocly/theme/markdoc/components/CodeGroup/CodeGroup';
|
|
22
22
|
export * from '@redocly/theme/markdoc/components/Icon/Icon';
|
|
23
|
+
export * from '@redocly/theme/markdoc/components/ConnectMCP/ConnectMCP';
|
package/src/markdoc/default.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { input } from '@redocly/theme/markdoc/tags/input';
|
|
|
25
25
|
import { toggle } from '@redocly/theme/markdoc/tags/code-toggle';
|
|
26
26
|
import { codeGroup } from '@redocly/theme/markdoc/tags/code-group';
|
|
27
27
|
import { icon } from '@redocly/theme/markdoc/tags/icon';
|
|
28
|
+
import { connectMcp } from '@redocly/theme/markdoc/tags/connect-mcp';
|
|
28
29
|
|
|
29
30
|
export const tags = {
|
|
30
31
|
[admonition.tagName]: admonition.schema,
|
|
@@ -45,4 +46,5 @@ export const tags = {
|
|
|
45
46
|
[input.tagName]: input.schema,
|
|
46
47
|
[codeGroup.tagName]: codeGroup.schema,
|
|
47
48
|
[icon.tagName]: icon.schema,
|
|
49
|
+
[connectMcp.tagName]: connectMcp.schema,
|
|
48
50
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { MarkdocSchemaWrapper } from '@redocly/theme/markdoc/tags/types';
|
|
2
|
+
|
|
3
|
+
export const connectMcp: MarkdocSchemaWrapper = {
|
|
4
|
+
schema: {
|
|
5
|
+
render: 'ConnectMCP',
|
|
6
|
+
attributes: {
|
|
7
|
+
placement: {
|
|
8
|
+
type: String,
|
|
9
|
+
default: 'bottom',
|
|
10
|
+
matches: ['top', 'bottom'],
|
|
11
|
+
},
|
|
12
|
+
alignment: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: 'start',
|
|
15
|
+
matches: ['start', 'end'],
|
|
16
|
+
},
|
|
17
|
+
options: {
|
|
18
|
+
type: Array,
|
|
19
|
+
default: ['cursor', 'vscode', 'copy'],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
selfClosing: true,
|
|
23
|
+
},
|
|
24
|
+
tagName: 'connect-mcp',
|
|
25
|
+
};
|