Using AI Coding Assistants - Terminal 3 Documentation
Install it
- Claude Code
- Codex
- Cursor
- Windsurf / other
Save the file below as .claude/skills/t3n-adk-quickstart/SKILL.md in your project (or your global ~/.claude/skills/ directory), then invoke it with /t3n-adk-quickstart or just describe what you’re building — Claude Code will pick it up automatically based on the skill’s description.
Save everything below the frontmatter (the --- block at the top) as AGENTS.md in your project root — Codex CLI and Codex cloud agents read this automatically at the start of a session.
Save everything below the frontmatter (the --- block at the top) as .cursorrules in your project root.
Paste the body of the file (below the frontmatter) as a system-context or rules file, using whatever mechanism your tool supports for persistent project instructions.
Full skill file — t3n-adk-quickstart/SKILL.md
---
name: t3n-adk-quickstart
description: Scaffold and debug a Terminal 3 (T3N) ADK integration — use when the user wants to "build with Terminal 3", "use the T3N SDK", or "add a TEE contract" to their app.
---
# Building with Terminal 3 ADK
You are helping a developer — possibly a first-timer, possibly vibe-coding — integrate the Terminal 3 Agent Developer Kit. Follow this order. Do not jump to contract code or advanced features before step 3 actually works. Do the scaffolding yourself — don't just print code and tell the user to run it.
## Step 1: Confirm the basics before writing any contract code
Ask the user (don't assume):
1. Do they already have a T3N API key and test credits? If not, point them to the claim page linked from `/developers/adk/get-started/prerequisites/request-test-tokens` on the Terminal 3 docs — it's self-serve, no approval wait.
2. Is this a Next.js/Vite/Webpack project? If so, be ready for possible WASM-loading friction — see the troubleshooting note in `/developers/adk/get-started/quickstart`.
## Step 2: Scaffold the project BEFORE writing the connection code
A blank folder needs this before the SDK's `await`-based sample code will even parse:
```bash
mkdir -p my-t3n-app && cd my-t3n-app
npm init -y
npm pkg set type=module # required — the sample code uses top-level await
npm install @terminal3/t3n-sdk tsx
Then get the user's T3N_API_KEY (from the claim page) and export it into the shell you'll run scripts from — don't write it into a file:
export T3N_API_KEY="<their key>"
Step 3: Get a basic authenticated connection working FIRST
Save this as quickstart.ts in the project you just scaffolded, and actually run it — don't just write it:
import {
T3nClient,
setEnvironment,
loadWasmComponent,
eth_get_address,
metamask_sign,
createEthAuthInput,
} from "@terminal3/t3n-sdk";
setEnvironment("testnet"); // SDK defaults to production — always set this explicitly
const T3N_API_KEY = process.env.T3N_API_KEY!;
const wasmComponent = await loadWasmComponent();
const address = eth_get_address(T3N_API_KEY);
const t3n = new T3nClient({
wasmComponent,
handlers: { EthSign: metamask_sign(address, undefined, T3N_API_KEY) },
});
await t3n.handshake();
const did = await t3n.authenticate(createEthAuthInput(address));
const tenantDid = did.value; // did:t3n:... — keep this variable, reuse it in every later step
console.log("Connected as:", tenantDid);
Run with npx tsx quickstart.ts. Do not proceed to contract code until this prints a did:t3n:... value.
Keep building in this same file. t3n and tenantDid are plain JS variables scoped to this one running script. Later snippets (TenantClient construction, contract registration, invocation) all assume t3n/tenantDid/tenant are already in scope — append new code to the bottom of quickstart.ts rather than starting fresh files, unless you deliberately repeat the connect-and-authenticate block at the top of a new one.
Step 4: Contract code lives in a SEPARATE project
The Rust/WASM contract is not part of the Node project above — it's its own crate. Clone the reference implementation instead of hand-typing it, as a sibling folder (not nested inside your Node project):
cd ..
git clone https://github.com/Terminal-3/z-tenant-flight.git
cd z-tenant-flight
rustup target add wasm32-wasip2 # install rustup first via https://rustup.rs if `rustup`/`cargo` aren't found
cargo build --target wasm32-wasip2 --release
This produces target/wasm32-wasip2/release/z_tenant_flight.wasm. Back in my-t3n-app/quickstart.ts for the registration step, point WASM_PATH across the two folders, e.g. ../z-tenant-flight/target/wasm32-wasip2/release/z_tenant_flight.wasm.
Step 5: Known pitfalls — check these BEFORE assuming it's your bug
| Symptom | Likely cause | Fix |
|---|---|---|
Top-level await is currently not supported with the "cjs" output format |
No package.json, or missing "type": "module" |
npm pkg set type=module (see Step 2) |
Invalid Ethereum private key from eth_get_address |
T3N_API_KEY was never exported into the shell running the script |
export T3N_API_KEY="..." before running (see Step 2) |
ReferenceError: tenant is not defined / tenantDid is not defined in a later snippet |
Each doc page's code sample assumes earlier variables are still in scope — they aren't, across separate files | Append new code to the same running file instead of starting a new one (see Step 3) |
ENOENT reading a .wasm file |
WASM_PATH is relative to the wrong project — the contract lives in its own cloned repo, not the Node project |
Point WASM_PATH across folders, e.g. ../z-tenant-flight/target/... (see Step 4) |
WASM/module parse error, works with tsx but not node/webpack |
Bundler compatibility | Try a plain Node script or server-only route first to isolate the bundler from the SDK |
Generic HTTP 500 with no detail |
Could be egress not authorized, wrong action name, or a real platform issue — not necessarily your code | Check the outbound-HTTP-grant requirement below before assuming it's a bug; if it's consistent and reproducible, it may be platform-side |
| Contract can't reach an external API even though the code looks right | Outbound HTTP is authorized by the calling user's grant, not the contract itself | Confirm a user (or self-grant, for direct calls) actually authorized that host via agent-auth-update |
tenant not found |
The DID was derived/hardcoded instead of read from the session | Always read tenantDid/did.value from the authenticated session, never construct it |
AccessDenied reading your own contract's secret |
KV map ACLs default to deny | Explicitly set readers/writers when creating the map |
| Map path looks right but reads/writes silently miss | Tenant DID may have been hex-encoded twice when building the map path | Use tenant_did()'s return value directly — don't wrap it in another hex::encode |
| Re-registering a contract breaks old pinned-version calls | Registering a newer version can reroute calls that pin an older one | Re-verify any version-pinned calls after any re-registration |
Agent can authenticate but its calls fail with host/http.egress_denied |
The agent is authenticated but no one has authorized it yet — those are two different steps | The user (data owner), not the agent, must sign an agent-auth-update grant naming the agent's DID before it can do anything with outbound HTTP |
Step 6: Where to look things up
- Full onboarding walkthrough, starting point:
/developers/adk/get-started/quickstart - Every confirmed SDK method, host interface, and REST tag:
/developers/adk/reference - Common errors reference:
/developers/adk/tips/common-errors - Conceptual map of the whole ADK:
/developers/adk/overview/adk-tour
Step 7: When something seems broken and isn't in this list
Say so plainly to the user rather than guessing. Some rough edges are real platform issues with no clean workaround, not something you can code around. Don't fabricate a fix — point them at the developer Telegram (https://t.me/terminal3developer) or devrel@terminal3.io.
Compatibility: this file uses Claude Code's Skill frontmatter (name/description). For Codex, save everything below the frontmatter as AGENTS.md in your project root — Codex reads it automatically. For Cursor, save everything below the frontmatter as .cursorrules. For Windsurf or any other tool that accepts a plain system-context file, paste the body as-is.