Contract — P0.9.2 ν Claude API Wrappers

1. Module identity

Attribute Value
File src/domains/integrations/claude.ts
Test file src/__tests__/domains/integrations/claude.test.ts
Exported from src/domains/integrations/index.ts
Concept ν — Integrations (P0.9.2)
Layer Library-only. No MCP tools registered.

2. Public API surface

2.1 createCompletion

export async function createCompletion(
  prompt: string,
  options?: CompletionOptions,
): Promise<CompletionResult>

Behaviour:

  • Sends a POST to https://api.anthropic.com/v1/messages.
  • Model: options.model ?? config.COLIBRI_ANTHROPIC_MODEL ?? 'claude-sonnet-4-5'.
  • Max tokens: options.maxTokens ?? 1024.
  • Validates ANTHROPIC_API_KEY is set before making the request; throws AnthropicConfigError if absent.
  • Logs to stderr: [claude] model=<m> prompt_tokens=<n> completion_tokens=<n> latency_ms=<n>.
  • On 429: exponential backoff, max 3 retries (delays: 100 ms → 200 ms → 400 ms).
  • On 5xx: same retry policy.
  • On 4xx (excluding 429): terminal failure — throws AnthropicApiError.
  • On network error: terminal failure — throws AnthropicApiError.
  • After 3 failed retries: throws AnthropicApiError with retries_exhausted.

2.2 createCompletionWithTools

export async function createCompletionWithTools(
  prompt: string,
  tools: AnthropicTool[],
  options?: CompletionOptions,
): Promise<CompletionResult>

Behaviour:

  • Same as createCompletion with the addition of a tools array in the request body.
  • AnthropicTool: { name: string; description: string; input_schema: Record<string, unknown> }.
  • When tools is empty, degrades to a plain createCompletion call (no tools key in body).
  • Same retry and logging contract as createCompletion.

2.3 CompletionOptions

export interface CompletionOptions {
  readonly model?: string;
  readonly maxTokens?: number;
  readonly systemPrompt?: string;
  /** Inject a custom fetch for tests. Defaults to global `fetch`. */
  readonly fetchFn?: typeof fetch;
  /** Inject a custom logger for tests. Defaults to `console.error`. */
  readonly logger?: (...args: unknown[]) => void;
  /** Inject a delay function for tests. Defaults to `sleep` (real delay). */
  readonly delayFn?: (ms: number) => Promise<void>;
}

2.4 CompletionResult

export interface CompletionResult {
  readonly content: string;
  readonly model: string;
  readonly promptTokens: number;
  readonly completionTokens: number;
  readonly latencyMs: number;
  readonly stopReason: string;
}

2.5 AnthropicTool

export interface AnthropicTool {
  readonly name: string;
  readonly description: string;
  readonly input_schema: Record<string, unknown>;
}

2.6 Error types

/** Thrown when ANTHROPIC_API_KEY is missing at call time. */
export class AnthropicConfigError extends Error {
  readonly code = 'ANTHROPIC_CONFIG_ERROR';
}

/** Thrown when the API returns a terminal error or retries are exhausted. */
export class AnthropicApiError extends Error {
  readonly code: 'ANTHROPIC_API_ERROR' | 'ANTHROPIC_RETRIES_EXHAUSTED';
  readonly status?: number;
}

3. Env schema additions (to src/config.ts)

Variable Type Default Required
ANTHROPIC_API_KEY string Optional in schema; required at call-time
COLIBRI_ANTHROPIC_MODEL string 'claude-sonnet-4-5' No
COLIBRI_ANTHROPIC_TIMEOUT_MS number (coerce) 30000 No

ANTHROPIC_API_KEY is declared .optional() so the server boots cleanly even when the key is absent — this matches the acceptance criteria (“validated at startup” means validated before use, not at module load).

4. Retry algorithm

attempt = 0
delayMs = 100
maxRetries = 3

loop:
  try POST /v1/messages
  if success (2xx): return result
  if retryable (429, 5xx):
    if attempt >= maxRetries: throw AnthropicApiError('retries_exhausted')
    await delay(delayMs)
    delayMs *= 2
    attempt++
    continue
  else (4xx not 429, network error):
    throw AnthropicApiError(status)

5. Logging format

[claude] model=claude-sonnet-4-5 prompt_tokens=42 completion_tokens=128 latency_ms=1234

Logged to stderr (console.error / injected logger). Never to stdout.

6. Migration decision

No migration. Logging is stderr-only per P0.9.3 precedent. The spec says “logged”, not “stored in DB”. A 008_nu_anthropic.sql would be overkill and add coupling.

7. Security invariants

  • ANTHROPIC_API_KEY is never logged, never echoed in errors, never present in tests.
  • Tests use 'sk-ant-test-fake-key' dummy value.
  • No real network calls in tests — fetchFn is always injected.

8. Out of scope (Phase 0)

  • Circuit breaker (donor resilience.js).
  • Prompt cache / deduplication.
  • Streaming / SSE.
  • Extended thinking.
  • Token budget management.
  • MCP tool registration for Claude API.

Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.