Skip to content

Commit 65b466f

Browse files
nearestnaborstorresmateovfanelle
authored
"Create an MCP Server" + UI update (#482)
* initial stab at rejiggering first authoring page. * adds note component to MDX * style terminal to look like terminal * Enhancing codeblocks * fix syntax highlighting * remove duplicate copy button from code blocks * breaking down the transport commands * breaking down the transport commands * update the first page of Build Tools * adds overview * ok, final tweak * updating types * fixing linting warnings * update Tool Context page * updating the tool pages * fix borked copy button by returning to Nextra's default * use Callout not Note * Update app/en/home/build-tools/create-a-mcp-server/page.mdx Co-authored-by: Mateo Torres <[email protected]> * Update app/en/home/build-tools/create-a-mcp-server/page.mdx Co-authored-by: vfanelle <[email protected]> * updating the tool-context * removing uv recommended * Using Arcade Engine * fixing escaped underscore --------- Co-authored-by: Rachel Lee Nabors <[email protected]> Co-authored-by: Mateo Torres <[email protected]> Co-authored-by: vfanelle <[email protected]>
1 parent b3db164 commit 65b466f

File tree

12 files changed

+891
-357
lines changed

12 files changed

+891
-357
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,21 @@ See the `package.json` for the dependencies required.
99
First, run `pnpm install` to install the dependencies.
1010

1111
Then, run `pnpm dev` to start the development server and visit localhost:3000.
12+
13+
## Styling
14+
15+
We use Nextra's built-in Callout component for callouts in MDX:
16+
17+
```mdx
18+
<Callout>A general callout. Use sparingly.</Callout>
19+
20+
<Callout type="warning">
21+
Use this when there's something potentially destructive to call attention to.
22+
</Callout>
23+
24+
<Callout type="info">
25+
Use this for asides you don't have time to get into, like mentioning that
26+
folks should checkout the [official MCP documentation](https://mcp.arcade.dev)
27+
to learn more. (Hint: it always has a link!)
28+
</Callout>
29+
```

app/_components/custom-pre.tsx

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"use client";
2+
3+
import { FileText, Terminal } from "lucide-react";
4+
import { Pre } from "nextra/components";
5+
import type React from "react";
6+
7+
type CustomPreProps = {
8+
children?: React.ReactNode;
9+
className?: string;
10+
"data-language"?: string;
11+
[key: string]: unknown;
12+
};
13+
14+
const languageDisplayNames: Record<string, string> = {
15+
js: "JavaScript",
16+
javascript: "JavaScript",
17+
ts: "TypeScript",
18+
typescript: "TypeScript",
19+
py: "Python",
20+
python: "Python",
21+
java: "Java",
22+
cpp: "C++",
23+
c: "C",
24+
go: "Go",
25+
rust: "Rust",
26+
php: "PHP",
27+
rb: "Ruby",
28+
ruby: "Ruby",
29+
sql: "SQL",
30+
json: "JSON",
31+
yaml: "YAML",
32+
yml: "YAML",
33+
xml: "XML",
34+
html: "HTML",
35+
css: "CSS",
36+
scss: "SCSS",
37+
sass: "Sass",
38+
jsx: "JSX",
39+
tsx: "TSX",
40+
md: "Markdown",
41+
markdown: "Markdown",
42+
toml: "TOML",
43+
};
44+
45+
const getLanguageDisplayName = (lang: string): string =>
46+
languageDisplayNames[lang.toLowerCase()] || lang.toUpperCase();
47+
48+
const CustomPre: React.FC<CustomPreProps> = ({
49+
children,
50+
className,
51+
"data-language": dataLanguage,
52+
...props
53+
}) => {
54+
// Parse language (remove filename support for simplicity)
55+
const language = dataLanguage || className?.replace("language-", "") || "";
56+
57+
const isTerminalLanguage = [
58+
"bash",
59+
"shell",
60+
"sh",
61+
"zsh",
62+
"fish",
63+
"powershell",
64+
"ps1",
65+
"cmd",
66+
].includes(language.toLowerCase());
67+
68+
// If we have a language, add custom wrapper with header
69+
if (language && language.trim() !== "") {
70+
if (isTerminalLanguage) {
71+
// Terminal styling
72+
return (
73+
<div className="my-4 overflow-hidden rounded-lg border border-gray-300 bg-gray-900 dark:border-gray-700">
74+
{/* Terminal Header */}
75+
<div className="flex items-center justify-between bg-gray-800 px-4 py-2 text-gray-300">
76+
<div className="flex items-center gap-2">
77+
<Terminal className="h-4 w-4" />
78+
<span className="font-medium text-sm">Terminal</span>
79+
</div>
80+
<div className="flex gap-2">
81+
<div className="h-3 w-3 rounded-full bg-red-500" />
82+
<div className="h-3 w-3 rounded-full bg-yellow-500" />
83+
<div className="h-3 w-3 rounded-full bg-green-500" />
84+
</div>
85+
</div>
86+
87+
{/* Code content with syntax highlighting preserved */}
88+
<div className="overflow-x-auto">
89+
<Pre
90+
className={`p-4 text-gray-100 text-sm ${className || ""}`}
91+
style={{ margin: 0, borderRadius: 0, background: "transparent" }}
92+
{...props}
93+
>
94+
{children}
95+
</Pre>
96+
</div>
97+
</div>
98+
);
99+
}
100+
// Code block styling
101+
return (
102+
<div className="my-4 overflow-hidden rounded border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950">
103+
{/* Code Header */}
104+
<div className="flex items-center justify-between border-gray-200 border-b bg-gray-50 px-4 py-2 dark:border-gray-800 dark:bg-gray-900">
105+
<div className="flex items-center gap-2">
106+
<FileText className="h-4 w-4 text-gray-600 dark:text-gray-400" />
107+
<span className="font-medium text-gray-700 text-sm dark:text-gray-300">
108+
{getLanguageDisplayName(language)}
109+
</span>
110+
</div>
111+
</div>
112+
113+
{/* Code content with syntax highlighting preserved */}
114+
<div className="overflow-x-auto">
115+
<Pre
116+
className={`bg-white p-4 text-sm dark:bg-gray-950 ${className || ""}`}
117+
style={{ margin: 0, borderRadius: 0 }}
118+
{...props}
119+
>
120+
{children}
121+
</Pre>
122+
</div>
123+
</div>
124+
);
125+
}
126+
127+
// For code blocks without language, use default pre component
128+
return (
129+
<Pre className={className} data-language={dataLanguage} {...props}>
130+
{children}
131+
</Pre>
132+
);
133+
};
134+
135+
export default CustomPre;
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
"use client";
2+
3+
import { Copy, FileText } from "lucide-react";
4+
import type React from "react";
5+
6+
type EnhancedCodeBlockProps = {
7+
children: string;
8+
className?: string;
9+
language: string;
10+
filename?: string;
11+
};
12+
13+
const languageDisplayNames: Record<string, string> = {
14+
js: "JavaScript",
15+
javascript: "JavaScript",
16+
ts: "TypeScript",
17+
typescript: "TypeScript",
18+
py: "Python",
19+
python: "Python",
20+
java: "Java",
21+
cpp: "C++",
22+
c: "C",
23+
go: "Go",
24+
rust: "Rust",
25+
php: "PHP",
26+
rb: "Ruby",
27+
ruby: "Ruby",
28+
sql: "SQL",
29+
json: "JSON",
30+
yaml: "YAML",
31+
yml: "YAML",
32+
xml: "XML",
33+
html: "HTML",
34+
css: "CSS",
35+
scss: "SCSS",
36+
sass: "Sass",
37+
jsx: "JSX",
38+
tsx: "TSX",
39+
md: "Markdown",
40+
markdown: "Markdown",
41+
toml: "TOML",
42+
};
43+
44+
const getLanguageDisplayName = (lang: string): string =>
45+
languageDisplayNames[lang.toLowerCase()] || lang.toUpperCase();
46+
47+
const EnhancedCodeBlock: React.FC<EnhancedCodeBlockProps> = ({
48+
children,
49+
className = "",
50+
language,
51+
filename,
52+
}) => {
53+
const displayName = filename || getLanguageDisplayName(language);
54+
55+
return (
56+
<div className="my-4 overflow-hidden rounded border border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-950">
57+
{/* Header Bar */}
58+
<div className="flex items-center justify-between border-gray-200 border-b bg-gray-50 px-4 py-2 dark:border-gray-800 dark:bg-gray-900">
59+
<div className="flex items-center gap-2">
60+
<FileText className="h-4 w-4 text-gray-600 dark:text-gray-400" />
61+
<span className="font-medium text-gray-700 text-sm dark:text-gray-300">
62+
{displayName}
63+
</span>
64+
</div>
65+
<button
66+
className="flex cursor-pointer items-center gap-1 rounded px-2 py-1 text-gray-600 text-xs transition-colors hover:bg-gray-200 hover:text-gray-800 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-200"
67+
onClick={async () => {
68+
try {
69+
await navigator.clipboard.writeText(children);
70+
} catch {
71+
// Fallback: try to select the text for manual copying
72+
const textArea = document.createElement("textarea");
73+
textArea.value = children;
74+
document.body.appendChild(textArea);
75+
textArea.select();
76+
try {
77+
document.execCommand("copy");
78+
} catch {
79+
// Silent fallback failure
80+
}
81+
document.body.removeChild(textArea);
82+
}
83+
}}
84+
type="button"
85+
>
86+
<Copy className="h-3 w-3" />
87+
Copy
88+
</button>
89+
</div>
90+
91+
{/* Code Content */}
92+
<div className="overflow-x-auto">
93+
<pre className={`bg-white p-4 text-sm dark:bg-gray-950 ${className}`}>
94+
<code>{children}</code>
95+
</pre>
96+
</div>
97+
</div>
98+
);
99+
};
100+
101+
export default EnhancedCodeBlock;

app/_components/guide-overview.tsx

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { cn } from "@arcadeai/design-system/lib/utils";
2+
import React from "react";
3+
4+
type GuideOverviewProps = {
5+
children: React.ReactNode;
6+
className?: string;
7+
};
8+
9+
type SectionProps = {
10+
children: React.ReactNode;
11+
};
12+
13+
export function GuideOverview({ children, className }: GuideOverviewProps) {
14+
const childrenArray = React.Children.toArray(children);
15+
16+
// Find specific sections by their type
17+
const outcomes = childrenArray.find(
18+
(child) =>
19+
React.isValidElement(child) && child.type === GuideOverview.Outcomes
20+
);
21+
const prerequisites = childrenArray.find(
22+
(child) =>
23+
React.isValidElement(child) && child.type === GuideOverview.Prerequisites
24+
);
25+
const youWillLearn = childrenArray.find(
26+
(child) =>
27+
React.isValidElement(child) && child.type === GuideOverview.YouWillLearn
28+
);
29+
30+
return (
31+
<div
32+
className={cn(
33+
"my-6 rounded-lg border border-border bg-card p-6",
34+
className
35+
)}
36+
>
37+
{/* Outcomes section (full width) */}
38+
{outcomes && (
39+
<div className="mb-6">
40+
<h2 className="mb-3 font-semibold text-card-foreground text-xl">
41+
Outcomes
42+
</h2>
43+
<div className="text-muted-foreground">
44+
{React.isValidElement(outcomes) &&
45+
(outcomes.props as { children: React.ReactNode }).children}
46+
</div>
47+
</div>
48+
)}
49+
50+
{/* Two column layout - You will Learn (2/3) and Prerequisites (1/3) */}
51+
<div className="grid gap-6 md:grid-cols-3">
52+
{/* You will Learn column - takes 2/3 of width */}
53+
{youWillLearn && (
54+
<div className="md:col-span-2">
55+
<h3 className="mb-3 font-medium text-card-foreground text-lg">
56+
You will Learn
57+
</h3>
58+
<div className="space-y-2 text-muted-foreground text-sm">
59+
{React.isValidElement(youWillLearn) &&
60+
(youWillLearn.props as { children: React.ReactNode }).children}
61+
</div>
62+
</div>
63+
)}
64+
65+
{/* Prerequisites column - takes 1/3 of width */}
66+
{prerequisites && (
67+
<div className="md:col-span-1">
68+
<h3 className="mb-3 font-medium text-card-foreground text-lg">
69+
Prerequisites
70+
</h3>
71+
<div className="space-y-2 text-muted-foreground text-sm">
72+
{React.isValidElement(prerequisites) &&
73+
(prerequisites.props as { children: React.ReactNode }).children}
74+
</div>
75+
</div>
76+
)}
77+
</div>
78+
</div>
79+
);
80+
}
81+
82+
// Sub-components for nested structure
83+
GuideOverview.Outcomes = function Outcomes({ children }: SectionProps) {
84+
return <>{children}</>;
85+
};
86+
87+
GuideOverview.Prerequisites = function Prerequisites({
88+
children,
89+
}: SectionProps) {
90+
return <>{children}</>;
91+
};
92+
93+
GuideOverview.YouWillLearn = function YouWillLearn({ children }: SectionProps) {
94+
return <>{children}</>;
95+
};

0 commit comments

Comments
 (0)