@zereight/mcp-gitlab 2.0.7 → 2.0.9

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.
@@ -0,0 +1,315 @@
1
+ /**
2
+ * Remote Authorization Tests
3
+ * Tests for per-session HTTP header-based authorization
4
+ */
5
+ import { describe, test, after, before } from 'node:test';
6
+ import assert from 'node:assert';
7
+ import fetch from 'node-fetch';
8
+ import { launchServer, findAvailablePort, cleanupServers, TransportMode, checkHealthEndpoint, HOST } from './utils/server-launcher.js';
9
+ console.log('🔐 Remote Authorization Tests');
10
+ console.log('');
11
+ // Configuration check
12
+ const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com";
13
+ const GITLAB_TOKEN = process.env.GITLAB_TOKEN_TEST || process.env.GITLAB_TOKEN;
14
+ const TEST_PROJECT_ID = process.env.TEST_PROJECT_ID;
15
+ console.log('🔧 Test Configuration:');
16
+ console.log(` GitLab URL: ${GITLAB_API_URL}`);
17
+ console.log(` Token: ${GITLAB_TOKEN ? '✅ Provided' : '❌ Missing'}`);
18
+ console.log(` Project ID: ${TEST_PROJECT_ID || '❌ Missing'}`);
19
+ // Validate required configuration
20
+ if (!GITLAB_TOKEN) {
21
+ console.error('❌ Error: GITLAB_TOKEN_TEST or GITLAB_TOKEN environment variable is required for testing');
22
+ console.error(' Set one of these variables to your GitLab API token');
23
+ process.exit(1);
24
+ }
25
+ if (!TEST_PROJECT_ID) {
26
+ console.error('❌ Error: TEST_PROJECT_ID environment variable is required for testing');
27
+ console.error(' Set this variable to a valid GitLab project ID (e.g., "123" or "group/project")');
28
+ process.exit(1);
29
+ }
30
+ console.log('✅ Configuration validated');
31
+ console.log('');
32
+ let servers = [];
33
+ // Cleanup function for all tests
34
+ const cleanup = () => {
35
+ cleanupServers(servers);
36
+ servers = [];
37
+ };
38
+ // Handle process termination
39
+ process.on('SIGINT', cleanup);
40
+ process.on('SIGTERM', cleanup);
41
+ process.on('exit', cleanup);
42
+ /**
43
+ * Helper to send MCP request with custom headers
44
+ */
45
+ async function sendMCPRequest(url, method, headers = {}) {
46
+ const response = await fetch(url, {
47
+ method: 'POST',
48
+ headers: {
49
+ 'Content-Type': 'application/json',
50
+ ...headers,
51
+ },
52
+ body: JSON.stringify(method),
53
+ });
54
+ if (!response.ok) {
55
+ const text = await response.text();
56
+ throw new Error(`HTTP ${response.status}: ${text}`);
57
+ }
58
+ return response.json();
59
+ }
60
+ describe('Remote Authorization - Streamable HTTP with Authorization header', () => {
61
+ let server;
62
+ let port;
63
+ let mcpUrl;
64
+ before(async () => {
65
+ port = await findAvailablePort();
66
+ server = await launchServer({
67
+ mode: TransportMode.STREAMABLE_HTTP,
68
+ port,
69
+ timeout: 3000,
70
+ env: {
71
+ STREAMABLE_HTTP: 'true',
72
+ REMOTE_AUTHORIZATION: 'true',
73
+ GITLAB_API_URL: `${GITLAB_API_URL}/api/v4`,
74
+ GITLAB_PROJECT_ID: TEST_PROJECT_ID,
75
+ GITLAB_READ_ONLY_MODE: 'true',
76
+ // Explicitly no GITLAB_PERSONAL_ACCESS_TOKEN
77
+ }
78
+ });
79
+ servers.push(server);
80
+ mcpUrl = `http://${HOST}:${port}/mcp`;
81
+ // Verify server started successfully
82
+ assert.ok(server.process.pid !== undefined, 'Server process should have PID');
83
+ // Verify health check
84
+ if (server.port) {
85
+ const health = await checkHealthEndpoint(server.port);
86
+ assert.strictEqual(health.status, 'healthy', 'Health status should be healthy');
87
+ }
88
+ console.log('Server started with remote authorization enabled');
89
+ });
90
+ after(async () => {
91
+ cleanup();
92
+ console.log('Server stopped');
93
+ });
94
+ test('should reject request without Authorization header', async () => {
95
+ const initRequest = {
96
+ jsonrpc: '2.0',
97
+ id: 1,
98
+ method: 'initialize',
99
+ params: {
100
+ protocolVersion: '2024-11-05',
101
+ capabilities: {},
102
+ clientInfo: { name: 'test-client', version: '1.0.0' }
103
+ }
104
+ };
105
+ try {
106
+ await sendMCPRequest(mcpUrl, initRequest, {
107
+ 'mcp-session-id': 'test-session-no-auth'
108
+ });
109
+ assert.fail('Should have rejected request without auth header');
110
+ }
111
+ catch (error) {
112
+ assert.ok(error instanceof Error);
113
+ assert.ok(error.message.includes('401'), 'Should get 401 Unauthorized');
114
+ }
115
+ });
116
+ test('should accept request with Authorization Bearer header', async () => {
117
+ const sessionId = `test-session-bearer-${Date.now()}`;
118
+ const initRequest = {
119
+ jsonrpc: '2.0',
120
+ id: 1,
121
+ method: 'initialize',
122
+ params: {
123
+ protocolVersion: '2024-11-05',
124
+ capabilities: {},
125
+ clientInfo: { name: 'test-client', version: '1.0.0' }
126
+ }
127
+ };
128
+ const response = await sendMCPRequest(mcpUrl, initRequest, {
129
+ 'mcp-session-id': sessionId,
130
+ 'authorization': `Bearer ${GITLAB_TOKEN}`
131
+ });
132
+ assert.ok(response, 'Should get response');
133
+ assert.ok(response.result, 'Should have result');
134
+ assert.strictEqual(response.result.protocolVersion, '2024-11-05', 'Protocol version should match');
135
+ });
136
+ test('should reuse auth from first request in subsequent requests', async () => {
137
+ const sessionId = `test-session-reuse-${Date.now()}`;
138
+ // First request with auth
139
+ const initRequest = {
140
+ jsonrpc: '2.0',
141
+ id: 1,
142
+ method: 'initialize',
143
+ params: {
144
+ protocolVersion: '2024-11-05',
145
+ capabilities: {},
146
+ clientInfo: { name: 'test-client', version: '1.0.0' }
147
+ }
148
+ };
149
+ const initResponse = await sendMCPRequest(mcpUrl, initRequest, {
150
+ 'mcp-session-id': sessionId,
151
+ 'authorization': `Bearer ${GITLAB_TOKEN}`
152
+ });
153
+ assert.ok(initResponse.result, 'Init should succeed');
154
+ // Second request without auth header (should reuse)
155
+ const listToolsRequest = {
156
+ jsonrpc: '2.0',
157
+ id: 2,
158
+ method: 'tools/list',
159
+ params: {}
160
+ };
161
+ const listResponse = await sendMCPRequest(mcpUrl, listToolsRequest, {
162
+ 'mcp-session-id': sessionId
163
+ // No authorization header
164
+ });
165
+ assert.ok(listResponse.result, 'List tools should succeed with reused auth');
166
+ assert.ok(Array.isArray(listResponse.result.tools), 'Should return tools array');
167
+ assert.ok(listResponse.result.tools.length > 0, 'Should have at least one tool');
168
+ });
169
+ test('should call tool with Bearer token', async () => {
170
+ const sessionId = `test-session-tool-${Date.now()}`;
171
+ // Initialize
172
+ const initRequest = {
173
+ jsonrpc: '2.0',
174
+ id: 1,
175
+ method: 'initialize',
176
+ params: {
177
+ protocolVersion: '2024-11-05',
178
+ capabilities: {},
179
+ clientInfo: { name: 'test-client', version: '1.0.0' }
180
+ }
181
+ };
182
+ await sendMCPRequest(mcpUrl, initRequest, {
183
+ 'mcp-session-id': sessionId,
184
+ 'authorization': `Bearer ${GITLAB_TOKEN}`
185
+ });
186
+ // Call tool
187
+ const callToolRequest = {
188
+ jsonrpc: '2.0',
189
+ id: 2,
190
+ method: 'tools/call',
191
+ params: {
192
+ name: 'get_project',
193
+ arguments: {
194
+ project_id: TEST_PROJECT_ID
195
+ }
196
+ }
197
+ };
198
+ const toolResponse = await sendMCPRequest(mcpUrl, callToolRequest, {
199
+ 'mcp-session-id': sessionId
200
+ });
201
+ assert.ok(toolResponse.result, 'Tool call should succeed');
202
+ assert.ok(toolResponse.result.content, 'Should have content');
203
+ assert.ok(Array.isArray(toolResponse.result.content), 'Content should be array');
204
+ assert.ok(toolResponse.result.content.length > 0, 'Should have content items');
205
+ const projectData = JSON.parse(toolResponse.result.content[0].text);
206
+ assert.ok(projectData.id, 'Should have project id');
207
+ assert.ok(projectData.name, 'Should have project name');
208
+ });
209
+ });
210
+ describe('Remote Authorization - Streamable HTTP with Private-Token header', () => {
211
+ let server;
212
+ let port;
213
+ let mcpUrl;
214
+ before(async () => {
215
+ port = await findAvailablePort();
216
+ server = await launchServer({
217
+ mode: TransportMode.STREAMABLE_HTTP,
218
+ port,
219
+ timeout: 3000,
220
+ env: {
221
+ STREAMABLE_HTTP: 'true',
222
+ REMOTE_AUTHORIZATION: 'true',
223
+ GITLAB_API_URL: `${GITLAB_API_URL}/api/v4`,
224
+ GITLAB_PROJECT_ID: TEST_PROJECT_ID,
225
+ GITLAB_READ_ONLY_MODE: 'true',
226
+ }
227
+ });
228
+ servers.push(server);
229
+ mcpUrl = `http://${HOST}:${port}/mcp`;
230
+ console.log('Server started for Private-Token tests');
231
+ });
232
+ after(async () => {
233
+ cleanup();
234
+ console.log('Server stopped');
235
+ });
236
+ test('should accept request with Private-Token header', async () => {
237
+ const sessionId = `test-session-private-${Date.now()}`;
238
+ const initRequest = {
239
+ jsonrpc: '2.0',
240
+ id: 1,
241
+ method: 'initialize',
242
+ params: {
243
+ protocolVersion: '2024-11-05',
244
+ capabilities: {},
245
+ clientInfo: { name: 'test-client', version: '1.0.0' }
246
+ }
247
+ };
248
+ const response = await sendMCPRequest(mcpUrl, initRequest, {
249
+ 'mcp-session-id': sessionId,
250
+ 'private-token': GITLAB_TOKEN
251
+ });
252
+ assert.ok(response.result, 'Should succeed with Private-Token');
253
+ assert.strictEqual(response.result.protocolVersion, '2024-11-05', 'Protocol version should match');
254
+ });
255
+ test('should call tool with Private-Token', async () => {
256
+ const sessionId = `test-session-private-tool-${Date.now()}`;
257
+ // Initialize
258
+ const initRequest = {
259
+ jsonrpc: '2.0',
260
+ id: 1,
261
+ method: 'initialize',
262
+ params: {
263
+ protocolVersion: '2024-11-05',
264
+ capabilities: {},
265
+ clientInfo: { name: 'test-client', version: '1.0.0' }
266
+ }
267
+ };
268
+ await sendMCPRequest(mcpUrl, initRequest, {
269
+ 'mcp-session-id': sessionId,
270
+ 'private-token': GITLAB_TOKEN
271
+ });
272
+ // Call tool
273
+ const callToolRequest = {
274
+ jsonrpc: '2.0',
275
+ id: 2,
276
+ method: 'tools/call',
277
+ params: {
278
+ name: 'list_merge_requests',
279
+ arguments: {
280
+ project_id: TEST_PROJECT_ID
281
+ }
282
+ }
283
+ };
284
+ const toolResponse = await sendMCPRequest(mcpUrl, callToolRequest, {
285
+ 'mcp-session-id': sessionId
286
+ });
287
+ assert.ok(toolResponse.result, 'Tool call should succeed with Private-Token');
288
+ assert.ok(toolResponse.result.content, 'Should have content');
289
+ });
290
+ });
291
+ describe('Remote Authorization - SSE mode should be disabled', () => {
292
+ test('should fail to start with SSE and REMOTE_AUTHORIZATION', async () => {
293
+ const port = await findAvailablePort();
294
+ try {
295
+ const server = await launchServer({
296
+ mode: TransportMode.SSE,
297
+ port,
298
+ timeout: 3000,
299
+ env: {
300
+ SSE: 'true',
301
+ REMOTE_AUTHORIZATION: 'true',
302
+ GITLAB_API_URL: `${GITLAB_API_URL}/api/v4`,
303
+ }
304
+ });
305
+ // If we get here, the server started when it shouldn't have
306
+ servers.push(server);
307
+ assert.fail('Server should not start with SSE and REMOTE_AUTHORIZATION=true');
308
+ }
309
+ catch (error) {
310
+ // Expected: server should fail to start
311
+ assert.ok(error instanceof Error, 'Should throw an error');
312
+ console.log('✅ Server correctly rejected SSE mode with remote authorization');
313
+ }
314
+ });
315
+ });
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Mock GitLab API Server for Testing
3
+ * Implements minimal GitLab API endpoints for testing remote authorization
4
+ */
5
+ import express from 'express';
6
+ export class MockGitLabServer {
7
+ app;
8
+ server = null;
9
+ config;
10
+ requestCount = 0;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.app = express();
14
+ this.setupMiddleware();
15
+ this.setupRoutes();
16
+ }
17
+ /**
18
+ * Setup middleware including auth validation
19
+ */
20
+ setupMiddleware() {
21
+ this.app.use(express.json());
22
+ // Request counter for rate limiting tests
23
+ this.app.use((req, res, next) => {
24
+ this.requestCount++;
25
+ next();
26
+ });
27
+ // Artificial delay middleware (for timeout testing)
28
+ if (this.config.responseDelay) {
29
+ this.app.use((req, res, next) => {
30
+ setTimeout(next, this.config.responseDelay);
31
+ });
32
+ }
33
+ // Rate limiting middleware
34
+ if (this.config.rateLimitAfter) {
35
+ this.app.use((req, res, next) => {
36
+ if (this.requestCount > this.config.rateLimitAfter) {
37
+ res.status(429).json({
38
+ message: 'Rate limit exceeded',
39
+ retry_after: 60
40
+ });
41
+ return;
42
+ }
43
+ next();
44
+ });
45
+ }
46
+ // Authentication middleware - applies to all /api/v4/* routes
47
+ this.app.use('/api/v4', (req, res, next) => {
48
+ const authHeader = req.headers['authorization'];
49
+ const privateToken = req.headers['private-token'];
50
+ let token = null;
51
+ if (authHeader) {
52
+ // Extract token from "Bearer <token>"
53
+ const match = authHeader.match(/^Bearer\s+(.+)$/i);
54
+ token = match ? match[1].trim() : null;
55
+ }
56
+ else if (privateToken) {
57
+ token = privateToken.trim();
58
+ }
59
+ if (!token) {
60
+ res.status(401).json({
61
+ message: 'Unauthorized',
62
+ error: 'Missing authentication token'
63
+ });
64
+ return;
65
+ }
66
+ if (!this.config.validTokens.includes(token)) {
67
+ res.status(401).json({
68
+ message: 'Unauthorized',
69
+ error: 'Invalid authentication token'
70
+ });
71
+ return;
72
+ }
73
+ // Store validated token in request
74
+ req.gitlabToken = token;
75
+ next();
76
+ });
77
+ }
78
+ setupRoutes() {
79
+ // GET /api/v4/user - Get current user
80
+ this.app.get('/api/v4/user', (req, res) => {
81
+ const token = req.gitlabToken || 'unknown';
82
+ res.json({
83
+ id: 1,
84
+ username: `user_${token.substring(0, 8)}`,
85
+ name: 'Test User',
86
+ email: 'test@example.com',
87
+ state: 'active'
88
+ });
89
+ });
90
+ // GET /api/v4/projects/:projectId - Get project
91
+ this.app.get('/api/v4/projects/:projectId', (req, res) => {
92
+ const projectId = req.params.projectId;
93
+ res.json({
94
+ id: parseInt(projectId) || 123,
95
+ name: 'Test Project',
96
+ path: 'test-project',
97
+ path_with_namespace: 'test-group/test-project',
98
+ description: 'A mock test project',
99
+ visibility: 'private',
100
+ created_at: '2024-01-01T00:00:00Z',
101
+ web_url: `https://gitlab.mock/project/${projectId}`,
102
+ namespace: {
103
+ id: 1,
104
+ name: 'Test Group',
105
+ path: 'test-group',
106
+ kind: 'group',
107
+ full_path: 'test-group'
108
+ }
109
+ });
110
+ });
111
+ // GET /api/v4/projects/:projectId/merge_requests - List merge requests
112
+ this.app.get('/api/v4/projects/:projectId/merge_requests', (req, res) => {
113
+ res.json([
114
+ {
115
+ id: 1,
116
+ iid: 1,
117
+ title: 'Test MR 1',
118
+ state: 'opened',
119
+ created_at: '2024-01-01T00:00:00Z',
120
+ author: {
121
+ id: 1,
122
+ username: 'test-user',
123
+ name: 'Test User'
124
+ }
125
+ },
126
+ {
127
+ id: 2,
128
+ iid: 2,
129
+ title: 'Test MR 2',
130
+ state: 'merged',
131
+ created_at: '2024-01-02T00:00:00Z',
132
+ author: {
133
+ id: 1,
134
+ username: 'test-user',
135
+ name: 'Test User'
136
+ }
137
+ }
138
+ ]);
139
+ });
140
+ // GET /api/v4/projects/:projectId/merge_requests/:mr_iid - Get single MR
141
+ this.app.get('/api/v4/projects/:projectId/merge_requests/:mr_iid', (req, res) => {
142
+ const mrIid = parseInt(req.params.mr_iid);
143
+ res.json({
144
+ id: mrIid,
145
+ iid: mrIid,
146
+ title: `Test MR ${mrIid}`,
147
+ state: 'opened',
148
+ created_at: '2024-01-01T00:00:00Z',
149
+ author: {
150
+ id: 1,
151
+ username: 'test-user',
152
+ name: 'Test User'
153
+ },
154
+ source_branch: 'feature-branch',
155
+ target_branch: 'main',
156
+ merge_status: 'can_be_merged'
157
+ });
158
+ });
159
+ // GET /api/v4/projects/:projectId/issues - List issues
160
+ this.app.get('/api/v4/projects/:projectId/issues', (req, res) => {
161
+ res.json([
162
+ {
163
+ id: 1,
164
+ iid: 1,
165
+ title: 'Test Issue 1',
166
+ state: 'opened',
167
+ created_at: '2024-01-01T00:00:00Z',
168
+ author: {
169
+ id: 1,
170
+ username: 'test-user',
171
+ name: 'Test User'
172
+ }
173
+ }
174
+ ]);
175
+ });
176
+ // GET /api/v4/projects - List projects
177
+ this.app.get('/api/v4/projects', (req, res) => {
178
+ res.json([
179
+ {
180
+ id: 123,
181
+ name: 'Test Project',
182
+ path: 'test-project',
183
+ path_with_namespace: 'test-group/test-project',
184
+ description: 'A mock test project',
185
+ visibility: 'private',
186
+ namespace: {
187
+ id: 1,
188
+ name: 'Test Group',
189
+ path: 'test-group',
190
+ kind: 'group',
191
+ full_path: 'test-group'
192
+ }
193
+ }
194
+ ]);
195
+ });
196
+ // Health check endpoint
197
+ this.app.get('/health', (req, res) => {
198
+ res.json({ status: 'ok', message: 'Mock GitLab API is running' });
199
+ });
200
+ // Catch-all for unimplemented endpoints
201
+ this.app.use((req, res) => {
202
+ console.log(`Mock GitLab: Unimplemented endpoint: ${req.method} ${req.path}`);
203
+ res.status(404).json({
204
+ message: '404 Not Found',
205
+ error: 'Endpoint not implemented in mock server'
206
+ });
207
+ });
208
+ }
209
+ async start() {
210
+ return new Promise((resolve) => {
211
+ this.server = this.app.listen(this.config.port, '127.0.0.1', () => {
212
+ console.log(`Mock GitLab API listening on http://127.0.0.1:${this.config.port}`);
213
+ resolve();
214
+ });
215
+ });
216
+ }
217
+ async stop() {
218
+ return new Promise((resolve, reject) => {
219
+ if (this.server) {
220
+ this.server.close((err) => {
221
+ if (err)
222
+ reject(err);
223
+ else {
224
+ console.log('Mock GitLab API stopped');
225
+ resolve();
226
+ }
227
+ });
228
+ }
229
+ else {
230
+ resolve();
231
+ }
232
+ });
233
+ }
234
+ getUrl() {
235
+ return `http://127.0.0.1:${this.config.port}`;
236
+ }
237
+ }
238
+ /**
239
+ * Helper to find available port for mock server
240
+ */
241
+ export async function findMockServerPort(basePort = 9000, maxAttempts = 10) {
242
+ const net = await import('net');
243
+ const tryPort = async (port, attemptsLeft) => {
244
+ if (attemptsLeft === 0) {
245
+ throw new Error(`Could not find available port after ${maxAttempts} attempts starting from ${basePort}`);
246
+ }
247
+ return new Promise((resolve, reject) => {
248
+ const server = net.createServer();
249
+ server.unref();
250
+ server.on('error', async () => {
251
+ try {
252
+ const nextPort = await tryPort(port + 1, attemptsLeft - 1);
253
+ resolve(nextPort);
254
+ }
255
+ catch (err) {
256
+ reject(err);
257
+ }
258
+ });
259
+ server.listen(port, '127.0.0.1', () => {
260
+ const addr = server.address();
261
+ const actualPort = typeof addr === 'object' && addr ? addr.port : port;
262
+ server.close(() => {
263
+ resolve(actualPort);
264
+ });
265
+ });
266
+ });
267
+ };
268
+ return tryPort(basePort, maxAttempts);
269
+ }
270
+ /**
271
+ * Reset request counter (useful for rate limit testing)
272
+ */
273
+ export function resetMockServerState(server) {
274
+ server.requestCount = 0;
275
+ }
@@ -21,21 +21,27 @@ export async function launchServer(config) {
21
21
  const GITLAB_API_URL = process.env.GITLAB_API_URL || "https://gitlab.com";
22
22
  const GITLAB_TOKEN = process.env.GITLAB_TOKEN_TEST || process.env.GITLAB_TOKEN;
23
23
  const TEST_PROJECT_ID = process.env.TEST_PROJECT_ID;
24
- // Validate that we have required configuration
25
- if (!GITLAB_TOKEN) {
24
+ // Check if remote authorization is enabled
25
+ const isRemoteAuth = env.REMOTE_AUTHORIZATION === 'true';
26
+ // Validate that we have required configuration (unless using remote auth)
27
+ if (!GITLAB_TOKEN && !isRemoteAuth) {
26
28
  throw new Error('GITLAB_TOKEN_TEST or GITLAB_TOKEN environment variable is required for server testing');
27
29
  }
28
- if (!TEST_PROJECT_ID) {
30
+ if (!TEST_PROJECT_ID && !isRemoteAuth) {
29
31
  throw new Error('TEST_PROJECT_ID environment variable is required for server testing');
30
32
  }
31
33
  const serverEnv = {
32
34
  // Add all environment variables from the current process
33
35
  ...process.env,
34
36
  GITLAB_API_URL: `${GITLAB_API_URL}/api/v4`,
35
- GITLAB_PROJECT_ID: TEST_PROJECT_ID,
37
+ ...(TEST_PROJECT_ID ? { GITLAB_PROJECT_ID: TEST_PROJECT_ID } : {}),
36
38
  GITLAB_READ_ONLY_MODE: 'true', // Use read-only mode for testing
37
39
  ...env,
38
40
  };
41
+ // Only set GITLAB_PERSONAL_ACCESS_TOKEN if not using remote auth
42
+ if (!isRemoteAuth && GITLAB_TOKEN) {
43
+ serverEnv.GITLAB_PERSONAL_ACCESS_TOKEN = GITLAB_TOKEN;
44
+ }
39
45
  // Set transport-specific environment variables
40
46
  switch (mode) {
41
47
  case TransportMode.SSE:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zereight/mcp-gitlab",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "MCP server for using the GitLab API",
5
5
  "license": "MIT",
6
6
  "author": "zereight",
@@ -22,8 +22,9 @@
22
22
  "watch": "tsc --watch",
23
23
  "deploy": "npm publish --access public",
24
24
  "changelog": "auto-changelog -p",
25
- "test": "node test/validate-api.js",
25
+ "test": "node test/validate-api.js && npm run test:remote-auth",
26
26
  "test:integration": "node test/validate-api.js",
27
+ "test:remote-auth": "npm run build && npx tsx --test test/remote-auth-simple-test.ts",
27
28
  "test:server": "npm run build && node build/test/test-all-transport-server.js",
28
29
  "test:mcp:readonly": "tsx test/readonly-mcp-tests.ts",
29
30
  "test:all": "npm run test && npm run test:mcp:readonly",