Skip to content

Commit 95ed1f9

Browse files
authored
Merge pull request #71 from metatool-ai/fix-auth
Fix auth + other bug fixes
2 parents a19ce21 + eb05484 commit 95ed1f9

File tree

7 files changed

+200
-18
lines changed

7 files changed

+200
-18
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ If you have questions, feel free to leave **GitHub issues** or **PRs**.
122122
- 🔑 **API key auth** for external access via `Authorization: Bearer <api-key>` header
123123
- Note though: the repo is not designed for multi-tenancy and each org should self-host an instance for org wide. E.g., MCP server has no user_id association, so every account have access to every MCP server configs hosted on the instance.
124124

125+
## SSE conf for Nginx
126+
127+
Since MCP leverages SSE for long connection, if you are using reverse proxy like nginx, please refer to an example setup [nginx.conf.example](nginx.conf.example)
128+
125129
## 🏗️ Architecture
126130

127131
- **Frontend**: Next.js

apps/frontend/app/(sidebar)/mcp-servers/page.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ export default function MCPServersPage() {
4444
// tRPC mutation for creating MCP server
4545
const createServerMutation = trpc.frontend.mcpServers.create.useMutation({
4646
onSuccess: (data) => {
47-
console.log("MCP server response:", data);
48-
4947
// Check if the operation was actually successful
5048
if (data.success) {
5149
toast.success("MCP Server Created", {

apps/frontend/app/(sidebar)/search/components/CardGrid.tsx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CreateServerFormData, createServerFormSchema } from "@repo/zod-types";
44
import { Github, Plus } from "lucide-react";
55
import { ChevronDown } from "lucide-react";
66
import Link from "next/link";
7-
import { useState } from "react";
7+
import { useEffect, useState } from "react";
88
import { useForm } from "react-hook-form";
99
import { toast } from "sonner";
1010

@@ -63,24 +63,13 @@ function CreateServerDialog({
6363
// tRPC mutation for creating MCP server
6464
const createServerMutation = trpc.frontend.mcpServers.create.useMutation({
6565
onSuccess: (data) => {
66-
console.log("MCP server response:", data);
67-
6866
// Check if the operation was actually successful
6967
if (data.success) {
7068
toast.success("MCP Server Created", {
7169
description: `Successfully created "${form.getValues().name}" server`,
7270
});
7371
onOpenChange(false);
74-
form.reset({
75-
name: "",
76-
description: "",
77-
type: McpServerTypeEnum.Enum.STDIO,
78-
command: "",
79-
args: "",
80-
url: "",
81-
bearerToken: "",
82-
env: "",
83-
});
72+
form.reset(defaultValues);
8473
// Invalidate and refetch the server list
8574
utils.frontend.mcpServers.list.invalidate();
8675
} else {
@@ -167,6 +156,16 @@ function CreateServerDialog({
167156
defaultValues,
168157
});
169158

159+
// Reset form when dialog opens with new defaultValues
160+
useEffect(() => {
161+
if (open) {
162+
form.reset(defaultValues);
163+
} else {
164+
// Reset form when dialog closes to ensure clean state
165+
form.reset(defaultValues);
166+
}
167+
}, [open, defaultValues, form]);
168+
170169
const onSubmit = async (data: CreateServerFormData) => {
171170
setIsSubmitting(true);
172171
try {
@@ -391,6 +390,7 @@ function CreateServerDialog({
391390
type="button"
392391
variant="outline"
393392
onClick={() => {
393+
form.reset(defaultValues);
394394
onOpenChange(false);
395395
}}
396396
disabled={isSubmitting}
@@ -416,7 +416,7 @@ export default function CardGrid({ items }: { items: SearchIndex }) {
416416

417417
const handleAddServer = (item: SearchIndex[string]) => {
418418
// Prepare default values for the form
419-
const sanitizedName = item.name.replace(/[^a-zA-Z0-9_-]/g, "_");
419+
const sanitizedName = item.name.replace(/[^a-zA-Z0-9_-]/g, "-");
420420
const envString =
421421
item.envs && item.envs.length > 0
422422
? item.envs.map((env) => `${env}=`).join("\n")

apps/frontend/app/(sidebar)/search/page.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ function SearchContent() {
3838
});
3939

4040
if (error) console.error("Search error:", error);
41-
console.log("Search data:", data);
4241

4342
useEffect(() => {
4443
const timer = setTimeout(() => {

apps/frontend/middleware.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,24 @@ export default async function middleware(request: NextRequest) {
2323
}
2424

2525
try {
26+
// Get the original host for nginx compatibility
27+
const originalHost =
28+
request.headers.get("x-forwarded-host") ||
29+
request.headers.get("host") ||
30+
"";
31+
2632
// Check if user is authenticated by calling the session endpoint
2733
const { data: session } = await betterFetch("/api/auth/get-session", {
28-
baseURL: request.nextUrl.origin,
34+
// this hardcoded is correct, because in same container, we should use localhost, outside url won't work
35+
baseURL: "http://localhost:12009",
2936
headers: {
3037
cookie: request.headers.get("cookie") || "",
38+
// Pass nginx-forwarded host headers for better-auth baseURL resolution
39+
host: originalHost,
40+
// Include nginx forwarding headers if present
41+
"x-forwarded-host": request.headers.get("x-forwarded-host") || "",
42+
"x-forwarded-proto": request.headers.get("x-forwarded-proto") || "",
43+
"x-forwarded-for": request.headers.get("x-forwarded-for") || "",
3144
},
3245
});
3346

nginx.conf.example

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
2+
#user nobody;
3+
worker_processes 1;
4+
5+
#error_log logs/error.log;
6+
#error_log logs/error.log notice;
7+
#error_log logs/error.log info;
8+
9+
#pid logs/nginx.pid;
10+
11+
12+
events {
13+
worker_connections 1024;
14+
}
15+
16+
17+
http {
18+
include mime.types;
19+
default_type application/octet-stream;
20+
21+
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
22+
# '$status $body_bytes_sent "$http_referer" '
23+
# '"$http_user_agent" "$http_x_forwarded_for"';
24+
25+
#access_log logs/access.log main;
26+
27+
sendfile on;
28+
#tcp_nopush on;
29+
30+
#keepalive_timeout 0;
31+
keepalive_timeout 300; # Increased for better SSE support
32+
33+
#gzip on;
34+
35+
server {
36+
listen 8080;
37+
server_name localhost;
38+
39+
#charset koi8-r;
40+
41+
#access_log logs/host.access.log main;
42+
43+
location / {
44+
root html;
45+
index index.html index.htm;
46+
}
47+
48+
#error_page 404 /404.html;
49+
50+
# redirect server error pages to the static page /50x.html
51+
#
52+
error_page 500 502 503 504 /50x.html;
53+
location = /50x.html {
54+
root html;
55+
}
56+
57+
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
58+
#
59+
#location ~ \.php$ {
60+
# proxy_pass http://127.0.0.1;
61+
#}
62+
63+
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
64+
#
65+
#location ~ \.php$ {
66+
# root html;
67+
# fastcgi_pass 127.0.0.1:9000;
68+
# fastcgi_index index.php;
69+
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
70+
# include fastcgi_params;
71+
#}
72+
73+
# deny access to .htaccess files, if Apache's document root
74+
# concurs with nginx's one
75+
#
76+
#location ~ /\.ht {
77+
# deny all;
78+
#}
79+
}
80+
81+
82+
# another virtual host using mix of IP-, name-, and port-based configuration
83+
#
84+
#server {
85+
# listen 8000;
86+
# listen somename:8080;
87+
# server_name somename alias another.alias;
88+
89+
# location / {
90+
# root html;
91+
# index index.html index.htm;
92+
# }
93+
#}
94+
95+
96+
# HTTPS server
97+
#
98+
#server {
99+
# listen 443 ssl;
100+
# server_name localhost;
101+
102+
# ssl_certificate cert.pem;
103+
# ssl_certificate_key cert.key;
104+
105+
# ssl_session_cache shared:SSL:1m;
106+
# ssl_session_timeout 5m;
107+
108+
# ssl_ciphers HIGH:!aNULL:!MD5;
109+
# ssl_prefer_server_ciphers on;
110+
111+
# location / {
112+
# root html;
113+
# index index.html index.htm;
114+
# }
115+
#}
116+
server {
117+
listen 8081;
118+
server_name metamcp.dev.local;
119+
120+
location / {
121+
proxy_pass http://localhost:12008; # Your backend/frontend
122+
proxy_set_header Host $host;
123+
proxy_set_header X-Real-IP $remote_addr;
124+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
125+
proxy_set_header X-Forwarded-Proto $scheme;
126+
127+
# SSE-specific optimizations
128+
proxy_buffering off;
129+
proxy_cache off;
130+
proxy_read_timeout 86400s; # 24 hours for long-lived SSE connections
131+
proxy_send_timeout 86400s;
132+
133+
# HTTP/1.1 with proper connection handling for SSE
134+
proxy_set_header Connection '';
135+
proxy_http_version 1.1;
136+
137+
# Additional headers for better SSE support
138+
proxy_set_header Cache-Control 'no-cache';
139+
proxy_set_header X-Accel-Buffering 'no';
140+
}
141+
}
142+
143+
include servers/*;
144+
}

packages/zod-types/src/mcp-servers.zod.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export const createServerFormSchema = z
1212
.regex(
1313
/^[a-zA-Z0-9_-]+$/,
1414
"Server name must only contain letters, numbers, underscores, and hyphens",
15+
)
16+
.refine(
17+
(value) => !/_{2,}/.test(value),
18+
"Server name cannot contain consecutive underscores",
1519
),
1620
description: z.string().optional(),
1721
type: McpServerTypeEnum,
@@ -72,6 +76,10 @@ export const EditServerFormSchema = z
7276
.regex(
7377
/^[a-zA-Z0-9_-]+$/,
7478
"Server name must only contain letters, numbers, underscores, and hyphens",
79+
)
80+
.refine(
81+
(value) => !/_{2,}/.test(value),
82+
"Server name cannot contain consecutive underscores",
7583
),
7684
description: z.string().optional(),
7785
type: McpServerTypeEnum,
@@ -131,6 +139,10 @@ export const CreateMcpServerRequestSchema = z
131139
.regex(
132140
/^[a-zA-Z0-9_-]+$/,
133141
"Server name must only contain letters, numbers, underscores, and hyphens",
142+
)
143+
.refine(
144+
(value) => !/_{2,}/.test(value),
145+
"Server name cannot contain consecutive underscores",
134146
),
135147
description: z.string().optional(),
136148
type: McpServerTypeEnum,
@@ -285,6 +297,10 @@ export const UpdateMcpServerRequestSchema = z
285297
.regex(
286298
/^[a-zA-Z0-9_-]+$/,
287299
"Server name must only contain letters, numbers, underscores, and hyphens",
300+
)
301+
.refine(
302+
(value) => !/_{2,}/.test(value),
303+
"Server name cannot contain consecutive underscores",
288304
),
289305
description: z.string().optional(),
290306
type: McpServerTypeEnum,
@@ -349,6 +365,10 @@ export const McpServerCreateInputSchema = z.object({
349365
.regex(
350366
/^[a-zA-Z0-9_-]+$/,
351367
"Server name must only contain letters, numbers, underscores, and hyphens",
368+
)
369+
.refine(
370+
(value) => !/_{2,}/.test(value),
371+
"Server name cannot contain consecutive underscores",
352372
),
353373
description: z.string().nullable().optional(),
354374
type: McpServerTypeEnum,
@@ -367,6 +387,10 @@ export const McpServerUpdateInputSchema = z.object({
367387
/^[a-zA-Z0-9_-]+$/,
368388
"Server name must only contain letters, numbers, underscores, and hyphens",
369389
)
390+
.refine(
391+
(value) => !/_{2,}/.test(value),
392+
"Server name cannot contain consecutive underscores",
393+
)
370394
.optional(),
371395
description: z.string().nullable().optional(),
372396
type: McpServerTypeEnum.optional(),

0 commit comments

Comments
 (0)