Microsoft SQL Server MCP Server (MSSQL)
by dperussina
Verified
// MCP Client implementation for MS SQL Server
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import readline from 'readline';
import path from 'path';
import dotenv from 'dotenv';
import { fileURLToPath } from 'url';
import util from 'util';
import { spawn } from 'child_process';
// Load environment variables
dotenv.config();
// Get current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Create command-line interface
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Client configuration
const clientConfig = {
name: "MSSQL-MCP-Client",
version: "1.0.0"
};
// Capabilities configuration
const capabilities = {
capabilities: {
resources: {},
tools: {},
prompts: {}
}
};
// Create client
const client = new Client(clientConfig, capabilities);
// Debug logging utility
function debugLog(message, obj) {
if (process.env.DEBUG === 'true') {
console.log(message);
if (obj) {
console.log(util.inspect(obj, { depth: 3, colors: true }));
}
}
}
// Helper to print formatted results with better formatting
function printResult(result) {
console.log('\nš Result:');
if (result.isError) {
console.log('ā Error returned from server:');
}
if (Array.isArray(result.content)) {
result.content.forEach(item => {
if (item.type === 'text') {
console.log('š Text response:');
console.log('-------------------------------------------');
console.log(item.text);
console.log('-------------------------------------------');
} else {
console.log(`š ${item.type} response:`);
console.log(item);
}
});
} else {
console.log('ā ļø Unexpected response format:', result);
}
}
// Enhance client methods with better logging and error handling
function enhanceClientMethods() {
// Enhance readResource method
const originalReadResource = client.readResource.bind(client);
client.readResource = async function(uri) {
console.log(`š¤ Sending request: readResource ${uri}`);
try {
const result = await originalReadResource(uri);
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in readResource:`, err);
throw err;
}
};
// Enhance callTool method
const originalCallTool = client.callTool.bind(client);
client.callTool = async function(name, args) {
console.log(`š¤ Sending request: callTool ${name}`);
debugLog(` Arguments:`, args);
try {
const result = await originalCallTool(name, args);
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in callTool:`, err);
throw err;
}
};
// Enhance listResources method
const originalListResources = client.listResources.bind(client);
client.listResources = async function() {
console.log(`š¤ Sending request: listResources`);
try {
const result = await originalListResources();
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in listResources:`, err);
throw err;
}
};
// Enhance listTools method
const originalListTools = client.listTools.bind(client);
client.listTools = async function() {
console.log(`š¤ Sending request: listTools`);
try {
const result = await originalListTools();
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in listTools:`, err);
throw err;
}
};
// Enhance listPrompts method
const originalListPrompts = client.listPrompts.bind(client);
client.listPrompts = async function() {
console.log(`š¤ Sending request: listPrompts`);
try {
const result = await originalListPrompts();
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in listPrompts:`, err);
throw err;
}
};
// Enhance getPrompt method
const originalGetPrompt = client.getPrompt.bind(client);
client.getPrompt = async function(name, args) {
console.log(`š¤ Sending request: getPrompt ${name}`);
debugLog(` Arguments:`, args);
try {
const result = await originalGetPrompt(name, args);
debugLog(`š„ Received response: success`, result);
return result;
} catch (err) {
console.error(`ā Error in getPrompt:`, err);
throw err;
}
};
}
// Connect to the MCP server
async function connectToServer() {
console.log('\n=======================================');
console.log(' š MSSQL MCP CLIENT š');
console.log('=======================================');
console.log('š Starting MSSQL MCP Client...');
// Create appropriate transport based on environment variable
const transportType = process.env.TRANSPORT || 'stdio';
console.log(`š Connecting to MCP server using ${transportType} transport...`);
let transport;
// Create appropriate transport
if (transportType === 'stdio') {
// For stdio transport, start the server as a child process
transport = new StdioClientTransport();
} else if (transportType === 'sse') {
// For SSE transport, we need to connect to a server that's already running
// Ensure the URL is properly formed
let serverUrl = process.env.SERVER_URL || 'http://localhost:3333';
// Make sure serverUrl doesn't have a trailing slash
if (serverUrl.endsWith('/')) {
serverUrl = serverUrl.slice(0, -1);
}
// Verify that the server is running separately before trying to connect
console.log(`š Connecting to server at ${serverUrl}`);
console.log(` Make sure the server is running with: TRANSPORT=sse npm start`);
console.log(` The server should be listening on port ${new URL(serverUrl).port}`);
// Create SSE transport with properly formatted URLs
const sseEndpoint = `${serverUrl}/sse`;
const messagesEndpoint = `${serverUrl}/messages`;
console.log(` - SSE endpoint: ${sseEndpoint}`);
console.log(` - Messages endpoint: ${messagesEndpoint}`);
try {
// The SSEClientTransport constructor expects the URL of the SSE endpoint
// It will automatically receive the messages endpoint when connecting
transport = new SSEClientTransport(new URL(sseEndpoint));
console.log(' Created SSE transport successfully');
// Add error handler to transport for better debugging
transport.onerror = (error) => {
console.error(`ā SSE Transport error:`, error);
};
} catch (err) {
console.error(` ā Error creating SSE transport: ${err.message}`);
console.error(` ${err.stack}`);
throw err;
}
} else {
throw new Error(`Unknown transport type: ${transportType}`);
}
// Enhance client methods to add logging
enhanceClientMethods();
// Connect to the transport
try {
console.log(' Attempting to connect to transport...');
await client.connect(transport);
console.log(`> ā
Connected to MCP server using ${transportType} transport`);
console.log(`š Client connection established`);
} catch (err) {
console.error(`ā Connection error: ${err.message}`);
console.error(` If using SSE transport, make sure the server is running separately with TRANSPORT=sse npm start`);
// Additional debugging for SSE transport errors
if (err.stack) {
console.error(err.stack);
}
process.exit(1);
}
}
// List available resources
async function listResources() {
try {
console.log('\nš Listing available resources...');
const resources = await client.listResources();
console.log('\nā
Resources retrieved successfully!');
console.log('\nš Available Resources:');
console.log('-------------------------------------------');
if (resources.length === 0) {
console.log('No resources available.');
} else {
resources.forEach(resource => {
console.log(`š ${resource.name}: ${resource.description || 'No description'}`);
console.log(` Pattern: ${resource.uriPattern}`);
console.log();
});
}
console.log('-------------------------------------------');
} catch (err) {
console.error('ā Error listing resources:', err);
}
}
// List available tools
async function listTools() {
try {
console.log('\nš Listing available tools...');
const tools = await client.listTools();
console.log('\nā
Tools retrieved successfully!');
console.log('\nš Available Tools:');
console.log('-------------------------------------------');
if (tools.length === 0) {
console.log('No tools available.');
} else {
tools.forEach(tool => {
console.log(`š§ ${tool.name}: ${tool.description || 'No description'}`);
console.log();
});
}
console.log('-------------------------------------------');
} catch (err) {
console.error('ā Error listing tools:', err);
}
}
// List available prompts
async function listPrompts() {
try {
console.log('\nš Listing available prompts...');
const prompts = await client.listPrompts();
console.log('\nā
Prompts retrieved successfully!');
console.log('\nš Available Prompts:');
console.log('-------------------------------------------');
if (prompts.length === 0) {
console.log('No prompts available.');
} else {
prompts.forEach(prompt => {
console.log(`š ${prompt.name}: ${prompt.description || 'No description'}`);
console.log();
});
}
console.log('-------------------------------------------');
} catch (err) {
console.error('ā Error listing prompts:', err);
}
}
// Read database schema through resource
async function readSchema() {
try {
console.log('\nš Reading database schema...');
const schema = await client.readResource('schema://database');
console.log('\nā
Database schema retrieved successfully!');
console.log('\nš Database Schema:');
console.log('-------------------------------------------');
schema.contents.forEach(content => {
console.log(content.text);
});
console.log('-------------------------------------------');
} catch (err) {
console.error('ā Error reading schema:', err);
}
}
// Read database tables list through resource
async function readTablesList() {
try {
console.log('\nš Reading tables list...');
// Add a more detailed debug message
console.log('š¤ Sending request: readResource tables://list');
try {
const tables = await client.readResource('tables://list');
console.log('\nā
Database tables list retrieved successfully!');
console.log('\nš Database Tables:');
console.log('-------------------------------------------');
if (!tables || !tables.contents || !Array.isArray(tables.contents)) {
console.error('ā Unexpected response format:', tables);
return;
}
tables.contents.forEach(content => {
console.log(content.text);
});
console.log('-------------------------------------------');
} catch (readError) {
console.error('ā Error in readResource:', readError);
// Add more detailed error logging
if (readError.cause) {
console.error('Caused by:', readError.cause);
}
if (readError.message && readError.message.includes('POSTing to endpoint')) {
console.error('\nā ļø Server may have responded with an invalid JSON-RPC response');
console.error('Please check server logs for details');
}
throw readError;
}
} catch (err) {
console.error('ā Error reading tables list:', err);
}
}
// Generate SQL query through prompt
async function generateQuery(description, tables = []) {
try {
console.log('\nš Generating SQL query...');
console.log(`Description: ${description}`);
if (tables.length > 0) {
console.log(`Tables: ${tables.join(', ')}`);
}
// Get prompt from server
const prompt = await client.getPrompt('generate-query', {
description,
tables
});
// Execute prompt with LLM
const result = await client.executePrompt(prompt);
console.log('\nā
SQL query generated successfully!');
printResult(result);
} catch (err) {
console.error('ā Error generating query:', err);
}
}
// Execute SQL query through tool
async function executeQuery(sql) {
try {
console.log('\nš Executing SQL query...');
console.log(`Query: ${sql}`);
const result = await client.callTool('execute-query', { sql });
console.log('\nā
SQL query executed successfully!');
printResult(result);
} catch (err) {
console.error('ā Error executing query:', err);
}
}
// Get table details
async function getTableDetails(tableName) {
try {
console.log(`\nš Getting details for table: ${tableName}...`);
const result = await client.callTool('table-details', { tableName });
console.log('\nā
Table details retrieved successfully!');
printResult(result);
} catch (err) {
console.error('ā Error getting table details:', err);
}
}
// Display menu and handle user interaction
function showMenu() {
console.log('\n======================================');
console.log(' š MSSQL MCP CLIENT MENU š');
console.log('======================================');
console.log('1. š List available resources');
console.log('2. š§ List available tools');
console.log('3. š List available prompts');
console.log('4. š Execute SQL query');
console.log('5. š Get table details');
console.log('6. š Read database schema');
console.log('7. š Read tables list');
console.log('8. š Generate SQL query');
console.log('9. šŖ Exit');
console.log('======================================');
rl.question('\nš Select an option (1-9): ', async (answer) => {
switch (answer) {
case '1':
await listResources();
showMenu();
break;
case '2':
await listTools();
showMenu();
break;
case '3':
await listPrompts();
showMenu();
break;
case '4':
rl.question('āļø Enter SQL query: ', async (sql) => {
await executeQuery(sql);
showMenu();
});
break;
case '5':
rl.question('āļø Enter table name: ', async (tableName) => {
await getTableDetails(tableName);
showMenu();
});
break;
case '6':
await readSchema();
showMenu();
break;
case '7':
await readTablesList();
showMenu();
break;
case '8':
rl.question('āļø Enter query description: ', async (description) => {
rl.question('āļø Enter tables (comma-separated) or leave empty: ', async (tablesInput) => {
const tables = tablesInput ? tablesInput.split(',').map(t => t.trim()) : [];
await generateQuery(description, tables);
showMenu();
});
});
break;
case '9':
console.log('š Goodbye!');
rl.close();
process.exit(0);
break;
default:
console.log('ā Invalid option. Please try again.');
showMenu();
break;
}
});
}
// Main entry point
async function main() {
try {
// Connect to server
await connectToServer();
// Show interactive menu
showMenu();
} catch (err) {
console.error('ā Error starting client:', err);
process.exit(1);
}
}
// Start the client
main();