diff --git a/MssqlMcp/Node/README.md b/MssqlMcp/Node/README.md index 0f09d14..a686ed0 100644 --- a/MssqlMcp/Node/README.md +++ b/MssqlMcp/Node/README.md @@ -92,12 +92,14 @@ This server leverages the Model Context Protocol (MCP), a versatile framework th "env": { "SERVER_NAME": "your-server-name.database.windows.net", "DATABASE_NAME": "your-database-name", + "AUTH_METHOD": "azure-ad", "READONLY": "false" } } } } } + } ``` 4. **Restart VS Code** @@ -126,25 +128,116 @@ This server leverages the Model Context Protocol (MCP), a versatile framework th "env": { "SERVER_NAME": "your-server-name.database.windows.net", "DATABASE_NAME": "your-database-name", + "AUTH_METHOD": "azure-ad", "READONLY": "false" } } } } + } ``` 3. **Restart Claude Desktop** - Close and reopen Claude Desktop for the changes to take effect +## Authentication Configuration + +The MCP server supports multiple authentication methods to connect to your MSSQL database. Choose the method that best fits your environment and security requirements. + +### Authentication Methods + +#### 1. Azure Active Directory (Default) +**Best for:** Azure SQL Database, managed identities, modern security requirements + +This method uses Azure AD authentication with interactive browser login. No username/password needed in configuration. + +```json +{ + "env": { + "SERVER_NAME": "your-server.database.windows.net", + "DATABASE_NAME": "your-database-name", + "AUTH_METHOD": "azure-ad" + } +} +``` + +**Required Environment Variables:** +- `AUTH_METHOD`: Set to `"azure-ad"` or `"azuread"` +- `SERVER_NAME`: Your Azure SQL server name +- `DATABASE_NAME`: Your database name + +**Notes:** +- Uses interactive browser authentication +- Automatically handles token refresh +- Requires Azure AD permissions for the database +- Most secure option for Azure SQL Database + +#### 2. SQL Server Authentication +**Best for:** Traditional SQL Server instances, local development + +Uses SQL Server's built-in authentication with username and password. + +```json +{ + "env": { + "SERVER_NAME": "your-server.database.windows.net", + "DATABASE_NAME": "your-database-name", + "AUTH_METHOD": "sql", + "SQL_USERNAME": "your-sql-username", + "SQL_PASSWORD": "your-sql-password" + } +} +``` + +**Required Environment Variables:** +- `AUTH_METHOD`: Set to `"sql"` or `"sqlserver"` +- `SERVER_NAME`: Your SQL Server instance +- `DATABASE_NAME`: Your database name +- `SQL_USERNAME`: SQL Server username +- `SQL_PASSWORD`: SQL Server password + +#### 3. Windows Authentication (NTLM) +**Best for:** On-premises SQL Server with Windows domain authentication + +Uses Windows credentials for authentication. + +```json +{ + "env": { + "SERVER_NAME": "your-server-instance", + "DATABASE_NAME": "your-database-name", + "AUTH_METHOD": "windows", + "DOMAIN": "your-domain", + "USERNAME": "your-windows-username", + "PASSWORD": "your-windows-password" + } +} +``` + +**Required Environment Variables:** +- `AUTH_METHOD`: Set to `"windows"` or `"ntlm"` +- `SERVER_NAME`: Your SQL Server instance +- `DATABASE_NAME`: Your database name +- `DOMAIN`: Windows domain (optional, can be empty) +- `USERNAME`: Windows username +- `PASSWORD`: Windows password + ### Configuration Parameters +#### Common Parameters (All Authentication Methods) - **SERVER_NAME**: Your MSSQL Database server name (e.g., `my-server.database.windows.net`) - **DATABASE_NAME**: Your database name +- **AUTH_METHOD**: Authentication method to use (`"azure-ad"`, `"sql"`, or `"windows"`). Defaults to `"azure-ad"` if not specified. - **READONLY**: Set to `"true"` to restrict to read-only operations, `"false"` for full access - **Path**: Update the path in `args` to point to your actual project location. - **CONNECTION_TIMEOUT**: (Optional) Connection timeout in seconds. Defaults to `30` if not set. - **TRUST_SERVER_CERTIFICATE**: (Optional) Set to `"true"` to trust self-signed server certificates (useful for development or when connecting to servers with self-signed certs). Defaults to `"false"`. +#### Authentication-Specific Parameters +- **SQL_USERNAME** & **SQL_PASSWORD**: Required for SQL Server authentication (`AUTH_METHOD="sql"`) +- **USERNAME**, **PASSWORD** & **DOMAIN**: Required for Windows authentication (`AUTH_METHOD="windows"`) +- **No additional parameters needed**: For Azure AD authentication (`AUTH_METHOD="azure-ad"`) + ## Sample Configurations You can find sample configuration files in the `src/samples/` folder: diff --git a/MssqlMcp/Node/src/index.ts b/MssqlMcp/Node/src/index.ts index 8dc1f30..d93ea5f 100644 --- a/MssqlMcp/Node/src/index.ts +++ b/MssqlMcp/Node/src/index.ts @@ -29,36 +29,77 @@ let globalSqlPool: sql.ConnectionPool | null = null; let globalAccessToken: string | null = null; let globalTokenExpiresOn: Date | null = null; -// Function to create SQL config with fresh access token, returns token and expiry -export async function createSqlConfig(): Promise<{ config: sql.config, token: string, expiresOn: Date }> { - const credential = new InteractiveBrowserCredential({ - redirectUri: 'http://localhost' - // disableAutomaticAuthentication : true - }); - const accessToken = await credential.getToken('https://database.windows.net/.default'); - +// Function to create SQL config with configurable authentication method +export async function createSqlConfig(): Promise<{ config: sql.config, token?: string, expiresOn?: Date }> { + const authMethod = process.env.AUTH_METHOD?.toLowerCase() || 'azure-ad'; const trustServerCertificate = process.env.TRUST_SERVER_CERTIFICATE?.toLowerCase() === 'true'; const connectionTimeout = process.env.CONNECTION_TIMEOUT ? parseInt(process.env.CONNECTION_TIMEOUT, 10) : 30; - return { - config: { - server: process.env.SERVER_NAME!, - database: process.env.DATABASE_NAME!, - options: { - encrypt: true, - trustServerCertificate - }, - authentication: { - type: 'azure-active-directory-access-token', - options: { - token: accessToken?.token!, - }, - }, - connectionTimeout: connectionTimeout * 1000, // convert seconds to milliseconds + const baseConfig = { + server: process.env.SERVER_NAME!, + database: process.env.DATABASE_NAME!, + options: { + encrypt: true, + trustServerCertificate, + useUTC: false }, - token: accessToken?.token!, - expiresOn: accessToken?.expiresOnTimestamp ? new Date(accessToken.expiresOnTimestamp) : new Date(Date.now() + 30 * 60 * 1000) + connectionTimeout: connectionTimeout * 1000, // convert seconds to milliseconds }; + + switch (authMethod) { + case 'azure-ad': + case 'azuread': + // Azure Active Directory authentication + const credential = new InteractiveBrowserCredential({ + redirectUri: 'http://localhost' + }); + const accessToken = await credential.getToken('https://database.windows.net/.default'); + + return { + config: { + ...baseConfig, + authentication: { + type: 'azure-active-directory-access-token', + options: { + token: accessToken?.token!, + }, + }, + }, + token: accessToken?.token!, + expiresOn: accessToken?.expiresOnTimestamp ? new Date(accessToken.expiresOnTimestamp) : new Date(Date.now() + 30 * 60 * 1000) + }; + + case 'windows': + case 'ntlm': + // Windows authentication + return { + config: { + ...baseConfig, + authentication: { + type: 'ntlm', + options: { + domain: process.env.DOMAIN || '', + userName: process.env.USERNAME || '', + password: process.env.PASSWORD || '' + } + }, + } + }; + + case 'sql': + case 'sqlserver': + // SQL Server authentication + return { + config: { + ...baseConfig, + user: process.env.SQL_USERNAME!, + password: process.env.SQL_PASSWORD!, + } + }; + + default: + throw new Error(`Unsupported authentication method: ${authMethod}. Supported methods: azure-ad, windows, sql`); + } } const updateDataTool = new UpdateDataTool(); @@ -164,21 +205,36 @@ runServer().catch((error) => { // Connect to SQL only when handling a request async function ensureSqlConnection() { - // If we have a pool and it's connected, and the token is still valid, reuse it - if ( - globalSqlPool && - globalSqlPool.connected && - globalAccessToken && - globalTokenExpiresOn && - globalTokenExpiresOn > new Date(Date.now() + 2 * 60 * 1000) // 2 min buffer - ) { - return; + const authMethod = process.env.AUTH_METHOD?.toLowerCase() || 'azure-ad'; + + // For Azure AD, check token expiry; for other methods, just check connection + if (authMethod === 'azure-ad' || authMethod === 'azuread') { + // If we have a pool and it's connected, and the token is still valid, reuse it + if ( + globalSqlPool && + globalSqlPool.connected && + globalAccessToken && + globalTokenExpiresOn && + globalTokenExpiresOn > new Date(Date.now() + 2 * 60 * 1000) // 2 min buffer + ) { + return; + } + } else { + // For Windows and SQL Server auth, just check if connection exists + if (globalSqlPool && globalSqlPool.connected) { + return; + } } - // Otherwise, get a new token and reconnect - const { config, token, expiresOn } = await createSqlConfig(); - globalAccessToken = token; - globalTokenExpiresOn = expiresOn; + // Get new config (and token if using Azure AD) + const result = await createSqlConfig(); + const { config } = result; + + // Store token info for Azure AD + if (authMethod === 'azure-ad' || authMethod === 'azuread') { + globalAccessToken = result.token!; + globalTokenExpiresOn = result.expiresOn!; + } // Close old pool if exists if (globalSqlPool && globalSqlPool.connected) {