New: Fix Your Agent Harness

< Building developer tools for agents (not just humans) />

humans-and-agents.png

The core problem

Most developer tools were built for humans. A developer reads your docs, copies your quickstart, integrates your SDK, and ships. That loop worked for 20 years.

The loop is changing. AI agents do more of that work now: reading docs, writing integration code, making API calls. Claude Code, Cursor, and Copilot don't browse your docs the way a human does. They need your product to be machine-readable, composable, and permission-aware in ways that most tools weren't designed for.

This doesn't mean rebuilding from scratch. It means understanding what changes when the primary consumer of your developer product is an AI agent rather than a human, then adapting deliberately.

The companies that do this well will deepen their moat. The companies that don't will find themselves irrelevant as agent-native alternatives emerge.

1. API design — the foundation

If your API isn't clean, nothing else matters. Agents pattern-match. Inconsistency breaks that pattern-matching immediately. A human developer can work around a poorly designed API by reading between the lines. An agent can't.

Consistency above everything

Every design decision that seemed minor when humans were the primary users becomes critical when agents are calling your API thousands of times autonomously.

Resource naming:

// Inconsistent breaks agent pattern matching
GET /getUserById
POST /create_project
DELETE /org-member-remove

// Consistent agents can predict URL structure
GET /users/{id}
POST /projects
DELETE /organizations/{org_id}/members/{user_id}

Rules that matter:

  • Nouns for resources, verbs for actions
  • Consistent casing throughout. Pick snake_case or camelCase, never mix
  • Predictable URL hierarchy: /resource/{id}/sub-resource/{sub_id}
  • HTTP methods used semantically. GET retrieves, POST creates, PUT replaces, PATCH updates, DELETE removes

Predictable response structures

Every response, regardless of endpoint, should follow the same shape. Agents build internal models of what to expect. Surprise response structures break those models.

// Success
{
  "data": { ... },
  "meta": {
    "request_id": "req_123",
    "timestamp": "2026-04-27T10:00:00Z"
  }
}
// Error — every error looks the same
{
  "error": {
    "code": "user_not_found",
    "message": "No user with ID usr_123 exists",
    "retryable": false,
    "docs_url": "https://docs.example.com/errors/user_not_found"
  }
}

Never return different shapes for the same endpoint depending on state. Agents can't handle conditional response structures reliably.

Idempotency

Agents retry. Networks fail. Operations get called twice. Your API needs to handle this without creating duplicate records, duplicate charges, or duplicate emails.

POST /payments
Idempotency-Key: pay_abc123

Same key, same result every time, regardless of how many times the agent calls it. The agent doesn't need to track whether the call succeeded. It retries safely and trusts the server to dedupe.

Semantic error codes

Generic HTTP status codes aren't enough. An agent receiving a 400 needs to know exactly what went wrong and what to do about it.

{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "You have exceeded 100 requests per minute",
    "retryable": true,
    "retry_after": 45,
    "docs_url": "https://docs.example.com/errors/rate_limit_exceeded"
  }
}

Error codes should be:

  • Machine-readable strings, not just numbers
  • Stable: never change them once published
  • Documented with recommended recovery actions
  • Categorized: auth errors, validation errors, rate limits, server errors

Pagination that works at scale

Agents process large datasets. Cursor-based pagination is better than offset because it's stable. Inserting new records doesn't shift results mid-traversal.

{
  "data": [...],
  "pagination": {
    "next_cursor": "cur_abc123",
    "has_more": true
  }
}

Rate limiting with transparency

Agents are fast and don't naturally pace themselves. Return rate limit state in every response so agents can self-regulate before hitting limits.

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 47
X-RateLimit-Reset: 1714215600
Retry-After: 45

Webhooks over polling

Agents shouldn't poll. For operations that take time, use webhooks and give the agent a job ID to reference when the webhook fires.

// Immediate response
{
  "job_id": "job_abc123",
  "status": "processing"
}
// Webhook when complete
{
  "event": "job.completed",
  "job_id": "job_abc123",
  "result": { ... },
  "timestamp": "2026-04-27T10:05:00Z"
}

2. MCP — making your product callable

Model Context Protocol is the new integration layer between AI coding tools and external services. Without an MCP server, agents have to parse your docs and construct API calls from scratch every time. With one, your product becomes a first-class tool that agents can discover and call through natural language. Headless APIs were already designed for programmatic consumption; MCP adds the discoverability layer they were missing.

