hawkeye-mcp-server 2.2.0 → 2.2.1

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 (54) hide show
  1. package/README.md +82 -2
  2. package/build/config/config.d.ts +6 -1
  3. package/build/config/config.js +24 -5
  4. package/build/config/config.js.map +1 -1
  5. package/build/db/index.d.ts +51 -0
  6. package/build/db/index.js +174 -0
  7. package/build/db/index.js.map +1 -0
  8. package/build/db/postgres-state-provider.d.ts +52 -0
  9. package/build/db/postgres-state-provider.js +165 -0
  10. package/build/db/postgres-state-provider.js.map +1 -0
  11. package/build/index.d.ts +4 -1
  12. package/build/index.js +17 -84
  13. package/build/index.js.map +1 -1
  14. package/build/mcp-server-factory.d.ts +36 -0
  15. package/build/mcp-server-factory.js +92 -0
  16. package/build/mcp-server-factory.js.map +1 -0
  17. package/build/server.d.ts +9 -0
  18. package/build/server.js +337 -0
  19. package/build/server.js.map +1 -0
  20. package/build/services/auth.service.d.ts +38 -0
  21. package/build/services/auth.service.js +64 -0
  22. package/build/services/auth.service.js.map +1 -1
  23. package/build/services/project.service.d.ts +11 -0
  24. package/build/services/project.service.js +31 -0
  25. package/build/services/project.service.js.map +1 -1
  26. package/build/services/state.service.d.ts +115 -0
  27. package/build/services/state.service.js +172 -0
  28. package/build/services/state.service.js.map +1 -0
  29. package/build/tools/create-connection.d.ts +6 -0
  30. package/build/tools/create-connection.js +3 -0
  31. package/build/tools/create-connection.js.map +1 -1
  32. package/build/tools/create-manual-investigation.js +10 -6
  33. package/build/tools/create-manual-investigation.js.map +1 -1
  34. package/build/tools/get-connection-info.js +1 -0
  35. package/build/tools/get-connection-info.js.map +1 -1
  36. package/build/tools/get-session-link.js +1 -2
  37. package/build/tools/get-session-link.js.map +1 -1
  38. package/build/tools/get-status.js +2 -4
  39. package/build/tools/get-status.js.map +1 -1
  40. package/build/tools/index.js +11 -1
  41. package/build/tools/index.js.map +1 -1
  42. package/build/tools/inspect-session.js +1 -2
  43. package/build/tools/inspect-session.js.map +1 -1
  44. package/build/tools/list-connection-types.js +9 -0
  45. package/build/tools/list-connection-types.js.map +1 -1
  46. package/build/types/hawkeye.d.ts +6 -0
  47. package/build/types/hawkeye.js.map +1 -1
  48. package/build/utils/connection-builders.d.ts +4 -0
  49. package/build/utils/connection-builders.js +24 -1
  50. package/build/utils/connection-builders.js.map +1 -1
  51. package/build/utils/state.d.ts +24 -17
  52. package/build/utils/state.js +63 -72
  53. package/build/utils/state.js.map +1 -1
  54. package/package.json +12 -4
package/README.md CHANGED
@@ -183,7 +183,20 @@ Works with any MCP-compatible client:
183
183
 
184
184
  ## 🔧 Configuration Options
185
185
 
186
- ### Required Environment Variables
186
+ ### Transport Modes
187
+
188
+ Hawkeye MCP Server supports two transport modes:
189
+
190
+ | Mode | Use Case | Authentication |
191
+ |------|----------|----------------|
192
+ | **Stdio** (default) | npm/npx usage with MCP clients | Environment variables |
193
+ | **HTTP/SSE** | Kubernetes deployment, multi-user | Headers (Bearer token or password) |
194
+
195
+ ### Stdio Transport (Default)
196
+
197
+ For use with Claude Desktop, Cursor, and other MCP clients:
198
+
199
+ #### Required Environment Variables
187
200
 
188
201
  | Variable | Description |
189
202
  |----------|-------------|
@@ -191,7 +204,7 @@ Works with any MCP-compatible client:
191
204
  | `HAWKEYE_PASSWORD` | Your Hawkeye account password |
192
205
  | `HAWKEYE_BASE_URL` | Hawkeye API endpoint (e.g., `https://app.neubird.ai/api`) |
193
206
 
