clerk-nextjs-skills by gocallum/nextjs16-agent-skills
npx skills add https://github.com/gocallum/nextjs16-agent-skills --skill clerk-nextjs-skillspnpm add @clerk/nextjs
# 如需集成 MCP 服务器,同时安装:
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
proxy.ts 文件替代了 Next.js 15 中的 middleware.ts。在项目根目录或 /src 目录下创建:
// proxy.ts (或 src/proxy.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}
在项目根目录创建 .env.local 文件:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key_here
CLERK_SECRET_KEY=your_secret_key_here
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
// app/layout.tsx
import {
ClerkProvider,
SignInButton,
SignUpButton,
SignedIn,
SignedOut,
UserButton,
} from '@clerk/nextjs'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '我的应用',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<ClerkProvider>
<html lang="en">
<body>
<header className="flex justify-end items-center p-4 gap-4 h-16">
<SignedOut>
<SignInButton />
<SignUpButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
{children}
</body>
</html>
</ClerkProvider>
)
}
pnpm dev
访问 http://localhost:3000 并点击 "注册" 来创建你的第一个用户。
proxy.ts 作为 Clerk 中间件middleware.ts,代码完全相同(仅文件名不同)clerkMiddleware() 函数无论文件名如何都是相同的matcher 配置确保了正确的路由处理和性能默认情况下,clerkMiddleware() 不保护任何路由——所有路由都是公开的。使用 auth.protect() 来要求身份验证:
// 保护特定路由
import { auth } from '@clerk/nextjs/server'
export default async function Page() {
const { userId } = await auth()
if (!userId) {
// 重定向由 clerkMiddleware 处理
}
return <div>为 {userId} 提供的受保护内容</div>
}
或者在 proxy.ts 中保护所有路由:
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware(async (auth, req) => {
await auth.protect()
})
在运行时之前检查必需的 Clerk 密钥:
// lib/clerk-config.ts
export function validateClerkEnv() {
const required = [
'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
'CLERK_SECRET_KEY',
]
const missing = required.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(`缺少必需的 Clerk 环境变量: ${missing.join(', ')}`)
}
}
在客户端组件中使用 Clerk 钩子:
// app/components/user-profile.tsx
'use client'
import { useUser } from '@clerk/nextjs'
export function UserProfile() {
const { user, isLoaded } = useUser()
if (!isLoaded) return <div>加载中...</div>
if (!user) return <div>未登录</div>
return (
<div>
<h1>{user.fullName}</h1>
<p>{user.primaryEmailAddress?.emailAddress}</p>
</div>
)
}
或者在服务器组件/操作中使用:
// app/actions.ts
'use server'
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function getUserData() {
const { userId } = await auth()
if (!userId) {
throw new Error('未授权')
}
const clerk = await clerkClient()
const user = await clerk.users.getUser(userId)
return user
}
middleware.ts 重命名为 proxy.ts(位置保持不变:根目录或 /src)// 迁移前 (middleware.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = { ... }
// 迁移后 (proxy.ts) - 代码相同
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = { ... }
pnpm add next@latest
.env.local 中(无需更改)pnpm dev
proxy.ts 位于正确的位置(根目录或 /src).env.local 是否包含所有必需的 Clerk 密钥.next 缓存:rm -rf .next && pnpm devpnpm list next完整的 MCP 服务器集成请参阅 CLERK_MCP_SERVER_SETUP.md。
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
app/[transport]/route.ts 处创建 MCP 路由:
import { verifyClerkToken } from '@clerk/mcp-tools/next'
import { createMcpHandler, withMcpAuth } from '@vercel/mcp-adapter'
import { auth, clerkClient } from '@clerk/nextjs/server'
const clerk = await clerkClient()
const handler = createMcpHandler((server) => {
server.tool(
'get-clerk-user-data',
'获取授权此请求的 Clerk 用户的数据',
{},
async (_, { authInfo }) => {
const userId = authInfo!.extra!.userId! as string
const userData = await clerk.users.getUser(userId)
return {
content: [{ type: 'text', text: JSON.stringify(userData) }],
}
},
)
})
const authHandler = withMcpAuth(
handler,
async (_, token) => {
const clerkAuth = await auth({ acceptsToken: 'oauth_token' })
return verifyClerkToken(clerkAuth, token)
},
{
required: true,
resourceMetadataPath: '/.well-known/oauth-protected-resource/mcp',
},
)
export { authHandler as GET, authHandler as POST }
.well-known 端点:
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/.well-known/oauth-authorization-server(.*)',
'/.well-known/oauth-protected-resource(.*)',
])
export default clerkMiddleware(async (auth, req) => {
if (isPublicRoute(req)) return
await auth.protect()
})
.env.local(切勿提交敏感密钥)NEXT_PUBLIC_ 前缀// 选项 A: 保护所有路由
export default clerkMiddleware(async (auth, req) => {
await auth.protect()
})
// 选项 B: 保护特定路由
import { createRouteMatcher } from '@clerk/nextjs/server'
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/api/user(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) {
await auth.protect()
}
})
// 选项 C: 公开路由,选择性地保护
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
.well-known 端点公开,但使用 OAuth 保护所有 MCP 工具auth() 中使用 acceptsToken: 'oauth_token' 来要求机器令牌verifyClerkToken() 验证令牌clerkClient() 进行服务器端用户查询(自动缓存)'use client')中使用 @clerk/nextjs 钩子@clerk/nextjs 更新:pnpm update @clerk/nextjs| 问题 | 解决方案 |
|---|---|
| "缺少环境变量" | 确保 .env.local 包含 NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY 和 CLERK_SECRET_KEY |
| 中间件未保护路由 | 验证 proxy.ts 位于根目录或 /src 目录,而不是 app/ 目录中 |
| 登录/注册页面不工作 | 检查 .env.local 中的 NEXT_PUBLIC_CLERK_SIGN_IN_URL 和 NEXT_PUBLIC_CLERK_SIGN_UP_URL |
| 用户数据返回 null | 确保用户已通过身份验证:调用 getUser() 前检查 userId 不为 null |
| MCP 服务器 OAuth 失败 | 在 Clerk 仪表板的 OAuth 应用程序中启用动态客户端注册 |
| 更改未生效 | 清除 .next 缓存:rm -rf .next 并重启 pnpm dev |
| "proxy.ts" 未被识别 | 验证 Next.js 版本为 16.0+:pnpm list next |
proxy.ts(而不是 middleware.ts)proxy.ts 放在项目根目录或 /src 目录中,不要放在 app/ 目录中proxy.ts 导出配置对象才能使匹配器生效clerkMiddleware() 支持异步;使用 await auth.protect() 进行路由保护启用调试日志记录:
// proxy.ts
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware((auth, req) => {
if (process.env.DEBUG_CLERK) {
console.log('请求 URL:', req.nextUrl.pathname)
console.log('用户 ID:', auth.sessionClaims?.sub)
}
})
使用调试模式运行:
DEBUG_CLERK=1 pnpm dev
每周安装量
204
仓库
GitHub 星标数
18
首次出现
2026 年 1 月 21 日
安全审计
安装于
gemini-cli148
opencode147
codex146
github-copilot137
claude-code133
cursor133
pnpm add @clerk/nextjs
# For MCP server integration, also install:
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
The proxy.ts file replaces middleware.ts from Next.js 15. Create it at the root or in /src:
// proxy.ts (or src/proxy.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = {
matcher: [
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
'/(api|trpc)(.*)',
],
}
Create .env.local in your project root:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_publishable_key_here
CLERK_SECRET_KEY=your_secret_key_here
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/
// app/layout.tsx
import {
ClerkProvider,
SignInButton,
SignUpButton,
SignedIn,
SignedOut,
UserButton,
} from '@clerk/nextjs'
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My App',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<ClerkProvider>
<html lang="en">
<body>
<header className="flex justify-end items-center p-4 gap-4 h-16">
<SignedOut>
<SignInButton />
<SignUpButton />
</SignedOut>
<SignedIn>
<UserButton />
</SignedIn>
</header>
{children}
</body>
</html>
</ClerkProvider>
)
}
pnpm dev
Visit http://localhost:3000 and click "Sign Up" to create your first user.
proxy.ts for Clerk middlewaremiddleware.ts with identical code (filename only differs)clerkMiddleware() function is the same regardless of filenamematcher configuration ensures proper route handling and performanceBy default, clerkMiddleware() does not protect routes—all are public. Use auth.protect() to require authentication:
// Protect specific route
import { auth } from '@clerk/nextjs/server'
export default async function Page() {
const { userId } = await auth()
if (!userId) {
// Redirect handled by clerkMiddleware
}
return <div>Protected content for {userId}</div>
}
Or protect all routes in proxy.ts:
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware(async (auth, req) => {
await auth.protect()
})
Check for required Clerk keys before runtime:
// lib/clerk-config.ts
export function validateClerkEnv() {
const required = [
'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY',
'CLERK_SECRET_KEY',
]
const missing = required.filter(key => !process.env[key])
if (missing.length > 0) {
throw new Error(`Missing required Clerk environment variables: ${missing.join(', ')}`)
}
}
Use Clerk hooks in client components:
// app/components/user-profile.tsx
'use client'
import { useUser } from '@clerk/nextjs'
export function UserProfile() {
const { user, isLoaded } = useUser()
if (!isLoaded) return <div>Loading...</div>
if (!user) return <div>Not signed in</div>
return (
<div>
<h1>{user.fullName}</h1>
<p>{user.primaryEmailAddress?.emailAddress}</p>
</div>
)
}
Or in server components/actions:
// app/actions.ts
'use server'
import { auth, clerkClient } from '@clerk/nextjs/server'
export async function getUserData() {
const { userId } = await auth()
if (!userId) {
throw new Error('Unauthorized')
}
const clerk = await clerkClient()
const user = await clerk.users.getUser(userId)
return user
}
Rename the file from middleware.ts to proxy.ts (location remains same: root or /src)
Keep the code identical - No functional changes needed:
// Before (middleware.ts)
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = { ... }
// After (proxy.ts) - Same code
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = { ... }
Update Next.js version :
pnpm add next@latest
Verify environment variables are still in .env.local (no changes needed)
:
proxy.ts is in the correct location (root or /src).env.local has all required Clerk keys.next cache if middleware changes don't take effect: rm -rf .next && pnpm devpnpm list nextSee CLERK_MCP_SERVER_SETUP.md for complete MCP server integration.
Install MCP dependencies :
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
Create MCP route at app/[transport]/route.ts:
import { verifyClerkToken } from '@clerk/mcp-tools/next'
import { createMcpHandler, withMcpAuth } from '@vercel/mcp-adapter'
import { auth, clerkClient } from '@clerk/nextjs/server'
const clerk = await clerkClient()
const handler = createMcpHandler((server) => {
server.tool(
'get-clerk-user-data',
'Gets data about the Clerk user that authorized this request',
{},
async (_, { authInfo }) => {
const userId = authInfo!.extra!.userId! as string
const userData = await clerk.users.getUser(userId)
return {
content: [{ type: 'text', text: JSON.stringify(userData) }],
}
},
)
})
const authHandler = withMcpAuth(
handler,
async (_, token) => {
const clerkAuth = await auth({ acceptsToken: 'oauth_token' })
return verifyClerkToken(clerkAuth, token)
},
{
required: true,
resourceMetadataPath: '/.well-known/oauth-protected-resource/mcp',
},
)
export { authHandler as GET, authHandler as POST }
.env.local for development (never commit sensitive keys)NEXT_PUBLIC_ prefix ONLY for non-sensitive keys that are safe to expose// Option A: Protect all routes
export default clerkMiddleware(async (auth, req) => {
await auth.protect()
})
// Option B: Protect specific routes
import { createRouteMatcher } from '@clerk/nextjs/server'
const isProtectedRoute = createRouteMatcher(['/dashboard(.*)', '/api/user(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (isProtectedRoute(req)) {
await auth.protect()
}
})
// Option C: Public routes with opt-in protection
const isPublicRoute = createRouteMatcher(['/sign-in(.*)', '/sign-up(.*)'])
export default clerkMiddleware(async (auth, req) => {
if (!isPublicRoute(req)) {
await auth.protect()
}
})
.well-known endpoints public but protect all MCP tools with OAuthacceptsToken: 'oauth_token' in auth() to require machine tokensverifyClerkToken() before exposing user dataclerkClient() for server-side user queries (cached automatically)@clerk/nextjs hooks only in Client Components ('use client')@clerk/nextjs updated: pnpm update @clerk/nextjs| Issue | Solution |
|---|---|
| "Missing environment variables" | Ensure .env.local has NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY |
| Middleware not protecting routes | Verify proxy.ts is in root or /src directory, not in app/ |
| Sign-in/sign-up pages not working | Check NEXT_PUBLIC_CLERK_SIGN_IN_URL and NEXT_PUBLIC_CLERK_SIGN_UP_URL in |
proxy.ts (not middleware.ts) for Next.js 16proxy.ts at project root or in /src directory, NOT in app/proxy.ts for matcher to workclerkMiddleware() is async-ready; use await auth.protect() for route protectionEnable debug logging:
// proxy.ts
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware((auth, req) => {
if (process.env.DEBUG_CLERK) {
console.log('Request URL:', req.nextUrl.pathname)
console.log('User ID:', auth.sessionClaims?.sub)
}
})
Run with debug:
DEBUG_CLERK=1 pnpm dev
Weekly Installs
204
Repository
GitHub Stars
18
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
gemini-cli148
opencode147
codex146
github-copilot137
claude-code133
cursor133
Azure RBAC 权限管理工具:查找最小角色、创建自定义角色与自动化分配
117,000 周安装
pnpm dev
Expose OAuth metadata endpoints (see references for complete setup)
Update proxy.ts to exclude .well-known endpoints:
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
const isPublicRoute = createRouteMatcher([
'/.well-known/oauth-authorization-server(.*)',
'/.well-known/oauth-protected-resource(.*)',
])
export default clerkMiddleware(async (auth, req) => {
if (isPublicRoute(req)) return
await auth.protect()
})
Enable Dynamic Client Registration in Clerk Dashboard
.env.local| User data returns null | Ensure user is authenticated: check userId is not null before calling getUser() |
| MCP server OAuth fails | Enable Dynamic Client Registration in Clerk Dashboard OAuth Applications |
| Changes not taking effect | Clear .next cache: rm -rf .next and restart pnpm dev |
| "proxy.ts" not recognized | Verify Next.js version is 16.0+: pnpm list next |