An AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models.
Mongoose Studio is meant to run as a sidecar to your Node.js application, using the same Mongoose connection config.
If your app runs on acme.app, Mongoose Studio will be on acme.app/studio or whichever path you prefer.
For local dev, if your app runs on localhost:3000, Mongoose Studio will be on localhost:3000/studio.
By default, Mongoose Studio does not provide any authentication or authorization. You can use Mongoose Studio for free for local development, but we recommend Mongoose Studio Pro for when you want to go into production. When you omit an API key, Mongoose Studio only accepts localhost connections by default.
First, npm install @mongoosejs/studio.
Mongoose Studio can be mounted as Express middleware as follows.
const mongoose = require('mongoose');
const studio = require('@mongoosejs/studio/express');
// Mount Mongoose Studio on '/studio'
// If your models are registered on a different connection, pass in the connection instead of `mongoose`
app.use('/studio', await studio('/studio/api', mongoose));If you have a Mongoose Studio Pro API key, you can set it as follows:
const opts = process.env.MONGOOSE_STUDIO_API_KEY ? { apiKey: process.env.MONGOOSE_STUDIO_API_KEY } : {};
// Optionally specify which ChatGPT model to use for chat messages
opts.model = 'gpt-4o-mini';
// Provide your own OpenAI, Anthropic, or Google Gemini API key to run chat completions locally
opts.openAIAPIKey = process.env.OPENAI_API_KEY;
opts.anthropicAPIKey = process.env.ANTHROPIC_API_KEY;
opts.googleGeminiAPIKey = process.env.GOOGLE_GEMINI_API_KEY;
// Apply a maximum execution time to all read operations, including reads in scripts.
// MongoDB does not support maxTimeMS for inserts and index operations.
// This is also a ceiling: a request may ask for a lower limit, never a higher one.
opts.maxTimeMS = 10000;
// Mount Mongoose Studio on '/studio'
app.use('/studio', await studio('/studio/api', mongoose, opts));Without an API key, you can allow access from additional IP addresses with bindIp, similar to MongoDB's bindIp option. bindIp may be a comma-separated string or an array of exact IP addresses. Set bindIp to null to allow unauthenticated connections from anywhere.
app.use('/studio', await studio('/studio/api', mongoose, {
bindIp: '127.0.0.1,192.168.0.10'
}));Without an API key, the Express integration exposes a Streamable HTTP MCP endpoint at /studio/mcp by default.
Set mcp: false to turn it off.
It provides authorized Dashboard and Model actions, plus Script.createScript, as MCP tools.
With a Pro API key, pass the logged-in Studio access token in the Authorization header (either directly or as a bearer token); tools receive that user's roles and permissions.
Without workspace authentication, the MCP endpoint uses the same localhost and bindIp restrictions as the rest of Mongoose Studio.
With a Pro API key, MCP is disabled by default. Set mcp: true to expose /studio/mcp as an OAuth 2.1 protected resource so MCP clients such as ChatGPT and Claude can connect to it directly.
Point the client at your /studio/mcp URL and it will discover the Mongoose Studio authorization server, walk the user through signing in and approving access, and receive a short-lived access token.
Users review and revoke these connections from their Mongoose Studio account page, and the access an AI client gets can never exceed the access the user who approved it already has.
Mongoose Studio advertises itself to MCP clients using its externally reachable URL.
When enabling MCP with a Pro API key, also set publicUrl to the URL where Mongoose Studio is mounted:
opts.publicUrl = 'https://app.example.com/studio';
opts.mcp = true;Mongoose Studio finds the authorization server through the mothership it is already configured to use, so it needs no separate authorization server configuration.
See docs/mcp-oauth.md for how the flow and the delegated access policy work.
First, add withMongooseStudio to your next.config.js file:
import withMongooseStudio from '@mongoosejs/studio/next';
// Mount Mongoose Studio frontend on /studio
export default withMongooseStudio({
// Your Next.js config here
reactStrictMode: true,
}, {
mcp: true,
publicUrl: process.env.MONGOOSE_STUDIO_PUBLIC_URL
});Add pages/api/studio/[[...studioRoute]].js to your Next.js project to host
both the Mongoose Studio API and MCP. If you already have
pages/api/studio.js, move its handler into the new file and remove the old
file. The optional catch-all keeps /api/studio working for existing Studio
actions and also accepts /api/studio/mcp and the OAuth metadata path.
// pages/api/studio/[[...studioRoute]].js
// Make sure to import the database connection
import db from '../../../src/db';
import { vercelAPIRouteHandler } from '@mongoosejs/studio/vercel';
const handler = vercelAPIRouteHandler(
db, // Mongoose connection or Mongoose global. Or null to use `import mongoose`.
{
apiKey: process.env.MONGOOSE_STUDIO_API_KEY, // optional
connection: db, // Optional: Connection or Mongoose global. If omitted, will use `import mongoose`
connectToDB: async () => { /* connection logic here */ }, // Optional: if you need to call a function to connect to the database put it here
mcp: true, // Also enabled by default without an API key
publicUrl: process.env.MONGOOSE_STUDIO_PUBLIC_URL // Required with an API key; e.g. https://app.example.com/studio
}
);
export default handler;With mcp: true, the same API function handles Streamable HTTP
at /studio/mcp. With an API key, it also serves OAuth discovery at
/studio/.well-known/oauth-protected-resource. withMongooseStudio installs
rewrites from those public URLs to the catch-all API route and shows
/studio/mcp in the frontend. No additional API function or mcpUrl setting
is needed. The rewrites are needed to keep the public URL at /studio/mcp;
the catch-all file itself lives under /api/studio. A Pro API key is required
for OAuth-based Vercel MCP. Without an API key, MCP is enabled by default and
is limited to localhost unless bindIp is configured. Set bindIp to an IP
address or an array of IP addresses to permit other clients; set it to null
to allow access from any IP. The same restriction applies to Studio API actions.
With an API key, set publicUrl to the externally reachable /studio URL. The OAuth resource
URL is derived from it, even if the workspace's baseUrl differs.
The handler is stateless and returns a JSON response for each POST. No Redis,
sticky sessions, or long-lived SSE connection is needed. Tool calls still count
against Vercel Function duration and payload limits, so set an appropriate
maxDuration for longer database operations and keep results reasonably sized.
If your project uses api/studio.js rather than Next.js, keep it for Studio
actions and add a separate api/studio-mcp.js function for MCP. Next.js
optional catch-all filenames such as [[...studioRoute]].js do not apply to
standalone Vercel Functions:
// api/studio-mcp.js
// Make sure to import the database connection
import db from '../../../src/db';
const { vercelMCPRouteHandler } = require('@mongoosejs/studio/vercel');
module.exports = vercelMCPRouteHandler(db, {
apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
publicUrl: process.env.MONGOOSE_STUDIO_PUBLIC_URL
});Set MONGOOSE_STUDIO_PUBLIC_URL to the externally reachable Studio URL, for
example https://app.example.com/studio, when using an API key. Without an
API key, MCP is enabled by default and follows the same localhost and bindIp
restriction described above. Connect MCP clients to /api/studio-mcp; no
rewrite is required.
For OAuth, the protected resource metadata is generated in the Studio frontend
by the same frontend() call used to write config.js. It registers the MCP
resource before the returned promise resolves, so copy the frontend after
that promise resolves. Existing publicUrl fallbacks can stay as they are;
the copy source must include hidden files:
const { execSync } = require('child_process');
const opts = {
apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
mcp: true,
publicUrl: process.env.MONGOOSE_STUDIO_PUBLIC_URL
};
require('@mongoosejs/studio/frontend')('/api/studio', true, opts).then(() => {
execSync('mkdir -p ./public/studio && cp -r ./node_modules/@mongoosejs/studio/frontend/public/. ./public/studio/');
});The /. source copies hidden directories such as .well-known; a /* source
does not. This places public/studio/.well-known/oauth-protected-resource
and registers /api/studio-mcp with the authorization server. The metadata is
needed only with an API key. Set Content-Type: application/json and
Access-Control-Allow-Origin: * for that static path, as in the
standalone Vercel example. Its
build script shows the complete asset
copy and metadata generation sequence.
Add MongooseStudioModule to your app module as follows.
Use getConnectionToken() so Mongoose Studio uses the same connection that MongooseModule.forRoot() creates.
import { Module } from '@nestjs/common';
import { MongooseModule, getConnectionToken } from '@nestjs/mongoose';
import { MongooseStudioModule } from '@mongoosejs/studio/nest';
@Module({
imports: [
MongooseModule.forRoot(process.env.MONGODB_URI),
MongooseStudioModule.forRoot({
connectionToken: getConnectionToken(),
apiKey: process.env.MONGOOSE_STUDIO_API_KEY // optional
})
]
})
export class AppModule {}With this setup, Mongoose Studio is available at /studio.
If you mount Mongoose Studio on a different path, update the path option as follows.
MongooseStudioModule.forRoot({
path: '/__studio', // Serve on `/__studio` rather than `/studio`
connectionToken: getConnectionToken()
})Here is a full example of how to add Mongoose Studio to a Netlify repo.
- Copy the Mongoose Studio frontend into
public/studioautomatically innpm run build.
const { execSync } = require('child_process');
// Sign up for Mongoose Studio Pro to get an API key, or omit `apiKey` for local dev.
const opts = {
apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
// Optionally specify which ChatGPT model to use for chat messages
model: 'gpt-4o-mini',
// Provide your own OpenAI, Anthropic, or Google Gemini API key to run chat completions locally
openAIAPIKey: process.env.OPENAI_API_KEY,
anthropicAPIKey: process.env.ANTHROPIC_API_KEY,
googleGeminiAPIKey: process.env.GOOGLE_GEMINI_API_KEY
};
console.log('Creating Mongoose studio', opts);
require('@mongoosejs/studio/frontend')(`/.netlify/functions/studio`, true, opts).then(() => {
execSync(`
mkdir -p ./public/imdb
cp -r ./node_modules/@mongoosejs/studio/frontend/public/* ./public/imdb/
`);
});- Create a
/studioNetlify function innetlify/functions/studio.js, or wherever your Netlify functions directory is. The function path should match the/.netlify/functions/studioparameter in the build script above.
const mongoose = require('mongoose');
const handler = require('@mongoosejs/studio/backend/netlify')({
apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
model: 'gpt-4o-mini',
openAIAPIKey: process.env.OPENAI_API_KEY,
anthropicAPIKey: process.env.ANTHROPIC_API_KEY,
googleGeminiAPIKey: process.env.GOOGLE_GEMINI_API_KEY
}).handler;
let conn = null;
module.exports = {
handler: async function studioHandler(params) {
if (conn == null) {
conn = await mongoose.connect(process.env.MONGODB_CONNECTION_STRING, { serverSelectionTimeoutMS: 3000 });
}
return handler.apply(null, arguments);
}
};- Redeploy and you're live!
Try our IMDB demo for an example of Mongoose Studio running on Netlify, or check out the studio.mongoosejs.io GitHub repo for the full source code.