Skip to content

Commit bd6da3f

Browse files
feat: add GLEAN_SERVER_URL support with highest priority (#344)
* feat: add GLEAN_SERVER_URL support with highest priority * docs: use fully qualified URLs and mark instance as deprecated
1 parent c9f24bc commit bd6da3f

File tree

8 files changed

+133
-15
lines changed

8 files changed

+133
-15
lines changed

packages/local-mcp-server/README.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,13 @@ To configure this MCP server in your MCP client (such as Claude Desktop, Windsur
4141

4242
```bash
4343
# Configure for Cursor
44-
npx @gleanwork/configure-mcp-server --client cursor --token your_api_token --instance instance_name
44+
npx @gleanwork/configure-mcp-server --client cursor --token your_api_token --server-url https://your-company-be.glean.com
4545

4646
# Configure for Claude Desktop
47-
npx @gleanwork/configure-mcp-server --client claude --token your_api_token --instance instance_name
47+
npx @gleanwork/configure-mcp-server --client claude --token your_api_token --server-url https://your-company-be.glean.com
48+
49+
# Using deprecated --instance flag (use --server-url instead)
50+
# npx @gleanwork/configure-mcp-server --client cursor --token your_api_token --instance instance_name
4851
```
4952

5053
For more details see: [@gleanwork/configure-mcp-server](https://github.com/gleanwork/configure-mcp-server).
@@ -60,7 +63,7 @@ To manually configure an MCP client (such as Claude Desktop, Windsurf, Cursor, e
6063
"command": "npx",
6164
"args": ["-y", "@gleanwork/local-mcp-server"],
6265
"env": {
63-
"GLEAN_INSTANCE": "<glean instance name>",
66+
"GLEAN_SERVER_URL": "<glean server URL>",
6467
"GLEAN_API_TOKEN": "<glean api token>"
6568
}
6669
}
@@ -98,7 +101,7 @@ Configure your MCP client to use the Docker image. Most MCP clients support pass
98101
"ghcr.io/gleanwork/local-mcp-server:latest"
99102
],
100103
"env": {
101-
"GLEAN_INSTANCE": "your-instance",
104+
"GLEAN_SERVER_URL": "https://your-instance-be.glean.com",
102105
"GLEAN_API_TOKEN": "your-token"
103106
}
104107
}
@@ -118,7 +121,7 @@ If your MCP client doesn't pass the `env` block to Docker, use `-e` flags in the
118121
"-i",
119122
"--rm",
120123
"-e",
121-
"GLEAN_INSTANCE=your-instance",
124+
"GLEAN_SERVER_URL=https://your-instance-be.glean.com",
122125
"-e",
123126
"GLEAN_API_TOKEN=your-token",
124127
"ghcr.io/gleanwork/local-mcp-server:latest"
@@ -132,7 +135,8 @@ If your MCP client doesn't pass the `env` block to Docker, use `-e` flags in the
132135

133136
### Environment Variables
134137

135-
- `GLEAN_INSTANCE` (required): Your Glean instance name
138+
- `GLEAN_SERVER_URL` (recommended): Your Glean server URL (e.g. `https://your-instance-be.glean.com`)
139+
- `GLEAN_INSTANCE`: Your Glean instance name (deprecated alternative to `GLEAN_SERVER_URL`)
136140
- `GLEAN_API_TOKEN` (required): Your Glean API token
137141

138142
### Troubleshooting
@@ -146,7 +150,7 @@ If your MCP client doesn't pass the `env` block to Docker, use `-e` flags in the
146150
**Permission or authentication errors:**
147151

148152
- Verify your `GLEAN_API_TOKEN` is valid
149-
- Check your `GLEAN_INSTANCE` matches your Glean deployment
153+
- Check your `GLEAN_SERVER_URL` or `GLEAN_INSTANCE` matches your Glean deployment
150154

151155
**MCP client can't connect:**
152156

packages/local-mcp-server/src/common/client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
*
44
* This module provides a client for interacting with the Glean API.
55
*
6-
* Required environment variables:
6+
* Required environment variables (one of):
7+
* - GLEAN_SERVER_URL: Full Glean server URL (recommended, highest priority)
8+
* - GLEAN_URL: Full Glean URL (alternative)
79
* - GLEAN_INSTANCE or GLEAN_SUBDOMAIN: Name of the Glean instance
810
* - GLEAN_API_TOKEN: API token for authentication
911
*

packages/local-mcp-server/src/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,26 @@ async function main() {
2424
$ npx @gleanwork/local-mcp-server [options]
2525
2626
Options
27-
--instance, -i Glean instance name
27+
--server-url, -s Glean server URL (e.g. https://my-company-be.glean.com)
28+
--instance, -i Glean instance name (deprecated, use --server-url instead)
2829
--token, -t Glean API token
2930
--help, -h Show this help message
3031
--trace Enable trace logging
3132
3233
Examples
3334
$ npx @gleanwork/local-mcp-server
34-
$ npx @gleanwork/local-mcp-server --instance my-company --token glean_api_xyz
35+
$ npx @gleanwork/local-mcp-server --server-url https://my-company-be.glean.com --token glean_api_xyz
36+
$ npx @gleanwork/local-mcp-server --instance my-company --token glean_api_xyz # deprecated, use --server-url
3537
3638
Version: v${VERSION}
3739
`,
3840
{
3941
importMeta: import.meta,
4042
flags: {
43+
serverUrl: {
44+
type: 'string',
45+
shortFlag: 's',
46+
},
4147
token: {
4248
type: 'string',
4349
shortFlag: 't',
@@ -66,8 +72,8 @@ async function main() {
6672

6773
await checkAndOpenLaunchWarning(VERSION);
6874

69-
const { instance, token } = cli.flags;
70-
runServer({ instance, token }).catch((error) => {
75+
const { serverUrl, instance, token } = cli.flags;
76+
runServer({ serverUrl, instance, token }).catch((error) => {
7177
console.error('Error starting MCP server:', error);
7278
process.exit(1);
7379
});

packages/local-mcp-server/src/server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,16 @@ server.setRequestHandler(CallToolRequestSchema, callToolHandler);
240240
* @throws {Error} If server initialization or connection fails
241241
*/
242242
export async function runServer(options?: {
243+
serverUrl?: string;
243244
instance?: string;
244245
token?: string;
245246
}) {
246247
// Set environment variables from command line args if provided
248+
if (options?.serverUrl) {
249+
process.env.GLEAN_SERVER_URL = options.serverUrl;
250+
}
251+
252+
// GLEAN_INSTANCE is deprecated; prefer GLEAN_SERVER_URL / --server-url
247253
if (options?.instance) {
248254
process.env.GLEAN_INSTANCE = options.instance;
249255
}

packages/local-mcp-server/src/test/server.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import '@gleanwork/mcp-test-utils/mocks/setup';
55

66
describe('MCP Server Handlers (integration)', () => {
77
beforeEach(() => {
8+
delete process.env.GLEAN_SERVER_URL;
89
delete process.env.GLEAN_URL;
910
process.env.GLEAN_INSTANCE = 'test';
1011
process.env.GLEAN_API_TOKEN = 'test-token';
1112
});
1213

1314
afterEach(() => {
15+
delete process.env.GLEAN_SERVER_URL;
1416
delete process.env.GLEAN_INSTANCE;
1517
delete process.env.GLEAN_API_TOKEN;
1618
});

packages/mcp-server-utils/src/config/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,17 @@ export async function getConfig(): Promise<GleanConfig> {
5454
return getLocalConfig();
5555
}
5656

57+
function normalizeUrl(url: string): string {
58+
if (!/^https?:\/\//i.test(url)) {
59+
return `https://${url}`;
60+
}
61+
return url;
62+
}
63+
5764
function getLocalConfig(): GleanConfig {
5865
const instance = process.env.GLEAN_INSTANCE || process.env.GLEAN_SUBDOMAIN;
59-
const baseUrl = process.env.GLEAN_URL;
66+
const serverUrl = process.env.GLEAN_SERVER_URL;
67+
const baseUrl = serverUrl ? normalizeUrl(serverUrl) : process.env.GLEAN_URL;
6068
const token = process.env.GLEAN_API_TOKEN;
6169
const actAs = process.env.GLEAN_ACT_AS;
6270

packages/mcp-server-utils/src/test/config/config.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,51 @@ describe('getConfig', () => {
105105

106106
expect(config.baseUrl).toBe('https://test-subdomain-be.glean.com/');
107107
});
108+
109+
it('uses GLEAN_SERVER_URL when provided', async () => {
110+
process.env.GLEAN_SERVER_URL = 'https://custom-be.glean.com/';
111+
process.env.GLEAN_API_TOKEN = 'test-token';
112+
113+
const config = await getConfig();
114+
115+
expect(config.baseUrl).toBe('https://custom-be.glean.com/');
116+
});
117+
118+
it('GLEAN_SERVER_URL takes precedence over GLEAN_URL', async () => {
119+
process.env.GLEAN_SERVER_URL = 'https://server-url-be.glean.com/';
120+
process.env.GLEAN_URL = 'https://glean-url-be.glean.com/';
121+
process.env.GLEAN_API_TOKEN = 'test-token';
122+
123+
const config = await getConfig();
124+
125+
expect(config.baseUrl).toBe('https://server-url-be.glean.com/');
126+
});
127+
128+
it('GLEAN_SERVER_URL takes precedence over GLEAN_INSTANCE', async () => {
129+
process.env.GLEAN_SERVER_URL = 'https://server-url-be.glean.com/';
130+
process.env.GLEAN_INSTANCE = 'test-company';
131+
process.env.GLEAN_API_TOKEN = 'test-token';
132+
133+
const config = await getConfig();
134+
135+
expect(config.baseUrl).toBe('https://server-url-be.glean.com/');
136+
});
137+
138+
it('normalizes schemeless GLEAN_SERVER_URL by adding https://', async () => {
139+
process.env.GLEAN_SERVER_URL = 'acme-be.glean.com';
140+
process.env.GLEAN_API_TOKEN = 'test-token';
141+
142+
const config = await getConfig();
143+
144+
expect(config.baseUrl).toBe('https://acme-be.glean.com');
145+
});
146+
147+
it('preserves GLEAN_SERVER_URL that already has https://', async () => {
148+
process.env.GLEAN_SERVER_URL = 'https://acme-be.glean.com';
149+
process.env.GLEAN_API_TOKEN = 'test-token';
150+
151+
const config = await getConfig();
152+
153+
expect(config.baseUrl).toBe('https://acme-be.glean.com');
154+
});
108155
});

