Skip to content

Commit 17accee

Browse files
committed
Update MCP setup page and dependencies
- Refactored the MCP setup page to dynamically generate installation URLs based on the current MCP URL, enhancing flexibility and maintainability. - Updated `package.json` to use compatible versions of `@types/react` and `@types/react-dom`. - Removed deprecated dependencies from `pnpm-lock.yaml` to streamline the project and improve compatibility with the latest Node.js types. - Adjusted tests to reflect changes in the MCP setup page, ensuring accurate validation of installation instructions.
1 parent ba71a51 commit 17accee

6 files changed

Lines changed: 135 additions & 248 deletions

File tree

apps/e2e/tests/backend/endpoints/api/v1/internal/mcp.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ it("MCP service root should redirect GET and POST to /mcp", async ({ expect }) =
164164
});
165165

166166
it("MCP setup page should show client installation instructions", async ({ expect }) => {
167+
const mcpUrl = new URL("/mcp", STACK_MCP_BASE_URL).toString();
167168
const response = await niceFetch(new URL("/mcp", STACK_MCP_BASE_URL), {
168169
method: "GET",
169170
headers: {
@@ -178,8 +179,9 @@ it("MCP setup page should show client installation instructions", async ({ expec
178179
expect(response.body).toContain("Codex");
179180
expect(response.body).toContain("Claude Code");
180181
expect(response.body).toContain("VS Code");
181-
expect(response.body).toContain("codex mcp add stack-auth --url https://mcp.stack-auth.com/mcp");
182-
expect(response.body).toContain("https://mcp.stack-auth.com/mcp");
182+
expect(response.body).toContain(`codex mcp add stack-auth --url ${mcpUrl}`);
183+
expect(response.body).toContain(mcpUrl);
184+
expect(response.body).not.toContain("https://mcp.stack-auth.com/mcp");
183185
expect(response.body).not.toContain("Set up Stack Auth's Model Context Protocol (MCP) server to get intelligent code assistance in your development environment.");
184186
expect(response.body).toContain("<details class=\"markdown-section\">");
185187
expect(response.body).not.toContain("<details class=\"markdown-section\" open>");

apps/mcp/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
},
2424
"devDependencies": {
2525
"@types/node": "20.17.6",
26-
"@types/react": "19.2.7",
27-
"@types/react-dom": "19.2.3",
26+
"@types/react": "^18.2.0",
27+
"@types/react-dom": "^18.2.0",
2828
"rimraf": "^5.0.5",
2929
"typescript": "5.9.3"
3030
},

apps/mcp/src/app/mcp/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export function GET(req: Request) {
1111
return handler(req);
1212
}
1313

14-
return new Response(renderSetupPageHtml(), {
14+
return new Response(renderSetupPageHtml(new URL("/mcp", req.url).toString()), {
1515
headers: {
1616
"Content-Type": "text/html; charset=utf-8",
1717
},

apps/mcp/src/setup-page.ts

Lines changed: 98 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
const mcpUrl = "https://mcp.stack-auth.com/mcp";
2-
3-
const cursorInstallUrl = "cursor://anysphere.cursor-deeplink/mcp/install?name=stack-auth&config=eyJ1cmwiOiJodHRwczovL21jcC5zdGFjay1hdXRoLmNvbS9tY3AifQ==";
4-
const vsCodeInstallUrl = "https://insiders.vscode.dev/redirect?url=vscode:mcp/install?%7B%22type%22%3A%22http%22%2C%22name%22%3A%22stack-auth%22%2C%22url%22%3A%22https%3A%2F%2Fmcp.stack-auth.com%2Fmcp%22%7D";
5-
61
type SetupTab = {
72
id: string,
83
label: string,
@@ -25,84 +20,103 @@ function codeBlock(language: string, value: string): string {
2520
</div>`;
2621
}
2722

28-
const tabs: SetupTab[] = [
29-
{
30-
id: "cursor",
31-
label: "Cursor",
32-
content: `<p>Configure Stack Auth MCP in Cursor IDE for enhanced code assistance.</p>
33-
<p><a class="button" href="${escapeHtml(cursorInstallUrl)}"><span class="button-icon">C</span>Add to Cursor</a></p>
34-
<h2>Manual Installation</h2>
35-
<p>Add the following to your <code>mcp.json</code> file:</p>
36-
${codeBlock("mcp.json", `{
23+
function getCursorInstallUrl(mcpUrl: string): string {
24+
const url = new URL("cursor://anysphere.cursor-deeplink/mcp/install");
25+
url.searchParams.set("name", "stack-auth");
26+
url.searchParams.set("config", Buffer.from(JSON.stringify({ url: mcpUrl })).toString("base64url"));
27+
return url.toString();
28+
}
29+
30+
function getVsCodeInstallUrl(mcpUrl: string): string {
31+
const url = new URL("https://insiders.vscode.dev/redirect");
32+
const installPayload = JSON.stringify({
33+
type: "http",
34+
name: "stack-auth",
35+
url: mcpUrl,
36+
});
37+
url.searchParams.set("url", `vscode:mcp/install?${encodeURIComponent(installPayload)}`);
38+
return url.toString();
39+
}
40+
41+
function getTabs(mcpUrl: string, cursorInstallUrl: string, vsCodeInstallUrl: string): SetupTab[] {
42+
return [
43+
{
44+
id: "cursor",
45+
label: "Cursor",
46+
content: `<p>Configure Stack Auth MCP in Cursor IDE for enhanced code assistance.</p>
47+
<p><a class="button" href="${escapeHtml(cursorInstallUrl)}"><span class="button-icon">C</span>Add to Cursor</a></p>
48+
<h2>Manual Installation</h2>
49+
<p>Add the following to your <code>mcp.json</code> file:</p>
50+
${codeBlock("mcp.json", `{
3751
"mcpServers": {
3852
"stack-auth": {
3953
"url": "${mcpUrl}"
4054
}
4155
}
4256
}`)}`,
43-
},
44-
{
45-
id: "vscode",
46-
label: "VS Code",
47-
content: `<p>Configure Stack Auth MCP in VS Code for enhanced code assistance.</p>
48-
<p><a class="button" href="${escapeHtml(vsCodeInstallUrl)}"><span class="button-icon">VS</span>Add to VS Code</a></p>
49-
<h2>Manual Installation</h2>
50-
<p>Open a terminal and run the following command:</p>
51-
${codeBlock("Terminal", `code --add-mcp '{"type":"http","name":"stack-auth","url":"${mcpUrl}"}'`)}
52-
<p>Then, from inside VS Code, open the <code>.vscode/mcp.json</code> file and click "Start server".</p>`,
53-
},
54-
{
55-
id: "codex",
56-
label: "Codex",
57-
content: `<p>Configure Stack Auth MCP in Codex CLI and the Codex IDE extension. The configuration is shared between both.</p>
58-
<p>Open a terminal and run the following command:</p>
59-
${codeBlock("Terminal", `codex mcp add stack-auth --url ${mcpUrl}`)}
60-
<p>Verify it is configured:</p>
61-
${codeBlock("Terminal", "codex mcp list")}
62-
<h2>Manual Installation</h2>
63-
<p>Alternatively, add the following to <code>~/.codex/config.toml</code>:</p>
64-
${codeBlock("config.toml", `[mcp_servers.stack-auth]
57+
},
58+
{
59+
id: "vscode",
60+
label: "VS Code",
61+
content: `<p>Configure Stack Auth MCP in VS Code for enhanced code assistance.</p>
62+
<p><a class="button" href="${escapeHtml(vsCodeInstallUrl)}"><span class="button-icon">VS</span>Add to VS Code</a></p>
63+
<h2>Manual Installation</h2>
64+
<p>Open a terminal and run the following command:</p>
65+
${codeBlock("Terminal", `code --add-mcp '{"type":"http","name":"stack-auth","url":"${mcpUrl}"}'`)}
66+
<p>Then, from inside VS Code, open the <code>.vscode/mcp.json</code> file and click "Start server".</p>`,
67+
},
68+
{
69+
id: "codex",
70+
label: "Codex",
71+
content: `<p>Configure Stack Auth MCP in Codex CLI and the Codex IDE extension. The configuration is shared between both.</p>
72+
<p>Open a terminal and run the following command:</p>
73+
${codeBlock("Terminal", `codex mcp add stack-auth --url ${mcpUrl}`)}
74+
<p>Verify it is configured:</p>
75+
${codeBlock("Terminal", "codex mcp list")}
76+
<h2>Manual Installation</h2>
77+
<p>Alternatively, add the following to <code>~/.codex/config.toml</code>:</p>
78+
${codeBlock("config.toml", `[mcp_servers.stack-auth]
6579
url = "${mcpUrl}"`)}`,
66-
},
67-
{
68-
id: "claudecode",
69-
label: "Claude Code",
70-
content: `<p>Open a terminal and run the following command:</p>
71-
${codeBlock("Terminal", `claude mcp add --transport http stack-auth ${mcpUrl}`)}
72-
<p>From within Claude Code, you can use the <code>/mcp</code> command to get more information about the server.</p>`,
73-
},
74-
{
75-
id: "claudedesktop",
76-
label: "Claude Desktop",
77-
content: `<p>Open Claude Desktop and navigate to Settings &gt; Connectors &gt; Add Custom Connector.</p>
78-
<p>Enter the name as <code>stack-auth</code> and the remote MCP server URL as <code>${escapeHtml(mcpUrl)}</code>.</p>`,
79-
},
80-
{
81-
id: "windsurf",
82-
label: "Windsurf",
83-
content: `<p>Copy the following JSON to your Windsurf MCP config file:</p>
84-
${codeBlock("mcp.json", `{
80+
},
81+
{
82+
id: "claudecode",
83+
label: "Claude Code",
84+
content: `<p>Open a terminal and run the following command:</p>
85+
${codeBlock("Terminal", `claude mcp add --transport http stack-auth ${mcpUrl}`)}
86+
<p>From within Claude Code, you can use the <code>/mcp</code> command to get more information about the server.</p>`,
87+
},
88+
{
89+
id: "claudedesktop",
90+
label: "Claude Desktop",
91+
content: `<p>Open Claude Desktop and navigate to Settings &gt; Connectors &gt; Add Custom Connector.</p>
92+
<p>Enter the name as <code>stack-auth</code> and the remote MCP server URL as <code>${escapeHtml(mcpUrl)}</code>.</p>`,
93+
},
94+
{
95+
id: "windsurf",
96+
label: "Windsurf",
97+
content: `<p>Copy the following JSON to your Windsurf MCP config file:</p>
98+
${codeBlock("mcp.json", `{
8599
"mcpServers": {
86100
"stack-auth": {
87101
"serverUrl": "${mcpUrl}"
88102
}
89103
}
90104
}`)}`,
91-
},
92-
{
93-
id: "chatgpt",
94-
label: "ChatGPT",
95-
content: `<div class="info">In Team, Enterprise, and Edu workspaces, only workspace owners and admins have permission to set this.</div>
96-
<p>Navigate to <strong>Settings &gt; Connectors</strong>.</p>
97-
<p>Add a custom connector with the server URL: <code>${escapeHtml(mcpUrl)}</code></p>
98-
<p>After this, it should be visible in Composer &gt; Deep Research Tool.</p>
99-
<div class="info">Connectors can only be used with <strong>Deep Research</strong>.</div>`,
100-
},
101-
{
102-
id: "gemini",
103-
label: "Gemini CLI",
104-
content: `<p>Add the following JSON to your Gemini CLI configuration file (<code>~/.gemini/settings.json</code>):</p>
105-
${codeBlock("settings.json", `{
105+
},
106+
{
107+
id: "chatgpt",
108+
label: "ChatGPT",
109+
content: `<div class="info">In Team, Enterprise, and Edu workspaces, only workspace owners and admins have permission to set this.</div>
110+
<p>Navigate to <strong>Settings &gt; Connectors</strong>.</p>
111+
<p>Add a custom connector with the server URL: <code>${escapeHtml(mcpUrl)}</code></p>
112+
<p>After this, it should be visible in Composer &gt; Deep Research Tool.</p>
113+
<div class="info">Connectors can only be used with <strong>Deep Research</strong>.</div>`,
114+
},
115+
{
116+
id: "gemini",
117+
label: "Gemini CLI",
118+
content: `<p>Add the following JSON to your Gemini CLI configuration file (<code>~/.gemini/settings.json</code>):</p>
119+
${codeBlock("settings.json", `{
106120
"mcpServers": {
107121
"stack-auth": {
108122
"httpUrl": "${mcpUrl}",
@@ -112,10 +126,12 @@ url = "${mcpUrl}"`)}`,
112126
}
113127
}
114128
}`)}`,
115-
},
116-
];
129+
},
130+
];
131+
}
117132

118-
const markdownInstructions = `<details name="mcp-install-instructions">
133+
function getMarkdownInstructions(mcpUrl: string, cursorInstallUrl: string, vsCodeInstallUrl: string): string {
134+
return `<details name="mcp-install-instructions">
119135
<summary>Cursor</summary>
120136
121137
#### Installation Link
@@ -236,8 +252,9 @@ Add the following JSON to your Gemini CLI configuration file (\`~/.gemini/settin
236252
</details>
237253
238254
by [![Hypr MCP](https://hyprmcp.com/hyprmcp_20px.svg)](https://hyprmcp.com/)`;
255+
}
239256

240-
function renderTabs(): string {
257+
function renderTabs(tabs: SetupTab[]): string {
241258
const tabButtons = tabs.map((tab, index) => `<button class="tab-trigger${index === 0 ? " active" : ""}" type="button" role="tab" aria-selected="${index === 0 ? "true" : "false"}" aria-controls="panel-${escapeHtml(tab.id)}" id="tab-${escapeHtml(tab.id)}" data-tab="${escapeHtml(tab.id)}">${escapeHtml(tab.label)}</button>`).join("");
242259
const tabPanels = tabs.map((tab, index) => `<section class="tab-panel${index === 0 ? " active" : ""}" role="tabpanel" id="panel-${escapeHtml(tab.id)}" aria-labelledby="tab-${escapeHtml(tab.id)}" data-panel="${escapeHtml(tab.id)}">${tab.content}</section>`).join("");
243260
return `<div class="tabs">
@@ -246,7 +263,12 @@ function renderTabs(): string {
246263
</div>`;
247264
}
248265

249-
export function renderSetupPageHtml(): string {
266+
export function renderSetupPageHtml(mcpUrl: string): string {
267+
const cursorInstallUrl = getCursorInstallUrl(mcpUrl);
268+
const vsCodeInstallUrl = getVsCodeInstallUrl(mcpUrl);
269+
const tabs = getTabs(mcpUrl, cursorInstallUrl, vsCodeInstallUrl);
270+
const markdownInstructions = getMarkdownInstructions(mcpUrl, cursorInstallUrl, vsCodeInstallUrl);
271+
250272
return `<!doctype html>
251273
<html lang="en">
252274
<head>
@@ -637,7 +659,7 @@ export function renderSetupPageHtml(): string {
637659
<div class="mcp-icon">MCP</div>
638660
</div>
639661
640-
${renderTabs()}
662+
${renderTabs(tabs)}
641663
642664
<details class="markdown-section">
643665
<summary>

packages/stack-shared/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@
7979
"ip-regex": "^5.0.0",
8080
"jose": "^6.1.3",
8181
"oauth4webapi": "^3.8.3",
82-
"safe-regex2": "^5.1.1",
8382
"semver": "^7.6.3",
8483
"uuid": "^9.0.1"
8584
},

0 commit comments

Comments
 (0)