Skip to content

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)

typescript
/**
 * 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:

typescript
export async function executeCommand(
  commandName: string,
  args: Record<string, any> = {}
): Promise<ExecutionResult & { commandName: string }>

AFTER:

typescript
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:

typescript
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:

typescript
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)

typescript
import { ConnectionContext } from './connection-context.js';

Change 2: Update tool request handler where commands are executed (line ~1325)

BEFORE:

typescript
// Execute the command
try {
  const result = await executeCommand(actualCommandName, args || {});
  const formattedOutput = formatResult(result);

  return {
    content: [
      {
        type: 'text',
        text: formattedOutput,
      },
    ],
  };
}

AFTER:

typescript
// 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:

typescript
tools.push({
  name: `hana_${sanitizeToolName(name)}`,
  description: fullDescription,
  inputSchema: info.schema,
});

AFTER:

typescript
// 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)

typescript
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

javascript
// 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

javascript
// 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)

javascript
// 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

bash
# 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 there

Test 2: Verify Environment Variables

Add to executor.ts for debugging:

typescript
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

javascript
// 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 command

Validation Checklist

  • [ ] connection-context.ts file created
  • [ ] executor.ts updated with context parameter
  • [ ] index.ts extracts and passes context
  • [ ] Tool schemas include __projectContext property
  • [ ] connections.js checks for HANA_CLI_PROJECT_PATH env 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

ScenarioBeforeAfter
Agent calls hana_tables without contextUses ~/.hana-cli/default.jsonUses ~/.hana-cli/default.json (same)
Agent calls hana_tables with projectPathUses ~/.hana-cli/default.json (WRONG!)Uses /project/path/.env (CORRECT!)
Agent switches projects mid-conversationSame DB for all commands (WRONG!)Each project uses its own DB (CORRECT!)
Multiple agents work different projectsConflicts, uses wrong DBIsolated contexts, correct DBs

Migration Path

  1. Deploy Phase 1: Add interfaces and update executor, index (2-3 hours)
  2. Deploy Phase 2: Update CLI connection logic (1 hour)
  3. Test: Manual testing with different project structures (2 hours)
  4. Release: No breaking changes, backward compatible
  5. Documentation: Update README with examples (1 hour)

Security Considerations

  1. Password in Parameters: Only use password in __projectContext for automation/secure systems. Prefer .env files.

  2. Path Validation: Consider validating projectPath to prevent directory traversal attacks:

    typescript
    if (context?.projectPath) {
      const normalized = path.resolve(context.projectPath);
      // Optional: Check against whitelist of allowed paths
    }
  3. Logging: Never log passwords:

    typescript
    // WRONG:
    base.debug(context);  // Would log password!
    
    // RIGHT:
    const safeContext = { ...context };
    delete safeContext.password;
    base.debug(safeContext);
  4. Environment Cleanup: Consider clearing sensitive env vars after command execution


Rollback Plan

If issues arise:

  1. Remove __projectContext handling from index.ts tool handler
  2. Remove context parameter from executeCommand() call
  3. Remove env variable checks from connections.js
  4. Server reverts to install-path-only behavior

No persistent state to clean up - fully reversible.

Released under the Apache License 2.0