packages/mcp-server-utils/src/util/preflight.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import { trace, error } from '../log/logger.js';
22

33
/**
4-
* Validates that the given instance name is valid by checking its liveness endpoint.
5-
* Makes a fetch request to https://{instance}-be.glean.com/liveness_check
4+
* Validates that the given instance name or server URL is valid by checking its liveness endpoint.
5+
* When GLEAN_SERVER_URL is set, validates using that URL directly.
6+
* Otherwise, makes a fetch request to https://{instance}-be.glean.com/liveness_check
67
*
78
* @param instance - The instance name to validate
89
* @returns A Promise that resolves to true if the instance is valid
910
*/
1011
export async function validateInstance(instance: string): Promise<boolean> {
12+
// If GLEAN_SERVER_URL is set, skip instance name validation and validate the server URL directly
13+
const serverUrl = process.env.GLEAN_SERVER_URL;
14+
if (serverUrl) {
15+
return validateServerUrl(serverUrl);
16+
}
17+
1118
if (!instance) {
1219
trace('No instance provided for validation');
1320
return false;
@@ -40,3 +47,39 @@ export async function validateInstance(instance: string): Promise<boolean> {
4047
return false;
4148
}
4249
}
50+
51+
/**
52+
* Validates a server URL by checking its liveness endpoint.
53+
*
54+
* @param serverUrl - The full server URL to validate
55+
* @returns A Promise that resolves to true if the server is reachable
56+
*/
57+
async function validateServerUrl(serverUrl: string): Promise<boolean> {
58+
try {
59+
const normalizedUrl = /^https?:\/\//i.test(serverUrl)
60+
? serverUrl
61+
: `https://${serverUrl}`;
62+
const url = `${normalizedUrl.replace(/\/+$/, '')}/liveness_check`;
63+
trace(`Checking server URL validity with: ${url}`);
64+
65+
const response = await fetch(url, {
66+
method: 'GET',
67+
headers: {
68+
Accept: 'application/json',
69+
},
70+
});
71+
72+
if (!response.ok) {
73+
error(
74+
`Server URL validation failed for ${serverUrl}: ${response.status} ${response.statusText}`,
75+
);
76+
return false;
77+
}
78+
79+
return true;
80+
} catch (err) {
81+
const cause = err instanceof Error ? err : new Error(String(err));
82+
error(`Server URL validation failed: ${cause.message}`);
83+
return false;
84+
}
85+
}

0 commit comments

Comments
 (0)