194
- ### Optional Environment Variables
207
+ #### Optional Environment Variables
195
208
 
196
209
  | Variable | Default | Description |
197
210
  |----------|---------|-------------|
@@ -200,6 +213,73 @@ Works with any MCP-compatible client:
200
213
  | `HAWKEYE_MAX_POLL_ATTEMPTS` | `30` | Max polling attempts |
201
214
  | `HAWKEYE_POLL_INTERVAL_MS` | `2000` | Polling interval (ms) |
202
215
 
216
+ ### HTTP/SSE Transport (Kubernetes Deployment)
217
+
218
+ For multi-user deployments, use the HTTP/SSE transport with per-request authentication.
219
+
220
+ #### Server Configuration
221
+
222
+ | Variable | Description |
223
+ |----------|-------------|
224
+ | `TRANSPORT_MODE` | Set to `http` to enable HTTP/SSE transport |
225
+ | `HTTP_PORT` | HTTP server port (default: 3000) |
226
+ | `HAWKEYE_BASE_URL` | Hawkeye API endpoint |
227
+ | `CONFIGDB_URI` | PostgreSQL connection string |
228
+ | `CONFIGDB_USER` | PostgreSQL username |
229
+ | `CONFIGDB_PASSWORD` | PostgreSQL password |
230
+
231
+ #### Client Authentication
232
+
233
+ The HTTP/SSE transport supports two authentication methods:
234
+
235
+ **1. Bearer Token (Auth0/OAuth) - Recommended**
236
+ ```
237
+ Authorization: Bearer <auth0_access_token>
238
+ ```
239
+
240
+ **2. Password Credentials (Legacy)**
241
+ ```
242
+ X-Hawkeye-Email: user@example.com
243
+ X-Hawkeye-Password: your-password
244
+ ```
245
+
246
+ #### MCP Client Configuration (HTTP/SSE)
247
+
248
+ **With Bearer Token:**
249
+ ```json
250
+ {
251
+ "mcpServers": {
252
+ "hawkeye": {
253
+ "url": "https://hawkeye-mcp.example.com/mcp",
254
+ "headers": {
255
+ "Authorization": "Bearer <auth0_access_token>"
256
+ }
257
+ }
258
+ }
259
+ }
260
+ ```
261
+
262
+ **With Password Credentials:**
263
+ ```json
264
+ {
265
+ "mcpServers": {
266
+ "hawkeye": {
267
+ "url": "https://hawkeye-mcp.example.com/mcp",
268
+ "headers": {
269
+ "X-Hawkeye-Email": "user@example.com",
270
+ "X-Hawkeye-Password": "your-password"
271
+ }
272
+ }
273
+ }
274
+ }
275
+ ```
276
+
277
+ #### Obtaining an Auth0 Token
278
+
279
+ Clients can obtain Auth0 tokens by:
280
+ 1. **Via Hawkeye UI**: Login to hawkeye-ui, extract token from localStorage (`access_token`)
281
+ 2. **Via Auth0 SDK**: Use Auth0 SDK to perform OAuth flow directly
282
+
203
283
  ## 👥 Contributing
204
284
 
205
285
  We welcome contributions from NeuBird team members! This section is for internal developers.