When Clerk shipped their MCP server, a developer using Claude Code could say "add authentication to this Next.js app" and Claude called Clerk's APIs directly. No copy-pasting from docs, no configuration errors, no context switching.

What an MCP server does

An MCP server wraps your API and exposes it as a set of tools. It handles:

  • Tool discovery, so the agent asks what your product can do and gets a structured list
  • Parameter validation before the API call is made
  • Response formatting into something the agent can reason about
  • Error handling with actionable messages

Basic MCP server structure

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new Server(
  { name: "your-product-mcp", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_user",
      description: "Retrieve a user by their ID. Returns the user's profile including email, name, created date, and subscription status. Use this before performing any action that requires knowing the user's current state. Returns null if the user does not exist — check for this before proceeding.",
      inputSchema: {
        type: "object",
        properties: {
          user_id: {
            type: "string",
            description: "The unique identifier of the user (e.g., usr_abc123)"
          }
        },
        required: ["user_id"]
      }
    }
  ]
}));

Tool description is everything

The description field is where most MCP implementations fail. Agents use it to decide when and how to call the tool. A weak description means the agent calls the wrong tool at the wrong time or passes the wrong parameters. This is one of the harness-level mistakes that breaks agents before any model upgrade can help.

Bad:

"Gets a user"

Good:

"Retrieve a user by their ID. Returns the user's profile including email, name,
created date, and current subscription status. Use this before performing any
action that requires knowing the user's current state. Returns null if the user
does not exist — check for this before proceeding."

The description should tell the agent what the tool does, when to use it, what it returns, and edge cases to watch for.

What to expose in your MCP server

Not everything in your API needs to be an MCP tool. Start with the actions developers perform most often and the ones that are most error-prone when done manually. Auth flows, resource creation, and configuration are high value. Internal admin operations, bulk data exports, and rarely used endpoints can wait.

3. Documentation — built for machines and humans

Documentation in the agentic era has to serve two audiences. A human developer evaluating your product needs narrative, context, and examples that tell a story. An AI agent needs structured, machine-readable specifications it can parse and act on.

Most developer tool docs were built for humans only. That worked when humans were the ones doing the integration. It doesn't work anymore.

OpenAPI spec — the machine-readable foundation

A well-maintained OpenAPI spec is the minimum requirement. Agents use these to understand your API surface without reading prose.

paths:
  /users/{id}:
    get:
      summary: Retrieve a user by ID
      description: >
        Returns a single user object. Use this endpoint when you need to look up
        a specific user's details before performing an action. Returns 404 if the
        user does not exist — always handle this case before proceeding.
      parameters:
        - name: id
          in: path
          required: true
          description: The unique identifier of the user
          schema:
            type: string
            example: usr_abc123
      responses:
        '200':
          description: User found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
              example:
                id: usr_abc123
                email: luke@example.com
                created_at: "2026-01-01T00:00:00Z"
        '404':
          description: User not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'

Critical requirements:

  • Every endpoint documented with description, parameters, request body, and response schemas
  • Examples on every parameter and response
  • Error codes enumerated with meanings, not just HTTP status codes
  • Keep the spec in sync with the actual API. Stale specs break agents and erode developer trust

Copy-to-install examples

Shadcn-style copy-to-install patterns have become the standard for a reason. Developers and agents both gravitate toward examples they can paste directly and have working in under a minute. Every quickstart should have this pattern.

# Install
npm install @your-product/sdk

# Configure
export YOUR_PRODUCT_API_KEY=sk_live_abc123

# First API call
import { YourProduct } from '@your-product/sdk'

const client = new YourProduct({ apiKey: process.env.YOUR_PRODUCT_API_KEY })

const user = await client.users.get('usr_abc123')
console.log(user.email)

One install command. One config step. One working call. If your quickstart takes more than that, you're losing developers and agents at the starting line.

Keeping docs current

Stale documentation is worse than no documentation for agents. A human notices when docs don't match behavior. An agent acts on what the docs say and produces bugs that are hard to trace.

This means:

  • Docs as code, with documentation living alongside the codebase and updated in the same PR
  • Automated API reference generation from the OpenAPI spec, so nobody updates API references by hand
  • Changelogs that call out breaking changes explicitly with migration paths
  • Versioned documentation when you have multiple API versions active

Error documentation that enables recovery

Most products document happy paths well and error paths poorly. Agents hit error paths constantly. Every error code should have a dedicated doc page that explains what caused it and exactly how to recover.

Error: rate_limit_exceeded
Cause: You have made more than 100 requests per minute
Recovery: Wait for the retry_after value in the response header, then retry the request
Prevention: Implement exponential backoff and respect the X-RateLimit-Remaining header

