Prepare a Server-Side TypeScript Project
The project is simple, but a couple of decisions in it are fundamental. The examples use ESM, TypeScript, and the official SDKs of all three providers. Versions aren't pinned in the book on purpose: check the release notes before installing and keep a genuinely tested build in the lockfile - API lines change faster than product logic.
Everything installs in a few commands.
npm init -y
npm pkg set type=module
npm install openai @anthropic-ai/sdk @google/genai zod
npm install -D typescript tsx @types/nodeKeys and the model live in the environment, not in the code. And the model is deploy config, not a domain constant: today it's gpt-5.6, tomorrow something else, and you change it with a variable, not by editing sources.
# .env.local, never commit this file
OPENAI_API_KEY=...
ANTHROPIC_API_KEY=...
GEMINI_API_KEY=...
# The model is deploy config, not a domain constant
OPENAI_MODEL=gpt-5.6
ANTHROPIC_MODEL=claude-sonnet-5
GOOGLE_MODEL=gemini-3.6-flashFiles are laid out to survive a provider switch: the contract and runtime separately, the provider adapters separately, tools separately, endpoints and evals nearby.
src/
agent/
contracts.ts
prompt.ts
runner.ts
tool-registry.ts
providers/
openai.ts
anthropic.ts
google.ts
tools/
policy-search.ts
order-read.ts
refund-preview.ts
handoff-prepare.ts
api/
turn.ts
approve.ts
evals/
cases.ts
run.tsAnd one rule whose violation costs the most.
Keys stay on the server. Neither a NEXT_PUBLIC_-prefixed variable nor a client-side fetch to a provider API will do. The browser talks to your endpoint, where session, limits, and audit apply. A key that lands in the bundle is a key handed to everyone.The project is ready - now we assemble its core. We start with the contract that makes the agent portable across the three APIs.