+ {formatBlogDate(post.data.date)} +
+{post.data?.title}
++ {post.data?.description.substring(0, 100)}... +
++ {post.data.structuredData.contents[0].content.substring(0, 250)} + ... +
+ +Read More
+ + + View Article + +From 9cc2e3d8abcf7fddb8ac765c2f10e8c0028b41c7 Mon Sep 17 00:00:00 2001 From: Bereket Engida <86073083+Bekacru@users.noreply.github.com> Date: Fri, 23 May 2025 12:44:51 -0700 Subject: [PATCH] feat: MCP plugin (#2666) * chore: wip * wip * feat: mcp plugin * wip * chore: fix lock file * clean up * schema * docs * chore: lint * chore: release v1.2.9-beta.1 * blog * chore: lint --- .../oauth-authorization-server/route.ts | 4 + demo/nextjs/app/api/[transport]/route.ts | 38 + demo/nextjs/app/api/auth/[...all]/route.ts | 8 +- demo/nextjs/lib/auth.ts | 17 +- docs/app/blog/[[...slug]]/page.tsx | 219 +++ docs/app/blog/_components/_layout.tsx | 110 ++ .../app/blog/_components/changelog-layout.tsx | 135 ++ .../blog/_components/default-changelog.tsx | 254 ++++ docs/app/blog/_components/fmt-dates.tsx | 25 + docs/app/blog/_components/icons.tsx | 35 + docs/app/blog/_components/stat-field.tsx | 219 +++ docs/app/blog/layout.tsx | 18 + docs/app/blogs/layout.tsx | 18 + docs/app/blogs/page.tsx | 104 ++ docs/components/nav-bar.tsx | 5 +- docs/components/sidebar-content.tsx | 32 + docs/content/blogs/mcp-auth.mdx | 178 +++ docs/content/blogs/meta.json | 10 + docs/content/docs/plugins/mcp.mdx | 224 +++ docs/lib/blog.ts | 76 + docs/lib/source.ts | 7 +- docs/public/avatar/beka.jpg | Bin 0 -> 10038 bytes docs/public/images/blogs/better auth (1).png | Bin 0 -> 35824 bytes docs/source.config.ts | 17 + examples/nextjs-mcp/.gitignore | 41 + examples/nextjs-mcp/README.md | 93 ++ .../oauth-authorization-server/route.ts | 4 + .../nextjs-mcp/app/api/[transport]/route.ts | 38 + .../nextjs-mcp/app/api/auth/[...all]/route.ts | 4 + examples/nextjs-mcp/app/favicon.ico | Bin 0 -> 25931 bytes examples/nextjs-mcp/app/globals.css | 26 + examples/nextjs-mcp/app/layout.tsx | 34 + examples/nextjs-mcp/app/login/page.tsx | 64 + examples/nextjs-mcp/app/page.tsx | 103 ++ examples/nextjs-mcp/lib/auth.ts | 16 + examples/nextjs-mcp/lib/authClient.ts | 3 + examples/nextjs-mcp/next.config.ts | 7 + examples/nextjs-mcp/package.json | 29 + examples/nextjs-mcp/postcss.config.mjs | 5 + examples/nextjs-mcp/public/file.svg | 1 + examples/nextjs-mcp/public/globe.svg | 1 + examples/nextjs-mcp/public/next.svg | 1 + examples/nextjs-mcp/public/vercel.svg | 1 + examples/nextjs-mcp/public/window.svg | 1 + examples/nextjs-mcp/tsconfig.json | 33 + examples/svelte-kit-example/tsconfig.json | 3 +- packages/better-auth/package.json | 2 +- packages/better-auth/src/plugins/index.ts | 1 + .../better-auth/src/plugins/mcp/authorize.ts | 232 +++ packages/better-auth/src/plugins/mcp/index.ts | 928 ++++++++++++ .../src/plugins/oidc-provider/index.ts | 3 +- .../src/plugins/oidc-provider/types.ts | 8 + packages/cli/package.json | 2 +- packages/expo/package.json | 2 +- packages/stripe/package.json | 2 +- pnpm-lock.yaml | 1338 ++++++++++++++--- 56 files changed, 4540 insertions(+), 239 deletions(-) create mode 100644 demo/nextjs/app/.well-known/oauth-authorization-server/route.ts create mode 100644 demo/nextjs/app/api/[transport]/route.ts create mode 100644 docs/app/blog/[[...slug]]/page.tsx create mode 100644 docs/app/blog/_components/_layout.tsx create mode 100644 docs/app/blog/_components/changelog-layout.tsx create mode 100644 docs/app/blog/_components/default-changelog.tsx create mode 100644 docs/app/blog/_components/fmt-dates.tsx create mode 100644 docs/app/blog/_components/icons.tsx create mode 100644 docs/app/blog/_components/stat-field.tsx create mode 100644 docs/app/blog/layout.tsx create mode 100644 docs/app/blogs/layout.tsx create mode 100644 docs/app/blogs/page.tsx create mode 100644 docs/content/blogs/mcp-auth.mdx create mode 100644 docs/content/blogs/meta.json create mode 100644 docs/content/docs/plugins/mcp.mdx create mode 100644 docs/lib/blog.ts create mode 100644 docs/public/avatar/beka.jpg create mode 100644 docs/public/images/blogs/better auth (1).png create mode 100644 examples/nextjs-mcp/.gitignore create mode 100644 examples/nextjs-mcp/README.md create mode 100644 examples/nextjs-mcp/app/.well-known/oauth-authorization-server/route.ts create mode 100644 examples/nextjs-mcp/app/api/[transport]/route.ts create mode 100644 examples/nextjs-mcp/app/api/auth/[...all]/route.ts create mode 100644 examples/nextjs-mcp/app/favicon.ico create mode 100644 examples/nextjs-mcp/app/globals.css create mode 100644 examples/nextjs-mcp/app/layout.tsx create mode 100644 examples/nextjs-mcp/app/login/page.tsx create mode 100644 examples/nextjs-mcp/app/page.tsx create mode 100644 examples/nextjs-mcp/lib/auth.ts create mode 100644 examples/nextjs-mcp/lib/authClient.ts create mode 100644 examples/nextjs-mcp/next.config.ts create mode 100644 examples/nextjs-mcp/package.json create mode 100644 examples/nextjs-mcp/postcss.config.mjs create mode 100644 examples/nextjs-mcp/public/file.svg create mode 100644 examples/nextjs-mcp/public/globe.svg create mode 100644 examples/nextjs-mcp/public/next.svg create mode 100644 examples/nextjs-mcp/public/vercel.svg create mode 100644 examples/nextjs-mcp/public/window.svg create mode 100644 examples/nextjs-mcp/tsconfig.json create mode 100644 packages/better-auth/src/plugins/mcp/authorize.ts create mode 100644 packages/better-auth/src/plugins/mcp/index.ts diff --git a/demo/nextjs/app/.well-known/oauth-authorization-server/route.ts b/demo/nextjs/app/.well-known/oauth-authorization-server/route.ts new file mode 100644 index 00000000..c236fffd --- /dev/null +++ b/demo/nextjs/app/.well-known/oauth-authorization-server/route.ts @@ -0,0 +1,4 @@ +import { oAuthDiscoveryMetadata } from "better-auth/plugins"; +import { auth } from "../../../lib/auth"; + +export const GET = oAuthDiscoveryMetadata(auth); diff --git a/demo/nextjs/app/api/[transport]/route.ts b/demo/nextjs/app/api/[transport]/route.ts new file mode 100644 index 00000000..056410b4 --- /dev/null +++ b/demo/nextjs/app/api/[transport]/route.ts @@ -0,0 +1,38 @@ +import { createMcpHandler } from "@vercel/mcp-adapter"; +import { withMcpAuth } from "better-auth/plugins"; +import { z } from "zod"; +import { auth } from "@/lib/auth"; + +const handler = withMcpAuth(auth, (req, session) => { + return createMcpHandler( + (server) => { + server.tool( + "echo", + "Echo a message", + { message: z.string() }, + async ({ message }) => { + return { + content: [{ type: "text", text: `Tool echo: ${message}` }], + }; + }, + ); + }, + { + capabilities: { + tools: { + echo: { + description: "Echo a message", + }, + }, + }, + }, + { + redisUrl: process.env.REDIS_URL, + basePath: "/api", + verboseLogs: true, + maxDuration: 60, + }, + )(req); +}); + +export { handler as GET, handler as POST, handler as DELETE }; diff --git a/demo/nextjs/app/api/auth/[...all]/route.ts b/demo/nextjs/app/api/auth/[...all]/route.ts index 6cba29b7..5b67b064 100644 --- a/demo/nextjs/app/api/auth/[...all]/route.ts +++ b/demo/nextjs/app/api/auth/[...all]/route.ts @@ -1,10 +1,4 @@ import { auth } from "@/lib/auth"; import { toNextJsHandler } from "better-auth/next-js"; -import { NextRequest } from "next/server"; -export const { GET } = toNextJsHandler(auth); - -export const POST = async (req: NextRequest) => { - const res = await auth.handler(req); - return res; -}; +export const { GET, POST } = toNextJsHandler(auth); diff --git a/demo/nextjs/lib/auth.ts b/demo/nextjs/lib/auth.ts index 53691a45..45635944 100644 --- a/demo/nextjs/lib/auth.ts +++ b/demo/nextjs/lib/auth.ts @@ -8,8 +8,8 @@ import { oneTap, oAuthProxy, openAPI, - oidcProvider, customSession, + mcp, } from "better-auth/plugins"; import { reactInvitationEmail } from "./email/invitation"; import { LibsqlDialect } from "@libsql/kysely-libsql"; @@ -19,9 +19,9 @@ import { MysqlDialect } from "kysely"; import { createPool } from "mysql2/promise"; import { nextCookies } from "better-auth/next-js"; import { passkey } from "better-auth/plugins/passkey"; -import { expo } from "@better-auth/expo"; import { stripe } from "@better-auth/stripe"; import { Stripe } from "stripe"; +import Database from "better-sqlite3"; const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev"; const to = process.env.TEST_EMAIL || ""; @@ -52,10 +52,7 @@ const STARTER_PRICE_ID = { export const auth = betterAuth({ appName: "Better Auth Demo", - database: { - dialect, - type: process.env.USE_MYSQL ? "mysql" : "sqlite", - }, + database: new Database("auth.db"), emailVerification: { async sendVerificationEmail({ user, url }) { const res = await resend.emails.send({ @@ -117,6 +114,9 @@ export const auth = betterAuth({ }, }, plugins: [ + mcp({ + loginPage: "/sign-in", + }), organization({ async sendInvitationEmail(data) { await resend.emails.send({ @@ -160,9 +160,7 @@ export const auth = betterAuth({ multiSession(), oAuthProxy(), nextCookies(), - oidcProvider({ - loginPage: "/sign-in", - }), + oneTap(), customSession(async (session) => { return { @@ -198,7 +196,6 @@ export const auth = betterAuth({ ], }, }), - expo(), ], trustedOrigins: ["exp://"], }); diff --git a/docs/app/blog/[[...slug]]/page.tsx b/docs/app/blog/[[...slug]]/page.tsx new file mode 100644 index 00000000..7cf64d11 --- /dev/null +++ b/docs/app/blog/[[...slug]]/page.tsx @@ -0,0 +1,219 @@ +import { blogs } from "@/lib/source"; +import { notFound } from "next/navigation"; +import { absoluteUrl, formatDate } from "@/lib/utils"; +import DatabaseTable from "@/components/mdx/database-tables"; +import { cn } from "@/lib/utils"; +import { Step, Steps } from "fumadocs-ui/components/steps"; +import { Tab, Tabs } from "fumadocs-ui/components/tabs"; +import { GenerateSecret } from "@/components/generate-secret"; +import { AnimatePresence } from "@/components/ui/fade-in"; +import { TypeTable } from "fumadocs-ui/components/type-table"; +import { Features } from "@/components/blocks/features"; +import { ForkButton } from "@/components/fork-button"; +import Link from "next/link"; +import defaultMdxComponents from "fumadocs-ui/mdx"; +import { File, Folder, Files } from "fumadocs-ui/components/files"; +import { Accordion, Accordions } from "fumadocs-ui/components/accordion"; +import { Pre } from "fumadocs-ui/components/codeblock"; +import { DocsBody } from "fumadocs-ui/page"; +import { Glow } from "../_components/default-changelog"; +import { IconLink } from "../_components/changelog-layout"; +import { BookIcon, GitHubIcon, XIcon } from "../_components/icons"; +import { DiscordLogoIcon } from "@radix-ui/react-icons"; +import { StarField } from "../_components/stat-field"; +import Image from "next/image"; + +const metaTitle = "Blogs"; +const metaDescription = "Latest changes , fixes and updates."; +const ogImage = "https://better-auth.com/release-og/changelog-og.png"; + +export default async function Page({ + params, +}: { + params: Promise<{ slug?: string[] }>; +}) { + const { slug } = await params; + const page = blogs.getPage(slug); + if (!page) { + notFound(); + } + const MDX = page.data?.body; + const toc = page.data?.toc; + const { title, description, date } = page.data; + return ( +
{description}
+
+
+ Better Auth is comprehensive authentication library for TypeScript that + provides a wide range of features to make authentication easier and more + secure. +
+
+ Brought to you by{" "}
+
+ Better Auth is comprehensive authentication library for TypeScript + that provides a wide range of features to make authentication easier + and more secure. +
+
+
+ {props.children?.toString().includes("date=") && + props.children?.toString().split("date=")[1]} +
+ + ), + h3: (props) => ( ++ Latest updates, articles, and insights about Better Auth +
+
+
+ {formatBlogDate(post.data.date)} +
++ {post.data?.description.substring(0, 100)}... +
++ {post.data.structuredData.contents[0].content.substring(0, 250)} + ... +
+ +Read More
+ + + View Article + +iiaWr{yZ5WKR z#%|z>m$x@g1RFN$(ZbakB3R|lclXcWR|g$J zq7TZwcK2gpK|w)ZriN6OI@pE1t?g|&xiG~zs=RlPStEPxDY<&7uGu`il_^mqef0Y> zT@wMST}|c$?K)o}=eO)8nwy&oA8d*d%Iy Z^N)!J_dw9g)OK{YvyY9v&WVAKp6!UMnUp?)B%lTO76Utv7Gp zB$1x&ADd$LP41|!7NC_5mbi@%UMsJ3a?xX`4qgF^&a*n&+6yha69Zd62TqwM9<*}Q z&p#br8ag^W&@nbHjiumuFYuv$Fne)zRoBuoCnh#l(C%~Y_351(qmL64UxLLKzI@4e zz7Pxb=xte64+Ub>s?NLuE)x=n^&mSM{uQ<~ezp*BwbjI7xNN9MtH5g1d+aWj0D;_O&~Ap* z!SXc#7YSYgzR%@dNl6#R1J+vX?d;AypF0mLBM&P>c^=lqf6Eruko$YrRm~R{f7)>C zuMe(HYrT&QnVj8N__MbOzN})jd-nxER%i6Zr|Ic5BenW3@07gY(Cmx4(Q{yb_r-pr zGU1xS%;w(U*cc~fn7Ymh%)=S~4-+r{->$ zcl7b8L-38|X7W{5R#y9f-7${@FmlhWh5mY6JFsSIEOOL~$ua7x6cc}Cp4Q(MZBnE3 ziLZ-qIF%aKsL+S}acdim6CMzFnvqf3*eIs +b7%x#R0k0(;D2(uD4 zqZ0QnN#?qf&zk8g3ob9_x$wx|!C`wgL0XZp2Ar^awjbPOd$UiP11^4X`&A|9hjdY` zfSu*&4h<}nnJWS?IF4ZO?>~Nw*Pmti rSmIM2w)FeD{Fr y2eA_EFDJ@~%Bm`B1M-dsSg3OefKt3>fC~}k4^;c j?Xv7_SwTU;R8bF`-uo;0 zcJBd3!51cj4@z~*jEcT|`4Swimuy`Ghux`j)-g9Xe|y`h+wIBo=S5^3x(g6$&=^dD z2x-5hOUS{BFcN+~imyF1#N~j%0^rcn(&8p=0wD~?hVe9t;MwrfN)?4p2zHA6*<76W z<8{>MJhN9^OuKm}y4gOfIhOP_3V&*M{hoOl;L5PhIRgSBb)|#|fp_1 qbi1^E!67C(v3bEvNJ;_|`SFfJs^yzT#;I$g9b$541d^|5dUy<+)OHoaY&TyG= zY=em~Py&Yj4F_;u^Eyiu8S!e|_uG>pHY^>k^O>^%7X&{^$o`m!>>h^U5~JbM1?pL= zRc1}pnh2m5>y~QD$jA)RWk#4^6%^FDct!O#glvHSoZ8We3A(DgK&&9nP*xjQW1*s0 zA!fIS5^hiBOEamd5t}WXCBodtukBvk_-=ABo>n{`SwC=BMmaaj(qc4P1-1dX3%CS3 zZ_TW}f{!0>?FF_zTe-r*awfuGY3574C5|ja=g^ZKuVb%P&Yig4@e)_Je7YJFx;1&c z`Sj`2Z =&NW~XM<*K`VUmz$0x{|s@ zxvYx?No9Te_RVvu9m}}Ulg+* ( 9zEwsIc%VZ?Rd)`unAAGyvSt(9q(-!r5L~$~bXHR|w(g;^Kk9 zBCS(BIb=q4PIn;Q1njO3_2($%;8?BBuQe`l=jG;hjtysK(#_4!_Xw!5u(IAv@(&0& zl>lK7os%OsK0bbr&en(+g{s3oxt4wLo+-0Z3SkO`F}^VfKgF a*Fa1^7}|Va$;o9@f16s^nPBis40%$6>cAipO7!#!zyS;A^9~LTs%+R` zA1p>zaWt;k^nUu7A^tQov+UtR0wBN~dS$2Sz-PsR_N#;Uf3jvs1sTrwWev9-AN2?V z*&9Ic!+GSx>b)m7gjGA7&NA{EY3u6R59F(*@>wdr6=eOqw+b6G>TOmlQ)d>y>Fi5p zR6cG@7aWIgdMrE{nDdlFZ?X6JHNFmx(OR$Fai3y{jX!p7YTDT3^Iy9rmkbtd|L6C{ zz2R7LF3o^|rqt9_&*|4^f_}YYMbJ)6OpNKv0yfHw_dJ4RoSH5V(i$f2*wvrW8+&3Q z;@bBUjb pnHhrm0}65KnDGAm!_ z0E)+i$E~gYDEy^;XA}t M cK_0lUtCZfCH$Q7D{_5FjO>Sd9C5lIrYBtrs+konxeCFuP7kPVqYR ztOy;;OOn$W{g;`pm}_hl&SJA)h~l$SfBTv#O~&lBO!}lq@HVlRXFMJwV<^+vdk;=y z6EK7omM}URG!_Sk_&OVmYx_`OC0)<#6QxO7RT*z29M?}+3*tErXJpFe=;F =-H&;O|{B&4p15If|w2<*t!t5@5r?FQC+c-q=6D%z)AeQA`EnB^4}EdWav zM(Z$IR~-k-jWtLIhlg`syg)(BEi5S!sFCMkW Y3_ZUDOG(LYGTfUi zZjpBtwjaD&Vo-%cEd7TU`_!pR79=G7Ua#o%<>dz%;>O!)ZB5KoZt6>&txoq7sx5qb zqts6rWz^_-?szTh*zq-o{qqQdOZUM~&8OP$-Fm9NnoRU<#k<6`jn2TpV5H849_34j zu`=j_EH5!5BW|K4Ks;kq%LRS^K6Y+i-pi7bUTt48Qc}phu1vwkDgnQ1iJ$&v*+ZL? z<&zPHMn^?OUB7jU2$)4og4F)myM3A4-!49kids FDUA zByR@B^KJKTaq%TH!AkRv2=nRJ;R{2>DO$GHq$bgi?m}F@p{_pcO}+JHQzeP{A(bE= zEYw+i`~f--!gmk7sUIdJbl*FLs~WB EvAUFulM|O5Clgbn4W$_giAI>9E}5=J3ATOwu)O?_5l11; zJGBJkjb0wqCO-pAl#?ES*OF-EtG=4 UTc>7`s^a&m-J(7x@>)rbBLKRaH! zwC>?NW>xQkprN|9OmXpI81Tmj4<7iIFbxPtPz>vvnkGP?d;t(2a4Xqbzf`x{dEC(9 zG|vytkBmPqf8>%a;WlnGd;f$D*^64|@%Q8BqN5g4%|jLB<@I`v^ImV$U>e3zHMw0s zxU9Ps8utu1$q6p;p9Vyet#cl)?taLg&8&RCV>%R#hKw5*RwE43ZNH$&*iAw@2}+1v zifzG%>Fk(LX({n-$vRSnSNq#be2}uxh RX*l0`P2#9Hj(BdHK_go GI{or%%I8TKw%^#x||1 z2xv~$yIsz|l@T*gpzfffnAwFDJn7`RG4m!RT(4|4YI6wurLgcUAcY8^cF{UJ0|Tm} zqT=nlcMZ#bR;fDfaFYM|v)T81XVhim6)wFQWG1&>CX4hnc+9)5j)~Gp1yW`O{yO*i z_3QPm0rgo(vlRL=B?%A0f41rAkO`#`LV416dq_`1OUr{L#0ai8qF&)F2?>c`Cmv^G zVsxH!Vq)Ukw{P30UWH|34)cT!MC#Sl)acha(?a&=_WdmhX8NhfaJhL$IS3vEl)UXf z|E$Wg=;&zZ>qmhY1e#DP9_D^n7@zs;)67?{F!A!9kIA-`d0x=94*nz;7${-$F+JL8 zv{sZ0f3OY5AC1n=&c?2?`5+5_5^@|tMRvSAEO9vIo=RYO0zArfaT^xjQxLJ^e!?(D zmDyQ`Tf+PrgrsC-;myqwV;+5yDciN~c~|k4y~XkH@e$XKiNU)XGvie!qnxxp6-La& z%8DCwuoKR#Z1`xltd1N-N=nMW#&!;}ynJ?AAg^(5vt5kb+(a2-UhY&%eS!~xU()8Q zXT|qP9S{It`Btf*j#VRqgW@)6jsp3K%FWG9zs8;n?Ai0H(Uu3<@LVGY6F8b29|8m% zHl72YpUn!fxK#~bKqqb8%yQmo`20axM~8r3iYDavV3N 6q+9Z6X>sT&?|`H z$gzGjQ&HnE%p@v$2`t$audMeot^v@!w;w*ddH<9{Lr-s>uM;~I(pPHBu7}h7R)7lj z1m#gtg|7^M5uQ7cB@qk(ySSOmDT6qfwRh-2Ibb<# nI~D ziw*I=MK{s!@<%@qU^tB%Nhbq0;|3h2#c&lg&QnrGfoye}khPPzbE?6oOsm<# bM~5s;$mw;+ z(I&l9ii{7&z}B{~cJuPTmWN{(v!35D{zss0sljCARID?zTm*rPt}gk}pLuDAgiC^s zDa_}HB2R5^Z$k*buBeDZL?be_z1DQrKZ1}t0?C-BpHeE( mQ>J~5E<^#!dxzzUC4PX3UX{<^lA2AxRoy`KBT+{MA)fL zM!lu5)4kQD?T~z9V*}wDf)u8vrk!0~7W-R27e43pSkDiIU_t^RLA;@+_NF!HKvmcw zAu6i<_r`30or|eHCpp~66c8GDAaTQ4AbBY$D4cO_S`n)2hFG?>{PkjRaB%tPib ySZqKyP7b$!GnZ zG!TSqI!sMj8PW04ZX2Kb_ZxM`+;1x%f_+tD$3{h!;US)8U*1*n8vGkJY2j;C0r`wC zet+4{z7dBAgxj_@X+)Hu5pivwt!a#R(Z-q)i)dAkuiqd`K%%as|CVcj<*mUB(Q|>Y zWxxRB0Y|1>MXHStCBh{U?^jhNxRoX4gr*=vJ&267Tpepzm~3k^f9%Z{e-=YSgbS#> z0m}C?=MnB6ehUkWM_F0K^z`%zO{|O3O2F&S!|wp;A~Zx0d^mgHD+NHS4*T@!_Q;C& ziU*L5G4~!W{nkLyowZ2}mx8B~w*93bspzG%cz6~k>>~VRw{BQQT%E~Zs~I-w$W?wU zfuldX-?$}gMIL(w{LFVcoB~2 4nIW&YV7NOQylp{7ZnIPZ#hfU-kKY!2h(xX-IJ+5iGoP=@LNbWg4PL zt$M2(!)aHM^XJcpCSF#ufPhZd=eIb>=CrEV(xt6qeR7L&|J))sBdM?(Sh-x2_e!dj zL0Y}2xZf{kQiY91iFrf*s_zT_(-&mzKO)Q|Gc!Ia>TE(2A>KbB{`0`VM%(eBy2JO? zXlCX5 *s*~IgW%v#{1;y)93!gxVX5MWA$z~Zrr%;>RMj37nOkm zRU{s6!*r*)-w2p?PJ&cSZZ7j!@WIM$9i2PLTwNrV#poFE)%sJgo&ZA5eZ^Pt?CtG8 zp>>LTWWD)(Oid?khPod`MLA3SjZsv^VoH+-@1HhBbnR2~ZL}2Rf6NRv`BSnaN_}~Z zuTka-69YRtUPD8Jq3;~GHuejy9^ KLYLl9&i$`gj100yhaAS(T&*+e^cj z+1TEB`jWv~f@?wEbt*&L$D3->7 N) zn06uw!3X7KY>|+oz~2W4)E^b3>6^xhiicw8gMVW|g`zW4GC(!l%|fqZZ*%^#h{%W? ztDgE(D?2;8k=CH5VZSIe-(gK-=F;nM{Cxj72>%ToC;+FZF^e0@AMP MT+L z3)fS8`eJ|m{j{qYB=EjFU+->=y0rN@<#J~d6BBng`)!`5qYF0B&!bm*pa%vv((DIf z%` JkS4JuDV@HgQwq$R=yhK|4s1x4gPjPX)#LIN>ez%rkejSUl|s+F7$ z_tKgacT>2HUsqc9tWN~2J;$ra$ c>gQJgrlm|YFat>x{hGKv$ zL{~^Z=1FTQ!}VgaUjPV0L_xMPo?hD7zD`ENug2^ZHUX8uTee4T6iHuKyh$l2E=x+% zl97>_oTOzhUNFcjDP3k^c{Au7#>9EB(M`|iwWz&3)^HJx_9vVT3<}x+{SJW@<0kK+ zvJW-S8;|@!F)X)ug9jW@3BOU|Ljh=Tzk5uNEPki|7Ht}jx9pDhb36@v={sQ^_DQZk zg?1CvndHjOzj8F8q^QVr?b=09IfaIhnkdnkGiToR_I4s^=!(!;6hQpm-X5idZ+Z7R zUZ{5~)TWR`2&gCTP`M bgn6@Zlwmy}6bg6`!P_B_Ba#GRK >-wdLNgxrJ{v#a|xLx|$Y2dRXf+881BHdkP5%C3v4NGHL*k1mFS#Ufrs1 zGFD|#metT8$qYI2L(*66IWp8kT51<4 zrgt-qN01mo80;~!9gW^AcnTWT8JlC_lBja8C0$S-Z~) L&nHSRN6 zx}EKQC^{Td2-nj5>pe$)2L~kDP*z8fMYnh}Bsk!kq#I =&xMiLo zqWJJ~eBJ2ERZdQMNKA<6rItNuSG5Oe}9SYK^g^C!e~215J-EuI(r&FSlly5Ziw zK4Nek1$%pT=Y~1*QkC~wzEEn6JA+UDj0n@YbRI-eNPxFiM)d-Wq~nj4fIQ5Y9`C0V zZC-@*`ZXR9t!$1?bZ*+fsg4Ln0fB0bu0>#51G}Yv{_qBm)hk}2MoAsD9Xr$>M#;*` zo~Ndc1*a2}T>aLhtc_^1Gk|moHa1rvnhI-?X+Y+DGld&T^FXW_kI=iHebsPI$Iy@p z%5Xou=zzh9h*0wJ@%b*5)&X@e6QMs$51r&lZ#`HQSIv^51wA}P(nTPC$o|bje@^1F zXHj&L{`D) mj_=l?gw)h z2V^f0s=r)&Kc4ki>X|=cOzG*%Le1{9#f^ ?#^O~~pH%F^f~`|SX8#_RNVp!R`4=7lft9^q_JYFGM~_0GUBb_Cq%3O| z;2*B)A&7I=HAuClhqTls!$F^%{&L5~i< eK7{p4LN#+Z9h}GRM5A9n{(g6`V^sI zL)R~FQbt3Xo@5_zu5L1z8kd+(KoSQmo)uvoQEzjSDZgn96qS~~0l)uLq{Z4X|Hi!y zP_}vN^UdFLDW<4)PXz;T7e41Pk<+J7x7*h=IuWe7ia^v7a{BgQJ~My_`faX3W;yI8 zuQ0g$wTgt4^fbzuqd9c)lwf4XJ)jv(e0(Gjj9f)%@{?s6SB0*6+j&aflHP$(2&@IG zX?%4MHA$ du2onRb2b0z>GUIip#a|HV55buN;_;z8S{$5@L=ZnFdkQix z{U&cN=rF+gqmh?Od;`hqKs=b8&kBh^qx1bmu-bTr0{?7u_ZRE&)HQ8J<8i?C0J^AV zhz)uALiv+JVtY^vA^e~;wQ#GS4xT*YI{z^vDmGTmz~B>JC40yI0w3$6Q$9KD&6)D%1> zR;XL@^1}oiunmsSLZk_2|rKkRJ3EPkAs*l%mua F9(*)rd)0c$m%cm(UkupK-+a%%O;VYuIJ*w(0WcdZ$sL-ZWui zr35-bK_W0NgMWk}3tt8K;YrY25>r!SfLcrg@lgQf!4-rQ$8Xi=vVwLl#nXslxBsaG zjSbj42Q k;LRcYC2wz8$T$T`uzqt zt0*ZU0QFoBGN&*fpyEG}Kt^KBIPrwU7}S*)&CN7GKwdl5jYoVby^{lRx$S751Gog{ zcVH#wWngx&+mOzuNMbO|nmS}_tq0sd+0rvTSnksRGjOVV1^R{Ut7lCyR}Y=TvS0V~ z-i={aekS6^q^YU-38c3U2RFjEKv+UyiDxo=CSHLXs>Z!Uv*Qp_<#JIE>Nz&x&!Wtr z%6Q|}tu833*096?ZyCOTB;45?pR|RoOT2TE7l2fVxK4$VzYy3uEVzmXWe>#YSNMF^ ziXi7V|Bxz37ohR;R1N@6Zhn3?$e45~da)|pv2X5!Lx3T@$j*MPebEc`Ou+8KuV25; z)6h8TkFzXv)FZ44y3G{4ylT4nIyca0CJ5PIzkZF1iD}GUUu>Aql(Vxd(v1W4-Ln+Y zEt#0=d@rl0t=OkzM<40H0znqp Kz%f_3xVHU2 z!meJ13qi`&uQ}8SM-_ZosA^N5KJ_C6 | zL4wPrEM<2Bd8lf6^vh8(+0X2+^C(~N-(8IZ?MCAFR9Jp8At;Z_-)it-k4CEPF47Pw z#qJw1GiB$y?X^vS?N~*T;m8ZqkxW6+g;I(~wiMDj$H~_5*#M(tYXWK^0;nd-+1g%3 z`Ia (-as +wm_UF|{YbSf_1tt396f{U7sxVx=`gVmc ziFq0rgGj(mN%yYL?&>&_qd?nFM^Q|V7sd6UIW15M!EJ8q>i+P2Sm5CI ku>D1*S=+7gb&Wm3$OY$h_FgV?JC<$>Ve)3_MPW(a>} zEJ!{($iT(dj18_oenWo93LJiFZ4D#&Lq$g?T{;91O^~i{2%KrmXEMkz 0buFL~766)98b3O!u>Zt=8nL;YVt*$K=r#KNOli0jE|Wd~WXS$c zR#y@;@SZWRVPoe5 sy7&nh15ZwHcsP0b%)89}r7H@{_8Q9wa=^8a0{?ss~lX8%3k8 z{{R)S9@=rcXTMe3g@L;SNc?~Wz9qhOiT3jAVo)JrOrgUvFUdCq3cMFDUL>KSTJ-vg zNLpz0>&2mBsG~Tst6u0qK{Ey-OiOHil8d-WZ#-{VH(KE_e+x2X3$XbVV|-AbEl(KK zhs;*oo0bnPw?x(hw(l?OeI5~ZRdefjo4@UO?UD5*5Si*de 3r-2DupI7Q*9+ zSoU0q8#B-@be4$7d*8tAA+z#TZf->ji|pZum6@t8iwXwtbWnAHrABsNS)5sdR)uEJ zru3VA`Jf=Puq}9g9*>w<$=h29Lann%{8Av9f{dLV8(6T0kx}nB4(yP!8-*-b@Xprs z&^AFu=ZC^2o A3ldRv=|fVmERb1=BISzTKVd!x4N+VdE?fYXn#64WIae9M`<1%MRX*(tNPN&K z)@78Ire{^p94z~g_9_h55>j5E3!T=(FS9PMch5UoTc6mQzW&>jD}7giD6^@OBve6L z{YoR1*7(@i*i*B!r%>MriwdZvax%I1RR}g}3c1hEM!!%wskDNm3+kl#rx!BoySu`U z-i`ugOT+u!V_b6XEvpd50|Mis-ahk{rj_&`EFZqO-W{+4rNK8qk;ib p#WN9d9Mqd3 zP5}s-kCx#g$A=j52y(iE)uz5B5m+FHk)zKydCVb&E@_6aXLAn`DaX%~D#$=)d$f@d zVzgr<$MK|WD?&AidCYs%={FQNVY$D&;GY cf e))TWT2I*(vYTko_F0$d2@4o`pivISfV;tF9vD4#V8cEG#dZo71Ps01-I;Ox8n8 zf0NqC`dA|i-L=osMbT2)`B`?O<|t)pR yEr|BNSP3s;dVrx068h zl)fqLz9Z)c1}UC7+%hi8GQGNGx%g)IGPzo(GZo&?o4!9Fi1_f+O=&*U)(=&kW zdChWrb(|WMNJ>HbCj)zNT_n>wz|Ej6wsE*S8DNA1C6htYziI>TjcJGLo;kNk&mf@) zusQPEH^YQ(&@!L}a~A9kC$!k4wiwFduOFPdp{gnmdUwtILo~aBD+H`?@F#Pqmjg=i z3ns%Ai?}i%j2?vX#umCDUl!#H&~Su=7vR|gHyNX&qNpzZjpkFO=6^n*Sw22@ri0wM zwUrJkzY5SjI$ddcXbIgAav%mc3Q+@p82VOTMs2~W0sEo)oc|0kgWm&LN8ddkE(P4w zmV#8=0y>8Dt8Gb6Qt_UP3TM6|`4bu0+*W$idysX@@AlDkzIg))FX5OIsM)DpC-KTG zF6i-qku#d|$N6zzZ~|B&q+J|(Sl+F;F?q&7ISShTYS^z75BVz$=`(HFLrx5$JfhSC z%@SWpUj*%C9elpTum%shT14Dt9{l9PC7=vNO5sSH6PZ(?H^FKL#g!K-@JJ#71&J+0 zGmHDDLZBsZ@vbX$T;#wCWCicD0b{WqAuw*sRZn8xS{%YhxNyE&S_EW??cLo<_>~W( z5@1a@d)7BL?qM)IIK*@hfjl(-o{3%G-1Io`*ymeXTtuYN=?}t_ozU*DDyW%aeJN;v zO9p{tf=k)PkxgCe5LTvLUsP9 dVEH}1YSxrLBiHm&ud@XLZpxO z`a-hRv!tM?*jz&V^e6yI#8gx R?5bWDQu5hZYf4v;|>w)SSIETB)N z5RC^uGZu1uNXcu4il>u|sjTFuk?=haU4TkzYJ{MGKu u@Ef^PFq{j1}`c2-W zt}7GIgnw+uMFy1e*$Kqt8&*~?P}M#?b5Hlo%NI3*h2_g1n<-kWGQ#s{WPMUH)>xrx zTXn6ka{T3H&+(o#bohs+3s0&Tf2E2IO2ePUD))rcTZ@-+kWA0$B@879 5y9&Z}! z>lcEo4%#T&r9UHm6$QBaheuE5jS3gvw`QcL4?=5A0qCD{v$M1BoPJoYRe>u1cY;BG z{n^fwceVm(AO`5Fs>cy4s8F47=xr_M{Cd{KeMOc-PgW3h(~>fxtE;OTT+P^T?r9^~ zZ4#DxBU|{#`L9n6?p?pGxxX1&< k6$^H_y??iJ6kXL8Z_LrU?J?fxlN5UVkSQtfTYv z{;L #jEt-a zIS!^3b(c?&+<68KtDxIIshnA`kjDS`= G}GIo{T@#7 lBxM!939f}QD2R@&-|fSy^n^Vl z^mYW^z+FSV#OTziQ>xUbQQrxXWke@B44F8iug_vrI!_Z+Ub%8*3;J{xL6jT-qF3X$ zX(K2gu+X?vHUP^}3PBJu1v`6t6%g*fbap;~;{^l+a$z!pkeHYw@kZ~t=j0Z8CSM6h zsi~;AeOoJb9a8gQJ(02lRqZL%FCwI&`dvDO?{g_ 2LS#{%yy#E`r_ Z;k-qqDTkzIXm2^^&AJKyVrW=TA{@>59Im&s7y;Rf4os8T z*x2xk*`QGTe__NqQ+9z|RR){Cdv?PXvKV_87rmgMpl?G2d7JYx0X45+QNOJ>4jYG!>e5M5HD%}c2bLrKi ;9rI@ zge(mh64$Q`TswTQ>5I7++hIZg6-QZ2%TDSli1*+7=J7G<#e}uh)eDdkRdUASDWgI; zkiPEZ-r0@wj#rQM7(L5^e{9^YCM+R6Xx6xMrx@0C9LUPM&3A$GWC!ywaOpqyRBsE~ zTsi0xwEWJXTu+QENwlCix5>AW@>RTX=_CB~fyG~kGyRpEek~^0oMz!8^A(GW_K?RF zRaCgWidfm14GRnN|CyIEI6ht~&y=`3Pme;SpDYy&G{OX*VuGEp;KwsC1~tEF%zL}1 zV39Mck2Nddm> (d*|Njj*V6uVS5NrUB2?;2#2J?vA1CWA8%t=EQ^wihdVxCcP}hKbZN z)wb`X$<{w1Tz`UcVh;2~e%HTiW22e>i__-)_;T!!Cq)^@rO>>C3~`?O8Ea{hwbvP5 zTxVcN=+1$fI~g_plrCKO+V0XlT)Iyn@v+$*!7b}a(CIN>=!dq$f>*C(bips{lQnMN zZa2M-YER-KE#qW)V6b4(d#UmJ{k{DGPOr4R+MXwGjwUFoxI)8WRN&VycUlqG>i}Nr zx|R6{B{0yZ&zTQa4wJ7i0eIyUSm{_49s-!0J52U0+_mm2_nr7=-+f=3w|F;KfmNIc zb8axq1eN|2%?x{w2)zwoqrZp?t_aYM8vZ-r%Fl5AEJ)vKhE+Ckxea8`;ARBW;0k8D z6N&-eU*3)y!ofnF2mgl08j!%)1=MP@cHNpNzI^)h2pOrm$a?EGm`3~YT$lSTWI`@; z^Wc!3oju8%((Ygq&Qw&yh8P13HC@p1!$OrKhKoX3y$!?{7CA8Gs^)?jIQcUt=f{GJ zy!$a-`QRC?E;3;U1>@Vs#u*o)A4t#<%fKN_fZ5=mOyse4uCBL}t&Lsn&D7N5OhOL5 z01;RYEU-{h-X>XnyBJ4e 4(4Z9z-T`{R44V!}o)VcMSL8;fp9DFmQ*&~2>p}_uqf}dv{#rSWp-|tk0cKGs zhEoI`pRz)Zii*n`RiKmL*TEr-qOuK~ChT}*ZSc+&1~9bKgk5xD7XVW$MFUZ&%D;1= zZy)(eAXEWL!9VlUoKpvK$9W%$GD4eAG)y^rf`lHIkZ{*w0al9a@8UB?m@fc9g+VS& zKfeaZUy`JKoGl~uYoVS4DzF+r)BfIk*3W40Q`)~**U{D84KtG)PtN!JNWZl}Pz8LZ z0ECgl-?Ir~e!r~T_ZeaFuLFmHuX$!piA<(IrLzP^R>xgC2;bR-<=f?J>x;^v9m*OM z;Zmt6DL(-L5-8AwVaq^;zaP9OnXwMVvc|dOJJrxxDavs{tgWuN0(vizDa+MWXBevG z%Q%mk!aJG!yq-)M@e(v=whPg=(4N80#+KMOf75TQwMLJln)4=*SHmXnQba+8c2?7` zR8lC^rN1-yR*8}sE9+Z^urT0LTde^eSCKPbDX `wP7ZW zJeI@|3uS~*KJXm3(*P26mSnOghoHU-oZKV4_}~6p_3=MXb_j5c-#zZ$(0fXkZ@Bu! zAh5<$E8AvJQ$SOzn|vtyO%$P5;e%TxvAUiYBTFL`_?T`8-@RaIJ729sTjg9Nmn@=p z)*^34!0_;paofXoO^bLpFEb%GWMNyhIbd>isda65yP@pw-N9`{{P(9hpyd7{Av}fP z<=+u;m`(nK#3$rGGusn$g5P-mT>(m}6OV-V2;xo_3x4~r)BV4;cJ^Y8u)yrEVVq+K z#w_^jD9Fj}p+KplPhv6*RW^9Iz*omK=o;e?7Osn7f8E3+BBBqIruJV-o(Bd7YPSUk z4VB)}+g+O+hFsBech%Uq#a{;mH$7e5WaqK^>4l_8JZ^68#j%DeC`6J`QeJg&aq;u_ zhq~j<1Ukvj(5&b+|B=?X!Ce8$F36-Dcp;6k5Ne8R?)&1R;qn4gsLxkW 7| lC#>R}0_z8nT{_9isj5kf2yw&LF=)AYNxVgVTi;U;mgd|KcA%(^F z@80!;E{0qsB#*siZX7C5Btaf{0o=97dAw0~mxGfN<`9$NX&%r&T&7ZT9(9|x0vq2I zN_4{Ek&!*1>{^UMv-Eo_el}_8Hazx#1DGH&*sjNJZ|Z~MCAK?&0kCgNb61ZjUegvn*01TCol zxVI34fcS27^iI+isDSqCQ?HV8L8|4Dlr)Ave}=4YnQoHq%422d(Ta(St68dmg~*4# zNHQuaG%O2Dj4Qy_V;-nrp@vAX$fvHuEDQg0RoxmdAD>|mv1=^e#Hf^^7oma8Kw=x2 z71q%^&u$FOeS;u)@z=q)g>jS5TG{KEbup8aN4X&1>D&k g-3po2 ze@$fO8xXJr?KnCh9t(iZ2CPHpZT(z-wgNs3r#irl6O2vg>taldSvGNSaUDP|5-PR1 z#>&fk8~PDag tJDF!Tyq&*Yb!4H1=?L9KeGT_OgvW zKxt1EI-#tcr9BtUV bw_2yPA>8qjxgS6BL0tnneg_Ke=uZ`} zg2X +;YzGSy8=)~whOFh?n>Px; zX))F7=m%gC2z`V~xb~t%Efud>C_+xC1li{;TT)U|pxr|sW;uJHFZ1Q^-@jF;=~1^O zAsiE3pr!^t7f#z35f}Gh^m##`o+;4}i_r7OZ)+hj=O&~sWMt2S;_0LU7uI97X4Nz_ zH0BoRFJ9Ctb1*dEhxStt(TYEJg&2U@fk~sSxbG0qh}?!r6ScgDk`7-u;qevKnq9$P zA>ro=2{~5D!bPp`%#g~q%YeSUvPg01QW1=DYP$-ykGeqJELF%UHK!-^67W1^Dw@_9 z9s$tg &~J)& z?wkhLigs20TtXMLDnPTrXAlG5-w6(f>DDA@R=e}kXISvLs?FM};S4 5#b>TCp<`#%)xKR4hgF?A+5R^rVp>-6p zsLK1ruwiOmi-XWym )9TXv{N3Gy3>KIbfRUW4#lZ#u4STRyF<8H%(G*UDZ1`RW znBdVJf~?eNWu!t5=!?*>u>};a3~L}S=SPv>(^lYrG1%ctG zP-vE0U~$OMq>Z#I1?==npWTC}eQ)06X<~LYlBopk-*$Hwd@i%LAXZmbhfNS^B silJ@H+PSZ{w|a459)^FAW#O-L z8E>o^ h~sfLe;MxH>#8V`5>m5}?rmq5?Tu5Ch|DG11Zep;)H|p%v%3>J?FO z@g-=(Xmp#6Kw9D987}~K#YUUWy#oVKjW>sh3oS4eDgoPdiwV$(#v448V5bIQJ&@-& zK+mkC?|S @{s^j7EgK3vyu6 RayBYBjfP>8s}jKbQBx51~!n9wY{61G=*@W z1HBbU?G7&7&(9AE^U4sP5Y}sAk_iuNfJcaEI6 kp%EtM~_}PD4ML*Y9=nDj687dI0rkM|9N#jS0Qu z-?PLZv3`F#2_q+k& `(i{-XHey?k`VINBFt#`?}6+o$Fj{-R73 Yh$?b%a)KQyKj z@&WSxmg}aVEE@@U0IWR6^L`5*!)J(`ufEe@N2Q*8rfVHeU(A15Vg1JtKM`in#O%1;H=xb z9tIi9)|9frn+XD-FGSz3HT^YKT~(F3tupTruqYPm;U5CRQc{;l28y|PEF@T02QNnA zlzl2@6WQI<1A&Ld`5(|~5uzI|%P)KdONJMAzy5GY$;j$Qy4Ck1Xp2wXbY-mv%1qF< znL%r4hHX}fLeP@I3l(t_#3>0!j=142pMHL1Mz$pbBrh~^FRN#X{s|Mqk@q9^aFsw4 zm=bqQM&{0ixIjhVxth_h-*1d{SkCI7)zJwL=G8KEcz~)A2dRLAOEN?-(HK;jJM7j~ zQ;WiG8p7cjZB<}D6x_OupJp Cp+s_>x;d;9OBr(cwf^tSq;55CNZ`AI+>@%}>ECH>1 hh8Axf<~4CR^mg@B@dh zA|}_Fze-+AFWv2do$ERzN1k3@Z6a=0BqSsnqE0a&!3QK)rJ*C|F{v_JIGjis7z9i= zNZ;m(uq_hs0Sd3Me;j9-j^A!6Jt!!We#<5ZgR5DIsIcym!7%3(Dv>Ir4 a!=M$#6p7vauZKng=rx<1=s;i5{Mh4e9Pp@AL}R PoQ`WW`gE$^y?uS+1y`t4KID0b7#X)D89WJ5 zkb&) x3^ho{<$EoO zkR4iZg+8&%o*03kcj)^0^CM4aMKpFi5#22 1hB$z>DJ zvg&7F4qw>#z9e3fy4s#~t~XB~`pgpGIDG?yqFc`}KkkB%1{2`h_vE#+3-E1?x#ku? zJH)T=G-s+iRvHa=|A`YPY9tYdcS2wP<;g|y8>2f?{l(nePQj1zIx8zQbxSiniM2Ae z$-qoT2H{{9ll3q0Yx1`_8$C;-l}@tK)2a7mj1hHyaNY1T}+se3_HRR(=+(v2}a z7<|Xu-eBI2$xr{V5%f^?A6~q9v(N6pFpl}q9^t5|r2_!eP@c15BM1JzxkYW=b$_UO zO-)VjSFn3fHzkdktbZ(kFu~ruWaYDG6Y~u=!(;_iO9bccE=URL{Q2`9WaO!bH>aP| zOwxC6`Id3z>pB45ylbjNvx3&$_d@~S0d8($TZFajv7=LjpWmjdmX>859hH?dXg9jk zht--eqUuJVN?m<@z+e_Sn@53xM*VI$nHa!Y@=ly64&gm`unS^2f-+)zD(L|c;&pS? zhdsJba$C|Zm^W_R7`lp*q6PYa2M->UwrK>x9oG$DMeoOE2@EY0$S-Qy#)$nw(hTf& zK=W49u7}%f_&1@tx;iwKrZ!xh!y)1$H*DzeW_u&v0yrto4UB+_nwkglA)s+rNZWEQ zCN55Bby|IO+0 eE#)G7Juu&G{N8xACBeR7^@i;1>(-i%~eX{JC2UU2ern!e}9(| zAUyr 9e;+N zp}8q(Xi$q3IC5k30TzqkB0BS;%1WQPEZj)Sy9Y3=W&gYoy8&V_8M{ZTmO}V(ug{oW zgcTNzl&oY1xAQJBtl6?%1(j$v5i})lW^Nt{mZYw+(Gx7@!ra&vB_$=~A?QT%$&?ZY zw#f^e@Jg0$&NZ*GKwtyrcUxXMa#8irQ1b;$0Fr|jfl`SjDBR^_SnEe^$!$zbWm?Zu zUqzU{6g)05^i+ov#x>{!9T%n#puY poIla!p?H9XZP zJPJ3D0ph1*7PqhpQ)56}h~7WS_JMt0mCgp@N&Whjp{*c3{y{1Y^N@+ZKa8xflXpW= z3L6&>T;(NsbZ8O}5UmW3!1W`5Mcbh{xvQlP-$yfCWTLnTm ovq#&n zzzMib-0tn=%a^Uy#g!EmyCrtn@apAlgGkzNR_V+cIRyo3;B#UN0}-?nr6+F}Z*#oX zT`&r;31-VZ3kzcr;!;&t_adSb;+gRClSTKImYz-$43@^+{`Tl}G^SlS?|Abj&2teh z-!Y|g&S8O^P;_tEB_WLt3vqYE@S!(<69`y5qzqG-!m27?@iUP5q6B+`$Wv!_;pgYS zoti3yyQ6;0SB|wxJ?bDAm%`71r+E6f3!ma;Dubx{0XgVh++W7URkiA!JLeAE?RzYr zw k&IgCT&fJvR`$@5HveIg}wG!o+DOE>GG z=KI*KpN9j1#Q7{yz6Ln2c79&*a3@1sQ(bK;exWHlu6_H~p+o;>%0$AJ1V9Vm4u;zG zYrwZoJQa^=F44-gaNWK@_4EMO%e+V#ZsVL&fBJfR*PvLu8X5ahkocH?7N6}n(Y^Ni z_3LY_5OH)H+oTwBsn`nbZ~4v&H*9UY<73aOsqL-5N_7rl8iqJ_MY!951=e9hnAZH% zPtVQH7I>RM1#eDC$!hGR#sa0&&>5*QLhx{p1WQ_3S;=H(CMDg*GlTkAe6B=QMddc4 zUYaz|=$n{$p~p9g2t0mw-TICCSV4`y7l9U{a}2}xY}qaO f*wX) bhnWs|+u0`@zORyRoa1d_YMZuXIOnmq59 gJy(DI2- z$zV~~9O|<8IS?kYID>{WDKj&1LQ7R!+Xv9B2k0ovwvUFy&pZ@B#Rp1ZH%{|N+RVkx z?GvQ=NSHU?`wa@+dL6@JZ8 H zhh)A4)mJKaeo+m3NaEPBFwq+YOOqQ+kM2eU4sGZL4ne^H4Qu)Ez2hl{$SA1JI}gcg z5zwoLq%$fPHSxlrgbaw` @@qN2?)Hc{ zMJsi6b^By`8+0yDQ=uc!jhrz44C#?CdRKkm*y? Cxa0y=|~o zj6zGMemu7#8dbSOy$_|58RhZjZMpn7RI4crVP)~?=S|7X9N6eXwkhsz9{nb80(R@< z2uL|&NTXI0KrehNwi4c9i#2QV)ikotnwOBJ@=eP5cOg&nX?^`YsXl5bG8RwnjnzkQ z04L%NMxY042i9riiFBEQv^-YX{uhuvw2DWIDQ55}Scz9k!N1k!Dfdjfzs72;?z4b* zI*(EqW=F@_VV2Y%&BYhO;(@|tF3AJqgv+v_ %5UshjF4@!gacMo7u zVWBkQ*ScXeCN7i#Wq)p+)P0;2Zhz<8fD?lpMULZ2^{-v?2mI{>D$~rk8m1sbH}utB z1hPVu?Eab7Kb-HQT_|q-3di6Ph)ArWGpFF{#({Zcr(Eiu(w0Jwgy}6{?@%`?nR{kj zw`CP}5=Yd)kwtJvd%%9_U%Y-@Sg844S|y$6Vd$ET`};cs3)M9{%iG(-?Q*g2C(m47 z0%wxv`bXO=7nI7$3(sdqzoYo;>D5Ru$9pSkmF?%ELpTG-YrUT2I}y^xo*8F3_w9?s z+@!9#SsrA<%b1w^*o9=lqn*2hxo=|O29 $caH>uI!M36>zWeFJ_x+)#u+1147*NXd&*2B{z>74< z&RWdm0oZQCvNTSkvq~Bhr-b}BTlw@hT3A@rw0cI?)zuY3Lr?NaFvy*3dn2ig5KH&t&`MgX#jbvk#}@ZcZ~#v?6l zZN=^FJD_PKASpO_>zIWoo5ya_22p8NEopAa!+}WecXmxrYPF0?$_QV&*%z74F|GMz ztB$-+I~LCa`-G1KI9F9@tFKL3&EdQ|<+5LTx`@!J5!84`{~HtCvf%T3fbOzvde(uU zz5@)f2h9Z9BGSc-1J3FhF)=gufOIE^+tfFG>=JA$!HW1qcR5MTF}jzNE|*kQi(BP2 z^JOFl=D=B0OJ3Of`J%=%5bk1kn~J&J0Bvc$?}HZ+YS_%uigHE0@7s9Xz^Rse|NfzA zDxm|V1*_2G`SYDnyVkPw_9{Vqi@1_vbnv0gj~A}%GWDf)D&eEUQzKW6ntR0e-9@%g zclfc!Z8bteN8(? Ohu!#K)H&zm?wy%CH;rSVTe8i@DV0%aCc+BOHfB^QU7VNQ~?a<=xhTH)7^O$ z!)QNDkG}A>I1U *`^Yl()OoLs`d>cEf?Zvttje?h3X`sdKl z31~vZh1R}Prf650STD9ZXp47zxapmwj8C<-%rTTYhKB4Y{FhVc*;JfHl2cO|TABcm zHMO){wmu@r44Rs{iM^j)E^zYj_zInpcrkbUgep1^tT8C2BWq?^Q09<+gYX$63`?P5 zUZbq6yjPp1zqi)|d}~o_tCqfXG!|o3W#!2nD{5OXs5Q>EsN85|;^|I8X=haPl0 |p#=T1NHs=h963e=A`t_6HHh9ao<>RDyw%dUYl zKfiDwA@ueXr4%ytX!P&jtNjcMPKKrt5L$#*#Qayq)Af6-WK&^W)%y{-L8(FeNAewd zJk-39Elt4~ZI+;ZNiB`9o}Qit0K9n3G_P@A{;GI*jgl!f<6AAT V_cyMA`;X3X$F0-FgL>rxZw(bS>^9SX5rm zpX>TbC~w}pIlS2$)MO`ikj%I3RZbllmfH~%S2Lvj67krUmLs(^ny_RNe-;5DptT!< zo9Jb-&QbV2(%yG9Og0EVcIs(@4Q8}86v5Cm7GCSVXAM|cE$K}Egk};7%6sY0_m=9i zmW#f!g~wjAr|L-4>Lx;O|5T8C8yB}hLqkJMOpL!^)Bd2}{gQ@#`3zo6!i@W!96^>> zuS %0J!WmJt>4iVc57=>Co%Yo22U+@l~57aFRK35oU7qVC^BF2U2b8S8^AY0QiG7O z2)-p$_8K)?_iKu+Z0R4%%e}-cZvqh_ZY-~KQB^~u0z`Opz@89=Q43y!D<8A{%o%tt zZPsA_-iCoL6|q5_IdkSw$lIzkUW3mc84sVA*@h)%a$qj;;GR9wW)w;%PORpTdmyc% zLfg>TD9#{|y6dSd!VW&R9@@X(8 7M| z8F*Y9#4F-FpxQ0z{4Vg1PaG~CELtI8D5)te-@X}r3gAT3)7kzW7L`yzW174VAEXvt zjPl-NUKuKMTjcuBy4pJ*vGc>LsFV~H#X`1F`VC2-tzR`eJ4<|C(=#)itV0kYm*C|D zpqqE#@)Y0rUZ+)k@#VPyTe28|NjrQ#RQ~ VXJp+73>n-ud7L;9Zs+D6HQkLy{r+ktw9ey`lif>KUFIjFTNdy8_;kk$hQm39 z4GS|M8Ct?x+ r_U+pgk8R7r#BiWrgdjBQ*4!av zRVF!%X!n<}(QV jy~#YV&T(x8@8G7O3AW=u^xfC-BQtAo)%h z&?v-gz#&9d#q?0~7NoAxww C}46%`j#;>a>&-bbwW^b8E((|Gz`b4J;$?7qv`t@e_f+OXzRxv*vsl& zTb6_UFL+U0X)8#FJ#aD})J~&q+b=1(3y3?e fr-d FhR z5}Lxjzt|ergC`^OS*NKh=B4~b CEW!Jp{WY zv^ef`x WlQbUm@9QhlKYjV~n5EFryhV%5tlK}_Vg_`i{Duw9J>7K#ogXk$z=D9MhllmLBno{Ta!BaP z(>%2ZsO2MIRBk;Bnw&}8@%)SNFaskaBaS9<$Ep5Jss1m+68$D}7Dm4x)yZ|(1k^zJ z4LTqJ=ik;`JUp*a-DE1nZ_V z^a0yV2i5YGQpTIT$=Be61eqTrSf!S8sYBZV@RV?Ar~I zxHLEm(-)Wr8eqn6=vM&QXuqkYnHkqup@Ylyp7Kp5L88pe%+sUaFZL#b!CPtn_E@(% z7=3u361?n(^ky71ox>c8w9&6#m~PmxAuB6OA3=q`LBAwpBt$|_A)UcSU+p^I$6Pal z+5So*2Z-8gmt>! (H1p8g_;}@?Ef^@FN$!`BY%0l# zj^-Rt-G|`-LQ#V3Hl9nedW3)u8gPv ZUVLssVY!V)8pP_W#KbL_Hb^-CR^hJDWzMoHb+4q@%EOW%3~|lfROtTRkU+q~ z+of+oe$;Lp#CB>>*il; ~S_dEz&R-9fc5?s-X`irl#KHc_QQq z;}%p_Sx~>--Q9zEh2Fx53Sq~w6J?3yY0k;Rku5p)JgGih6!DtLlo(dtMssOefN@n3 zLajiBZnvujtW|h&DK5V=us~TugBgkinUE}(SQI((2w_&NZ5;z*pmh78nHYysDRyHt zyv_mBHaIY8ET!N>RagXNHjjrNK7t8E?}g(@nDtkOrNuAq{D&>L_XN9HPzh7huT^c! zfH^(Yy5oJ5oX3Ij;cq|xVaF*rC2`xcoaa*-c*6f6rlMLB=W98}!Jsa-phj)mwoUCX zHP6_g4VsOn+G%F)AV7#$onf0MAU$Ef2Snqf`>wmaw;fLL3KHA-PKQ0sdEU0ArKRxW z$DgSM;u?kqHZa`Y*V>$q!UBv_h-P>X-(M%1PbelxC)@@e-)PhV^EnaMiku=4#n;4l zZzhvNob7t^LpP^?7Fqm~KjC^wvGVh0zvioZQCu`HUNpiif#lvmOu}8CvR%t=>9@_} z1*)}c8|xpdDV{#<2`rUJV?w|vNOW*v{9v9*&)?Fr4fIbLdanuS(%5ja0)bU0V>Z+| zI;uAmbjr90v<@9JbH%W!nb|#zv`N$`$T@3kYiV$Y*lbjxp`o1+j+7FJ-usCGY@)aL zu4an_z=N{qpEdRLNCMI0OpA{L;#^$!>_k?pgnjc*k1PUjFGRJqa7 G3rh+S=SUvTwjDLFs7cIwWM%eG*OA3H@)-7$)DGF|XNiP`d8~-kz}?MX4^uSjoiA z=9t^^4#4Bi_(66)K7YU~BXG?kZJ4qen3`(vRP2kv=$(U?_d%-QBj5u(c78|-ko!Y& zra<}0 1alDS>k+yGW3%Bdy&;n|?W0OsK5HT9omW{M`Z%0+@K7KfZb%#;g?#x79( zq@YGfxjh8xyFS{^0v@t=W*OGuQj7`Amm-4X< &fdSOCnRg}E)02uNts%o zK7M=^UXND@vxY}!r+(he85(3ofr^sG$ $b9~9tICx zGyCzQ!u {!0O9qstwi^E)7GE#BFR^E51w=p*kSs50#hQ#Cb zP~1eawSN)nXSxOP`Iz#Xx=e2)-@X&tFV*;{b}CZVcYLBp-b4x&nIwUe> %boU`Y9WQGsD$<}!31vzx#o{7i=W2sJO>3{@yG9wKxL`tE&`<%@0?FN* zK$q-3a$~feE{^fT9w1l3=5*w{@JwiKns+&{34kF~$n@dIu@M|_#T^|H&9+urB{x_Y zG#X{DK)nqXO*df-FX$jgwEostX7`|=DynAUih-7kwBJw;R_Eiq92^_tQ?1K;`BFMk zhGx^egw)MxYJh`u2S2oTCB%=yW5iG&3aWzkfMG%qA8cMfpCz5e%NXxy&G(|VFt%;o zwd*115TEohY_+9@p~SrW>~12vgR|bRp*&CoLx!Z3lwy?ZC_z2YGH4;nc`o_Jx8*sd zD%XsMp*n(S-IxBX4Xi&}W^`6wsXq|f`sm4%y AsRyQ3?mOWZCg63)Q@ z%^2o*0(QC3RvTf&fb?j$8D4Gm=s4sM#O#a{_VDr&yz~}jSPX6B&YkC?aqY_CVsrpR z0Sl|IAj}Avi0h9YJ&M!&l>P9+Wu%6J0B1ob5+&(?c=UbBp?m6W<47;+=U0Yth0|OC zP7S|Ewn+1j1M?^op~!8Wa@AIY;LF<9wis5q=p%>NJb?BUPJ?3+GnH5s^mY7eeSK6) z0l4e_k^(e rf+?+nrVZ`hQ8Nt!Wwgs1c`?ej`=xWjyYX99*UY?$7 zm!Z|WZxtLN;Y1`Dn7=j+?JdSMihLjd)#cG2U5J4}7Y(ehF)V-|LxOlv`uXo_NcY8; zqs6;_jAuW}n}dS`-XB4`0{W>pZDO*BKPxgl7cGpKp`!2KcVYv3B_tfgN^!^kl6N!! zypj~@yxsL)A%e`C-TMtLR5c`pJ2RGI&-d7NZI;f Rs%-DEzwpH84hD&;t)?hu&_nR! ztwm6BmKp_xg!JIN;ujT_hyR=L;X~Rx+y?JRU!oIOa66i{L~F?O-9b=Avvz7K(D@%Qh~Z)i PAb9+v*(@ z#Dwy8sEM}-;sS)vQ=_dchhEgFPmBIdvaSXa0i613FnkU&r=0L|jzA& }5DijKP*K(9X4rDs!&*R4@D9`)P+_h? zi-hggg(Hk~l>n_gfkLZT?*Ne&fvJKfe-oG)UvLG4b3@@Hxk=c0HlT{`hpeLgL~^Jh z5P}G+QOFdT93+DpJBz>tc6OO~^RvaI5y#zydDHBBjtJHVdDjY5Zftlh8c68=^zf5N z1%e&r4)88cZW8kYxoa0Ns_N0E@?y{|zybK)8B-~+TZkx^MSLoxDYX{y&|TW9n^)J= zcx7Z9{=D>91)H1 C$c(JR8TTUfT9MSXmu{qLNH2@F0Fd|Kq&>DD zT8Hi~&AgEYb& UF63K1!^`A+IreA^fLGc584RC<<~0H=cCf?z>8z>st9-N^_el` zqj;m jg&J~&QL_V@QwQ&a2rmv$q? zh!J(nD?3{pImjHI9RGZ@rrCOfoi8~(R(@gQt5+X@XfXI$_lmtQo08kqj>M#+3xf72 zG5VkHh{2)%pI_D)H=n$J1nM#jDKENh#`vGc<|0kzv(chkOiPe^WY769!M|O zK92q5q94037RIh-EMzBtA-^~^-R{2s@;5d?GNt~X|HgLm4LN)N{Yo~EkHY^v*MGkt zOzz){Uikv{um9Z3f4}@PE2c9mpFsX|Pnpnp|M|g>ul(0v;s3o4T}Gw=k(;)_&kg(L z9=D+@!(0#65LY>c1tl@G-WFN$ofke`v5@PZUUBv&?OU;VYBG=)(o)2)Czf1_rvLwK z2($P GV)@T!$ZXGXd5Aiu0xc|MWrSDw~MhLvY`Z{Nz3q&>azT=zWv@BNLPL-x8F z2=nSNO*+1EL-ITLH}*M7jJmAb{=a|s(q4Q(C qSd0^ucctLpckiFg5Vt)JB zoNey+ {?O^zW+Yx~H@mR|ImNS2k98L;=Kl!knb( { + //session => This isn’t a typical Better Auth session - instead, it returns the access token record along with the scopes and user ID. + return createMcpHandler( + (server) => { + server.tool( + "echo", + "Echo a message", + { message: z.string() }, + async ({ message }) => { + return { + content: [{ type: "text", text: `Tool echo: ${message}` }], + }; + }, + ); + }, + { + capabilities: { + tools: { + echo: { + description: "Echo a message", + }, + }, + }, + }, + { + redisUrl: process.env.REDIS_URL, + basePath: "/api", + verboseLogs: true, + maxDuration: 60, + }, + )(req); +}); + +export { handler as GET, handler as POST, handler as DELETE }; +``` + +And that's it!! \ No newline at end of file diff --git a/examples/nextjs-mcp/app/.well-known/oauth-authorization-server/route.ts b/examples/nextjs-mcp/app/.well-known/oauth-authorization-server/route.ts new file mode 100644 index 00000000..c236fffd --- /dev/null +++ b/examples/nextjs-mcp/app/.well-known/oauth-authorization-server/route.ts @@ -0,0 +1,4 @@ +import { oAuthDiscoveryMetadata } from "better-auth/plugins"; +import { auth } from "../../../lib/auth"; + +export const GET = oAuthDiscoveryMetadata(auth); diff --git a/examples/nextjs-mcp/app/api/[transport]/route.ts b/examples/nextjs-mcp/app/api/[transport]/route.ts new file mode 100644 index 00000000..a8b9b5b6 --- /dev/null +++ b/examples/nextjs-mcp/app/api/[transport]/route.ts @@ -0,0 +1,38 @@ +import { auth } from "@/lib/auth"; +import { createMcpHandler } from "@vercel/mcp-adapter"; +import { withMcpAuth } from "better-auth/plugins"; +import { z } from "zod"; + +const handler = withMcpAuth(auth, (req, sesssion) => { + return createMcpHandler( + (server) => { + server.tool( + "echo", + "Echo a message", + { message: z.string() }, + async ({ message }) => { + return { + content: [{ type: "text", text: `Tool echo: ${message}` }], + }; + }, + ); + }, + { + capabilities: { + tools: { + echo: { + description: "Echo a message", + }, + }, + }, + }, + { + redisUrl: process.env.REDIS_URL, + basePath: "/api", + verboseLogs: true, + maxDuration: 60, + }, + )(req); +}); + +export { handler as GET, handler as POST, handler as DELETE }; diff --git a/examples/nextjs-mcp/app/api/auth/[...all]/route.ts b/examples/nextjs-mcp/app/api/auth/[...all]/route.ts new file mode 100644 index 00000000..5b67b064 --- /dev/null +++ b/examples/nextjs-mcp/app/api/auth/[...all]/route.ts @@ -0,0 +1,4 @@ +import { auth } from "@/lib/auth"; +import { toNextJsHandler } from "better-auth/next-js"; + +export const { GET, POST } = toNextJsHandler(auth); diff --git a/examples/nextjs-mcp/app/favicon.ico b/examples/nextjs-mcp/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 zcmeHv30#a{`}aL_*G&7qml|y<+KVaDM2m#dVr!KsA!#An?kSQM(q<_dDNCpjEux83 zLb9Z^XxbDl(w>%i@8hT6>)&Gu{h#Oeyszu?xtw#Zb1mO{pgX9699l+Qppw7jXaYf~-84xW z)w4x8?=youko|}Vr~(D$UX IbiXABHh`p1?nn8Po~fxRJv}|0e(BPs|G`(TT%kKVJAdg5*Z|x0leQq0 zkdUBvb#>9F()jo|T~kx@OM8$9wzs~t2l;K=woNssA3l6|sx2r3+kdfVW@e^8e*E}v zA1y5{bRi+3Z`uD3{F7LgFJDdvm;nJilkzDku>BwXH(8ItVCXk*-lSJnR?-2UN% hJ){&rlvg`CDTj z)Bzo!3v7Ou#83zEDEFcKt(f1E0~=rqeEbTnMvWR#{+9pg%7G8y>u1OVRUSoox-ovF z2Ydma(;=YuBY(eI|04{hXzZD6_f(v~H;C~y5=DhAC{MMS>2fm~1H_t2$56pc$NH8( z5bH|<)71dV-_oCHIrzrT`2s-5w_+2CM0$95I6X8p^r!gHp+j_gd;9O<1~CEQQGS8) zS9Qh3#p&JM-G8rHekNmKVewU;pJRcTAog68KYo^dRo}(M>36U4Us zfgYWSiHZL3;lpWT= zNAW>Dh#mB!_@Lg%$ms8N-;aPqMn+C2HqZgz&9~Eu z4|Kp<`$q)Uw1R?y(~S>ePdonHxpV1#eSP1B;Ogo+-Pk}6#0GsZZ5!||ev2MGdh}_m z{DeR7?0-1^zVs&`AV6Vt;r3`I`OI_wgs*w=eO%_#7Kepl{B @xiyCANc(l zzIyd4y|c6PXWq9-|KM8(zIk8LPk(>a)zyFWjhT!$HJ$qX1vo@d25W< fvZQ2zUz5WRc(UnFMKHwe1| zWmlB1qdbiA(C0jmnV<}GfbKtmcu^2*P^O? MBLZKt|As~ge8&AAO~2K@zbXelK|4T<{|y4`raF{=72kC2Kn(L4YyenWgrPiv z@^mr$t{#X5VuIMeL!7Ab6_kG$&5p*Z{+?5U|TZ`B!7llpVmp@skYz&n^8QfPJzL z0G6K_OJM9x+Wu2gfN45phANGt{7=C>i34CV{Xqlx(fWpeAoj^N0Biu`w+MVcCUyU* zDZuzO0>4Z6fbu^T_arWW5n!E45vX8N=bxTVeFoep_G#VmNlQzAI_KTIc{6>c+04vr zx@W}zE5JNSU>!THJ{J=cqjz+4{L4A{Ob9$ZJ*S1?Ggg3klFp!+Y1@K+pK1DqI|_gq z5ZDXVpge8-cs!o|;K73#YXZ3AShj50wBvuq3NTOZ`M&qtjj#GOFfgExjg8Gn8>Vq5 z`85n+9|!iLCZF5$HJ$Iu($dm?8~-ofu}tEc+-pyke=3!im#6pk_Wo8IA|fJwD&~~F zc16osQ)EBo58U7XDuMexaPRjU@h8tXe%S{fA0NH3vGJFhuyyO!Uyl2^&EOpX{9As0 zWj+P>{@}jxH)8|r;2HdupP!vie{sJ28b&bo!8`D^x}TE$%zXNb^X1p@0PJ86`dZyj z%ce7*{^oo+6%&~I!8hQy-vQ7E)0t0ybH4l%KltWOo~8cO`T=157JqL(oq_rC%ea&4 z2NcTJe-HgFjNg-gZ$6!Y`SMHrlj}Etf7 ?r!zQTPPSv}{so2e>Fjs1{ gzk~LGeesX%r(Lh6rbhSo_n)@@G-FTQy93;l#E)hgP@d_SGvyCp0~o(Y;Ee8{ zdVUDbHm5`2taPUOY^MAGOw* >=s7=Gst=D+p+2yON!0%Hk` zz5mAhyT4lS*T3LS^WSxUy86q&GnoHxzQ6vm8)VS}_zuqG?+3td68_x;etQAdu@sc6 zQJ&5|4(I?~3d-QOAODHpZ=hlSg(lBZ!JZWCtHHSj`0Wh93-Uk)_S%zsJ~aD>{`A0~ z9{AG(e|q3g5B%wYKRxiL2Y$8(4w 6bzchKuloQW#e&S3n+P- z8!ds-%f;TJ1>)v)##>gd{PdS2Oc3VaR`fr=`O8QIO(6(N!A?pr5C#6fc~Ge@N%Vvu zaoAX2&(a6eWy_q&UwOhU)|P3J0Qc%OdhzW=F4D|pt0E4osw;%<%Dn58hAWD^XnZD= z>9~H(3bmLtxpF?a7su6J7M*x1By7YSUbxGi)Ot0P77`}P 3{)&5Un{KD?`-e?r21!4vTTnN(4Y6Lin?UkSM z`MXCTC1@4A4~mvz%Rh2&EwY))LeoT=*`tMoqcEXI>TZU9WTP#l?uFv+@Dn~b(>xh2 z;>B?;Tz2SR&KVb>vGiBSB`@U7VIWFSo=LDSb9F{GF^DbmWAfpms8Sx9OX4CnBJca3 zlj9(x!dIjN?OG1X4l*imJNvRCk}F%!?SOfiOq5y^mZW)jFL@ a|r-@d#f7 z2gmU8L3IZq0ynIws=}~m^#@&C%J6QFo~Mo4V`>v7MI-_!EBMMtb%_M&kvAaN)@ZVw z+`toz&WG#HkWDjnZE!6nk{e-oFdL^$YnbOCN}JC&{$#$O27@|Tn-skXr)2ml2~O!5 zX+gYoxhoc7qoU?C^3~&!U?kRFtnSEecWuH0B0OvLodgUAi}8p1 zrO6RSXHH}DMc$&|?D004 DiOVMHV8kXCP@7NKB zgaZq^^O<7PoKEp72kby@W0Z!Y*A y{&vfg#C&gG@YVR9g?FEocMUi1gSN$+V+ayF45{a zuDZDTN}mS|;BO%gEf}pjBfN2-gIrU#G5~cucA;dokXW89%>AyXJJI z9X4Ul IWA|ZYHgbI z5?oFk@A=Ik7lrEQPDH!H+b`7_Y~aDb_qa=B2^Y&Ow41cU=4WDd40dp5(QS-WMN-=Y z9g;6_-JdNU;|6cPwf$ak*aJIcwL@1n$#l~zi{c{EW?T;DaW*E8DYq?Umtz{nJ&w-M zEMyT DrC&9K$d|kZe2#ws6)L=7K+{ zQw{XnV6UC$6-rW0emqm8wJoeZK)wJIcV?dST}Z;G0Arq{dVDu0&4kd%N!3F1*;*pW zR&qUiFzK=@44#QGw7k1`3t_d8&*kBV->O##t|tonFc2YWrL7_eqg+=+k;!F-`^b8> z#KWCE8%u4k@EprxqiV$VmmtiWxDLgnGu$Vs<8rppV5E ajBXL4nyyZM$SWVm!wnCj-B!Wjqj5-5dNXukI2$$|Bu3Lrw}z65Lc=1G z^-#WuQOj$hwNGG?*CM_TO8Bg-1+qc>J7k5c51U8g?ZU5n?HYor;~JIjoWH-G>AoUP ztrWWLbRNqIjW#RT*WqZgPJXU7C)VaW5}MiijYbABmzoru6EmQ*N8cVK7a3|aOB#O& zBl8JY2WKfmj;h#Q!pN%9o@VNLv{OUL?rixHwOZuvX7{IJ{(EdPpuVFoQqIOa7gi