outline-mcp-server 4.12.2 → 5.0.0

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.
Files changed (47) hide show
  1. package/README.md +28 -17
  2. package/build/index.js +52 -51
  3. package/build/tools/archiveDocument.js +7 -13
  4. package/build/tools/askDocuments.js +21 -35
  5. package/build/tools/createCollection.js +14 -29
  6. package/build/tools/createComment.js +13 -25
  7. package/build/tools/createDocument.js +15 -34
  8. package/build/tools/createTemplateFromDocument.js +7 -13
  9. package/build/tools/deleteComment.js +7 -13
  10. package/build/tools/deleteDocument.js +7 -13
  11. package/build/tools/getCollection.js +7 -13
  12. package/build/tools/getDocument.js +9 -13
  13. package/build/tools/listCollections.js +18 -12
  14. package/build/tools/listDocuments.js +34 -55
  15. package/build/tools/listUsers.js +31 -46
  16. package/build/tools/moveDocument.js +15 -21
  17. package/build/tools/searchDocuments.js +14 -21
  18. package/build/tools/updateCollection.js +14 -29
  19. package/build/tools/updateComment.js +9 -21
  20. package/build/tools/updateDocument.js +11 -29
  21. package/build/utils/getMcpServer.js +16 -0
  22. package/build/utils/{importTools.js → loadAllTools.js} +8 -5
  23. package/build/utils/toolRegistry.js +27 -0
  24. package/package.json +18 -21
  25. package/bin/cli.js +0 -57
  26. package/build/e2e/archiveDocument.spec.js +0 -7
  27. package/build/e2e/askDocuments.spec.js +0 -7
  28. package/build/e2e/createCollection.spec.js +0 -7
  29. package/build/e2e/createComment.spec.js +0 -7
  30. package/build/e2e/createDocument.spec.js +0 -7
  31. package/build/e2e/createTemplateFromDocument.spec.js +0 -7
  32. package/build/e2e/deleteComment.spec.js +0 -7
  33. package/build/e2e/deleteDocument.spec.js +0 -7
  34. package/build/e2e/getCollection.spec.js +0 -7
  35. package/build/e2e/getDocument.spec.js +0 -7
  36. package/build/e2e/listCollections.spec.js +0 -7
  37. package/build/e2e/listDocuments.spec.js +0 -7
  38. package/build/e2e/moveDocument.spec.js +0 -7
  39. package/build/e2e/searchDocuments.spec.js +0 -7
  40. package/build/e2e/setup.js +0 -33
  41. package/build/e2e/updateCollection.spec.js +0 -7
  42. package/build/e2e/updateComment.spec.js +0 -7
  43. package/build/e2e/updateDocument.spec.js +0 -7
  44. package/build/e2e/util/smokeTest.js +0 -88
  45. package/build/types.js +0 -1
  46. package/build/utils/listTools.js +0 -10
  47. /package/build/{client.js → outline/outlineClient.js} +0 -0