@@ -6,6 +6,7 @@ export interface HawkeyeConfig {
6
6
  email: string;
7
7
  password: string;
8
8
  baseUrl: string;
9
+ uiBaseUrl: string;
9
10
  defaultProjectUuid?: string;
10
11
  defaultOrganizationUuid: string;
11
12
  defaultTimeframe: string;
@@ -36,6 +37,10 @@ export declare function setBaseUrl(newBaseUrl: string): HawkeyeConfig;
36
37
  * Get the current base URL
37
38
  */
38
39
  export declare function getCurrentBaseUrl(): string;
40
+ /**
41
+ * Get the current UI base URL
42
+ */
43
+ export declare function getCurrentUiBaseUrl(): string;
39
44
  /**
40
45
  * Constants used throughout the application
41
46
  */
@@ -59,7 +64,7 @@ export declare const CONSTANTS: {
59
64
  readonly TIMEOUTS: {
60
65
  readonly DEFAULT_REQUEST: 30000;
61
66
  readonly LONG_REQUEST: 60000;
62
- readonly AUTH_REQUEST: 10000;
67
+ readonly AUTH_REQUEST: 30000;
63
68
  };
64
69
  readonly RETRY: {
65
70
  readonly MAX_ATTEMPTS: 3;
@@ -22,6 +22,7 @@ else {
22
22
  console.error('[CONFIG] HAWKEYE_EMAIL:', process.env.HAWKEYE_EMAIL ? '✓ Set' : '✗ Not set');
23
23
  console.error('[CONFIG] HAWKEYE_PASSWORD:', process.env.HAWKEYE_PASSWORD ? '✓ Set' : '✗ Not set');
24
24
  console.error('[CONFIG] HAWKEYE_BASE_URL:', process.env.HAWKEYE_BASE_URL || 'Using default');
25
+ console.error('[CONFIG] HAWKEYE_UI_URL:', process.env.HAWKEYE_UI_URL || 'Using derived UI URL');
25
26
  }
26
27
  /**
27
28
  * Load and validate configuration from environment variables
@@ -29,7 +30,10 @@ else {
29
30
  export function loadConfig() {
30
31
  const email = process.env.HAWKEYE_EMAIL;
31
32
  const password = process.env.HAWKEYE_PASSWORD;
32
- if (!email || !password) {
33
+ const transportMode = process.env.TRANSPORT_MODE || 'stdio';
34
+ // In HTTP mode, credentials come from request headers, not environment
35
+ // In stdio mode, credentials are required from environment
36
+ if (transportMode === 'stdio' && (!email || !password)) {
33
37
  throw new Error('Missing required environment variables: HAWKEYE_EMAIL and HAWKEYE_PASSWORD must be set');
34
38
  }
35
39
  // Priority for base URL: state file > env var > default
@@ -37,11 +41,14 @@ export function loadConfig() {
37
41
  const baseUrl = stateManager.getBaseUrl() ||
38
42
  process.env.HAWKEYE_BASE_URL ||
39
43
  'https://sandbox.app.neubird.ai/api';
44
+ const uiBaseUrlRaw = process.env.HAWKEYE_UI_URL || process.env.HAWKEYE_BASE_URL || baseUrl;
45
+ const uiBaseUrl = uiBaseUrlRaw.replace(/\/api\/?$/, '');
40
46
  return {
41
- // Required
42
- email,
43
- password,
47
+ // Required (may be empty in HTTP mode where they come from headers)
48
+ email: email || '',
49
+ password: password || '',
44
50
  baseUrl,
51
+ uiBaseUrl,
45
52
  // Optional with defaults
46
53
  defaultProjectUuid: process.env.HAWKEYE_DEFAULT_PROJECT_UUID,
47
54
  defaultOrganizationUuid: process.env.HAWKEYE_DEFAULT_ORGANIZATION_UUID || 'ORGANIZATION_NAME_ROOT',
@@ -87,6 +94,12 @@ export function setBaseUrl(newBaseUrl) {
87
94
  }
88
95
  // Update the base URL
89
96
  config.baseUrl = newBaseUrl;
97
+ if (process.env.HAWKEYE_UI_URL) {
98
+ config.uiBaseUrl = process.env.HAWKEYE_UI_URL.replace(/\/api\/?$/, '');
99
+ }
100
+ else {
101
+ config.uiBaseUrl = newBaseUrl.replace(/\/api\/?$/, '');
102
+ }
90
103
  return config;
91
104
  }
92
105
  /**
@@ -95,6 +108,12 @@ export function setBaseUrl(newBaseUrl) {
95
108
  export function getCurrentBaseUrl() {
96
109
  return getConfig().baseUrl;
97
110
  }
111
+ /**
112
+ * Get the current UI base URL
113
+ */
114
+ export function getCurrentUiBaseUrl() {
115
+ return getConfig().uiBaseUrl;
116
+ }
98
117
  /**
99
118
  * Constants used throughout the application
100
119
  */
@@ -121,7 +140,7 @@ export const CONSTANTS = {
121
140
  TIMEOUTS: {
122
141
  DEFAULT_REQUEST: 30000, // 30 seconds
123
142
  LONG_REQUEST: 60000, // 1 minute
124
- AUTH_REQUEST: 10000, // 10 seconds
143
+ AUTH_REQUEST: 30000, // 30 seconds
125
144
  },
126
145
  // Retry configuration
127
146
  RETRY: {
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * PostgreSQL database connection module
3
+ * Provides connection pooling and health check functionality
4
+ */
5
+ import pg from 'pg';
6
+ /**
7
+ * Database configuration from environment variables
8
+ */
9
+ export interface DatabaseConfig {
10
+ uri: string;
11
+ user: string;
12
+ password: string;
13
+ maxConnections?: number;
14
+ idleTimeoutMs?: number;
15
+ connectionTimeoutMs?: number;
16
+ }
17
+ /**
18
+ * Get database configuration from environment variables
19
+ */
20
+ export declare function getDatabaseConfig(): DatabaseConfig;
21
+ /**
22
+ * Initialize the database connection pool
23
+ * @param config - Optional database configuration (defaults to environment variables)
24
+ */
25
+ export declare function initializePool(config?: DatabaseConfig): pg.Pool;
26
+ /**
27
+ * Get the database connection pool
28
+ * Initializes the pool if not already done
29
+ */
30
+ export declare function getPool(): pg.Pool;
31
+ /**
32
+ * Check if the database connection is healthy
33
+ * @returns true if the database is reachable, false otherwise
34
+ */
35
+ export declare function checkDatabaseHealth(): Promise<boolean>;
36
+ /**
37
+ * Close the database connection pool
38
+ * Should be called when shutting down the server
39
+ */
40
+ export declare function closePool(): Promise<void>;
41
+ /**
42
+ * Execute a query with automatic connection management
43
+ * @param text - SQL query text
44
+ * @param params - Query parameters
45
+ */
46
+ export declare function query<T extends pg.QueryResultRow = any>(text: string, params?: any[]): Promise<pg.QueryResult<T>>;
47
+ /**
48
+ * Run database migrations
49
+ * Creates necessary tables if they don't exist
50
+ */
51
+ export declare function runMigrations(): Promise<void>;
@@ -0,0 +1,174 @@
1
+ /**
2
+ * PostgreSQL database connection module
3
+ * Provides connection pooling and health check functionality
4
+ */
5
+ import pg from 'pg';
6
+ import { logger } from '../utils/logger.js';
7
+ const { Pool } = pg;
8
+ /**
9
+ * Get database configuration from environment variables
10
+ */
11
+ export function getDatabaseConfig() {
12
+ const uri = process.env.CONFIGDB_URI;
13
+ const user = process.env.CONFIGDB_USER;
14
+ const password = process.env.CONFIGDB_PASSWORD;
15
+ if (!uri || !user || !password) {
16
+ throw new Error('Missing required database environment variables. ' +
17
+ 'Please set CONFIGDB_URI, CONFIGDB_USER, and CONFIGDB_PASSWORD.');
18
+ }
19
+ return {
20
+ uri,
21
+ user,
22
+ password,
23
+ maxConnections: parseInt(process.env.CONFIGDB_MAX_CONNECTIONS || '10', 10),
24
+ idleTimeoutMs: parseInt(process.env.CONFIGDB_IDLE_TIMEOUT_MS || '30000', 10),
25
+ connectionTimeoutMs: parseInt(process.env.CONFIGDB_CONNECTION_TIMEOUT_MS || '5000', 10),
26
+ };
27
+ }
28
+ /**
29
+ * PostgreSQL connection pool singleton
30
+ */
31
+ let pool = null;
32
+ /**
33
+ * Initialize the database connection pool
34
+ * @param config - Optional database configuration (defaults to environment variables)
35
+ */
36
+ export function initializePool(config) {
37
+ if (pool) {
38
+ return pool;
39
+ }
40
+ const dbConfig = config || getDatabaseConfig();
41
+ // Parse the URI to extract host, port, and database
42
+ const url = new URL(dbConfig.uri);
43
+ // Determine SSL mode - disable for localhost, enable for remote
44
+ const isLocalhost = url.hostname === 'localhost' || url.hostname === '127.0.0.1';
45
+ const sslEnabled = process.env.CONFIGDB_SSL !== 'false' && !isLocalhost;
46
+ pool = new Pool({
47
+ host: url.hostname,
48
+ port: parseInt(url.port || '5432', 10),
49
+ database: url.pathname.slice(1), // Remove leading '/'
50
+ user: dbConfig.user,
51
+ password: dbConfig.password,
52
+ max: dbConfig.maxConnections,
53
+ idleTimeoutMillis: dbConfig.idleTimeoutMs,
54
+ connectionTimeoutMillis: dbConfig.connectionTimeoutMs,
55
+ ssl: sslEnabled ? { rejectUnauthorized: false } : false,
56
+ });
57
+ // Log pool events
58
+ pool.on('connect', () => {
59
+ logger.debug('New database client connected');
60
+ });
61
+ pool.on('error', (err) => {
62
+ logger.error('Unexpected database pool error', { error: err.message });
63
+ });
64
+ logger.info('Database connection pool initialized', {
65
+ host: url.hostname,
66
+ port: url.port,
67
+ database: url.pathname.slice(1),
68
+ maxConnections: dbConfig.maxConnections,
69
+ });
70
+ return pool;
71
+ }
72
+ /**
73
+ * Get the database connection pool
74
+ * Initializes the pool if not already done
75
+ */
76
+ export function getPool() {
77
+ if (!pool) {
78
+ return initializePool();
79
+ }
80
+ return pool;
81
+ }
82
+ /**
83
+ * Check if the database connection is healthy
84
+ * @returns true if the database is reachable, false otherwise
85
+ */
86
+ export async function checkDatabaseHealth() {
87
+ try {
88
+ const dbPool = getPool();
89
+ const result = await dbPool.query('SELECT 1 as health');
90
+ return result.rows.length > 0 && result.rows[0].health === 1;
91
+ }
92
+ catch (error) {
93
+ logger.error('Database health check failed', {
94
+ error: error instanceof Error ? error.message : String(error),
95
+ });
96
+ return false;
97
+ }
98
+ }
99
+ /**
100
+ * Close the database connection pool
101
+ * Should be called when shutting down the server
102
+ */
103
+ export async function closePool() {
104
+ if (pool) {
105
+ logger.info('Closing database connection pool');
106
+ await pool.end();
107
+ pool = null;
108
+ logger.info('Database connection pool closed');
109
+ }
110
+ }
111
+ /**
112
+ * Execute a query with automatic connection management
113
+ * @param text - SQL query text
114
+ * @param params - Query parameters
115
+ */
116
+ export async function query(text, params) {
117
+ const dbPool = getPool();
118
+ const start = Date.now();
119
+ try {
120
+ const result = await dbPool.query(text, params);
121
+ const duration = Date.now() - start;
122
+ logger.debug('Executed query', {
123
+ text: text.substring(0, 100), // Truncate for logging
124
+ duration,
125
+ rows: result.rowCount,
126
+ });
127
+ return result;
128
+ }
129
+ catch (error) {
130
+ logger.error('Query execution failed', {
131
+ text: text.substring(0, 100),
132
+ error: error instanceof Error ? error.message : String(error),
133
+ });
134
+ throw error;
135
+ }
136
+ }
137
+ /**
138
+ * Run database migrations
139
+ * Creates necessary tables if they don't exist
140
+ */
141
+ export async function runMigrations() {
142
+ logger.info('Running database migrations');
143
+ const migrations = [
144
+ // Migration 001: Create mcp_user_state table
145
+ `
146
+ CREATE TABLE IF NOT EXISTS mcp_user_state (
147
+ id SERIAL PRIMARY KEY,
148
+ user_email VARCHAR(255) NOT NULL UNIQUE,
149
+ default_project_uuid VARCHAR(255),
150
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
151
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
152
+ );
153
+ `,
154
+ // Create index on user_email for faster lookups
155
+ `
156
+ CREATE INDEX IF NOT EXISTS idx_mcp_user_state_email
157
+ ON mcp_user_state(user_email);
158
+ `,
159
+ ];
160
+ for (const migration of migrations) {
161
+ try {
162
+ await query(migration);
163
+ }
164
+ catch (error) {
165
+ // Ignore "already exists" errors
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ if (!message.includes('already exists')) {
168
+ throw error;
169
+ }
170
+ }
171
+ }
172
+ logger.info('Database migrations completed');
173
+ }
174
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * PostgreSQL-backed state provider for HTTP/SSE server mode
3
+ * Stores user state in the configdb database, keyed by user email
4
+ */
5
+ import { StateProvider, StateData } from '../services/state.service.js';
6
+ /**
7
+ * PostgreSQL state provider for multi-user server deployments
8
+ * Each user's state is stored in the mcp_user_state table, keyed by email
9
+ */
10
+ export declare class PostgresStateProvider implements StateProvider {
11
+ /**
12
+ * Get the default project UUID for a user
13
+ */
14
+ getDefaultProjectUuid(userIdentifier?: string): Promise<string | undefined>;
15
+ /**
16
+ * Set the default project UUID for a user
17
+ * Uses upsert (INSERT ... ON CONFLICT) to create or update
18
+ */
19
+ setDefaultProjectUuid(projectUuid: string, userIdentifier?: string): Promise<void>;
20
+ /**
21
+ * Clear the default project UUID for a user
22
+ */
23
+ clearDefaultProjectUuid(userIdentifier?: string): Promise<void>;
24
+ /**
25
+ * Get the base URL
26
+ * Note: In HTTP/SSE mode, base URL is fixed per deployment (from env var)
27
+ * This method always returns undefined - base URL switching is not supported
28
+ */
29
+ getBaseUrl(_userIdentifier?: string): Promise<string | undefined>;
30
+ /**
31
+ * Set the base URL
32
+ * Note: Not supported in HTTP/SSE mode - base URL is fixed per deployment
33
+ */
34
+ setBaseUrl(_baseUrl: string, _userIdentifier?: string): Promise<void>;
35
+ /**
36
+ * Clear the base URL
37
+ * Note: Not supported in HTTP/SSE mode
38
+ */
39
+ clearBaseUrl(_userIdentifier?: string): Promise<void>;
40
+ /**
41
+ * Clear all state for a user
42
+ */
43
+ clearAll(userIdentifier?: string): Promise<void>;
44
+ /**
45
+ * Get all state data for a user
46
+ */
47
+ getAll(userIdentifier?: string): Promise<StateData>;
48
+ }
49
+ /**
50
+ * Get the PostgreSQL state provider singleton
51
+ */
52
+ export declare function getPostgresStateProvider(): PostgresStateProvider;
@@ -0,0 +1,165 @@
1
+ /**
2
+ * PostgreSQL-backed state provider for HTTP/SSE server mode
3
+ * Stores user state in the configdb database, keyed by user email
4
+ */
5
+ import { query } from './index.js';
6
+ import { logger } from '../utils/logger.js';
7
+ /**
8
+ * PostgreSQL state provider for multi-user server deployments
9
+ * Each user's state is stored in the mcp_user_state table, keyed by email
10
+ */
11
+ export class PostgresStateProvider {
12
+ /**
13
+ * Get the default project UUID for a user
14
+ */
15
+ async getDefaultProjectUuid(userIdentifier) {
16
+ if (!userIdentifier) {
17
+ logger.warn('PostgresStateProvider.getDefaultProjectUuid called without userIdentifier');
18
+ return undefined;
19
+ }
20
+ try {
21
+ const result = await query('SELECT default_project_uuid FROM mcp_user_state WHERE user_email = $1', [userIdentifier]);
22
+ if (result.rows.length === 0) {
23
+ return undefined;
24
+ }
25
+ return result.rows[0].default_project_uuid || undefined;
26
+ }
27
+ catch (error) {
28
+ logger.error('Failed to get default project UUID from database', {
29
+ userIdentifier,
30
+ error: error instanceof Error ? error.message : String(error),
31
+ });
32
+ return undefined;
33
+ }
34
+ }
35
+ /**
36
+ * Set the default project UUID for a user
37
+ * Uses upsert (INSERT ... ON CONFLICT) to create or update
38
+ */
39
+ async setDefaultProjectUuid(projectUuid, userIdentifier) {
40
+ if (!userIdentifier) {
41
+ throw new Error('PostgresStateProvider.setDefaultProjectUuid requires userIdentifier');
42
+ }
43
+ try {
44
+ await query(`INSERT INTO mcp_user_state (user_email, default_project_uuid, updated_at)
45
+ VALUES ($1, $2, NOW())
46
+ ON CONFLICT (user_email)
47
+ DO UPDATE SET default_project_uuid = $2, updated_at = NOW()`, [userIdentifier, projectUuid]);
48
+ logger.info('Set default project UUID in database', { userIdentifier, projectUuid });
49
+ }
50
+ catch (error) {
51
+ logger.error('Failed to set default project UUID in database', {
52
+ userIdentifier,
53
+ projectUuid,
54
+ error: error instanceof Error ? error.message : String(error),
55
+ });
56
+ throw error;
57
+ }
58
+ }
59
+ /**
60
+ * Clear the default project UUID for a user
61
+ */
62
+ async clearDefaultProjectUuid(userIdentifier) {
63
+ if (!userIdentifier) {
64
+ throw new Error('PostgresStateProvider.clearDefaultProjectUuid requires userIdentifier');
65
+ }
66
+ try {
67
+ await query(`UPDATE mcp_user_state
68
+ SET default_project_uuid = NULL, updated_at = NOW()
69
+ WHERE user_email = $1`, [userIdentifier]);
70
+ logger.info('Cleared default project UUID in database', { userIdentifier });
71
+ }
72
+ catch (error) {
73
+ logger.error('Failed to clear default project UUID in database', {
74
+ userIdentifier,
75
+ error: error instanceof Error ? error.message : String(error),
76
+ });
77
+ throw error;
78
+ }
79
+ }
80
+ /**
81
+ * Get the base URL
82
+ * Note: In HTTP/SSE mode, base URL is fixed per deployment (from env var)
83
+ * This method always returns undefined - base URL switching is not supported
84
+ */
85
+ async getBaseUrl(_userIdentifier) {
86
+ // In HTTP/SSE mode, base URL is set via environment variable
87
+ // Instance switching is not supported (each deployment serves one Hawkeye instance)
88
+ return undefined;
89
+ }
90
+ /**
91
+ * Set the base URL
92
+ * Note: Not supported in HTTP/SSE mode - base URL is fixed per deployment
93
+ */
94
+ async setBaseUrl(_baseUrl, _userIdentifier) {
95
+ logger.warn('PostgresStateProvider.setBaseUrl called but base URL switching is not supported in HTTP/SSE mode. ' +
96
+ 'The base URL is fixed per deployment via HAWKEYE_BASE_URL environment variable.');
97
+ // No-op - base URL is fixed in server mode
98
+ }
99
+ /**
100
+ * Clear the base URL
101
+ * Note: Not supported in HTTP/SSE mode
102
+ */
103
+ async clearBaseUrl(_userIdentifier) {
104
+ // No-op - base URL is fixed in server mode
105
+ }
106
+ /**
107
+ * Clear all state for a user
108
+ */
109
+ async clearAll(userIdentifier) {
110
+ if (!userIdentifier) {
111
+ throw new Error('PostgresStateProvider.clearAll requires userIdentifier');
112
+ }
113
+ try {
114
+ await query('DELETE FROM mcp_user_state WHERE user_email = $1', [userIdentifier]);
115
+ logger.info('Cleared all state in database', { userIdentifier });
116
+ }
117
+ catch (error) {
118
+ logger.error('Failed to clear all state in database', {
119
+ userIdentifier,
120
+ error: error instanceof Error ? error.message : String(error),
121
+ });
122
+ throw error;
123
+ }
124
+ }
125
+ /**
126
+ * Get all state data for a user
127
+ */
128
+ async getAll(userIdentifier) {
129
+ if (!userIdentifier) {
130
+ return {};
131
+ }
132
+ try {
133
+ const result = await query('SELECT default_project_uuid FROM mcp_user_state WHERE user_email = $1', [userIdentifier]);
134
+ if (result.rows.length === 0) {
135
+ return {};
136
+ }
137
+ const row = result.rows[0];
138
+ return {
139
+ defaultProjectUuid: row.default_project_uuid || undefined,
140
+ // baseUrl is not stored in database (fixed per deployment)
141
+ };
142
+ }
143
+ catch (error) {
144
+ logger.error('Failed to get all state from database', {
145
+ userIdentifier,
146
+ error: error instanceof Error ? error.message : String(error),
147
+ });
148
+ return {};
149
+ }
150
+ }
151
+ }
152
+ /**
153
+ * Singleton instance for PostgreSQL state provider
154
+ */
155
+ let postgresStateProvider = null;
156
+ /**
157
+ * Get the PostgreSQL state provider singleton
158
+ */
159
+ export function getPostgresStateProvider() {
160
+ if (!postgresStateProvider) {
161
+ postgresStateProvider = new PostgresStateProvider();
162
+ }
163
+ return postgresStateProvider;
164
+ }
165
+ //# sourceMappingURL=postgres-state-provider.js.map