@zereight/mcp-gitlab 2.0.11 → 2.0.18

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
@@ -1,5 +1,7 @@
1
1
  # GitLab MCP Server
2
2
 
3
+ > **New Feature**: Dynamic GitLab API URL support with connection pooling! See [Dynamic API URL Documentation](docs/dynamic-api-url.md) for details.
4
+
3
5
  [![Star History Chart](https://api.star-history.com/svg?repos=zereight/gitlab-mcp&type=Date)](https://www.star-history.com/#zereight/gitlab-mcp&Date)
4
6
 
5
7
  ## @zereight/mcp-gitlab
@@ -0,0 +1,113 @@
1
+ import { Agent } from "http";
2
+ import { Agent as HttpsAgent } from "https";
3
+ import { HttpProxyAgent } from "http-proxy-agent";
4
+ import { HttpsProxyAgent } from "https-proxy-agent";
5
+ import { SocksProxyAgent } from "socks-proxy-agent";
6
+ import fs from "fs";
7
+ /**
8
+ * Manages a pool of HTTP/HTTPS agents for different GitLab API URLs.
9
+ * This allows the server to efficiently handle requests to multiple GitLab instances
10
+ * by reusing agents and their underlying TCP connections.
11
+ */
12
+ export class GitLabClientPool {
13
+ clients = new Map();
14
+ options;
15
+ constructor(options) {
16
+ this.options = options;
17
+ // Initialization is now done on-demand
18
+ }
19
+ /**
20
+ * Creates a pair of HTTP and HTTPS agents for a specific API URL,
21
+ * considering proxy and SSL/TLS settings.
22
+ * @param apiUrl The base URL for which to create the agents.
23
+ * @returns A `ClientAgents` object containing the configured agents.
24
+ */
25
+ createAgentsForUrl(apiUrl) {
26
+ const { httpProxy, httpsProxy, rejectUnauthorized, caCertPath } = this.options;
27
+ const url = new URL(apiUrl);
28
+ let sslOptions = {};
29
+ if (rejectUnauthorized === false) {
30
+ sslOptions.rejectUnauthorized = false;
31
+ }
32
+ else if (caCertPath) {
33
+ try {
34
+ sslOptions.ca = fs.readFileSync(caCertPath);
35
+ }
36
+ catch (error) {
37
+ console.error(`Failed to read CA certificate from ${caCertPath}:`, error);
38
+ throw new Error(`Failed to read CA certificate: ${caCertPath}`);
39
+ }
40
+ }
41
+ let httpAgent;
42
+ let httpsAgent;
43
+ // Configure HTTP agent with proxy if specified
44
+ if (httpProxy) {
45
+ httpAgent = httpProxy.startsWith("socks")
46
+ ? new SocksProxyAgent(httpProxy)
47
+ : new HttpProxyAgent(httpProxy);
48
+ }
49
+ else {
50
+ httpAgent = new Agent({ keepAlive: true });
51
+ }
52
+ // Configure HTTPS agent with proxy and SSL options if specified
53
+ if (httpsProxy) {
54
+ httpsAgent = httpsProxy.startsWith("socks")
55
+ // The `as any` cast is used here to bypass a TypeScript type mismatch error.
56
+ // The `socks-proxy-agent` documentation indicates that TLS options like
57
+ // `rejectUnauthorized` and `ca` are valid in the constructor's options
58
+ // object, but the type definitions in this environment seem to disagree.
59
+ // This cast ensures the options are passed through at runtime.
60
+ ? new SocksProxyAgent(httpsProxy, sslOptions)
61
+ : new HttpsProxyAgent(httpsProxy, { ...sslOptions });
62
+ }
63
+ else {
64
+ httpsAgent = new HttpsAgent({ ...sslOptions, keepAlive: true });
65
+ }
66
+ return { httpAgent, httpsAgent };
67
+ }
68
+ /**
69
+ * Retrieves the appropriate agent (HTTP or HTTPS) for a given API URL.
70
+ * If an agent for the URL does not exist, it creates and caches one.
71
+ * @param apiUrl The full URL of the request.
72
+ * @returns The corresponding `Agent` for the URL's protocol.
73
+ */
74
+ getOrCreateAgentForUrl(apiUrl) {
75
+ const url = new URL(apiUrl);
76
+ const baseUrl = `${url.protocol}//${url.host}${url.pathname.substring(0, url.pathname.lastIndexOf('/api/v4') + '/api/v4'.length)}`;
77
+ if (!this.clients.has(baseUrl)) {
78
+ // Check pool size limit
79
+ if (this.options.poolMaxSize !== undefined && this.clients.size >= this.options.poolMaxSize) {
80
+ throw new Error(`Server capacity reached: Connection pool is full (max ${this.options.poolMaxSize} instances). Please try again later.`);
81
+ }
82
+ this.clients.set(baseUrl, this.createAgentsForUrl(baseUrl));
83
+ }
84
+ const agents = this.clients.get(baseUrl);
85
+ if (!agents) {
86
+ // This should not happen given the logic above, but it satisfies TypeScript
87
+ throw new Error(`Failed to create or get client for URL: ${baseUrl}`);
88
+ }
89
+ return url.protocol === "https:" ? agents.httpsAgent : agents.httpAgent;
90
+ }
91
+ /**
92
+ * Retrieves the client agents for a specific base API URL.
93
+ * @param apiUrl The base API URL (e.g., "https://gitlab.com/api/v4").
94
+ * @returns The `ClientAgents` object or undefined if not found.
95
+ */
96
+ getClient(apiUrl) {
97
+ return this.clients.get(apiUrl);
98
+ }
99
+ /**
100
+ * Returns the default client agents, which corresponds to the first URL in the list.
101
+ * @returns The default `ClientAgents`.
102
+ */
103
+ getDefaultClient() {
104
+ const defaultUrl = this.options.apiUrls?.[0];
105
+ if (!defaultUrl) {
106
+ throw new Error("No default API URL configured.");
107
+ }
108
+ if (!this.clients.has(defaultUrl)) {
109
+ this.clients.set(defaultUrl, this.createAgentsForUrl(defaultUrl));
110
+ }
111
+ return this.clients.get(defaultUrl);
112
+ }
113
+ }