* Add restore_document MCP tool and archived/trashed listing
Closes the delete/restore asymmetry in the MCP server: previously documents
could be archived or trashed via delete_document but never recovered.
- Add restore_document tool to recover archived or trashed documents,
optionally into a different collection.
- Add a status option ("archived" | "trashed") to list_documents so agents
can discover what to restore.
- Extract the documents.restore route logic into a shared documentRestorer
command, used by both the REST endpoint and the MCP tool.
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
* Use type-only import for Document in documentRestorer
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
* Revert archived/trashed status option on list_documents
Keeps the restore_document tool and shared documentRestorer command;
removes the list_documents status filter and its tests.
https://claude.ai/code/session_01HpFcYtgEZJ96iaFMuGGCmc
---------
Co-authored-by: Claude <noreply@anthropic.com>
The fullWidth field is already persisted on the Document model and
supported in the REST API schemas (DocumentsCreateSchema, DocumentsUpdateSchema),
but the built-in MCP server did not expose it.
This patch adds fullWidth as an optional boolean parameter to both
create_document and update_document MCP tools, passing it through to
documentCreator and documentUpdater respectively.
* feat: Inline comment support
* fix: wrap inline comment mark and creation in transaction with row lock
* fix: lock document row when anchoring, error on failed anchor, use uuid import
* fix: Allow empty string in optional fields
* fix: Preserve empty strings for content fields in MCP tools
Address review feedback by reverting content/text fields (description, document
text, comment text) back to z.string().optional() so callers can intentionally
clear values via "". optionalString() is reserved for identifier and query
fields where "" is not a meaningful input.
* fix: relative path returned from MCP
* fix: MCP create_attachment uploadUrl and size validation
Make uploadUrl absolute against team.url so MCP clients can resolve it
without a base, tighten the size schema to match the REST endpoint
(int, nonnegative, finite), and stub cookies on the MCP API context so
LocalStorage's CSRF-aware getPresignedPost works for Bearer-authed
MCP requests. Adds tests covering the success path, persistence, size
limits, schema rejections, and read-only scope enforcement.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add delete_document and delete_collection MCP tools
Adds MCP tools for deleting (or archiving) documents and collections.
Refactors Document#delete into destroyWithCtx and extracts collection
archive logic into Collection#archiveWithCtx so the same code paths can
be shared between the REST API and MCP entry points.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix: Wrap MCP delete tools in DB transaction
Ensures delete/archive of documents and collections via MCP is atomic
and that row locks (transaction.LOCK.UPDATE) inside *WithCtx methods
actually apply, matching the pattern used by move_document.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs: Clarify delete_collection MCP tool description
Reflects that collection deletion only soft-deletes non-archived
documents via the BeforeDestroy hook.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* chore: promote no-explicit-any from warn to error and resolve violations
Upgrades the oxlint rule severity and removes all 40 existing
`no-explicit-any` warnings across the codebase. Most call sites gained
proper types (SharedEditor refs, JSONNode/JSONMark for ProseMirror JSON
walking, DocumentsStore, dd-trace `Span` parameter inference, prosemirror
Fragment public API in place of internal `(fragment as any).content`).
A few load-bearing `any` uses were preserved with scoped disable
comments where changing the type would cascade widely (Sequelize JSONB
columns on `Event`, the `withTracing` higher-order function generic,
`Extension.options` consumed by many subclasses, dd-trace's `req`
patching).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Add breadcrumb to MCP responses
* test: Update MCP test expectations for new response envelope
Tests were reading the old flat document shape; update them to read
through the new { document, breadcrumb } envelope.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* perf: Batch collection lookups when building breadcrumbs
Add getBreadcrumbsForDocuments helper that loads all referenced
collections in one query (with the user's memberships) and resolves
breadcrumbs from the per-collection cached documentStructure. Use it
in list_documents and move_document, replacing the per-document
Collection.findByPk that produced an N+1 query pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test: Add coverage for getBreadcrumbsForDocuments and parallelize doc + breadcrumb loads
Run presentDocument and getDocumentBreadcrumb concurrently in fetch,
create_document, and update_document so the breadcrumb lookup no
longer adds latency on top of the presenter.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: Extract search into pluggable provider system
Refactors the monolithic SearchHelper into a pluggable search provider
architecture, enabling alternative search backends (Elasticsearch,
Turbopuffer, etc.) while preserving PostgreSQL full-text search as the
default. The SEARCH_PROVIDER env var selects the active provider.
- Add BaseSearchProvider abstract class and SearchProviderManager
- Add Hook.SearchProvider to the plugin system
- Move PostgreSQL search logic into plugins/postgres-search/
- Add SearchIndexProcessor for event-driven index sync
- Update all callers to use the provider manager directly
- Keep SearchHelper as a deprecated thin wrapper for backwards compat
Closes#11347
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: Remove deprecated SearchHelper wrapper
All callers now use SearchProviderManager directly, so the thin
delegation wrapper is no longer needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: Rename postgres-search plugin to search-postgres
Renames the plugin folder and id so that future search provider plugins
(e.g. search-elasticsearch, search-turbopuffer) will be colocated
alphabetically.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: Remove special-case plugin import from SearchProviderManager
Make PluginManager.loadPlugins resilient to individual plugin load
failures so SearchProviderManager can use the standard getHooks path
without needing to directly import the search-postgres plugin.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: Add missing search provider tests for full coverage parity
Adds all tests that existed in the old SearchHelper.test.ts but were missing
from PostgresSearchProvider.test.ts, including searchTitlesForUser status
filters, collection filtering, group memberships, and sorting tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feedback
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: Data always included in list_documents response
* Remove resources, add fetch tool
Fix pagination arguments do not accept string
* type -> resource
* Add URL resolving
* Add create_attachment tool to get a presigned POST for file upload bypassing context
* fix: Data always included in list_documents response
* Remove resources, add fetch tool
Fix pagination arguments do not accept string
* type -> resource
* Add URL resolving
Wraps all MCP tool and resource handlers with Datadog APM spans so that
each invocation is visible in traces under the `outline-mcp` service.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Ensure full urls are returned from MCP
towards #11474
* Fix pathToUrl using path.join for URLs and add test coverage
path.join collapsed https:// to https:/ — use URL constructor instead.
Added assertions verifying full URLs are returned for collections and
documents across list, create, update, and resource endpoints.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add scopes_supported
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>