Skip to content

Add comprehensive authentication documentation and multi-auth support #57

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions MssqlMcp/Node/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down Expand Up @@ -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:
Expand Down
132 changes: 94 additions & 38 deletions MssqlMcp/Node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down