outline-mcp-server 5.4.1 → 5.5.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.
package/README.md CHANGED
@@ -59,13 +59,18 @@ One click install in Cursor:
59
59
  `outline-mcp-server` supports the latest streamable-http protocol, the deprecated sse protocol, and good ole fashioned stdio.
60
60
 
61
61
  ```bash
62
- # S-HTTP/SSE servers
62
+ # S-HTTP/SSE servers (with optional env var)
63
63
  OUTLINE_API_KEY=... npx -y outline-mcp-server@latest
64
64
 
65
- # STDIO
65
+ # S-HTTP/SSE servers (without env var, use headers for auth)
66
+ npx -y outline-mcp-server@latest
67
+
68
+ # STDIO (requires env var)
66
69
  OUTLINE_API_KEY=... npx -y --package=outline-mcp-server@latest -c outline-mcp-server-stdio
67
70
  ```
68
71
 
72
+ When running HTTP/SSE servers without an environment variable, you'll need to provide the API key in your request headers. The server will display available authentication methods on startup.
73
+
69
74
  ### Cursor (mcp.json)
70
75
 
71
76
  Add the following MCP definition to your configuration:
@@ -85,9 +90,33 @@ Add the following MCP definition to your configuration:
85
90
  }
86
91
  ```
87
92
 
93
+ ### Authentication
94
+
95
+ The Outline MCP server supports two authentication methods:
96
+
97
+ 1. **Environment Variable (Required for stdio mode)**: Set `OUTLINE_API_KEY` as an environment variable
98
+ 2. **Request Headers (HTTP/SSE modes)**: Provide the API key in request headers
99
+
100
+ For **stdio mode**, the API key environment variable is required and validated on startup.
101
+
102
+ For **HTTP/SSE modes**, you have two options:
103
+
104
+ - Set `OUTLINE_API_KEY` as an environment variable (fallback method)
105
+ - Provide API key in request headers (recommended for per-request authentication)
106
+
107
+ #### Header-based Authentication
108
+
109
+ When using HTTP/SSE endpoints, you can provide the API key using any of these headers:
110
+
111
+ - `x-outline-api-key: your_api_key_here`
112
+ - `outline-api-key: your_api_key_here`
113
+ - `authorization: Bearer your_api_key_here`
114
+
115
+ If no header is provided, the server will fall back to the `OUTLINE_API_KEY` environment variable. If neither is available, the request will fail with an authentication error.
116
+
88
117
  ### Env vars
89
118
 
90
- - `OUTLINE_API_KEY` (_required_): your API key for outline, duh
119
+ - `OUTLINE_API_KEY` (_required for stdio, optional for HTTP/SSE_): your API key for outline
91
120
  - `OUTLINE_API_URL` (_optional_): Alternative URL for your outline API (if using an alt domain/self-hosting)
92
121
  - `OUTLINE_MCP_PORT` (_optional_): Specify the port on which the server will run (default: 6060)
93
122
  - `OUTLINE_MCP_HOST` (_optional_): Host/IP to bind the server to (default: 127.0.0.1). Use 0.0.0.0 to bind to all network interfaces
package/build/index.js CHANGED
@@ -3,29 +3,70 @@ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/
3
3
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
4
4
  import fastify from 'fastify';
5
5
  import { getMcpServer } from './utils/getMcpServer.js';
6
- const mcpServer = await getMcpServer();
6
+ import { RequestContext } from './utils/toolRegistry.js';
7
+ /**
8
+ * Extracts API key from request headers
9
+ */
10
+ function extractApiKey(request) {
11
+ // Check common header variations
12
+ const headers = request.headers;
13
+ return (headers['x-outline-api-key'] ||
14
+ headers['outline-api-key'] ||
15
+ headers['authorization']?.replace(/^Bearer\s+/i, ''));
16
+ }
17
+ /**
18
+ * Sets up request context with API key
19
+ */
20
+ function setupRequestContext(request) {
21
+ const apiKey = extractApiKey(request);
22
+ const envApiKey = process.env.OUTLINE_API_KEY;
23
+ if (apiKey) {
24
+ // Use header API key
25
+ const context = RequestContext.getInstance();
26
+ context.setApiKey(apiKey);
27
+ console.log('Using API key from request headers');
28
+ }
29
+ else if (envApiKey) {
30
+ // Use environment variable API key
31
+ const context = RequestContext.getInstance();
32
+ context.setApiKey(envApiKey);
33
+ console.log('Using API key from environment variable');
34
+ }
35
+ else {
36
+ // No API key available
37
+ console.log('No API key provided in headers and no default environment variable set.');
38
+ console.log('Please set the OUTLINE_API_KEY environment variable or provide it in the request headers.');
39
+ throw new Error('API key required: Set OUTLINE_API_KEY environment variable or provide x-outline-api-key header');
40
+ }
41
+ }
7
42
  // HTTP mode - default behavior
8
43
  const app = fastify();
9
44
  // Stateless mode (default, recommended for most deployments)
10
45
  app.post('/mcp', async (request, reply) => {
11
46
  try {
47
+ // Setup request context with API key
48
+ setupRequestContext(request);
49
+ const mcpServer = await getMcpServer();
12
50
  const httpTransport = new StreamableHTTPServerTransport({
13
51
  sessionIdGenerator: undefined,
14
52
  });
15
53
  reply.raw.on('close', () => {
16
54
  httpTransport.close();
17
55
  mcpServer.close();
56
+ // Clean up context
57
+ RequestContext.resetInstance();
18
58
  });
19
59
  await mcpServer.connect(httpTransport);
20
60
  await httpTransport.handleRequest(request.raw, reply.raw, request.body);
21
61
  }
22
62
  catch (error) {
63
+ console.error('Error in /mcp endpoint:', error.message);
23
64
  if (!reply.sent) {
24
65
  reply.code(500).send({
25
66
  jsonrpc: '2.0',
26
67
  error: {
27
68
  code: -32603,
28
- message: 'Internal server error',
69
+ message: error.message || 'Internal server error',
29
70
  },
30
71
  id: null,
31
72
  });
@@ -56,6 +97,9 @@ app.delete('/mcp', async (request, reply) => {
56
97
  let sseTransport = null;
57
98
  app.get('/sse', async (request, reply) => {
58
99
  try {
100
+ // Setup request context with API key
101
+ setupRequestContext(request);
102
+ const mcpServer = await getMcpServer();
59
103
  // Create SSE transport for legacy clients
60
104
  if (!sseTransport) {
61
105
  sseTransport = new SSEServerTransport('/messages', reply.raw);
@@ -63,13 +107,13 @@ app.get('/sse', async (request, reply) => {
63
107
  }
64
108
  }
65
109
  catch (error) {
66
- console.error(error);
110
+ console.error('Error in /sse endpoint:', error.message);
67
111
  if (!reply.sent) {
68
112
  reply.code(500).send({
69
113
  jsonrpc: '2.0',
70
114
  error: {
71
115
  code: -32603,
72
- message: 'Internal server error',
116
+ message: error.message || 'Internal server error',
73
117
  },
74
118
  id: null,
75
119
  });
@@ -78,11 +122,18 @@ app.get('/sse', async (request, reply) => {
78
122
  });
79
123
  // Legacy message endpoint for older clients
80
124
  app.post('/messages', async (req, res) => {
81
- if (!sseTransport) {
82
- res.status(400).send('No transport found');
83
- return;
125
+ try {
126
+ setupRequestContext(req);
127
+ if (!sseTransport) {
128
+ res.status(400).send('No transport found');
129
+ return;
130
+ }
131
+ await sseTransport.handlePostMessage(req.raw, res.raw, req.body);
132
+ }
133
+ catch (error) {
134
+ console.error('Error in /messages endpoint:', error.message);
135
+ res.status(500).send(error.message || 'Internal server error');
84
136
  }
85
- await sseTransport.handlePostMessage(req.raw, res.raw, req.body);
86
137
  });
87
138
  const PORT = process.env.OUTLINE_MCP_PORT ? parseInt(process.env.OUTLINE_MCP_PORT, 10) : 6060;
88
139
  const HOST = process.env.OUTLINE_MCP_HOST || '127.0.0.1';
@@ -92,5 +143,9 @@ app.listen({ port: PORT, host: HOST }, (err, address) => {
92
143
  process.exit(1);
93
144
  }
94
145
  console.log(`\n\nOutline MCP Server running:\n\tstreamable-http: http://${HOST}:${PORT}/mcp\n\tsse (deprecated): http://${HOST}:${PORT}/sse\n\n`);
