mcpField
C
clerk-nextjs-skills

clerk-nextjs-skills

github.com/gocallum开发23436 次安装40 周前更新
代码仓库

关于 clerk-nextjs-skills

面向 Next.js 开发者的 Clerk 集成技能,说明用户认证、路由保护、用户数据读取及 Next.js 版本迁移。还提供使用 Vercel 适配器和 Clerk 工具构建带 OAuth 验证的 MCP 服务的配置指引。

主要功能

  1. 01配置 Clerk 用户认证
  2. 02保护 Next.js 路由
  3. 03读取客户端与服务端用户数据
  4. 04迁移 middleware.ts 至 proxy.ts
  5. 05设置带 OAuth 验证的 MCP 服务

适用场景

  1. 01为 Next.js 应用添加登录与注册
  2. 02限制未认证用户访问指定路由
  3. 03将 Next.js 15 项目迁移到 16
  4. 04为 MCP 工具验证 Clerk OAuth 令牌

MCP 相关

指导使用 @vercel/mcp-adapter 和 @clerk/mcp-tools 在 Next.js 中构建 MCP 服务,并配置 Clerk OAuth 令牌验证和动态客户端注册。

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gocallum/nextjs16-agent-skills --skill clerk-nextjs-skills

安装后在 Claude Code 中输入/clerk-nextjs-skills确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

CREATION_SUMMARY.md
QUICK_REFERENCE.md
README.md
references/
CLERK_ENV_SETUP.md
CLERK_MCP_SERVER_SETUP.md
EXAMPLES.md
PROXY_MIGRATION.md
scripts/
setup-clerk-nextjs.sh

详细介绍

链接

快速开始

1. 安装依赖(使用 pnpm)
pnpm add @clerk/nextjs
# For MCP server integration, also install:
pnpm add @vercel/mcp-adapter @clerk/mcp-tools
2. 创建 proxy.ts(Next.js 16)

proxy.ts 文件取代了 Next.js 15 中的 middleware.ts。在项目根目录或 /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)(.*)',
  ],
}
3. 设置环境变量

在项目根目录中创建 .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=/
4. 将 ClerkProvider 添加到布局中
// 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>
  )
}
5. 运行应用
pnpm dev

访问 http://localhost:3000,然后点击“注册”以创建首位用户。

核心概念

proxy.ts 与 middleware.ts
  • Next.js 16(App Router):使用 proxy.ts 配置 Clerk 中间件
  • Next.js ≤15:使用 middleware.ts,代码相同(仅文件名不同)
  • 无论文件名如何,Clerk 的 clerkMiddleware() 函数都相同
  • matcher 配置可确保正确处理路由并提升性能
保护路由

默认情况下,clerkMiddleware() 不会保护路由——所有路由都是公开的。使用 auth.protect() 要求进行身份验证:

// 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>
}

或者在 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(`Missing required Clerk environment variables: ${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>Loading...</div>
  
  if (!user) return <div>Not signed in</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('Unauthorized')
  }
  
  const clerk = await clerkClient()
  const user = await clerk.users.getUser(userId)
  
  return user
}

从 middleware.ts(Next.js 15)迁移到 proxy.ts(Next.js 16)

逐步迁移
  1. 重命名文件,将 middleware.ts 改为 proxy.ts(位置不变:项目根目录或 /src)

  2. 保持代码不变——无需进行功能性更改:

    // 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 = { ... }
    
  3. 更新 Next.js 版本:

    pnpm add next@latest
    
  4. 确认环境变量仍位于 .env.local 中(无需更改)

  5. 测试迁移:

    pnpm dev
    
迁移故障排查
  • 如果路由未受保护,请确认 proxy.ts 位于正确位置(项目根目录或 /src)
  • 检查 .env.local 是否包含所有必需的 Clerk 密钥
  • 如果中间件更改未生效,请清除 .next 缓存:rm -rf .next && pnpm dev
  • 确认 Next.js 版本为 16.0 或更高:pnpm list next

使用 Clerk 构建 MCP 服务

完整的 MCP 服务集成说明请参阅 CLERK_MCP_SERVER_SETUP.md。

MCP 快速设置概要
  1. 安装 MCP 依赖:

    pnpm add @vercel/mcp-adapter @clerk/mcp-tools
    
  2. 在 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',
        '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 }
    
  3. 公开 OAuth 元数据端点(完整设置请参阅参考资料)

  4. 更新 proxy.ts,排除 .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()
    })
    
  5. 在 Clerk 控制面板中启用动态客户端注册

最佳实践

1. 环境变量管理
  • 开发时始终使用 .env.local(切勿提交敏感密钥)
  • 在应用启动时验证环境变量
  • 仅对可安全公开的非敏感密钥使用 NEXT_PUBLIC_ 前缀
  • 在生产环境中,将环境变量设置在部署平台(Vercel 等)中
2. 路由保护策略
// 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()
  }
})
3. MCP 服务安全
  • 在 Clerk Dashboard 中启用 Dynamic Client Registration
  • 保持 .well-known 端点公开,但通过 OAuth 保护所有 MCP 工具
  • 在 auth() 中使用 acceptsToken: 'oauth_token',要求使用机器令牌
  • 公开测试期间 OAuth 令牌免费(定价待定)
  • 在公开用户数据前,务必使用 verifyClerkToken() 验证令牌
4. 性能与缓存
  • 使用 clerkClient() 在服务端查询用户(自动缓存)
  • 利用 React Server Components 安全地访问用户数据
  • 尽可能缓存用户数据,以减少 API 调用
  • 仅在客户端组件('use client')中使用 @clerk/nextjs 钩子
5. 生产环境部署
  • 在部署平台中设置所有环境变量
  • 使用 Clerk 的生产实例密钥(而非开发密钥)
  • 在部署到生产环境前,先在预发布环境中测试身份验证流程
  • 在 Clerk Dashboard 中监控身份验证错误
  • 保持 @clerk/nextjs 更新:pnpm update @clerk/nextjs

故障排除

问题与解决方案
问题解决方案
"Missing environment variables"确保 .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 Dashboard 的 OAuth Applications 中启用 Dynamic Client Registration
更改未生效清除 .next 缓存:运行 rm -rf .next 并重启 pnpm dev
无法识别 "proxy.ts"确认 Next.js 版本为 16.0 或更高:pnpm list next
Next.js 16 常见注意事项
  • 文件命名:Next.js 16 必须使用 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('Request URL:', req.nextUrl.pathname)
    console.log('User ID:', auth.sessionClaims?.sub)
  }
})

启用调试运行:

DEBUG_CLERK=1 pnpm dev

相关技能

资源

评论

支持 Markdown