32 Commits

Author SHA1 Message Date
Tom Moor 3c2e9a9723 fix: Default collections created through MCP to private (#12644) 2026-06-09 08:38:34 -04:00
Tom Moor bd01a62fc1 feat: MCP template support (#12639) 2026-06-09 07:43:21 -04:00
Tom Moor 492af6683b Add document restore functionality to MCP tools (#12575)
* 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>
2026-06-06 15:08:52 -04:00
Tom Moor fecca544f9 chore: Normalize permission logic between API/MCP doc creation (#12517) 2026-05-28 22:42:40 -04:00
Tom Moor a5c22bbb09 feat(mcp): Add commentCount to document info response (#12355)
* Add commentsCount to MCP document info response

* fix: refine MCP document comment counts
2026-05-16 08:42:48 -04:00
Tom Moor 4a324784be Refactor MCP tests (#12347) 2026-05-13 20:57:55 -04:00
Tor Anders Johansen e325867716 feat(mcp): add fullWidth parameter to create_document and update_document tools (#12338)
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.
2026-05-13 08:32:40 -04:00
Tom Moor 7c070df942 fix: Correct locking in comment anchor for update (#12332)
* fix: Lock error on MCP anchorText comment creation

* refactor
2026-05-12 22:22:52 -04:00
Tom Moor 925a43bd36 feat: Inline comment support (#12322)
* 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
2026-05-12 21:22:38 -04:00
Tom Moor 3109f49b40 feat: Allow MCP to access signed attachment urls through fetch tool (#12315)
* feat: Add ability for MCP to access signed attachment urls through fetch tool

* Potential fix for pull request finding

* fix: non-admin cannot fetch attachments
2026-05-11 22:16:20 -04:00
Tom Moor ff3b3ce552 fix: Allow empty string in optional MCP fields (#12310)
* 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.
2026-05-10 10:47:24 -04:00
Tom Moor 4058b54573 fix: Relative path returned from MCP (#12255)
* 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>
2026-05-04 07:52:32 -04:00
Tom Moor cae8c78eb9 feat: Add delete_document and delete_collection MCP tools (#12245)
* 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>
2026-05-02 12:29:22 -04:00
Tom Moor fca10221b9 chore: promote no-explicit-any from warn to error (#12244)
* 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>
2026-05-02 12:14:23 -04:00
Tom Moor 8a896ddd2d feat: Return breadcrumb in MCP responses (#12203)
* 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>
2026-05-01 22:57:03 -04:00
Tom Moor 4c8a1c89b2 chore: resolve no-explicit-any and no-base-to-string lint warnings (#12217) 2026-04-29 17:45:02 -04:00
Tom Moor 5cb4b71652 feat: Improve MCP ability to read tree hierarchy (#12102)
* feat: Improve MCP ability to read tree heirarchy

* PR feedback
2026-04-18 08:09:55 -04:00
Tom Moor c0ebed66f5 feat: Add patch support to MCP (#11987) 2026-04-09 20:57:02 -04:00
Copilot 27dc02aad1 Add anchor text to MCP comment tool responses (#11886)
* Initial plan

* Add comment anchor text to MCP comment tool responses

Agent-Logs-Url: https://github.com/outline/outline/sessions/294b6510-996f-4a86-a7d6-7ed1c336fc19

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

* Address PR review: fix auth gap, cache marks, add anchorText tests

- Always authorize read access in update_comment before exposing anchor text
- Cache comment marks per document in list_comments to avoid O(n * docSize)
- Add 4 MCP tests verifying anchorText presence/absence in responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
Co-authored-by: Tom Moor <tom@getoutline.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-28 15:08:34 -04:00
Tom Moor b91d9e9a72 feat: Extract search into pluggable provider system (#11448)
* 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>
2026-03-25 23:01:26 -04:00
Tom Moor c0a6bc911c Add create_attachment tool to get a presigned POST for file upload (#11823)
* 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
2026-03-22 10:49:51 -04:00
Tom Moor 1bd6ad830e MCP improvements (#11822)
* 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
2026-03-20 09:45:50 -04:00
Tom Moor 3740e09e5c feat: Expose moving documents within a collection (#11799) 2026-03-17 22:40:25 -04:00
Tom Moor ff0a1766f8 fix: Add exact ID/slug lookup for list_documents and list_collections (#11756) 2026-03-14 17:42:14 -04:00
Copilot 6708f5dbc2 Add dedicated tracing for MCP resources (#11569)
* Initial plan

* feat: Add separate tracing for MCP resources

Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: tommoor <380914+tommoor@users.noreply.github.com>
2026-02-25 14:40:08 -05:00
Tom Moor 9c7a53102b chore: Ensure ip address is passed through mcp events (#11542) 2026-02-24 08:31:25 -05:00
Tom Moor 6a69990833 Attribute MCP mutations separately in audit log events (#11533) 2026-02-23 21:31:34 -05:00
Tom Moor 05381ff101 Add move_document tool (#11510) 2026-02-20 21:39:14 -05:00
Tom Moor 519fd024f9 Add Datadog tracing to MCP tool handlers (#11509)
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>
2026-02-20 21:08:46 -05:00
Tom Moor 4aeea4f73c mcp: Add draft and publish (#11488)
* mcp: Add draft and publish

* refactor

* touch
2026-02-18 21:23:27 -05:00
Tom Moor c9fe7b3d5c Ensure full urls are returned from MCP (#11482)
* 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>
2026-02-17 18:05:43 -05:00
Tom Moor 1937043aed feat: MCP Server (#11464)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:14:18 -05:00