TL;DR
- xmcp gets an MCP server running fast, but a localhost prototype lacks the production fundamentals real users require: proper sign-in, per-user identity, and data isolation.
- The @xmcp-dev/scalekit plugin adds an OAuth 2.1 middleware layer to xmcp, so MCP clients are redirected to Scalekit for sign-in and every request carries a validated, server-scoped token.
- Inside any tool, getSession() returns the authenticated userId, organizationId, granted scopes, and permissions, and raw tokens never touch client config files.
- Keying reads and writes on userId (and optionally organizationId) isolates each customer's data, while token-embedded permissions let tools enforce role-based controls before mutating state.
- The create-xmcp-app --example scalekit template wires the full flow end to end, so you can connect a working, per-user-authenticated server to Cursor or Claude Code in minutes.
You can spin up a complete MCP server in minutes with xmcp. The framework gives you a clean, modern way to define tools, resources, and prompts that AI clients like Cursor or Claude can talk to.
But the moment you want real customers (or even a broader team) to connect to that server, the picture changes. A prototype that works fine on localhost suddenly needs the same production fundamentals every SaaS product ships with: proper sign-in, per-user identity, and the ability to isolate data and permissions.
Native Scalekit support for xmcp solves exactly that gap. You keep the fast development experience of xmcp and add production-grade OAuth 2.1 with per-user and per-organization sessions. One MCP server URL, individual sign-ins, and tools that can safely act on behalf of the right user.
xmcp makes building MCP servers fast
Most builders start with xmcp because it removes the boilerplate of writing an MCP server from scratch. That phase is fast and fun.
The transition point comes when any of these become true:
- You want to share the server with customers or external users, not just internal team members.
- Auditability and data isolation matter (each person should only see and affect their own data).
- You need to enforce authorization (some users can read, others can write).
- You plan to go live and treat the MCP server like a real product.
If you're still in pure exploration mode, you can stay with simpler auth. Once you're ready to ship to real users, the Scalekit plugin gives you the missing piece without forcing you to build OAuth, token validation, or session handling yourself.
Scalekit adds per-user and per-organization auth to xmcp
xmcp handles the MCP protocol, tool definitions, and server runtime. The @xmcp-dev/scalekit plugin adds a middleware layer that speaks OAuth 2.1 with Scalekit.
When an MCP client connects:
- The client is redirected to Scalekit for sign-in (social, SSO, magic links, etc.).
- Scalekit issues a token scoped to your registered MCP server.
- The middleware validates the token on every request.
- Inside your tools, getSession() gives you the authenticated identity: userId, organizationId, granted scopes, and any permissions.
You never see raw tokens in client config files. Secrets stay on the server. Each user or customer gets their own isolated session.
Per-user sessions keep customer data isolated
The session object makes scoping straightforward. A simple whoami tool shows what every tool can access:
import type { ToolMetadata } from "xmcp";
import { getSession } from "@xmcp-dev/scalekit";
export const metadata: ToolMetadata = {
name: "whoami",
description: "Returns the authenticated user's session information",
};
export default function whoami(): string {
const session = getSession();
return JSON.stringify(
{
userId: session.userId,
organizationId: session.organizationId,
scopes: session.scopes,
expiresAt: session.expiresAt.toISOString(),
},
null,
2
);
}
For data isolation, use userId (and optionally organizationId) as the key when reading or writing. The classic "notes" example becomes a customer-facing pattern:
- Each customer signs in once.
- Their notes are saved and listed under their own userId.
- No customer ever sees another customer's data.
If you also need role-based controls, Scalekit can include permissions in the token. A tool can check before mutating state:
const session = getSession();
if (!session.permissions?.includes("notes:write")) {
return "Missing notes:write permission.";
}
// ... proceed with write scoped to session.userId
Get started quickly with the official template
The official template is the quickest way to see everything wired up:
npx create-xmcp-app@latest my-mcp-app --example scalekit
cd my-mcp-app
cp .env.example .env
Edit .env with your Scalekit credentials (Environment URL, Client ID, Client Secret, and the Resource ID from your MCP server registration in the Scalekit dashboard). Then:
Add the resulting MCP server URL to Cursor, Claude Code, or any other MCP client. On first use the client will trigger the OAuth flow. After sign-in, tools can immediately call getSession().
Wire the Scalekit provider into your middleware
For a project you already have, install the plugin:
pnpm add @xmcp-dev/scalekit
Create src/middleware.ts:
import { scalekitProvider } from "@xmcp-dev/scalekit";
export default scalekitProvider({
environmentUrl: process.env.SCALEKIT_ENVIRONMENT_URL!,
clientId: process.env.SCALEKIT_CLIENT_ID!,
clientSecret: process.env.SCALEKIT_CLIENT_SECRET!,
baseURL: process.env.BASE_URL!,
});
Set the required environment variables (example for local development):
SCALEKIT_ENVIRONMENT_URL=https://your-env.scalekit.com
SCALEKIT_CLIENT_ID=skc_...
SCALEKIT_CLIENT_SECRET=skcs_...
BASE_URL=http://127.0.0.1:3001
In production, point BASE_URL at your deployed server and make sure the URL you registered in the Scalekit dashboard matches.
Access sessions inside your tools
Any tool can read the current user:
import { z } from "zod";
import { type InferSchema, type ToolMetadata } from "xmcp";
import { getSession } from "@xmcp-dev/scalekit";
export const schema = {
name: z.string().optional().describe("Optional name to greet"),
};
export const metadata: ToolMetadata = {
name: "greet",
description: "Greet the user with their authenticated identity",
};
export default function greet({ name }: InferSchema): string {
const session = getSession();
const displayName = name ?? session.userId;
return `Hello, ${displayName}! Your user ID is ${session.userId}`;
}
If you later need the full Scalekit SDK (for example to fetch organization details or call other management APIs), getClient() is available inside the same authenticated context.
Troubleshoot common auth issues
Common issues and fixes:
- 401 on every tool call — Finish the browser OAuth flow in the client and reconnect.
- "Session not initialized" — Make sure getSession is called from a tool that runs through the middleware, and that the request carries a valid bearer token.
- Token validation fails — Confirm SCALEKIT_ENVIRONMENT_URL and BASE_URL match the values registered in the Scalekit dashboard.
- Empty sign-in screen — Enable at least one authentication method (social, passwordless, SSO, etc.) in the Scalekit dashboard for your environment.
More details live in the XxmcpMCP Scalekit integration docs and the Scalekit MCP Auth guide.
Per-user auth makes servers safe for customers
XMCP removes the mechanical work of standing up an MCP server. Scalekit removes the mechanical work of standing up production auth.
Together they let you ship the same server to many customers or users while keeping data and actions properly isolated. Each customer signs in once, gets their own identity, and your tools can scope everything to that identity without any secrets leaking into client configuration.
The template gives you the fastest path to a working example. From there you can extend with your own tools, add permissions for finer-grained control, or call deeper into the Scalekit SDK when you need it.
Explore the xmcp and Scalekit documentation
- Explore the full xmcp documentation (append .md to any page for the Markdown version).
- Review the Scalekit MCP authentication docs.
- Try the Scalekit template and connect it to your favorite MCP client.
- When you're ready to go live, replace the local BASE_URL with your production endpoint and register the live URL in the Scalekit dashboard.
Create a Scalekit account and register your first MCP server at scalekit.com.
If you move fast with AI coding agents, the quickest way to add the integration is:
$ npx @scalekit-inc/cli setup
$ pick your coding agent (Claude, Cursor, Copilot, Codex + any)
$ claude "Help me add Scalekit to this XMCP server so my tools have proper per-user sessions and OAuth."
Try it out