Packet — P0.9.2 ν Claude API Wrappers

1. Files to create / modify

Action Path Purpose
CREATE src/domains/integrations/claude.ts Core library: createCompletion, createCompletionWithTools, retry, logging, error types
CREATE src/__tests__/domains/integrations/claude.test.ts Mock-based test suite covering all acceptance criteria
MODIFY src/config.ts Add ANTHROPIC_API_KEY, COLIBRI_ANTHROPIC_MODEL, COLIBRI_ANTHROPIC_TIMEOUT_MS to Zod schema
MODIFY src/domains/integrations/index.ts Add export * from './claude.js'

2. Implementation plan

2.1 src/config.ts additions

Add three fields to the existing Zod schema object:

ANTHROPIC_API_KEY: z.string().optional(),
COLIBRI_ANTHROPIC_MODEL: z.string().default('claude-sonnet-4-5'),
COLIBRI_ANTHROPIC_TIMEOUT_MS: z.coerce.number().int().positive().default(30000),

ANTHROPIC_API_KEY is .optional() so the server boots without it. Call-time validation in claude.ts raises AnthropicConfigError when absent.

2.2 src/domains/integrations/claude.ts structure

1. Imports: z (unused directly), config (for defaults), package-level constants
2. Constants:
     ANTHROPIC_API_BASE = 'https://api.anthropic.com/v1'
     ANTHROPIC_API_VERSION = '2023-06-01'
     DEFAULT_MAX_TOKENS = 1024
     MAX_RETRIES = 3
     BASE_DELAY_MS = 100
3. Error classes: AnthropicConfigError, AnthropicApiError
4. Interfaces: AnthropicTool, CompletionOptions, CompletionResult
5. Internal helpers:
     isRetryable(status: number): boolean
     buildRequestBody(prompt, tools?, options): object
     parseResult(json, startMs): CompletionResult
6. Core retry loop: attemptWithRetry(buildFn, options)
7. Public: createCompletion(prompt, options?)
8. Public: createCompletionWithTools(prompt, tools, options?)

2.3 Retry loop pseudocode

async function attemptWithRetry(request, options) {
  const fetchFn = options.fetchFn ?? fetch;
  const delayFn = options.delayFn ?? sleep;
  const logger  = options.logger  ?? console.error;
  let attempt = 0;
  let delayMs = BASE_DELAY_MS;

  while (true) {
    const startMs = Date.now();
    try {
      const res = await fetchFn(url, request);
      if (res.ok) {
        const json = await res.json();
        const result = parseResult(json, startMs);
        logger(`[claude] model=${result.model} prompt_tokens=${result.promptTokens} completion_tokens=${result.completionTokens} latency_ms=${result.latencyMs}`);
        return result;
      }
      if (isRetryable(res.status)) {
        if (attempt >= MAX_RETRIES) {
          throw new AnthropicApiError('retries_exhausted', res.status);
        }
        await delayFn(delayMs);
        delayMs *= 2;
        attempt++;
        continue;
      }
      // Terminal 4xx
      throw new AnthropicApiError(`HTTP ${res.status}`, res.status);
    } catch (err) {
      if (err instanceof AnthropicApiError || err instanceof AnthropicConfigError) throw err;
      // Network / fetch error — non-retryable at this level
      throw new AnthropicApiError(`Network error: ${err.message}`);
    }
  }
}

2.4 Test plan

Test case What it covers
Success path — createCompletion 200 response → correct CompletionResult
Success path — createCompletionWithTools tools array included in request body
429 → retry → success on 3rd attempt Retry logic; delayFn called twice
429 × 4 → retries exhausted AnthropicApiError with retries_exhausted
500 → retry → success 5xx retryable
400 terminal Throws without retry
Missing ANTHROPIC_API_KEY AnthropicConfigError
Token counting in log logger receives prompt_tokens / completion_tokens
Latency in log latency_ms present and ≥ 0
Empty tools array → no tools key in request Degrade path

3. Sequence diagram

caller
  │
  ├─ createCompletion(prompt, options)
  │     │ check ANTHROPIC_API_KEY → throw AnthropicConfigError if absent
  │     │ build request body
  │     └─ attemptWithRetry(...)
  │           │ POST /v1/messages
  │           │ 200 → parseResult → log → return CompletionResult
  │           │ 429/5xx → wait → retry (max 3)
  │           │ 4xx → throw AnthropicApiError
  │           └─ network err → throw AnthropicApiError

4. Acceptance criteria mapping

Criterion Implementation
createCompletion(prompt, options) calls API with configured model §2.2 + COLIBRI_ANTHROPIC_MODEL default
createCompletionWithTools(prompt, tools, options) §2.2 tools branch
API key from ANTHROPIC_API_KEY (validated at startup) AnthropicConfigError at call-time; optional in Zod
429 → exponential backoff, max 3 retries §2.3 retry loop
All calls logged with model, prompt_tokens, completion_tokens, latency_ms §2.3 logger call
Test (mock): retry logic and logging §2.4 test plan

5. Constraints

  • No new runtime dependency. Use global fetch.
  • Library-only — no MCP tool registration.
  • Never mock real network — inject fetchFn.
  • Never log or commit ANTHROPIC_API_KEY.
  • Do not touch src/domains/integrations/mcp-bridge.ts or 007_nu_broker.sql (P0.9.1 territory).

Back to top

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

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