/ docs · functional specification

The full platform
functional reference.

Every functional area of AI API Mapper documented at implementation depth so architects, security reviewers, and platform engineers can evaluate the product with real technical context.

SDK · Enterprise patterns

SDK Enterprise Patterns

Purpose

This area describes patterns for using the SDK in production and multi-tenant environments.

Main Capabilities

  • API key authentication with safe rotation
  • OAuth2 client credentials for regulated environments
  • Per-tenant client instances
  • Audit logging via client decorator
  • Human approval before high-risk tool calls

API Key Authentication

Read the key from an environment variable or a secret store. Never hardcode it.

To rotate: update the environment variable and restart the process. No code change is required. The SDK has no key lifecycle — rotation is a deployment concern.

// .NET
.WithApiKey(Environment.GetEnvironmentVariable("APIMAPPER_API_KEY")!);
// TypeScript
credentials: new ApiKeyCredentialProvider(process.env.APIMAPPER_API_KEY!)
# Python
credentials=ApiKeyCredentialProvider(os.getenv("APIMAPPER_API_KEY"))
// Java
.credentials(new ApiKeyCredentialProvider(System.getenv("APIMAPPER_API_KEY")))

OAuth2 Client Credentials

Use OAuth2 when operating in a regulated environment where all identities must originate from a trusted IdP. The SDK handles token acquisition and refresh automatically.

See SDK Authentication for full configuration examples across all languages.

Tenant-Aware Tool Filtering

In SaaS applications where one backend serves multiple tenants, create a separate client instance per tenant. Each instance maintains an independent MCP session. There is no cross-tenant data sharing.

// .NET — factory pattern
public IAiApiMapperClient GetClient(Guid tenantId, string clientId)
{
    var httpClient = _httpFactory.CreateClient();
    httpClient.BaseAddress = new Uri(
        $"{_baseUrl.TrimEnd('/')}/runtime/{tenantId:D}/{clientId}/mcp");

    return new ApiMapperClient(
        httpClient,
        Options.Create(new ApiMapperClientOptions
        {
            BaseUrl  = _baseUrl,
            TenantId = tenantId,
            ClientId = clientId,
        }),
        _credentials,
        _loggerFactory.CreateLogger<ApiMapperClient>());
}
// TypeScript — one client per tenant
function makeClient(tenantId: string, clientId: string) {
    return new ApiMapperClient({
        baseUrl:     process.env.APIMAPPER_BASE_URL!,
        tenantId,
        clientId,
        credentials: new ApiKeyCredentialProvider(process.env.APIMAPPER_API_KEY!),
    });
}
# Python — one client per tenant
def make_client(tenant_id: str, client_id: str) -> ApiMapperClient:
    return ApiMapperClient(ApiMapperClientOptions(
        base_url=os.environ["APIMAPPER_BASE_URL"],
        tenant_id=uuid.UUID(tenant_id),
        client_id=client_id,
        credentials=ApiKeyCredentialProvider(os.environ["APIMAPPER_API_KEY"]),
    ))

Audit Logging

Wrap IAiApiMapperClient with a decorator that logs tool name, elapsed time, and result status before and after every invocation. Arguments are not logged to avoid inadvertent PII or secret exposure.

// .NET decorator
public sealed class AuditingApiMapperClient : IAiApiMapperClient
{
    public async Task<McpToolCallResult?> InvokeToolAsync(
        string toolName, object? arguments, CancellationToken ct = default)
    {
        var sw = Stopwatch.StartNew();
        try
        {
            var result = await _inner.InvokeToolAsync(toolName, arguments, ct);
            _logger.LogInformation(
                "Tool {Tool} completed. IsError={IsError} ElapsedMs={Ms}",
                toolName, result?.IsError, sw.ElapsedMilliseconds);
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Tool {Tool} failed after {Ms}ms.", toolName, sw.ElapsedMilliseconds);
            throw;
        }
    }
    // delegate GetSystemPromptAsync, GetToolsAsync, ResetSession to _inner
}
// TypeScript decorator
class AuditingApiMapperClient {
    async invokeTool(toolName: string, args?: Record<string, unknown>) {
        const start = Date.now();
        try {
            const result = await this.inner.invokeTool(toolName, args);
            console.log(JSON.stringify({
                event: "tool_invoked", tool: toolName,
                success: true, isError: result?.isError ?? null,
                elapsedMs: Date.now() - start,
            }));
            return result;
        } catch (e) {
            console.error(JSON.stringify({
                event: "tool_failed", tool: toolName,
                elapsedMs: Date.now() - start, error: String(e),
            }));
            throw e;
        }
    }
}

Human Approval Before Tool Call

Suspend tool execution and wait for an out-of-band approval signal before invoking high-risk tools. The approval can come from any source — a SignalR message, a database poll, or an HTTP callback.

// .NET — approval interceptor
public sealed class HumanApprovalApiMapperClient : IAiApiMapperClient
{
    // Tools in this set require human approval before execution.
    public IReadOnlySet<string> RequiresApproval { get; init; } =
        new HashSet<string>(StringComparer.OrdinalIgnoreCase);

    public async Task<McpToolCallResult?> InvokeToolAsync(
        string toolName, object? arguments, CancellationToken ct = default)
    {
        if (RequiresApproval.Contains(toolName))
        {
            var approved = await WaitForApprovalAsync(toolName, ct);
            if (!approved) throw new OperationCanceledException($"'{toolName}' was denied.");
        }
        return await _inner.InvokeToolAsync(toolName, arguments, ct);
    }
}

The WaitForApprovalAsync implementation is application-specific. A minimal approach uses a TaskCompletionSource<bool> keyed by approval ID, resolved when the operator confirms or denies via any channel.