@mongoosejs/studio 0.6.0 → 0.6.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,38 @@
1
+ # Changelog
2
+
3
+ ## 0.6.1
4
+
5
+ ### Safer MCP defaults with API keys
6
+
7
+ MCP now defaults to disabled when Mongoose Studio is configured with an `apiKey`. Existing API-key integrations therefore continue to start without requiring additional MCP configuration.
8
+
9
+ To enable OAuth-protected MCP, set `mcp: true` and provide `publicUrl` with the publicly reachable base URL where Mongoose Studio is mounted:
10
+
11
+ ```javascript
12
+ app.use('/studio', await studio('/studio/api', mongoose, {
13
+ apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
14
+ mcp: true,
15
+ publicUrl: 'https://myproduct.app/studio'
16
+ }));
17
+ ```
18
+
19
+ Mongoose Studio only requires `publicUrl` when both `apiKey` and `mcp: true` are set. Without an API key, MCP remains enabled by default and protected by `bindIp`.
20
+
21
+ ## 0.6.0
22
+
23
+ ### MCP support
24
+
25
+ Mongoose Studio now serves an MCP endpoint at `<mount>/mcp` using the same access controls as the rest of Mongoose Studio.
26
+
27
+ - MCP is enabled by default. Without an API key, access is protected by `bindIp`, so only clients running on the same machine can connect by default.
28
+ - Set `mcp: false` to disable the MCP endpoint and its OAuth discovery metadata.
29
+ - With an `apiKey`, MCP uses OAuth and requires `publicUrl` containing the publicly reachable base URL where Mongoose Studio is mounted, such as `https://myproduct.app/studio`.
30
+
31
+ ```javascript
32
+ app.use('/studio', await studio('/studio/api', mongoose, {
33
+ apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
34
+ publicUrl: 'https://myproduct.app/studio'
35
+ }));
36
+ ```
37
+
38
+ Mongoose Studio throws an error during setup when MCP is enabled with an `apiKey` but `publicUrl` is missing.
package/README.md CHANGED
@@ -56,23 +56,24 @@ app.use('/studio', await studio('/studio/api', mongoose, {
56
56
  }));
57
57
  ```
58
58
 
59
- The Express integration also exposes a Streamable HTTP MCP endpoint at `/studio/mcp`.
60
- Set `mcp: false` to turn it off, along with the OAuth protected resource metadata that advertises it.
59
+ Without an API key, the Express integration exposes a Streamable HTTP MCP endpoint at `/studio/mcp` by default.
60
+ Set `mcp: false` to turn it off.
61
61
  It provides authorized Dashboard and Model actions, plus `Script.createScript`, as MCP tools.
62
62
  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.
63
- Without workspace authentication, the MCP endpoint uses the same localhost and `bindIp` restrictions as the rest of Studio.
63
+ Without workspace authentication, the MCP endpoint uses the same localhost and `bindIp` restrictions as the rest of Mongoose Studio.
64
64
 
65
65
  #### Connecting ChatGPT or Claude with OAuth
66
66
 
67
- With a Pro API key, `/studio/mcp` is also an OAuth 2.1 protected resource, so MCP clients such as ChatGPT and Claude can connect to it directly.
67
+ 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.
68
68
  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.
69
69
  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.
70
70
 
71
71
  Mongoose Studio advertises itself to MCP clients using its externally reachable URL.
72
- When MCP and a Pro API key are enabled, set `publicUrl` to the URL where Mongoose Studio is mounted:
72
+ When enabling MCP with a Pro API key, also set `publicUrl` to the URL where Mongoose Studio is mounted:
73
73
 
74
74
  ```javascript
75
75
  opts.publicUrl = 'https://app.example.com/studio';
76
+ opts.mcp = true;
76
77
  ```
77
78
 
78
79
  Mongoose Studio finds the authorization server through the mothership it is already configured to use, so it needs no separate authorization server configuration.
package/docs/mcp-oauth.md CHANGED
@@ -84,17 +84,18 @@ They can only narrow what the host application already allows: `maxTimeMS` is ca
84
84
 
85
85
  ## Configuration
86
86
 
87
- The Mongoose Studio side needs a Pro API key and its own externally reachable URL, since the resource identifier is that URL.
88
- Set `publicUrl` to the URL where Mongoose Studio is mounted:
87
+ MCP is disabled by default when Mongoose Studio has a Pro API key.
88
+ To enable it, set `mcp: true` and set `publicUrl` to the externally reachable URL where Mongoose Studio is mounted:
89
89
 
90
90
  ```javascript
91
91
  app.use('/studio', await studio('/studio/api', mongoose, {
92
92
  apiKey: process.env.MONGOOSE_STUDIO_API_KEY,
93
+ mcp: true,
93
94
  publicUrl: 'https://app.example.com/studio'
94
95
  }));
