Install
$ agentstack add mcp-kinde-starter-kits-jev-agent-authorization ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Jev Gatehouse: Jev agent authorization with Kinde
A starter kit that checks every AI agent tool call before it runs. Kinde checks who the agent acts for and what that user can do. Jev, the System One model from TypeSafe AI, judges each call in about 200 ms. Policy code in the app decides: allow the call, ask the user to approve it, or stop it.
Live demo: jev-gatehouse.vercel.app
[](https://makeapullrequest.com) [](https://kinde.com/docs/developer-tools) [](https://thekindecommunity.slack.com) [](https://askjev.ai) [](https://docs.convex.dev)
What it does
An agent calls a small workspace API through a Kinde MCP connection. The Kinde MCP server passes the user's access token to the API. The guard in front of the API runs these steps for each call:
- Verifies the Kinde access token (issuer, audience, signature).
- Reads the user's permissions and feature flags for the organization, from the token or from the Kinde Management API.
- Denies the call if a Kinde permission or flag is missing. Allows reads that have the permission.
- Sends every other call to Jev with the user's request, the tool call and the documents the agent read. Jev returns calibrated signals: does the call match the request, is it destructive, does it follow planted text, does it send data out, and how risky is it.
- Decides in code from the signals. A low-confidence call goes to an LLM judge.
- Writes the decision to a ledger before anything runs. If the write fails, the call does not run.
A call that needs approval is held for 10 minutes. The user approves it on a page that requires a fresh sign-in. The held call then runs once.
The app has five pages:
- Ledger (
/): a live stream of every decision, with the Kinde checks, the Jev signals and the cost. - Agent console (
/console): an in-app agent that uses the same Kinde MCP path as any external client. - Attack playground (
/playground): scripted attacks, such as planted instructions in a document, that go through the real guard. - Benchmark (
/benchmark): 300 labeled tool calls, three ways to decide, and the results. - Connect (
/connect): the Kinde MCP URL, the Cursor setup, and the tools that Kinde builds from the OpenAPI spec.
Who it is for and when to use it
Use this kit if your product has an API and your users want to reach it from an AI agent. For example:
- A SaaS team that ships an MCP server so customers can use the product from Cursor, Claude or their own agents.
- A platform team that puts internal APIs behind an agent.
- A developer who connects an agent to production data and wants a check before each write.
Add the guard when an agent can do something that is hard to undo: delete data, move money, change who has access, or send data out. Permissions alone do not stop these calls. An agent can follow text that it read in a document, and the user's permissions still allow the call. The guard asks a second question on every call: did the user ask for this?
Reads that the user has permission for skip Jev, so they add no cost.
How it works
flowchart LR
subgraph Clients
A1[Cursor or another MCP client]
A2[In-app agent]
end
subgraph Kinde
K1[Secure MCP connection]
K2[Sign-in, organizations, permissions, feature flags]
end
subgraph Convex
G[Guard]
L[(Decision ledger)]
W[Workspace API]
end
J[Jev on OpenRouter]
U[Approval page with fresh Kinde sign-in]
A1 --> K1
A2 --> K1
K1 -- "user token + X-Kinde-User-Id" --> G
G -- "verify token, read access" --> K2
G -- "state: call, request, what the agent read" --> J
J -- "typed signals" --> G
G --> L
G -- "allow" --> W
G -- "step-up: held call" --> U
U -- "approved once" --> W
For each call, the guard makes one decision:
sequenceDiagram
participant Agent
participant Kinde as Kinde MCP
participant Guard
participant Jev
participant Ledger
participant API as Workspace API
Agent->>Kinde: tools/call deleteProject
Kinde->>Guard: DELETE /api/v1/projects/acme-rebrand (user token)
Guard->>Guard: verify token, check permission and flag
Guard->>Jev: tool call, user request, content the agent read
Jev-->>Guard: matches 0.90, destructive 0.96, injected 0.06
Guard->>Ledger: step_up, high_impact_operation
Guard-->>Kinde: 403 with approval link
Kinde-->>Agent: held, give the user this link
Note over Guard,API: The user signs in again with Kinde and approves. The call runs once.
Screenshots
| The ledger | An attack, stopped | | --- | --- | | | | | A destructive call, held for approval | Connect any agent through Kinde MCP | | | |
Benchmark results
The benchmark sends the same 300 tool calls, 3 times each, to three deciders. The false-allow rate is the share of calls that should not run but were allowed.
| Decider | False-allow | Accuracy | p50 latency | Cost per 1,000 calls | | --- | --- | --- | --- | --- | | Jev signals with policy code (this kit) | 0.0% | 83.7% | 205 ms | $0.029 | | Jev single verdict | 12.4% | 71.3% | 204 ms | $0.033 | | LLM judge (Claude Sonnet 5) | 7.9% | 77.6% | 2,073 ms | $0.931 |
We wrote the cases and the labels. Run npm run bench to measure your own deployment. The benchmark page shows the limits of the method.
Development
Requirements
- Node.js 20.17 or later
- A Kinde business
- A Convex account
- An OpenRouter API key, for Jev, the LLM judge and the in-app agent
Initial set up
- Clone the repository to your machine:
``bash git clone https://github.com/kinde-starter-kits/jev-agent-authorization.git ``
- Go into the project:
``bash cd jev-agent-authorization ``
- Install the dependencies:
``bash npm install ``
- Copy the environment file:
``bash cp .env.local.sample .env.local ``
Set up Convex
Convex runs the guard, the ledger, the workspace API and the benchmark.
- Start Convex in its own terminal and leave it running. The first run asks you to sign in to Convex and create a project. It then writes
CONVEX_DEPLOYMENT,NEXT_PUBLIC_CONVEX_URLandNEXT_PUBLIC_CONVEX_SITE_URLto.env.local:
``bash npx convex dev ``
- The first push stops with
KINDE_ISSUER_URL is used in auth config file but its value was not set. This is expected, because the guard verifies Kinde tokens. Set the variable, then runnpx convex devagain:
``bash npx convex env set KINDE_ISSUER_URL https://.kinde.com ``
- Note your site URL. It is
NEXT_PUBLIC_CONVEX_SITE_URLin.env.localand looks likehttps://.convex.site. The workspace API and its OpenAPI spec live there, and the Kinde MCP connection needs it.
Set up OpenRouter for Jev
The guard calls Jev (typesafe/jev-1.13) through the OpenRouter System One endpoint. The same key runs the LLM judge and the in-app agent.
- Create an OpenRouter account and add credits. A guarded call costs about $0.00007. A full benchmark run costs about $1.
- Create an API key in OpenRouter.
- Store the key in Convex, because the guard runs there, not in Next.js:
``bash npx convex env set OPENROUTER_API_KEY ``
- Set the model for the LLM judge:
``bash npx convex env set LLM_JUDGE_MODEL anthropic/claude-sonnet-5 ``
Without it, a call that Jev is unsure about is held for approval, and npm run bench does not start. JEV_MODEL defaults to typesafe/jev-1.13 and AGENT_MODEL defaults to anthropic/claude-sonnet-5.
Set up Kinde
- API. Create an API with the audience
https://gatehouse.api. Add these permissions:
| Permission | Lets the user | | ---------------------------- | --------------------------- | | gatehouse:projects:read | List and read projects | | gatehouse:projects:write | Archive projects | | gatehouse:projects:delete | Delete projects | | gatehouse:docs:read | List and read documents | | gatehouse:docs:write | Create and change documents | | gatehouse:invoices:read | List invoices | | gatehouse:refunds:create | Refund invoices | | gatehouse:members:read | List members | | gatehouse:members:manage | Invite and remove members | | gatehouse:customers:export | Export customer records |
- Roles. Create
viewer(the read permissions),operator(the read permissions plusdocs:write,projects:writeandrefunds:create) andadmin(all permissions). Give your user a role in the organization the app uses. - Feature flag. Create a boolean flag
bulk_exportat organization scope, default off. Exports need it. - Web app. Create a back-end web application. Authorize it for the API. Set the callback URL to
http://localhost:3000/api/auth/kinde_callbackand the logout URL tohttp://localhost:3000. Put its values in theKINDE_*variables of.env.local. - M2M app. Create a machine-to-machine application with the Management API scopes
read:organization_user_permissionsandread:organization_feature_flags. The guard uses it when a token has no organization, which is the case for most MCP clients. - MCP connection. Create a Secure MCP connection:
- OpenAPI spec:
https://.convex.site/api/openapi.json - Back-end URL: your Convex site URL
- Authentication: pass the user token through
- Extra header:
X-Kinde-User-Id: {{kinde.user.id}}
Do not turn on role-based access control for the MCP sign-in. MCP sign-ins have no organization, so Kinde refuses them. The guard checks organization permissions itself.
Set the other Convex environment variables
Set the rest with npx convex env set NAME value. The table lists every variable, including the ones from the Convex and OpenRouter steps:
| Variable | Value | | --- | --- | | KINDE_ISSUER_URL | https://.kinde.com | | GATEHOUSE_AUDIENCE | https://gatehouse.api | | GATEHOUSE_ORG_CODE | The organization code the app uses | | KINDE_M2M_CLIENT_ID, KINDE_M2M_CLIENT_SECRET | From the M2M app | | KINDE_WEB_CLIENT_ID | The web app client ID | | GATEHOUSE_APP_URL | http://localhost:3000 | | GATEHOUSE_MCP_URL | Your Kinde MCP connection URL | | OPENROUTER_API_KEY | Your OpenRouter key | | JEV_MODEL | Optional. Default typesafe/jev-1.13 | | LLM_JUDGE_MODEL | The model for the LLM judge, for example anthropic/claude-sonnet-5 | | AGENT_MODEL | Optional. The model for the in-app agent |
Run the app
Run Convex and Next.js in two terminals:
npx convex dev
npm run dev
Open http://localhost:3000 and sign in. The app creates your workspace on the first call. To put the workspace back to its first state:
npx convex run seed:resetWorkspace '{"ownerSub":""}'
Connect an MCP client
Add your Kinde MCP connection URL to an MCP client such as Cursor. The client signs you in through Kinde. Every tool call then goes through the guard and shows on the ledger.
Test
| Command | What it does | | --- | --- | | npm test | Unit and integration tests for the guard, the policy, Jev parsing, the agent and the benchmark | | npm run e2e | A live check through Kinde MCP. Set E2E_MCP_URL and E2E_ACCESS_TOKEN first. See scripts/e2e.mjs | | npm run bench | Runs the 300-case benchmark on your deployment. It makes about 2,000 model calls | | npm run lint | ESLint | | npm run typecheck | TypeScript |
A commit runs ESLint and Prettier on the staged files.
Project structure
convex/guard/: token checks, Kinde access, policy, the guard handler and the approval routesconvex/jev/: the Jev questions, the Jev client and the LLM judgeconvex/agent/: the in-app agent, its MCP client and the attack scenariosconvex/bench/: benchmark cases, runner and metricsconvex/workspace/: the workspace operations that the agent callssrc/app/: the Next.js pages
Documentation
- Kinde docs for sign-in, permissions, feature flags and MCP connections
- Introducing System One models and Jev for Jev
- Convex docs for the back end
Publishing
This is a starter kit, so there is no package to publish. A GitHub Action runs lint, typecheck, tests and a build on every push and pull request to main.
Contributing
Please refer to Kinde’s contributing guidelines.
License
By contributing to Kinde, you agree that your contributions will be licensed under its MIT License.
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: kinde-starter-kits
- Source: kinde-starter-kits/jev-agent-authorization
- License: MIT
- Homepage: https://jev-gatehouse.vercel.app
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.