<!-- Fork of https://github.com/fr0ster/mcp-abap-adt — original project by fr0ster -->

# Handler Exporter Guide

This guide explains how to use the `HandlerExporter` class to integrate ABAP ADT handlers into your own MCP server.

## Overview

The `HandlerExporter` class allows you to register all ABAP ADT handlers on any `McpServer` instance. This is useful when you want to:

- Embed ABAP ADT functionality into an existing server (e.g., SAP CAP/CDS application)
- Use your own connection management logic
- Customize which handlers are available

## Installation

```bash
npm install @fr0ster/mcp-abap-adt
```

## Basic Usage

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { HandlerExporter } from "@fr0ster/mcp-abap-adt/handlers";
import { createAbapConnection } from "@mcp-abap-adt/connection";

// Create your MCP server
const mcpServer = new McpServer({ name: "my-server", version: "1.0.0" });

// Create handler exporter
const exporter = new HandlerExporter();

// Register handlers with a connection provider
exporter.registerOnServer(mcpServer, () => {
  return createAbapConnection({
    url: "https://your-sap-system.com",
    authType: "basic",
    username: "user",
    password: "pass",
  });
});
```

## Connection Provider

The `connectionProvider` function is called for **each handler invocation**. This allows you to:

- Return different connections for different requests
- Implement per-request authentication (e.g., different JWT tokens)
- Use connection pooling

### Async Connection Provider

```typescript
exporter.registerOnServer(mcpServer, async () => {
  // Get fresh token for each request
  const token = await getJwtToken();
  return createAbapConnection({
    url: sapUrl,
    authType: "jwt",
    jwtToken: token,
  });
});
```

### Per-Request Context (AsyncLocalStorage)

For web servers, you can use AsyncLocalStorage to pass request-specific config:

```typescript
import { AsyncLocalStorage } from "async_hooks";

interface RequestContext {
  sapConfig: SapConfig;
}

const requestContext = new AsyncLocalStorage<RequestContext>();

exporter.registerOnServer(mcpServer, () => {
  const ctx = requestContext.getStore();
  if (!ctx) throw new Error("No request context");
  return createAbapConnection(ctx.sapConfig);
});

// In your request handler:
app.post("/mcp", (req, res) => {
  const sapConfig = extractSapConfigFromHeaders(req);
  requestContext.run({ sapConfig }, async () => {
    await handleMcpRequest(req, res);
  });
});
```

## Handler Groups

By default, all handler groups are included. You can customize this:

```typescript
const exporter = new HandlerExporter({
  includeReadOnly: true,   // getProgram, getClass, getTable, etc.
  includeHighLevel: true,  // createProgram, updateClass, etc.
  includeLowLevel: true,   // low-level ADT operations
  includeSystem: true,     // getInactiveObjects, etc.
  includeSearch: true,     // whereUsed, quickFix, etc.
});
```

### Read-Only Mode

For a read-only server:

```typescript
const exporter = new HandlerExporter({
  includeReadOnly: true,
  includeHighLevel: false,
  includeLowLevel: false,
  includeSystem: true,
  includeSearch: true,
});
```

## Inspecting Handlers

```typescript
// Get all tool names
const toolNames = exporter.getToolNames();
console.log("Available tools:", toolNames);

// Get handler entries for custom registration
const entries = exporter.getHandlerEntries();
for (const entry of entries) {
  console.log(`Tool: ${entry.toolDefinition.name}`);
  console.log(`Description: ${entry.toolDefinition.description}`);
}
```

## Creating Registry for v2 Servers

If you're using v2 server classes directly:

```typescript
import { StreamableHttpServer } from "@fr0ster/mcp-abap-adt/server/v2";

const exporter = new HandlerExporter();
const registry = exporter.createRegistry();

const server = new StreamableHttpServer(registry, authBrokerFactory, {
  path: "/mcp",
});
```

## Integration with SAP CAP/CDS

Example for cloud-llm-hub style integration:

```typescript
import cds from "@sap/cds";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { HandlerExporter } from "@fr0ster/mcp-abap-adt";
import { createConnection } from "./connections/connectionFactory";

// Create MCP server instance
const mcpServer = new McpServer({ name: "cloud-llm-hub", version: "1.0.0" });

// Create handler exporter
const exporter = new HandlerExporter();

// Register handlers with CAP-aware connection provider
exporter.registerOnServer(mcpServer, async () => {
  const sapContext = await extractSapContext(req);
  return createConnection({
    sapConfig: sapContext.sapConfig,
    destinationName: sapContext.destination?.destinationName,
  });
});
```

## Logging

Pass a custom logger:

```typescript
import { createLogger } from "@mcp-abap-adt/logger";

const logger = createLogger({ level: "debug" });
const exporter = new HandlerExporter({ logger });
```

## Migration from v1 Server

If you were using `mcp_abap_adt_server` directly:

```typescript
// Old way (still works via legacy import)
import { mcp_abap_adt_server } from "@fr0ster/mcp-abap-adt";
const server = new mcp_abap_adt_server({ connection });
await server.run();

// New way (recommended for embedding)
import { HandlerExporter } from "@fr0ster/mcp-abap-adt/handlers";
const exporter = new HandlerExporter();
exporter.registerOnServer(yourMcpServer, () => connection);
```

## See Also

- [Architecture Overview](README.md)
- [Tools Architecture](TOOLS_ARCHITECTURE.md)
- [Connection Isolation](CONNECTION_ISOLATION.md)