4. Developer experience — reducing friction at every step

The best API in the world loses to a worse API with better DX. This was true before AI agents and it's still true now. The difference is that agents amplify both good and bad DX. They'll use a well-designed SDK flawlessly and get stuck in the same confusing flows that trip up humans.

SDKs that match how developers work

Generate your SDKs from your OpenAPI spec. Don't handwrite them. Handwritten SDKs drift from the API and introduce inconsistencies. Generated SDKs stay in sync automatically.

A well-designed SDK should:

  • Match the structure of the API without adding abstraction that obscures what's happening
  • Include TypeScript types for everything
  • Surface errors in a way that makes them easy to handle
  • Work with the frameworks your customers actually use: Next.js, Express, FastAPI, not just vanilla HTTP

Framework-specific quickstarts

A generic quickstart is less useful than a Next.js quickstart, a FastAPI quickstart, and a Rails quickstart. Developers work within frameworks, not in the abstract. The faster you can get someone from install to working implementation in their stack, the more likely they adopt.

Stripe does this well: separate quickstarts for Next.js, Rails, Flask, and more. Each one is tailored to how that framework handles payments, not a generic HTTP example with a note to adapt it.

Interactive examples and playgrounds

API playgrounds where developers can make calls without setting up an account lower the evaluation barrier. Agents can also use these to test calls before embedding them in code.

5. Authentication and permissions for agents

This is where most developer tools are least prepared for the agentic era. Human auth is designed for sessions: you log in, you stay logged in. Agent auth needs to be granular, short-lived, auditable, and revocable per task.

Scoped API keys

Never give agents a master API key. Give them a key scoped to exactly what they need for a specific task.

// Too broad — agent can do anything
API_KEY: sk_live_abc123 (full access)

// Better — agent can only read users and create orders
API_KEY: sk_live_xyz789 (scope: users:read, orders:write)

OAuth for agent delegation

When an agent acts on behalf of a human user, OAuth is the right pattern. The agent gets a token scoped to that user's permissions. It can't do anything the user couldn't do themselves.

POST /oauth/token
{
  "grant_type": "client_credentials",
  "client_id": "agent_abc123",
  "client_secret": "sk_agent_xyz789",
  "scope": "orders:read orders:write"
}

The token comes back scoped to exactly what the agent requested:

{
  "access_token": "tok_abc123",
  "scope": "orders:read orders:write",
  "expires_in": 900
}

Short-lived tokens

Agent tokens should expire quickly. A human session might last 30 days. An agent token for a specific task should last minutes to hours. This limits the blast radius if a token is compromised.

Audit logging

Every agent action should be logged with enough context to answer: who authorized this, what did the agent do, when, and why. Enterprise customers will not buy without it.

{
  "event": "order.created",
  "actor": {
    "type": "agent",
    "id": "agent_abc123",
    "acting_for": "usr_xyz789"
  },
  "resource": {
    "type": "order",
    "id": "ord_def456"
  },
  "timestamp": "2026-04-27T10:00:00Z",
  "request_id": "req_ghi789"
}

6. Agent skills — teaching agents how to use your product

Beyond MCP, some platforms are building agent skills: structured context that AI coding tools load before working with your product. Where MCP lets agents call your API, skills teach agents how to use your API well.

Clerk's agent skills embed authentication knowledge directly into Claude Code and Cursor. When a developer adds the Clerk skill, the AI already knows the full API surface, common patterns, which approach to use for which use case, and current examples that match the latest API version.

# clerk-auth-skill.md

## When to use Clerk
Use Clerk when the project needs authentication and the framework is
Next.js, Remix, or Astro. Do not use Clerk for server-only APIs
without a frontend.

## Setup pattern
Always use the App Router integration for Next.js 13+.
Never use the Pages Router integration for new projects.