95
96
  ```
96
97
 
97
- Pass `mcp: false` to serve Studio without an MCP endpoint at all.
98
+ Pass `mcp: false` to serve Mongoose Studio without an MCP endpoint at all.
98
99
  Neither the endpoint nor its protected resource metadata is then mounted, so there is nothing for an MCP client to discover.
99
100
  Without an API key, the MCP endpoint follows the normal localhost or `bindIp` restrictions and does not mount OAuth protected resource metadata.
100
101
 
@@ -102,5 +103,5 @@ Mongoose Studio registers that URL with the authorization server the first time
102
103
  It advertises the configured mothership's origin in `authorization_servers`, keeping authentication with the same service Mongoose Studio already uses.
103
104
 
104
105
  Browser based MCP clients, claude.ai among them, run discovery from the page rather than from a server.
105
- Studio therefore serves the MCP endpoint and its metadata with CORS, and exposes `WWW-Authenticate` via `Access-Control-Expose-Headers`.
106
+ Mongoose Studio therefore serves the MCP endpoint and its metadata with CORS, and exposes `WWW-Authenticate` via `Access-Control-Expose-Headers`.
106
107
  Without that last header a browser hides the challenge from the client, and the client cannot find the authorization server at all.
package/express.js CHANGED
@@ -17,7 +17,8 @@ const MCP_ACCESS_TOKEN_PREFIX = 'mcp_at_';
17
17
  module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options) {
18
18
  const router = express.Router();
19
19
  options = options ? { changeStream: true, ...options } : { changeStream: true };
20
- if (options.mcp !== false && options.apiKey && !options.publicUrl) {
20
+ options.mcp = options.mcp ?? !options.apiKey;
21
+ if (options.mcp && options.apiKey && !options.publicUrl) {
21
22
  throw new Error('Mongoose Studio requires the publicUrl option when MCP is enabled with an API key');
22
23
  }
23
24
  const hasBindIpOption = Object.prototype.hasOwnProperty.call(options, 'bindIp');
@@ -124,10 +125,10 @@ module.exports = async function mongooseStudioExpressApp(apiUrl, conn, options)
124
125
  // issued by the mothership. Mongoose Studio is the protected resource: it publishes the
125
126
  // metadata that points clients at the authorization server, and resolves each
126
127
  // token to its current grant before handling the request.
127
- const mcpEnabled = options.mcp !== false;
128
+ const mcpEnabled = options.mcp;
128
129
  const registeredMCPResources = new Set();
129
130
  const mcpAuthorizationServerUrl = new URL(mothershipUrl).origin;
130
- const mcpResource = workspace ? canonicalize(`${options.publicUrl.replace(/\/+$/, '')}/mcp`) : null;
131
+ const mcpResource = mcpEnabled && workspace ? canonicalize(`${options.publicUrl.replace(/\/+$/, '')}/mcp`) : null;
131
132
 
132
133
  async function registerMCPResource(resource) {
133
134
  if (registeredMCPResources.has(resource)) {
package/frontend/index.js CHANGED
@@ -37,7 +37,7 @@ module.exports = async function frontend(apiUrl, isLambda, options, workspace) {
37
37
  baseURL: apiUrl,
38
38
  isLambda,
39
39
  mcp: options?.mcp !== false,
40
- mcpUrl: workspaceBaseUrl ? `${workspaceBaseUrl}/mcp` : null,
40
+ mcpUrl: options?.mcp !== false && workspaceBaseUrl ? `${workspaceBaseUrl}/mcp` : null,
41
41
  mothershipUrl: apiKey ? mothershipUrl : null,
42
42
  workspace: workspaceData
43
43
  };
@@ -66655,7 +66655,7 @@ var src_default = VueToastificationPlugin;
66655
66655
  (module) {
66656
66656
 
66657
66657
  "use strict";
66658
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.6.0","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","@modelcontextprotocol/sdk":"^1.30.1","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.1","marked":"15.0.12","mpath":"^0.9.0","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15","zod":"^4.6.5"},"peerDependencies":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","@nestjs/common":"^11.1.9","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js","eval:agent":"node eval/agent/run.js"}}');
66658
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.6.1","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","@modelcontextprotocol/sdk":"^1.30.1","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.1","marked":"15.0.12","mpath":"^0.9.0","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15","zod":"^4.6.5"},"peerDependencies":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","@nestjs/common":"^11.1.9","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js","eval:agent":"node eval/agent/run.js"}}');
66659
66659
 
66660
66660
  /***/ }
66661
66661
 
package/index.d.ts CHANGED
@@ -9,7 +9,7 @@ declare module '@mongoosejs/studio' {
9
9
  apiKey?: string;
10
10
  bindIp?: string | string[] | null;
11
11
  maxTimeMS?: number;
12
- /** Serve the MCP endpoint at `<mount>/mcp`. Defaults to true. */
12
+ /** Serve the MCP endpoint at `<mount>/mcp`. Defaults to true without apiKey and false with apiKey. */
13
13
  mcp?: boolean;
14
14
  readPreference?: 'primary' | 'secondaryPreferred' | 'secondary';
15
15
  /** Public base URL Mongoose Studio is mounted on. Required when MCP and apiKey are enabled. */
package/nest.d.ts CHANGED
@@ -10,6 +10,7 @@ declare module '@mongoosejs/studio/nest' {
10
10
  apiKey?: string;
11
11
  bindIp?: string | string[] | null;
12
12
  maxTimeMS?: number;
13
+ /** Serve the MCP endpoint. Defaults to true without apiKey and false with apiKey. */
13
14
  mcp?: boolean;
14
15
  readPreference?: 'primary' | 'secondaryPreferred' | 'secondary';
15
16
  publicUrl?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.",
5
5
  "homepage": "https://mongoosestudio.app/",
6
6
  "repository": {