Repository navigation
MCP - #280
MCP#280
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5e7308fa94
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| then(({ user, permissions }) => { | ||
| req._internals = req._internals || {}; | ||
| req._internals.authorization = token; | ||
| req._internals.initiatedById = user._id; | ||
| req._internals.roles = permissions.roles; |
There was a problem hiding this comment.
Enforce the grant's collection allowlist
When token introspection returns a restricted permissions.collections value, this middleware discards it and propagates only the coarse roles and operation limits. Every Model tool still accepts any registered model name, so a client granted access to one collection can read or mutate every collection permitted by its role. Preserve the resolved collection scope and enforce it before invoking Model actions.
Useful? React with 👍 / 👎.
| 'Model.updateDocuments': ['owner', 'admin', 'member'] | ||
| 'Model.updateDocuments': ['owner', 'admin', 'member'], | ||
| 'Script.createScript': ['owner', 'admin', 'member', 'readonly'], | ||
| 'Script.executeScript': ['owner', 'admin', 'member', 'readonly'], |
There was a problem hiding this comment.
Remove readonly access from script execution
A user whose only role is readonly can call the regular Script.executeScript API for a script they own and run it with dryRun: false; sandbox scripts can issue writes through application models and collections. Marking the action mcp = false only hides the MCP tool and does not protect this API endpoint, so script execution should require a write-capable role.
Useful? React with 👍 / 👎.
| const mcpEnabled = options.mcp !== false; | ||
| const registeredMCPResources = new Set(); | ||
| const mcpAuthorizationServerUrl = new URL(mothershipUrl).origin; | ||
| const mcpResource = workspace ? canonicalize(`${options.publicUrl.replace(/\/+$/, '')}/mcp`) : null; |
There was a problem hiding this comment.
Skip MCP URL construction when MCP is disabled
With an API key, mcp: false, and no publicUrl, the validation at the start of this function intentionally permits startup, but workspace is then populated and this unconditional options.publicUrl.replace(...) throws. This prevents API-key deployments from disabling MCP unless they provide an otherwise unnecessary public URL; construct the resource only when MCP is enabled.
Useful? React with 👍 / 👎.
| const doc = await Model. | ||
| findById(documentId). | ||
| setOptions(omitNullish({ sanitizeFilter: true, maxTimeMS: options?.maxTimeMS })). | ||
| setOptions(readOperationOptions(options, { sanitizeFilter: true })). |
There was a problem hiding this comment.
Forward request constraints to single-document reads
For an OAuth request with a lower maxTimeMS or a secondary read preference, this passes { sanitizeFilter: true } as the request params instead of passing params and supplying the flag as the third argument. Consequently getDocument ignores the grant's limits and reads with host defaults; validateDocument contains the identical call. Pass params through and provide { sanitizeFilter: true } as extra so these individual operations honor the delegated policy.
AGENTS.md reference: AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
| for await (const chunk of { [Symbol.asyncIterator]: () => iterator }) { | ||
| chunks.push(chunk); | ||
| } |
There was a problem hiding this comment.
Exclude the unbounded change stream from MCP
When Model.streamDocumentChanges is called as an MCP tool, this loop buffers its async iterator until completion, but that generator normally waits for the process-wide change stream to end and therefore never returns a tool result. Cancellation also calls iterator.return() while the generator is blocked on its unresolved queue promise, so the request and listener can remain stuck. Either omit this action from MCP or implement abort-aware streaming instead of collecting it as a finite result.
Useful? React with 👍 / 👎.
| selectedRunLabel() { | ||
| return this.selectedRunMode === 'dryRun' ? 'Dry Run' : 'Run'; | ||
| }, | ||
| isDryRunResult() { | ||
| return this.script?.executionFinishedAt != null && this.script?.dryRun === true; |
There was a problem hiding this comment.
Inline the one-use display computations
selectedRunLabel and isDryRunResult are single-expression computed properties used only once in the template, adding indirection without reuse. Inline these expressions at their display sites as required by the frontend locality convention.
AGENTS.md reference: AGENTS.md:L74-L74
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Delegated OAuth limits and read-only boundaries are not consistently enforced, and several supported configurations fail or advertise unusable MCP URLs.
Review effort: Balanced
Findings: 8
Open (20)
Prevent read-only grants from invoking dashboard evaluations · New Apply OAuth limits to index operations · New Apply delegated query limits to agent tools · New Exclude never-ending change streams from MCP tools · New Prevent read-only users from executing write-capable scripts · New Avoid deriving publicUrl when MCP is disabled · New Fail closed when OAuth introspection data is inactive or incomplete · New Enforce OAuth collection restrictions in MCP tools · New Pass sanitizeFilter and request limits through extra options · New Preserve OAuth request limits and sanitizeFilter options · New Use publicUrl for review links with workspace fallback · New Populate userId for MCP dashboard evaluations · New Use configured publicUrl for the frontend MCP resource URL · New Document the repository's actual wrapper generation workflow · New Use the full Mongoose Studio project name · New Inline the two-use MCP capability check · New Add an accessible name and popup state to the dropdown · New Use mustache interpolation for script rendering · New Inline the single-use capability check · New Inline single-use computed expressions · New
What changed in this PR
Adds MCP/OAuth support to Mongoose Studio, including delegated database tools, standalone script review/execution, and per-request query limits.
Changes:
- Adds Streamable HTTP MCP endpoints, OAuth metadata, and authorization.
- Adds script persistence, execution, dry runs, and frontend review UI.
- Propagates
maxTimeMSand read preferences across database operations.
| File | Description |
|---|---|
vercel.d.ts |
Adds read-preference typing. |
test/setup.test.js |
Exposes shared backend options. |
test/Script.executeScript.test.js |
Tests script lifecycle and dry runs. |
test/mcpOAuth.test.js |
Tests OAuth, CORS, and metadata. |
test/mcp.test.js |
Tests MCP tools and authorization. |
test/maxTimeMS.test.js |
Tests per-request operation limits. |
test/frontend/script.test.js |
Tests script frontend behavior. |
test/archetypeToZodSchema.test.js |
Tests schema conversion. |
README.md |
Documents MCP configuration and OAuth. |
package.json |
Adds MCP SDK and Zod dependencies. |
nest.d.ts |
Adds MCP option typings. |
index.d.ts |
Expands Express option typings. |
frontend/src/script/script.js |
Implements script review and execution. |
frontend/src/script/script.html |
Adds the script interface. |
frontend/src/routes.js |
Adds MCP and script routes. |
frontend/src/navbar/navbar.js |
Adds MCP availability state. |
frontend/src/navbar/navbar.html |
Adds MCP navigation links. |
frontend/src/mcp/mcp.js |
Computes and copies the MCP URL. |
frontend/src/mcp/mcp.html |
Adds MCP connection guidance. |
frontend/src/index.js |
Tracks scripts and guards MCP routes. |
frontend/src/api.js |
Adds Script API wrappers. |
frontend/index.js |
Publishes MCP frontend configuration. |
express.js |
Mounts and authenticates MCP endpoints. |
eval/agent/run.js |
Updates sandbox construction. |
eslint.config.js |
Registers the URL global. |
docs/mcp-oauth.md |
Documents delegated OAuth behavior. |
CLAUDE.md |
Removes duplicate guidance. |
backend/util/archetypeToZodSchema.js |
Converts action schemas to Zod. |
backend/sandbox/createScriptDb.js |
Applies sandbox operation limits. |
backend/sandbox/createSandbox.js |
Accepts read-operation options. |
backend/next.js |
Shares workspace data with actions. |
backend/netlify.js |
Shares workspace data with actions. |
backend/mcp.js |
Exposes authorized actions as MCP tools. |
backend/integrations/mothership.js |
Adds OAuth mothership requests. |
backend/index.js |
Registers the script model. |
backend/helpers/readOperationOptions.js |
Resolves read preferences and limits. |
backend/helpers/operationOptions.js |
Resolves per-request time limits. |
backend/db/scriptSchema.js |
Defines persisted scripts and results. |
backend/authorize.js |
Adds authorization for new actions. |
backend/actions/Task/runTask.js |
Applies request time limits. |
backend/actions/Task/rescheduleTask.js |
Applies request time limits. |
backend/actions/Task/getTasksOverTime.js |
Applies request time limits. |
backend/actions/Task/getTasks.js |
Applies request time limits. |
backend/actions/Task/getTaskOverview.js |
Applies request time limits. |
backend/actions/Task/cancelTask.js |
Applies request time limits. |
backend/actions/Script/index.js |
Exports Script actions. |
backend/actions/Script/getScript.js |
Loads authorized scripts. |
backend/actions/Script/executeScript.js |
Executes and records scripts. |
backend/actions/Script/createScript.js |
Creates reviewable scripts. |
backend/actions/Model/validateDocument.js |
Adds MCP metadata and limits. |
backend/actions/Model/updateDocuments.js |
Adds metadata and request limits. |
backend/actions/Model/updateDocument.js |
Adds metadata and request limits. |
backend/actions/Model/streamDocumentChanges.js |
Exposes change streaming metadata. |
backend/actions/Model/streamChatMessage.js |
Exposes chat streaming metadata. |
backend/actions/Model/listModels.js |
Adds MCP metadata. |
backend/actions/Model/getSuggestedProjection.js |
Adds MCP metadata. |
backend/actions/Model/getIndexes.js |
Adds MCP metadata. |
backend/actions/Model/getEstimatedDocumentCounts.js |
Applies delegated read options. |
backend/actions/Model/getDocumentsStream.js |
Applies delegated read options. |
backend/actions/Model/getDocuments.js |
Applies delegated read options. |
backend/actions/Model/getDocument.js |
Applies delegated read options. |
backend/actions/Model/getCollectionInfo.js |
Applies delegated read options. |
backend/actions/Model/exportQueryResults.js |
Applies delegated read options. |
backend/actions/Model/executeDocumentScript.js |
Applies limits inside scripts. |
backend/actions/Model/dropIndex.js |
Adds destructive-action metadata. |
backend/actions/Model/dropCollection.js |
Adds destructive-action metadata. |
backend/actions/Model/deleteDocuments.js |
Adds metadata and request limits. |
backend/actions/Model/deleteDocument.js |
Adds metadata and request limits. |
backend/actions/Model/createDocument.js |
Adds MCP metadata. |
backend/actions/Model/createChatMessage.js |
Adds MCP metadata. |
backend/actions/Model/analyzeSchema.js |
Applies delegated read options. |
backend/actions/Model/addField.js |
Adds metadata and request limits. |
backend/actions/index.js |
Exports Script actions. |
backend/actions/Dashboard/updateDashboard.js |
Adds metadata and request limits. |
backend/actions/Dashboard/getDashboards.js |
Adds MCP metadata and limits. |
backend/actions/Dashboard/getDashboard.js |
Applies sandbox request limits. |
backend/actions/Dashboard/deleteDashboard.js |
Adds destructive metadata and limits. |
backend/actions/Dashboard/createDashboard.js |
Adds MCP metadata. |
backend/actions/ChatThread/toggleAgentMode.js |
Applies request time limits. |
backend/actions/ChatThread/streamChatMessage.js |
Applies request time limits. |
backend/actions/ChatThread/shareChatThread.js |
Applies request time limits. |
backend/actions/ChatThread/listChatThreads.js |
Applies request time limits. |
backend/actions/ChatThread/getChatThread.js |
Applies request time limits. |
backend/actions/ChatThread/createChatMessage.js |
Applies request time limits. |
backend/actions/ChatMessage/executeScript.js |
Applies delegated sandbox limits. |
AGENTS.md |
Expands repository development guidance. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| module.exports.paramsType = GetDashboardParams; | ||
| module.exports.tags = ['readOnly']; |
| module.exports.paramsType = GetDocumentsParams; | ||
| module.exports.tags = ['readOnly']; |
| module.exports.paramsType = StreamChatMessageParams; | ||
| module.exports.tags = ['readOnly']; |
| module.exports.paramsType = StreamDocumentChangesParams; | ||
| module.exports.tags = ['readOnly']; |
| 'Model.updateDocuments': ['owner', 'admin', 'member'] | ||
| 'Model.updateDocuments': ['owner', 'admin', 'member'], | ||
| 'Script.createScript': ['owner', 'admin', 'member', 'readonly'], | ||
| 'Script.executeScript': ['owner', 'admin', 'member', 'readonly'], |
| mcpEnabled() { | ||
| return window.MONGOOSE_STUDIO_CONFIG.mcp !== false; | ||
| }, |
| <button | ||
| class="px-1.5 py-1.5 text-sm bg-primary hover:bg-primary-hover text-primary-text border-l border-black/10 rounded-r-md" | ||
| @click.stop="showRunDropdown = !showRunDropdown"> |
| </button> | ||
| </div> | ||
| </div> | ||
| <pre v-else class="whitespace-pre-wrap !my-0 bg-muted"><code ref="code" class="language-javascript" v-text="script.script"></code></pre> |
| canUseDryRun() { | ||
| return this.state.capabilities?.supportsTransactions !== false; | ||
| }, |
| selectedRunLabel() { | ||
| return this.selectedRunMode === 'dryRun' ? 'Dry Run' : 'Run'; | ||
| }, | ||
| isDryRunResult() { | ||
| return this.script?.executionFinishedAt != null && this.script?.dryRun === true; |



No description provided.