package/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A Model Context Protocol (MCP) server that provides tools for interacting with [Outline](https://www.getoutline.com/)'s API, enabling AI agents to manage documents, collections, and other entities programmatically through the Outline knowledge base platform.
4
4
 
5
+ > **Upgrade Notice:**
6
+
7
+ - v5 has introduced several breaking changes:
8
+ - this server support for both `stdio` and `sse` transport interfaces. It now solely exposes a [Streamable HTTP endpoint](https://modelcontextprotocol.io/specification/draft/basic/transports#streamable-http) at the `/mcp` route. If you require sse/stdio, downgrade to v4
9
+ - the `--port` CLI flag has been migrated to an environment variable, `OUTLINE_MCP_PORT`
10
+
5
11
  ## Features
6
12
 
7
13
  - **Document Management**
@@ -32,38 +38,31 @@ A Model Context Protocol (MCP) server that provides tools for interacting with [
32
38
  - **User Management**
33
39
  - ✅ List and filter users
34
40
 
35
- > **Note:** The SSE server implementation uses [Supergateway](https://github.com/supercorp-ai/supergateway), which provides more reliable connectivity when used with Cursor's MCP integration.
36
-
37
41
  ## Quick Start
38
42
 
39
43
  ### Prerequisites
40
44
 
41
- - Node.js (v18 or higher)
42
- - An Outline account with API access
45
+ - Node.js (v20 or higher)
46
+ - Outline account with API access
43
47
  - Outline API key with appropriate permissions
48
+ - Note: if you need to use the AI-powered ask feature, you must enable the "AI Answers" feature in your Outline Workspace settings
44
49
 
45
50
  ### Installation
46
51
 
47
52
  ```bash
48
- # Run directly with npx
49
- OUTLINE_API_KEY=… npx outline-mcp-server
53
+ # (preferred) Run directly with npx
54
+ OUTLINE_API_KEY=... npx outline-mcp-server
50
55
 
51
56
  # or install from npm
52
57
  npm install -g outline-mcp-server
53
- OUTLINE_API_KEY=… outline-mcp-server
54
-
55
- # Run with a custom port (default is 6060)
56
- OUTLINE_API_KEY=… outline-mcp-server --port 7070
58
+ OUTLINE_API_KEY=... outline-mcp-server
57
59
  ```
58
60
 
59
61
  ### Env
60
62
 
61
63
  - `OUTLINE_API_KEY` (_required_): your API key for outline, duh
62
- - `OUTLINE_BASE_URL` (_optional_): Alternative URL for your outline API (if using an alt domain/self-hosting)
63
-
64
- ### CLI Options
65
-
66
- - `--port <number>` (_optional_): Specify the port on which the server will run (default: 6060)
64
+ - `OUTLINE_API_URL` (_optional_): Alternative URL for your outline API (if using an alt domain/self-hosting)
65
+ - `OUTLINE_MCP_PORT` (_optional_): Specify the port on which the server will run (default: 6060)
67
66
 
68
67
  ### Usage
69
68
 
@@ -79,6 +78,16 @@ Example queries your AI assistant can now handle:
79
78
  - "Update the content of a document"
80
79
  - "Add a comment to a document"
81
80
 
81
+ ### Run the MCP server
82
+
83
+ ```bash
84
+ # Default port 6060
85
+ OUTLINE_API_KEY=... npm run start:http
86
+
87
+ # Or specify a custom port
88
+ OUTLINE_API_KEY=... OUTLINE_MCP_PORT=9001 npm run start:http
89
+ ```
90
+
82
91
  ## Development
83
92
 
84
93
  ```bash
@@ -94,13 +103,15 @@ npm install
94
103
 
95
104
  ```
96
105
  OUTLINE_API_KEY=your_outline_api_key_here
97
- OUTLINE_BASE_URL=https://your-outline-instance.com/api # Optional, defaults to https://app.getoutline.com/api
106
+
107
+ # Optional -------
108
+ # OUTLINE_API_URL=https://your-outline-instance.com/api # defaults to https://app.getoutline.com/api
109
+ # OUTLINE_MCP_PORT=9001
98
110
  ```
99
111
 
100
112
  ```bash
101
113
  # Builds/watches the project alongside running @modelcontextprotocol/inspector
102
114
  npm run dev
103
-
104
115
  ```
105
116
 
106
117
  ## Contributing
package/build/index.js CHANGED
@@ -1,58 +1,59 @@
1
- #!/usr/bin/env node
2
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
- import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
5
- import { registerTools } from './utils/importTools.js';
6
- import { omit } from 'omit-ts';
7
- // Dynamically import all tool files
8
- const toolDefinitions = await registerTools();
9
- console.log('\n', `loaded ${Object.keys(toolDefinitions).length} tools`, JSON.stringify(Object.keys(toolDefinitions)), '\n');
10
- const server = new Server({
11
- name: 'outline-mcp',
12
- version: '1.0.0',
13
- }, {
14
- capabilities: {
15
- tools: Object.entries(toolDefinitions).reduce((acc, [name, definition]) => {
16
- acc[name] = true;
17
- return acc;
18
- }, {}),
19
- },
20
- });
21
- // Register request handlers
22
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
23
- tools: Object.values(toolDefinitions).map(x => omit(x, ['handler'])),
24
- }));
25
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
26
- const { params } = request;
27
- const toolName = params.name;
28
- const parameters = params.arguments || {};
29
- const toolDefinition = toolDefinitions[toolName];
1
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
2
+ import fastify from 'fastify';
3
+ import { getMcpServer } from './utils/getMcpServer.js';
4
+ const server = fastify();
5
+ // Stateless mode (default, recommended for most deployments)
6
+ server.post('/mcp', async (request, reply) => {
30
7
  try {
31
- // Check if the tool is supported
32
- if (!toolDefinition) {
33
- return {
34
- error: { code: ErrorCode.InvalidRequest, message: `Tool ${toolName} not supported` },
35
- };
36
- }
37
- // Call the handler with the provided parameters
38
- const result = await toolDefinition.handler(parameters);
39
- return {
40
- content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
41
- };
8
+ const mcpServer = await getMcpServer();
9
+ const httpTransport = new StreamableHTTPServerTransport({
10
+ sessionIdGenerator: undefined,
11
+ });
12
+ reply.raw.on('close', () => {
13
+ httpTransport.close();
14
+ mcpServer.close();
15
+ });
16
+ await mcpServer.connect(httpTransport);
17
+ await httpTransport.handleRequest(request.raw, reply.raw, request.body);
42
18
  }
43
19
  catch (error) {
44
- if (error instanceof McpError) {
45
- return { error: { code: error.code, message: error.message } };
20
+ if (!reply.sent) {
21
+ reply.code(500).send({
22
+ jsonrpc: '2.0',
23
+ error: {
24
+ code: -32603,
25
+ message: 'Internal server error',
26
+ },
27
+ id: null,
28
+ });
46
29
  }
47
- return { error: { code: ErrorCode.InternalError, message: error.message } };
48
30
  }
49
31
  });
50
- async function main() {
51
- const transport = new StdioServerTransport();
52
- await server.connect(transport);
53
- console.log('Outline MCP server running on stdio');
54
- }
55
- main().catch(error => {
56
- console.error('Server error:', error);
57
- process.exit(1);
32
+ server.get('/mcp', async (request, reply) => {
33
+ reply.code(405).send({
34
+ jsonrpc: '2.0',
35
+ error: {
36
+ code: -32000,
37
+ message: 'Method not allowed.',
38
+ },
39
+ id: null,
40
+ });
41
+ });
42
+ server.delete('/mcp', async (request, reply) => {
43
+ reply.code(405).send({
44
+ jsonrpc: '2.0',
45
+ error: {
46
+ code: -32000,
47
+ message: 'Method not allowed.',
48
+ },
49
+ id: null,
50
+ });
51
+ });
52
+ const PORT = process.env.OUTLINE_MCP_PORT ? parseInt(process.env.OUTLINE_MCP_PORT, 10) : 6060;
53
+ server.listen({ port: PORT }, (err, address) => {
54
+ if (err) {
55
+ console.error(err);
56
+ process.exit(1);
57
+ }
58
+ console.log(`Outline MCP Server (streamable-http) running at ${address}/mcp`);
58
59
  });
@@ -1,26 +1,20 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import { z } from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('archive-document', {
6
7
  name: 'archive_document',
7
8
  description: 'Archive a document',
8
9
  inputSchema: {
9
- properties: {
10
- id: {
11
- type: 'string',
12
- description: 'ID of the document to archive',
13
- },
14
- },
15
- required: ['id'],
16
- type: 'object',
10
+ id: z.string().describe('ID of the document to archive'),
17
11
  },
18
- handler: async function handleArchiveDocument(args) {
12
+ async callback(args) {
19
13
  try {
20
14
  const response = await outlineClient.post('/documents.archive', {
21
15
  id: args.id,
22
16
  });
23
- return response.data.data;
17
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
24
18
  }
25
19
  catch (error) {
26
20
  console.error('Error archiving document:', error.message);
@@ -1,43 +1,29 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('ask_documents', {
6
7
  name: 'ask_documents',
7
8
  description: 'Query documents with natural language. This method allows asking direct questions of your documents - where possible an answer will be provided. Search results will be restricted to those accessible by the current access token. Note that "AI answers" must be enabled for the workspace.',
8
9
  inputSchema: {
9
- properties: {
10
- query: {
11
- type: 'string',
12
- description: 'The natural language query to ask about your documents',
13
- },
14
- userId: {
15
- type: 'string',
16
- description: 'Any documents that have not been edited by the user identifier will be filtered out',
17
- },
18
- collectionId: {
19
- type: 'string',
20
- description: 'A collection to search within',
21
- },
22
- documentId: {
23
- type: 'string',
24
- description: 'A document to search within',
25
- },
26
- statusFilter: {
27
- type: 'string',
28
- enum: ['draft', 'archived', 'published'],
29
- description: 'Any documents that are not in the specified status will be filtered out',
30
- },
31
- dateFilter: {
32
- type: 'string',
33
- enum: ['day', 'week', 'month', 'year'],
34
- description: 'Any documents that have not been updated within the specified period will be filtered out',
35
- },
36
- },
37
- required: ['query'],
38
- type: 'object',
10
+ query: z.string().describe('The natural language query to ask about your documents'),
11
+ userId: z
12
+ .string()
13
+ .describe('Any documents that have not been edited by the user identifier will be filtered out')
14
+ .optional(),
15
+ collectionId: z.string().describe('A collection to search within').optional(),
16
+ documentId: z.string().describe('A document to search within').optional(),
17
+ statusFilter: z
18
+ .enum(['draft', 'archived', 'published'])
19
+ .describe('Any documents that are not in the specified status will be filtered out')
20
+ .optional(),
21
+ dateFilter: z
22
+ .enum(['day', 'week', 'month', 'year'])
23
+ .describe('Any documents that have not been updated within the specified period will be filtered out')
24
+ .optional(),
39
25
  },
40
- handler: async function handleAskDocuments(args) {
26
+ async callback(args) {
41
27
  try {
42
28
  const payload = {
43
29
  query: args.query,
@@ -58,7 +44,7 @@ registerTool({
58
44
  payload.dateFilter = args.dateFilter;
59
45
  }
60
46
  const response = await outlineClient.post('/documents.answerQuestion', payload);
61
- return response.data;
47
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
62
48
  }
63
49
  catch (error) {
64
50
  console.error('Error asking documents:', error.message);
@@ -1,37 +1,22 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('create_collection', {
6
7
  name: 'create_collection',
7
8
  description: 'Create a new collection',
8
9
  inputSchema: {
9
- properties: {
10
- name: {
11
- type: 'string',
12
- description: 'Title of the collection',
13
- },
14
- description: {
15
- type: 'string',
16
- description: 'Content of the collection in markdown format',
17
- },
18
- permission: {
19
- type: 'string',
20
- description: 'Permission level for the collection (read, read_write)',
21
- },
22
- color: {
23
- type: 'string',
24
- description: 'Hex color code for the collection',
25
- },
26
- private: {
27
- type: 'boolean',
28
- description: 'Whether this collection is private',
29
- },
30
- },
31
- required: ['name'],
32
- type: 'object',
10
+ name: z.string().describe('Title of the collection'),
11
+ description: z.string().describe('Content of the collection in markdown format').optional(),
12
+ permission: z
13
+ .enum(['read', 'read_write'])
14
+ .describe('Permission level for the collection')
15
+ .optional(),
16
+ color: z.string().describe('Hex color code for the collection').optional(),
17
+ private: z.boolean().describe('Whether this collection is private').optional(),
33
18
  },
34
- handler: async function handleCreateCollection(args) {
19
+ async callback(args) {
35
20
  try {
36
21
  const payload = {
37
22
  name: args.name,
@@ -49,7 +34,7 @@ registerTool({
49
34
  payload.private = args.private;
50
35
  }
51
36
  const response = await outlineClient.post('/collections.create', payload);
52
- return response.data.data;
37
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
53
38
  }
54
39
  catch (error) {
55
40
  console.error('Error creating collection:', error.message);
@@ -1,33 +1,21 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('create_comment', {
6
7
  name: 'create_comment',
7
8
  description: 'Create a new comment on a document',
8
9
  inputSchema: {
9
- properties: {
10
- documentId: {
11
- type: 'string',
12
- description: 'ID of the document to comment on',
13
- },
14
- text: {
15
- type: 'string',
16
- description: 'Content of the comment in markdown format',
17
- },
18
- parentCommentId: {
19
- type: 'string',
20
- description: 'ID of the parent comment (if replying to a comment)',
21
- },
22
- data: {
23
- type: 'object',
24
- description: 'Additional data for the comment (optional)',
25
- },
26
- },
27
- required: ['documentId', 'text'],
28
- type: 'object',
10
+ documentId: z.string().describe('ID of the document to comment on'),
11
+ text: z.string().describe('Content of the comment in markdown format'),
12
+ parentCommentId: z
13
+ .string()
14
+ .describe('ID of the parent comment (if replying to a comment)')
15
+ .optional(),
16
+ data: z.object({}).describe('Additional data for the comment (optional)').optional(),
29
17
  },
30
- handler: async function handleCreateComment(args) {
18
+ async callback(args) {
31
19
  try {
32
20
  const payload = {
33
21
  documentId: args.documentId,
@@ -40,7 +28,7 @@ registerTool({
40
28
  payload.data = args.data;
41
29
  }
42
30
  const response = await outlineClient.post('/comments.create', payload);
43
- return response.data.data;
31
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
44
32
  }
45
33
  catch (error) {
46
34
  console.error('Error creating comment:', error.message);
@@ -1,42 +1,23 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('create_document', {
6
7
  name: 'create_document',
7
8
  description: 'Create a new document',
8
9
  inputSchema: {
9
- properties: {
10
- title: {
11
- type: 'string',
12
- description: 'Title of the document',
13
- },
14
- text: {
15
- type: 'string',
16
- description: 'Content of the document in markdown format',
17
- },
18
- collectionId: {
19
- type: 'string',
20
- description: 'ID of the collection to add the document to',
21
- },
22
- parentDocumentId: {
23
- type: 'string',
24
- description: 'ID of the parent document (if creating a nested document)',
25
- },
26
- publish: {
27
- type: 'boolean',
28
- default: true,
29
- description: 'Whether to publish the document immediately',
30
- },
31
- template: {
32
- type: 'boolean',
33
- description: 'Whether this document is a template',
34
- },
35
- },
36
- required: ['title', 'text', 'collectionId'],
37
- type: 'object',
10
+ title: z.string().describe('Title of the document'),
11
+ text: z.string().describe('Content of the document in markdown format'),
12
+ collectionId: z.string().describe('ID of the collection to add the document to'),
13
+ parentDocumentId: z
14
+ .string()
15
+ .describe('ID of the parent document (if creating a nested document)')
16
+ .optional(),
17
+ publish: z.boolean().describe('Whether to publish the document immediately').optional(),
18
+ template: z.boolean().describe('Whether this document is a template').optional(),
38
19
  },
39
- handler: async function handleCreateDocument(args) {
20
+ async callback(args) {
40
21
  try {
41
22
  const payload = {
42
23
  title: args.title,
@@ -53,7 +34,7 @@ registerTool({
53
34
  payload.template = args.template;
54
35
  }
55
36
  const response = await outlineClient.post('/documents', payload);
56
- return response.data.data;
37
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
57
38
  }
58
39
  catch (error) {
59
40
  console.error('Error creating document:', error.message);
@@ -1,27 +1,21 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('create_template_from_document', {
6
7
  name: 'create_template_from_document',
7
8
  description: 'Create a template from an existing document. This method allows you to create a new template using an existing document as the basis.',
8
9
  inputSchema: {
9
- properties: {
10
- id: {
11
- type: 'string',
12
- description: 'The ID of the document to use as a basis for the template',
13
- },
14
- },
15
- required: ['id'],
16
- type: 'object',
10
+ id: z.string().describe('The ID of the document to use as a basis for the template'),
17
11
  },
18
- handler: async function handleCreateTemplateFromDocument(args) {
12
+ async callback(args) {
19
13
  try {
20
14
  const payload = {
21
15
  id: args.id,
22
16
  };
23
17
  const response = await outlineClient.post('/documents.templatize', payload);
24
- return response.data.data;
18
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
25
19
  }
26
20
  catch (error) {
27
21
  console.error('Error creating template from document:', error.message);
@@ -1,26 +1,20 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('delete_comment', {
6
7
  name: 'delete_comment',
7
8
  description: 'Delete a comment from a document',
8
9
  inputSchema: {
9
- properties: {
10
- id: {
11
- type: 'string',
12
- description: 'ID of the comment to delete',
13
- },
14
- },
15
- required: ['id'],
16
- type: 'object',
10
+ id: z.string().describe('ID of the comment to delete'),
17
11
  },
18
- handler: async function handleDeleteComment(args) {
12
+ async callback(args) {
19
13
  try {
20
14
  const response = await outlineClient.post('/comments.delete', {
21
15
  id: args.id,
22
16
  });
23
- return response.data.success;
17
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.success) }] };
24
18
  }
25
19
  catch (error) {
26
20
  console.error('Error deleting comment:', error.message);
@@ -1,26 +1,20 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('delete_document', {
6
7
  name: 'delete_document',
7
8
  description: 'Delete a document',
8
9
  inputSchema: {
9
- properties: {
10
- id: {
11
- type: 'string',
12
- description: 'ID of the document to delete',
13
- },
14
- },
15
- required: ['id'],
16
- type: 'object',
10
+ id: z.string().describe('ID of the document to delete'),
17
11
  },
18
- handler: async function handleDeleteDocument(args) {
12
+ async callback(args) {
19
13
  try {
20
14
  const response = await outlineClient.post('/documents.delete', {
21
15
  id: args.id,
22
16
  });
23
- return response.data.success;
17
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.success) }] };
24
18
  }
25
19
  catch (error) {
26
20
  console.error('Error deleting document:', error.message);
@@ -1,24 +1,18 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../client.js';
3
- import { registerTool } from '../utils/listTools.js';
2
+ import { outlineClient } from '../outline/outlineClient.js';
3
+ import toolRegistry from '../utils/toolRegistry.js';
4
+ import z from 'zod';
4
5
  // Register this tool
5
- registerTool({
6
+ toolRegistry.register('get_collection', {
6
7
  name: 'get_collection',
7
8
  description: 'Get details about a specific collection',
8
9
  inputSchema: {
9
- properties: {
10
- id: {
11
- type: 'string',
12
- description: 'ID of the collection to retrieve',
13
- },
14
- },
15
- required: ['id'],
16
- type: 'object',
10
+ id: z.string().describe('ID of the collection to retrieve'),
17
11
  },
18
- handler: async function handleGetCollection(args) {
12
+ async callback(args) {
19
13
  try {
20
14
  const response = await outlineClient.post(`/collections.info`, { id: args.id });
21
- return response.data.data;
15
+ return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
22
16
  }
23
17
  catch (error) {
24
18
  console.error('Error getting collection:', error.message);