@exposify/next 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,7 +6,7 @@ your own app.
6
6
 
7
7
  ```sh
8
8
  npm install @exposify/next
9
- npx exposify-next setup # pick the endpoints, get the route file to create
9
+ npx @exposify/next setup # pick the endpoints, get the route file to create
10
10
  ```
11
11
 
12
12
  Or by hand, in two files:
@@ -32,7 +32,7 @@ export { handler as GET, handler as POST, handler as DELETE };
32
32
  build` starts, and keeps it current in dev as route files change. It holds
33
33
  the route table, your Server Actions, your docs (`README.md`, `docs/**/*.md`) for `search_docs`,
34
34
  and `exposify.descriptions.json`'s texts, so nothing is read from disk at
35
- runtime. Commit it or ignore it; `npx exposify-next generate` writes it too.
35
+ runtime. Commit it or ignore it; `npx @exposify/next generate` writes it too.
36
36
 
37
37
  ## Which routes become tools
38
38
 
@@ -79,6 +79,22 @@ it. The MCP client's address and host are forwarded as
79
79
  - **Vercel Deployment Protection** blocks those requests on protected
80
80
  previews: set `VERCEL_AUTOMATION_BYPASS_SECRET` (Vercel's "Protection
81
81
  Bypass for Automation") and it's sent as `x-vercel-protection-bypass`.
82
+ Without it, a tool call fails with an error that says so. An MCP client
83
+ outside Vercel reaches a protected preview with
84
+ `?x-vercel-protection-bypass=<secret>` on the MCP URL.
85
+ - **The agent's address:** some hosts (Vercel) replace `x-forwarded-for` on
86
+ these requests with their own. Where a route rate-limits or logs by IP,
87
+ use `agentIp()`: the agent's address on a tool call (signed with
88
+ `EXPOSIFY_KEY`, so it can't be forged), the usual `x-forwarded-for`
89
+ otherwise.
90
+
91
+ ```ts
92
+ import { agentIp } from "@exposify/next";
93
+
94
+ export async function POST(req: Request) {
95
+ const ip = await agentIp(req);
96
+ }
97
+ ```
82
98
  - `basePath` and `trailingSlash` from `next.config` are applied.
83
99
  - `rateLimit` / `rateWindowMs`: MCP requests per client address per window
84
100
  (default 60 per 60 s), in memory and per instance.
@@ -136,6 +152,42 @@ was found. Name another one with `aiReady(exposify, { login: "signIn" })`
136
152
  (an action's export name) or `{ login: { path: "/api/login", fields:
137
153
  ["email", "password"] } }`, or turn it off with `login: false`.
138
154
 
155
+ **Logins in two steps.** If your login asks for a code next (two-factor:
156
+ it sets a pending cookie and redirects to a code page, or answers
157
+ `{ mfaRequired: true }`), the form asks for the code too and runs your second
158
+ step (a Server Action or route named like `verify`, `2fa` or `otp` that takes
159
+ one code and sets the session) with the cookies the first step set. The
160
+ password isn't kept between the two forms. Passwordless logins work the same
161
+ way: an email or phone number, then a code, with "Send a new code". Name the
162
+ second step with `loginVerify: "verifyCode"` (or `{ path, fields }`), or turn
163
+ it off with `loginVerify: false`. Magic links aren't supported; an email that
164
+ also carries a code is.
165
+
166
+ **Supabase Auth (`@supabase/ssr`).** The generator looks for Supabase, not
167
+ for a login form: it finds your `signInWithPassword`, `signInWithOtp`,
168
+ `signInWithOAuth` and `<Auth />` calls and whether each runs in the browser
169
+ or on your server.
170
+
171
+ - Signed in from the browser (as in Supabase's own starter), or nowhere it
172
+ could find: AI assistants sign in to your Supabase project directly. A
173
+ password goes on the form at `/mcp/login`, from your app's server straight
174
+ to Supabase; social logins and email codes go through Supabase on
175
+ Exposify's page; a user with an authenticator app enters its code too.
176
+ - Signed in from a Server Action or route handler: that's your login, and
177
+ the form runs it, since it may check more than Supabase does. Social
178
+ logins still go to Supabase directly.
179
+
180
+ Either way, tool calls carry the `sb-<ref>-auth-token` cookie your server
181
+ reads, and Exposify refreshes the session before supabase-js would. The
182
+ route reads `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY`
183
+ (or `_ANON_KEY`); pass `aiReady(exposify, { supabase: { url, key } })` to
184
+ name them, or `supabase: false` to turn it off. The dashboard shows what was
185
+ found and has the switch for passwords. `inspect` says which way it went and
186
+ why; `npx @exposify/next test-sign-in` signs in on your running app and checks
187
+ a protected page accepts the cookie. `@supabase/auth-helpers-nextjs` (an
188
+ older cookie format) isn't supported, and apps on `@supabase/supabase-js`
189
+ alone keep their session in the browser, where the server never sees it.
190
+
139
191
  **No login found, and no sign-in method in the dashboard?** Then agents use
140
192
  your app signed out: `/mcp` doesn't ask them to sign in, and your own checks
141
193
  decide what they can do. The startup log says which it is
@@ -286,8 +338,8 @@ errors; the thrown message stays in your server log.
286
338
 
287
339
  ## CLI
288
340
 
289
- `npx exposify-next setup | inspect | describe | generate`. The first three
290
- are the same as `exposify-express`'s; the scan reads your route files
341
+ `npx @exposify/next setup | inspect | describe | generate`. The first three
342
+ are the same as `@exposify/express`'s; the scan reads your route files
291
343
  instead of booting the app.
292
344
 
293
345
  Server Actions get their own rows: