Quick Implementation Guide: Project-Specific Connection Context for MCP
Problem Statement
The MCP server always uses connection files from the install path (~/default-env.json, ~/.hana-cli/default.json) instead of from the code project being analyzed by an AI Agent. This means it can't automatically use the correct database for the project context.
Solution Overview
Pass project connection context through MCP tool parameters → MCP Server sets this as environment/working directory → CLI looks for connections in project directory first.
Code Changes Required
1. Create Connection Context Interface
File: mcp-server/src/connection-context.ts (NEW FILE)
/**
* Connection context for project-specific database connections
* Passed from AI Agent through MCP tools to the CLI
*/
export interface ConnectionContext {
/** Absolute path to project directory */
projectPath?: string;
/** Connection file name relative to projectPath (e.g., '.env', 'default-env.json') */
connectionFile?: string;
/** Direct connection - host/port/credentials for explicit connection setup */
host?: string;
port?: number;
user?: string;
password?: string;
database?: string;
}2. Update Executor to Handle Connection Context
File: mcp-server/src/executor.ts
Change 1: Update function signature (line ~240)
BEFORE:
export async function executeCommand(
commandName: string,
args: Record<string, any> = {}
): Promise<ExecutionResult & { commandName: string }>AFTER:
import { ConnectionContext } from './connection-context.js';
export async function executeCommand(
commandName: string,
args: Record<string, any> = {},
context?: ConnectionContext
): Promise<ExecutionResult & { commandName: string }>Change 2: Update spawn call to use context (line ~260)
BEFORE:
let stdout = '';
let stderr = '';
// Spawn the CLI process
const child = spawn('node', [cliPath, ...commandArgs], {
env: {
...process.env,
// Ensure stdio output is captured
FORCE_COLOR: '0',
},
cwd: join(__dirname, '..', '..'),
});AFTER:
let stdout = '';
let stderr = '';
// Build environment with context
const env = {
...process.env,
FORCE_COLOR: '0',
};
// Apply project context to environment
if (context?.projectPath) {
env.HANA_CLI_PROJECT_PATH = context.projectPath;
}
if (context?.connectionFile) {
env.HANA_CLI_CONN_FILE = context.connectionFile;
}
// Set direct credentials if provided
if (context?.host) {
env.HANA_CLI_HOST = context.host;
env.HANA_CLI_PORT = String(context.port || 30013);
env.HANA_CLI_USER = context.user || '';
env.HANA_CLI_PASSWORD = context.password || '';
if (context.database) {
env.HANA_CLI_DATABASE = context.database;
}
}
// Spawn the CLI process
const child = spawn('node', [cliPath, ...commandArgs], {
env,
cwd: context?.projectPath ? context.projectPath : join(__dirname, '..', '..'),
});3. Update MCP Server Tool Handler
File: mcp-server/src/index.ts
Change 1: Import new interface (line 1)
import { ConnectionContext } from './connection-context.js';Change 2: Update tool request handler where commands are executed (line ~1325)
BEFORE:
// Execute the command
try {
const result = await executeCommand(actualCommandName, args || {});
const formattedOutput = formatResult(result);
return {
content: [
{
type: 'text',
text: formattedOutput,
},
],
};
}AFTER:
// Execute the command
try {
// Extract connection context if provided by agent
const context = (args as any)?.__projectContext as ConnectionContext | undefined;
// Remove context from args before passing to CLI (it's not a CLI parameter)
const cleanArgs = { ...args };
delete cleanArgs.__projectContext;
const result = await executeCommand(actualCommandName, cleanArgs, context);
const formattedOutput = formatResult(result);
return {
content: [
{
type: 'text',
text: formattedOutput,
},
],
};
}Change 3: Add __projectContext to command tool schemas (line ~145)
Find the tool schema building code and update each command's inputSchema:
BEFORE:
tools.push({
name: `hana_${sanitizeToolName(name)}`,
description: fullDescription,
inputSchema: info.schema,
});AFTER:
// Extend schema with project context
const extendedSchema = {
...info.schema,
properties: {
...(info.schema.properties || {}),
__projectContext: {
type: 'object',
description: 'Project-specific connection context (optional). Used to find project-specific connection files instead of install path.',
properties: {
projectPath: {
type: 'string',
description: 'Absolute path to the project directory. Example: "C:/Users/me/projects/my-app"'
},
connectionFile: {
type: 'string',
description: 'Connection file location relative to projectPath. Example: ".env" or "default-env.json"'
},
host: {
type: 'string',
description: 'Database host (for direct connection)'
},
port: {
type: 'number',
description: 'Database port (for direct connection)'
},
user: {
type: 'string',
description: 'Database user (for direct connection)'
},
password: {
type: 'string',
description: 'Database password (for direct connection - use file-based credentials when possible)'
},
database: {
type: 'string',
description: 'Database name (for direct connection)'
}
}
}
}
};
tools.push({
name: `hana_${sanitizeToolName(name)}`,
description: fullDescription,
inputSchema: extendedSchema,
});4. Update CLI Connection Resolution
File: utils/connections.js
Change: Add at the beginning of getConnOptions() function (line ~92)
export async function getConnOptions(prompts) {
base.debug(base.bundle.getText("debug.call", ["getConnOptions"]))
// NEW: Check for project-specific context from MCP server
const projectPath = process.env.HANA_CLI_PROJECT_PATH;
const connFile = process.env.HANA_CLI_CONN_FILE;
// If project path provided, change to that directory so connection resolution starts there
if (projectPath && fs.existsSync(projectPath)) {
process.chdir(projectPath);
base.debug(`Using project directory for connection resolution: ${projectPath}`);
}
// NEW: Check for direct database credentials from MCP (for explicit connection setup)
if (process.env.HANA_CLI_HOST) {
const directConnection = {
hana: {
host: process.env.HANA_CLI_HOST,
port: parseInt(process.env.HANA_CLI_PORT || '30013'),
user: process.env.HANA_CLI_USER,
password: process.env.HANA_CLI_PASSWORD,
database: process.env.HANA_CLI_DATABASE || 'SYSTEMDB',
}
};
base.debug('Using direct database connection from MCP context');
return directConnection;
}
// Rest of existing code continues...
delete process.env.VCAP_SERVICES
// Try .cdsrc-private.json with CDS binding first
const cdsrcPrivate = prompts?.admin ? undefined : getCdsrcPrivate()
// ... rest of function
}Usage: AI Agent Perspective
Example 1: Using Project's .env File
// Agent knows the project path and wants to use its .env file
await mcp.callTool('hana_tables', {
schema: 'MY_SCHEMA',
__projectContext: {
projectPath: '/home/user/projects/my-cap-app',
connectionFile: '.env'
}
});
// Result: CLI changes to /home/user/projects/my-cap-app
// Then looks for .env file there (not in install path)Example 2: Explicit Database Connection
// Agent has database credentials and wants to use them directly
await mcp.callTool('hana_import', {
file: 'data.csv',
table: 'MY_TABLE',
__projectContext: {
host: 'database.mydomain.com',
port: 30013,
user: 'DBAdmin',
password: 'MyPassword123',
database: 'SYSTEMDB'
}
});Example 3: Backward Compatible (No Context)
// If no context provided, works as before (uses install path)
await mcp.callTool('hana_status');
// Uses default-env.json from install directory or ~/.hana-cli/Testing the Implementation
Test 1: Basic Functionality
# Create test project with .env
mkdir -p /tmp/test-project
echo "host=myhost" > /tmp/test-project/.env
# Run MCP server and call a command with context
node mcp-server/build/index.js
# Send: hana_tables with __projectContext.projectPath="/tmp/test-project"
# Expected: CLI changes to /tmp/test-project and finds .env thereTest 2: Verify Environment Variables
Add to executor.ts for debugging:
if (context) {
console.error('[DEBUG] Applied context:', {
cwd: context.projectPath,
envVars: {
HANA_CLI_PROJECT_PATH: env.HANA_CLI_PROJECT_PATH,
HANA_CLI_CONN_FILE: env.HANA_CLI_CONN_FILE
}
});
}Test 3: Multiple Projects in One Conversation
// First command uses Project A
await mcp.callTool('hana_tables', {
__projectContext: { projectPath: '/path/to/project-a' }
});
// Second command uses Project B (different database)
await mcp.callTool('hana_import', {
__projectContext: { projectPath: '/path/to/project-b' }
});
// Expected: Different databases for each commandValidation Checklist
- [ ]
connection-context.tsfile created - [ ]
executor.tsupdated with context parameter - [ ]
index.tsextracts and passes context - [ ] Tool schemas include
__projectContextproperty - [ ]
connections.jschecks forHANA_CLI_PROJECT_PATHenv var - [ ] Commands work without context (backward compatible)
- [ ] Commands work with context (uses project path)
- [ ] Environment variable setting is correct
- [ ] CWD changing works properly
- [ ] No passwords logged in debug output
Expected Behavior After Implementation
| Scenario | Before | After |
|---|---|---|
Agent calls hana_tables without context | Uses ~/.hana-cli/default.json | Uses ~/.hana-cli/default.json (same) |
Agent calls hana_tables with projectPath | Uses ~/.hana-cli/default.json (WRONG!) | Uses /project/path/.env (CORRECT!) |
| Agent switches projects mid-conversation | Same DB for all commands (WRONG!) | Each project uses its own DB (CORRECT!) |
| Multiple agents work different projects | Conflicts, uses wrong DB | Isolated contexts, correct DBs |
Migration Path
- Deploy Phase 1: Add interfaces and update executor, index (2-3 hours)
- Deploy Phase 2: Update CLI connection logic (1 hour)
- Test: Manual testing with different project structures (2 hours)
- Release: No breaking changes, backward compatible
- Documentation: Update README with examples (1 hour)
Security Considerations
Password in Parameters: Only use
passwordin__projectContextfor automation/secure systems. Prefer.envfiles.Path Validation: Consider validating
projectPathto prevent directory traversal attacks:typescriptif (context?.projectPath) { const normalized = path.resolve(context.projectPath); // Optional: Check against whitelist of allowed paths }Logging: Never log passwords:
typescript// WRONG: base.debug(context); // Would log password! // RIGHT: const safeContext = { ...context }; delete safeContext.password; base.debug(safeContext);Environment Cleanup: Consider clearing sensitive env vars after command execution
Rollback Plan
If issues arise:
- Remove
__projectContexthandling fromindex.tstool handler - Remove context parameter from
executeCommand()call - Remove env variable checks from
connections.js - Server reverts to install-path-only behavior
No persistent state to clean up - fully reversible.