```tsx
// middleware.ts — always create this first
import { clerkMiddleware } from '@clerk/nextjs/server'
export default clerkMiddleware()
export const config = {
  matcher: ['/((?!.*\\..*|_next).*)', '/', '/(api|trpc)(.*)'],
}

Common mistakes

  • Don't wrap the entire app in ClerkProvider if you only need auth on specific routes
  • Don't use useAuth() in server components. Use auth() instead
  • Don't store Clerk user IDs as your primary user identifier. Sync to your own database on signup


This is what agents need that documentation alone doesn't provide: opinionated guidance on when to use what, which patterns to avoid, and decision logic that prevents the mistakes developers hit most often. Documentation tells an agent what your API can do. A skill tells it what your API should do in a specific context.


Building a skill means packaging your docs, examples, and best practices in a format the agent can load as context. Think of it as the difference between handing an agent your API reference and telling it which endpoints to call first, which patterns to avoid, and what to do when the first attempt fails.


A well-built skill should include:

- The most common integration patterns with working code
- Decision trees for when to use component A versus component B
- Known gotchas and how to avoid them
- Current examples that match your latest API version, not examples from two versions ago

## 7. Discoverability — being found in the agentic era


Developers used to discover tools through Google, Hacker News, and word of mouth. Agents discover tools through MCP registries, tool directories, and the context that gets loaded into their session. This is a new kind of SEO and most developer tool companies aren't thinking about it yet.


### MCP registries


As MCP adoption grows, registries of available MCP servers are emerging. Getting your MCP server listed and well-described in these registries is the new version of ranking on page one for your category keyword.


### LLM-optimized content


Developers ask ChatGPT, Perplexity, and Claude for tool recommendations now. The content those tools cite is specific and technical and answers concrete questions with working examples. Thin marketing content doesn't get cited. Deep technical content that helps developers solve problems does.


Write content that answers the questions developers are asking:

- "How do I add authentication to a Next.js app router project"
- "What's the difference between JWT and session-based auth"
- "How do I handle multi-tenant authorization for an enterprise SaaS"

### Being part of the agent's default context


The most powerful discoverability play is becoming the answer agents give when developers ask a question. When a developer asks Claude or ChatGPT "how do I add payments to a Next.js app," the response cites whoever wrote the most specific, implementation-level content for that use case. That's not marketing. That's product placement at the infrastructure level.


## What separates the winners


The companies that treat this as a technical checkbox will ship an MCP server and call it done. The companies that treat it as a strategic opportunity will be embedded in the agent's default context. The first group becomes the API your agent fell back to when the embedded one didn't fit.


## Checklist


**API design**

- [ ] Consistent RESTful naming and URL patterns
- [ ] Predictable response structures across all endpoints
- [ ] Idempotency keys on write operations
- [ ] Cursor-based pagination
- [ ] Semantic error codes with recovery guidance
- [ ] Rate limit headers on every response
- [ ] Webhooks for async operations

**MCP**

- [ ] MCP server wrapping core API surface
- [ ] Tool descriptions written for agent reasoning
- [ ] Parameter validation before API calls
- [ ] Listed in relevant MCP registries

**Documentation**

- [ ] Published OpenAPI spec kept in sync
- [ ] Copy-to-install quickstarts for major frameworks
- [ ] Error codes documented with recovery steps
- [ ] Docs as code, updated in the same PR as the API

**Auth and permissions**

- [ ] Scoped API keys (least privilege per task)
- [ ] OAuth delegation for acting on behalf of users
- [ ] Short-lived tokens for agent sessions
- [ ] Full audit logging with actor context

**Agent skills**

- [ ] Skill packaging your API knowledge for AI coding tools
- [ ] Decision trees for common integration choices
- [ ] Current examples matching latest API version

**Discoverability**

- [ ] MCP registry listings
- [ ] Technical content answering concrete developer questions
- [ ] Integration into AI coding tool ecosystems

Developer Writing Assistant

Handbook
Developer Marketing Handbook

Goals

Developer marketing builds trust first, pipeline second.
The work connects your product to how developers actually build and helps that credibility translate into adoption and revenue.

A great developer experience is the foundation. It starts with discoverability, continues through docs, and carries into the product itself. Good documentation shortens time to value and builds confidence that your product can scale with real teams. Developers trust what they can inspect, so show how the product works and let the system speak for itself.

Success isn't clicks or vanity metrics. It's measurable engagement that creates product-qualified leads, builds influence across teams, and contributes to both product-led and sales-led growth.
When developers use your product by choice and advocate for it inside their company, you've done the job right.

Strategy

Start with reality, not aspiration.

Map where your product fits in the developer workflow, then help them do that job faster or with less friction.

Lead with clarity. Explain what it is, what it does, and why it matters.

Show the system behind the product. Architecture, examples, and tradeoffs explain more than positioning ever will.
If you can do it in a clever or playful way that still feels authentic, that's bonus points.

The best developer marketing respects time, delivers value, and makes something complex feel obvious.

Journey

Awareness → Evaluation → Adoption → Advocacy.
Each stage should connect clearly to the next.

Awareness happens in places developers already spend time: GitHub, Reddit, newsletters, blogs.
Evaluation happens in your docs, demos, and sandboxes.

For most developers, the docs are the real homepage, so accuracy and structure matter more than polish.

Adoption depends on how fast they reach first success.
Advocacy is when they start teaching others what they learned from you.

Personas

Create personas based on who buys the product and who actually uses it. For example:

Buyers: CTO or Engineering Leader, Senior Engineer, Implementation Architect.
Users: Frontend, Full-stack, App Developer.
Adjacent: Ops, Product, Design.

Each persona has different pain points and goals.
CTOs and Engineering Leaders care about governance and ROI.
Senior Engineers look for performance, flexibility, and code quality.
Implementation Architects focus on how well a tool integrates and scales.
Write for what each person owns, not what you wish they cared about.

These categories are shifting. PMs and designers who build with AI tools aren't adjacent anymore. They're users. Update your personas to reflect how people actually work, not how the org chart defines them.

Messaging

Be clear first. Be clever only if it helps.
Make every message easy to scan. Lead with the point before expanding on it.
Good developer messaging is specific, practical, and rooted in how people actually build.

Clarity earns trust, but a bit of personality makes it stick.
The goal isn't to sound like marketing. It's to communicate something real that developers recognize and care about.

Build around three pillars:

  • Speed: faster builds, fewer tickets
  • Efficiency: consolidated stack, lower maintenance
  • Control: safe scale, long-term confidence

If you can back it with code, data, or proof, keep it.
If it only sounds good, cut it.

Campaigns

Treat campaigns like product launches.
Plan, ship, measure, repeat.

Each campaign should answer three questions:

  • What developer problem are we solving?
  • What proof are we showing?
  • What happens next?

Treat developer feedback like bug reports and close the loop quickly when something needs to be corrected or clarified.

Make it easy for developers to try, test, or share.
Run retros on every launch and capture what worked, what didn't, and what to change next time. Always learn from what you launch.

Content

Write with clarity and intention. Every piece should help developers build faster, learn something new, or solve a real problem.

Strong content earns attention because it's useful.
Lead with the outcome or insight, then show how to get there. Make it easy to skim from top to bottom.
Show working examples, explain tradeoffs, and include visuals or code where it helps understanding. If it doesn't teach or demonstrate something real, it doesn't belong.

Core content types

  • Blog posts: tutorials, technical breakdowns, or opinionated takes grounded in experience.
  • Guides and tutorials: step-by-step instructions that lead to a working result.
  • Integration or workflow content: explain how tools connect and where they fit in a developer's process.
  • Technical guides and code examples: deeper material for experienced readers who want implementation detail.
  • Explainer or glossary content: clear, factual definitions written to answer specific questions directly.
  • Video or live sessions: demos, interviews, or walkthroughs that show real workflows.
  • Research and surveys: reports or insights that help developers understand the state of their field.

Content strategy buckets

  1. Awareness — generate buzz and discussion. Hot takes, thought leadership, or topics that invite conversation.
  2. Acquisition — bring new developers in through problem-solving content. Tutorials, guides, and explainers that answer real questions.
  3. Enablement — help existing users succeed. Deep tutorials, documentation extensions, and practical how-to content with long-term value.
  4. Convert Paid — drive upgrades or signups. Feature-specific walkthroughs or advanced use cases that show value worth paying for.

Each piece should fit into one of these buckets and serve a clear purpose. Awareness earns attention. Acquisition builds trust. Enablement drives success. Convert Paid turns success into growth.

Clarity is the standard. Use it to earn credibility.

Community

Reddit. GitHub. Discord. Slack. YouTube and other social platforms.
Join conversations, don't start pitches.

Be helpful. Add context. Share working examples.
When your content becomes the answer people link to, you've earned credibility.

Metrics

Measure adoption and revenue, not reach.
Awareness is useful, but only if it drives activation or expansion.

Focus on signals that show impact:

  • Product or API usage
  • Time to first success
  • Product-qualified leads
  • Developer-influenced revenue
  • Retention and repeat engagement

The goal is to prove that trust earned from developers shows up later in product usage and revenue.

Developer Marketing Skill

I built a Developer Marketing Skill for Claude that helps evaluate content, strategy, and campaigns against the principles in this handbook.

Use it to stress-test messaging, review technical content, plan developer campaigns, or get feedback on positioning. It applies a "trust first, pipeline second" philosophy with an emphasis on clarity, technical credibility, and measurable engagement.

Need more resources?

Check out my curated collection of developer marketing tools, newsletters, and resources.

ESC