95
- console.log('To use this MCP server in stdio mode, run it via `outline-mcp-server-stdio`.');
146
+ console.log('API Key Configuration:');
147
+ console.log('- Set OUTLINE_API_KEY environment variable for default authentication');
148
+ console.log('- Or provide x-outline-api-key header in requests for per-request authentication');
149
+ console.log('- Or provide authorization header with Bearer token');
150
+ console.log('\nTo use this MCP server in stdio mode, run it via `outline-mcp-server-stdio`.');
96
151
  });
@@ -2,19 +2,56 @@ import axios from 'axios';
2
2
  import { config } from 'dotenv';
3
3
  import { dirname, join } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
+ import { RequestContext } from '../utils/toolRegistry.js';
5
6
  const __dirname = dirname(fileURLToPath(import.meta.url));
6
7
  config({ path: join(__dirname, '..', '.env') });
7
- const API_KEY = process.env.OUTLINE_API_KEY;
8
8
  const API_URL = process.env.OUTLINE_API_URL || 'https://app.getoutline.com/api';
9
- if (!API_KEY) {
10
- throw new Error('OUTLINE_API_KEY environment variable is required');
9
+ /**
10
+ * Creates an Outline API client with the specified API key
11
+ */
12
+ export function createOutlineClient(apiKey) {
13
+ const key = apiKey || process.env.OUTLINE_API_KEY;
14
+ if (!key) {
15
+ throw new Error('OUTLINE_API_KEY must be provided either as parameter or environment variable');
16
+ }
17
+ return axios.create({
18
+ baseURL: API_URL,
19
+ headers: {
20
+ Authorization: `Bearer ${key}`,
21
+ 'Content-Type': 'application/json',
22
+ Accept: 'application/json',
23
+ },
24
+ });
11
25
  }
12
- // Create axios client with authentication
13
- export const outlineClient = axios.create({
14
- baseURL: API_URL,
15
- headers: {
16
- Authorization: `Bearer ${API_KEY}`,
17
- 'Content-Type': 'application/json',
18
- Accept: 'application/json',
26
+ /**
27
+ * Gets an outline client using context API key first, then environment variable
28
+ */
29
+ export function getOutlineClient() {
30
+ const context = RequestContext.getInstance();
31
+ const contextApiKey = context.getApiKey();
32
+ if (contextApiKey) {
33
+ return createOutlineClient(contextApiKey);
34
+ }
35
+ return createOutlineClient();
36
+ }
37
+ /**
38
+ * Gets the default outline client using environment variable
39
+ * Only validates when called, not on import
40
+ */
41
+ export function getDefaultOutlineClient() {
42
+ return createOutlineClient();
43
+ }
44
+ /**
45
+ * Default client instance for backward compatibility
46
+ * Note: This will only validate API key when first accessed, not on import
47
+ */
48
+ let _defaultClient = null;
49
+ export const outlineClient = new Proxy({}, {
50
+ get(target, prop) {
51
+ if (!_defaultClient) {
52
+ _defaultClient = getDefaultOutlineClient();
53
+ }
54
+ const value = _defaultClient[prop];
55
+ return typeof value === 'function' ? value.bind(_defaultClient) : value;
19
56
  },
20
57
  });
package/build/stdio.js CHANGED
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env bun
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { getMcpServer } from './utils/getMcpServer.js';
4
+ // Validate API key for stdio mode
5
+ if (!process.env.OUTLINE_API_KEY) {
6
+ console.error('Error: OUTLINE_API_KEY environment variable is required for stdio mode');
7
+ process.exit(1);
8
+ }
4
9
  const mcpServer = await getMcpServer();
5
10
  // STDIO mode - for direct client connections
6
11
  const transport = new StdioServerTransport();
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import { z } from 'zod';
5
5
  // Register this tool
@@ -11,7 +11,8 @@ toolRegistry.register('archive-document', {
11
11
  },
12
12
  async callback(args) {
13
13
  try {
14
- const response = await outlineClient.post('/documents.archive', {
14
+ const client = getOutlineClient();
15
+ const response = await client.post('/documents.archive', {
15
16
  id: args.id,
16
17
  });
17
18
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -43,7 +43,8 @@ toolRegistry.register('ask_documents', {
43
43
  if (args.dateFilter) {
44
44
  payload.dateFilter = args.dateFilter;
45
45
  }
46
- const response = await outlineClient.post('/documents.answerQuestion', payload);
46
+ const client = getOutlineClient();
47
+ const response = await client.post('/documents.answerQuestion', payload);
47
48
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
48
49
  }
49
50
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -33,7 +33,8 @@ toolRegistry.register('create_collection', {
33
33
  if (args.private !== undefined) {
34
34
  payload.private = args.private;
35
35
  }
36
- const response = await outlineClient.post('/collections.create', payload);
36
+ const client = getOutlineClient();
37
+ const response = await client.post('/collections.create', payload);
37
38
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
38
39
  }
39
40
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -27,7 +27,8 @@ toolRegistry.register('create_comment', {
27
27
  if (args.data) {
28
28
  payload.data = args.data;
29
29
  }
30
- const response = await outlineClient.post('/comments.create', payload);
30
+ const client = getOutlineClient();
31
+ const response = await client.post('/comments.create', payload);
31
32
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
32
33
  }
33
34
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -33,7 +33,8 @@ toolRegistry.register('create_document', {
33
33
  if (args.template !== undefined) {
34
34
  payload.template = args.template;
35
35
  }
36
- const response = await outlineClient.post('/documents.create', payload);
36
+ const client = getOutlineClient();
37
+ const response = await client.post('/documents.create', payload);
37
38
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
38
39
  }
39
40
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -14,7 +14,8 @@ toolRegistry.register('create_template_from_document', {
14
14
  const payload = {
15
15
  id: args.id,
16
16
  };
17
- const response = await outlineClient.post('/documents.templatize', payload);
17
+ const client = getOutlineClient();
18
+ const response = await client.post('/documents.templatize', payload);
18
19
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
19
20
  }
20
21
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -11,7 +11,8 @@ toolRegistry.register('delete_comment', {
11
11
  },
12
12
  async callback(args) {
13
13
  try {
14
- const response = await outlineClient.post('/comments.delete', {
14
+ const client = getOutlineClient();
15
+ const response = await client.post('/comments.delete', {
15
16
  id: args.id,
16
17
  });
17
18
  return { content: [{ type: 'text', text: JSON.stringify(response.data.success) }] };
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -11,7 +11,8 @@ toolRegistry.register('delete_document', {
11
11
  },
12
12
  async callback(args) {
13
13
  try {
14
- const response = await outlineClient.post('/documents.delete', {
14
+ const client = getOutlineClient();
15
+ const response = await client.post('/documents.delete', {
15
16
  id: args.id,
16
17
  });
17
18
  return { content: [{ type: 'text', text: JSON.stringify(response.data.success) }] };
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -11,7 +11,8 @@ toolRegistry.register('get_collection', {
11
11
  },
12
12
  async callback(args) {
13
13
  try {
14
- const response = await outlineClient.post(`/collections.info`, { id: args.id });
14
+ const client = getOutlineClient();
15
+ const response = await client.post(`/collections.info`, { id: args.id });
15
16
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
16
17
  }
17
18
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -13,7 +13,8 @@ toolRegistry.register('get_document', {
13
13
  },
14
14
  async callback(args) {
15
15
  try {
16
- const response = await outlineClient.post('/documents.info', { id: args.id });
16
+ const client = getOutlineClient();
17
+ const response = await client.post('/documents.info', { id: args.id });
17
18
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
18
19
  }
19
20
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -15,7 +15,8 @@ toolRegistry.register('list_collections', {
15
15
  if (args.limit) {
16
16
  payload.limit = args.limit;
17
17
  }
18
- const response = await outlineClient.post('/collections.list', payload);
18
+ const client = getOutlineClient();
19
+ const response = await client.post('/collections.list', payload);
19
20
  return {
20
21
  content: [
21
22
  {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -49,7 +49,8 @@ toolRegistry.register('list_documents', {
49
49
  payload.query = args.query;
50
50
  }
51
51
  // Make the POST request to the documents.list endpoint
52
- const response = await outlineClient.post('/documents.list', payload);
52
+ const client = getOutlineClient();
53
+ const response = await client.post('/documents.list', payload);
53
54
  // Transform the response to a more usable format
54
55
  const documents = response.data.data;
55
56
  // Return the documents with additional metadata
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -55,7 +55,8 @@ toolRegistry.register('list_users', {
55
55
  if (args.role) {
56
56
  payload.role = args.role;
57
57
  }
58
- const response = await outlineClient.post('/users.list', payload);
58
+ const client = getOutlineClient();
59
+ const response = await client.post('/users.list', payload);
59
60
  return {
60
61
  content: [
61
62
  { type: 'text', text: `users: ${JSON.stringify(response.data.data)}` },
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -28,7 +28,8 @@ toolRegistry.register('move_document', {
28
28
  if (args.parentDocumentId) {
29
29
  payload.parentDocumentId = args.parentDocumentId;
30
30
  }
31
- const response = await outlineClient.post('/documents.move', payload);
31
+ const client = getOutlineClient();
32
+ const response = await client.post('/documents.move', payload);
32
33
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
33
34
  }
34
35
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -22,7 +22,8 @@ toolRegistry.register('search_documents', {
22
22
  if (args.limit) {
23
23
  payload.limit = args.limit;
24
24
  }
25
- const response = await outlineClient.post('/documents.search', payload);
25
+ const client = getOutlineClient();
26
+ const response = await client.post('/documents.search', payload);
26
27
  return {
27
28
  content: [
28
29
  { type: 'text', text: `documents: ${JSON.stringify(response.data.data)}` },
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -33,7 +33,8 @@ toolRegistry.register('update_collection', {
33
33
  if (args.color) {
34
34
  payload.color = args.color;
35
35
  }
36
- const response = await outlineClient.post('/collections.update', payload);
36
+ const client = getOutlineClient();
37
+ const response = await client.post('/collections.update', payload);
37
38
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
38
39
  }
39
40
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -22,7 +22,8 @@ toolRegistry.register('update_comment', {
22
22
  if (args.data) {
23
23
  payload.data = args.data;
24
24
  }
25
- const response = await outlineClient.post('/comments.update', payload);
25
+ const client = getOutlineClient();
26
+ const response = await client.post('/comments.update', payload);
26
27
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
27
28
  }
28
29
  catch (error) {
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
2
- import { outlineClient } from '../outline/outlineClient.js';
2
+ import { getOutlineClient } from '../outline/outlineClient.js';
3
3
  import toolRegistry from '../utils/toolRegistry.js';
4
4
  import z from 'zod';
5
5
  // Register this tool
@@ -30,7 +30,8 @@ toolRegistry.register('update_document', {
30
30
  if (args.done !== undefined) {
31
31
  payload.done = args.done;
32
32
  }
33
- const response = await outlineClient.post('/documents.update', payload);
33
+ const client = getOutlineClient();
34
+ const response = await client.post('/documents.update', payload);
34
35
  return { content: [{ type: 'text', text: JSON.stringify(response.data.data) }] };
35
36
  }
36
37
  catch (error) {
@@ -1,3 +1,34 @@
1
+ // Context to store request-specific data
2
+ export class RequestContext {
3
+ constructor() {
4
+ this.context = new Map();
5
+ }
6
+ static getInstance() {
7
+ if (!RequestContext.instance) {
8
+ RequestContext.instance = new RequestContext();
9
+ }
10
+ return RequestContext.instance;
11
+ }
12
+ static resetInstance() {
13
+ RequestContext.instance = null;
14
+ }
15
+ set(key, value) {
16
+ this.context.set(key, value);
17
+ }
18
+ get(key) {
19
+ return this.context.get(key);
20
+ }
21
+ clear() {
22
+ this.context.clear();
23
+ }
24
+ getApiKey() {
25
+ return this.get('apiKey');
26
+ }
27
+ setApiKey(apiKey) {
28
+ this.set('apiKey', apiKey);
29
+ }
30
+ }
31
+ RequestContext.instance = null;
1
32
  class ToolRegistry {
2
33
  constructor() {
3
34
  this.registry = new Map();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "outline-mcp-server",
3
- "version": "5.4.1",
3
+ "version": "5.5.0",
4
4
  "description": "An MCP server for interacting with Outline's API",
5
5
  "type": "module",
6
6
  "bin": {