npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

aiyard

v0.3.10

Published

Generate production-ready CLI, SDK, and MCP servers from NestJS controllers

Readme

🏗️ AIYard

Ship 10x faster - Generate production-ready CLIs, SDKs, and MCP servers from your NestJS controllers.

v0.2.8 - FIX: CLI and SDK generators now respect env.baseUrl configuration from aiyard.json!

aiyard generate

✓ CLI tool (Commander.js)
✓ Type-safe SDK (Fetch API + getToken)
✓ MCP server (Claude AI integration)

⚡ Quick Start

Install

npm install -g aiyard
# or use directly
npx aiyard generate

Configure

Create aiyard.json in your NestJS project root:

{
  "name": "myapi",
  "input": {
    "pattern": "src/**/*.controller.ts",
    "tsconfig": "tsconfig.json"
  },
  "baseUrl": "http://localhost:3000",
  "prefix": "/api/v1",
  "outputs": {
    "cli": { "enabled": true },
    "sdk": { "enabled": true },
    "mcp": { "enabled": false }
  }
}

Generate

aiyard generate

Use Generated Code

CLI:

myapi users create --name "Alice" --email "[email protected]"
myapi users list --limit 10
myapi users get user_123

SDK:

import { MyAPISDK } from '@myapi/sdk';

const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getHeaders: () => ({ 'x-api-key': localStorage.getItem('apiKey') || '' })
});

const users = await client.users.list();
const user = await client.users.create({
  name: "Alice",
  email: "[email protected]"
});

React (with React Query):

import { useQuery, useMutation, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MyAPISDK } from '@myapi/sdk';

// Setup client once
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getToken: () => localStorage.getItem('token') || ''
});

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <UsersList />
    </QueryClientProvider>
  );
}

function UsersList() {
  // Query
  const { data, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: () => client.users.findAll()
  });

  // Mutation
  const { mutate } = useMutation({
    mutationFn: (data) => client.users.create(data),
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['users'] })
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <ul>
        {data?.map(user => <li key={user.id}>{user.name}</li>)}
      </ul>
      <button onClick={() => mutate({ name: "Alice", email: "[email protected]" })}>
        Add User
      </button>
    </div>
  );
}

MCP (Claude Desktop):

{
  "mcpServers": {
    "myapi": {
      "command": "myapi-mcp"
    }
  }
}

✨ Features

🎯 Resource-Based CLI

Intuitive command structure inspired by kubectl and AWS CLI:

  • myapi users create --name "Alice" --email "[email protected]"
  • Boolean flags: --active sets to true
  • JSON fields: --metadata '{"role":"admin"}'
  • Arrays: --tags "typescript,api,backend"
  • Enum values shown in help text

📦 Type-Safe SDK

  • Full TypeScript type definitions
  • Clean interfaces (no Prisma leaks)
  • Promise-based API
  • Request/response types
  • API prefix baked into routes at generation time (no runtime overhead)
  • Flexible authentication with getHeaders and getToken

⚛️ React Integration

  • Works seamlessly with React Query (TanStack Query)
  • Works with SWR, Apollo, or any data fetching library
  • Type-safe SDK methods for all routes
  • Perfect for server state management
  • Automatic cache invalidation support

🔐 Flexible Authentication

  • getHeaders function for complete control over request headers
  • getToken function for Bearer token strategies (deprecated)
  • Supports any auth scheme: API keys, Bearer, Basic, custom headers
  • Works with localStorage, sessionStorage, cookies, or custom logic
  • Automatic token/header refresh on each request
  • Supports both sync and async retrieval
  • No stale tokens - always fresh authentication

🤖 MCP Server

  • NestJS-integrated HTTP-based MCP module (drop-in)
  • Standalone stdio MCP server for CLI usage
  • Native Claude Desktop integration
  • AI agents can call your API
  • Tool descriptions from JSDoc comments
  • Optional API key authentication

🔧 Advanced Type Extraction

  • PartialType support for update DTOs
  • Enum extraction with value display
  • Recursive type definitions
  • Clean HTML entities and import paths

📂 Package Strategies

Workspace (recommended):

packages/
├── cli/      (@myapi/cli v1.0.0)
├── sdk/      (@myapi/sdk v1.5.0)
└── mcp/      (@myapi/mcp v2.0.0)

Unified:

dist/
├── cli.js
├── sdk.js
└── mcp.js

📚 Configuration

Full Example

{
  "name": "myapi",
  "version": "1.0.0",
  "description": "My awesome API",

  "input": {
    "pattern": ["src/**/*.controller.ts", "src/types/enums.ts"],
    "tsconfig": "tsconfig.json",
    "exclude": ["**/*.spec.ts"]
  },

  "baseUrl": "http://localhost:3000",
  "prefix": "/api/v1",

  "auth": {
    "type": "api-key",
    "header": "X-API-Key",
    "envVar": "API_KEY"
  },

  "env": {
    "baseUrl": "MY_API_URL"
  },

  "package": {
    "strategy": "workspace",
    "metadata": {
      "author": "Your Name <[email protected]>",
      "license": "MIT",
      "repository": "https://github.com/username/repo"
    },
    "build": {
      "target": "ES2020",
      "formats": ["cjs", "esm"],
      "minify": false,
      "sourceMap": true
    }
  },

  "outputs": {
    "cli": {
      "enabled": true,
      "path": "packages/cli/src/index.ts",
      "options": {
        "binName": "myapi"
      },
      "package": {
        "name": "@myapi/cli",
        "version": "2.0.0"
      }
    },
    "sdk": {
      "enabled": true,
      "path": "packages/sdk/src/index.ts",
      "options": {
        "className": "MyAPISDK"
      },
      "package": {
        "name": "@myapi/sdk",
        "version": "1.5.0"
      }
    },
    "mcp": {
      "enabled": true,
      "path": "packages/mcp/src/index.ts",
      "options": {
        "serverName": "myapi"
      },
      "package": {
        "name": "@myapi/mcp",
        "version": "1.0.0"
      }
    }
  },

  "options": {
    "includeTypes": true,
    "prettify": true,
    "verbose": false,
    "allowForce": false
  }
}

Required Fields

  • name - Project name
  • input.pattern - Glob pattern for controllers
  • baseUrl - API base URL

Optional Fields

  • version - Default version (default: 1.0.0)
  • description - Project description
  • prefix - API path prefix (e.g., /api/v1) - baked into routes at generation time
  • auth.type - api-key, bearer, basic, none
  • env.baseUrl - Custom environment variable name for base URL (default: BASE_URL)
  • env.apiKey - Custom environment variable name for API key (default: API_KEY)
  • package.strategy - workspace or unified
  • outputs.*.package.version - Override global version per package
  • options.allowForce - Allow --force flag to bypass validation (default: false)

Environment Variables

The env field allows you to customize environment variable names used in generated MCP servers:

{
  "baseUrl": "http://localhost:3000",
  "env": {
    "baseUrl": "MY_API_URL"
  }
}

Generated code without env:

this.baseUrl = process.env.BASE_URL || 'http://localhost:3000';

Generated code with env.baseUrl:

this.baseUrl = process.env.MY_API_URL || 'http://localhost:3000';

This is useful for:

  • Using generic names like API_URL instead of app-specific names
  • Matching existing deployment environment variables
  • Standardizing across multiple services

Force Generation

By default, the --force flag is disabled to maintain code quality. This flag allows generation to continue even when validation errors are found, replacing missing types with any.

To enable the --force flag, add to your aiyard.json:

{
  "options": {
    "allowForce": true
  }
}

When validation fails without allowForce:

❌ Validation failed. Generation aborted.

The --force flag is disabled by default to maintain code quality.
To enable it, add to your aiyard.json:

  {
    "options": {
      "allowForce": true
    }
  }

Then you can use:
  aiyard generate --force

With allowForce: true:

# This will now work
aiyard generate --force

Why is this disabled by default?

  • Ensures all types are explicit and properly defined
  • Prevents any types from polluting generated code
  • Encourages following best practices (Response DTOs, Promise return types)
  • Makes code generation deterministic and type-safe

See examples/ for more configurations.


🏗️ Best Practices

Use Response DTOs

❌ Bad - Returning Prisma types:

@Get()
findAll(): Promise<User[]> {
  return this.prisma.user.findMany();
}

✅ Good - Explicit Response DTOs:

// user-response.dto.ts
export class UserResponse {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}

// users.controller.ts
@Get()
findAll(): Promise<UserResponse[]> {
  return this.service.findAll();
}

Naming Conventions

| Type | Pattern | Example | |------|---------|---------| | Input DTO | Create<Entity>Dto | CreateUserDto | | Update DTO | Update<Entity>Dto | UpdateUserDto | | Response | <Entity>Response | UserResponse |

File Structure

src/users/
└── dto/
    ├── create-user.dto.ts
    ├── update-user.dto.ts
    └── user-response.dto.ts

🔧 API Prefix Support

AIYard supports API path prefixes like /api/v1 for cleaner project organization. The prefix is baked into generated routes at generation time, not at runtime, for zero overhead.

How It Works

1. Configure prefix in aiyard.json:

{
  "baseUrl": "http://localhost:3000",
  "prefix": "/api/v1"
}

2. Routes generated with prefix baked in:

// Generated SDK code
subjects = {
  findAll: async () => {
    const url = '/api/v1/subjects';  // ✅ Prefix already included!
    return this.request('GET', url);
  }
}

3. Use SDK with clean config:

const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000'  // ✅ No prefix needed at runtime!
});

await client.subjects.findAll();
// Calls: http://localhost:3000/api/v1/subjects

Benefits

  • Zero Runtime Overhead: Prefix resolved at generation time, not on every request
  • Simpler SDK Config: Just baseUrl, no extra parameters
  • Explicit Routes: See the full path in generated code
  • Easier Debugging: Generated code shows complete URLs
  • Type Safety: Routes are constant strings, not dynamic concatenation

🔐 Flexible Authentication

AIYard supports dynamic authentication through the getHeaders and getToken functions, allowing you to implement any authentication strategy without hardcoding credentials.

Why Use getHeaders?

  • Complete Control: Set any headers (API keys, Bearer tokens, Basic auth, custom headers)
  • Always Fresh: Headers are retrieved before each request
  • Flexible Storage: Works with localStorage, sessionStorage, cookies, or any custom logic
  • Supports Async: Can await token refresh or async storage
  • No Stale Tokens: Perfect for short-lived JWTs
  • Multiple Headers: Send multiple authentication headers if needed

SDK Usage

Using getHeaders (Recommended):

import { MyAPISDK } from '@myapi/sdk';

// API Key authentication
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getHeaders: () => ({
    'x-api-key': localStorage.getItem('apiKey') || ''
  })
});

// Bearer token authentication
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getHeaders: () => ({
    'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
  })
});

// Multiple headers
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getHeaders: () => ({
    'Authorization': `Bearer ${localStorage.getItem('token') || ''}`,
    'X-Request-ID': generateRequestId(),
    'X-Client-Version': '1.0.0'
  })
});

// Async headers
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getHeaders: async () => ({
    'Authorization': `Bearer ${await tokenManager.getToken()}`
  })
});

Using getToken (Deprecated - use getHeaders instead):

import { MyAPISDK } from '@myapi/sdk';

const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getToken: () => localStorage.getItem('authToken') || ''
});

// Token retrieved automatically on each request
await client.users.list();

Async Token Refresh:

class TokenManager {
  private token = '';
  private expiresAt = 0;

  async getToken(): Promise<string> {
    if (Date.now() >= this.expiresAt) {
      await this.refreshToken();
    }
    return this.token;
  }

  private async refreshToken() {
    const response = await fetch('/api/auth/refresh', {
      method: 'POST',
      credentials: 'include'
    });
    const data = await response.json();
    this.token = data.token;
    this.expiresAt = Date.now() + (data.expiresIn * 1000);
  }
}

const tokenManager = new TokenManager();

const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getToken: () => tokenManager.getToken()
});

React Query Usage

Setup (once per app):

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { MyAPISDK } from '@myapi/sdk';

// Create SDK client with token
const client = new MyAPISDK({
  baseUrl: 'http://localhost:3000',
  getToken: () => localStorage.getItem('authToken') || ''
});

const queryClient = new QueryClient();

function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Dashboard />
    </QueryClientProvider>
  );
}

Using in Components:

import { useQuery, useMutation } from '@tanstack/react-query';

function UsersList() {
  // Query (GET)
  const { data: users, isLoading } = useQuery({
    queryKey: ['users'],
    queryFn: () => client.users.findAll()
  });

  // Mutation (POST/PUT/PATCH/DELETE)
  const createUser = useMutation({
    mutationFn: (data) => client.users.create(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['users'] })
    }
  });

  return (
    <div>
      {isLoading && <p>Loading...</p>}
      {users?.map(user => <div key={user.id}>{user.name}</div>)}
      <button onClick={() => createUser.mutate({ name: 'Alice', email: '[email protected]' })}>
        Add User
      </button>
    </div>
  );
}

Common Strategies

| Strategy | Use Case | Example | |----------|----------|---------| | API Key | Simple key-based auth | getHeaders: () => ({ 'x-api-key': localStorage.getItem('key') \|\| '' }) | | Bearer Token | JWT/OAuth tokens | getHeaders: () => ({ 'Authorization': \Bearer ${getToken()}` })| | **Basic Auth** | Username/password |getHeaders: () => ({ 'Authorization': `Basic ${btoa('user:pass')}` })| | **localStorage** | Persistent across tabs | Store tokens inlocalStorage.getItem('token')| | **sessionStorage** | Per-tab, cleared on close | Store tokens insessionStorage.getItem('token')| | **Cookies** | HttpOnly for security | Custom cookie parser function | | **Memory** | Fastest, lost on refresh | In-memory token manager | | **Async Refresh** | Auto-renew expired tokens |getHeaders: async () => ({ 'Authorization': `Bearer ${await refresh()}` })` |


🤖 MCP Server Setup (NestJS Integration)

AIYard generates a drop-in NestJS module that exposes your API as an MCP server over HTTP. This allows AI agents (like Claude) to interact with your NestJS backend directly.

Quick Setup

1. Enable MCP in aiyard.json:

{
  "name": "myapi",
  "baseUrl": "http://localhost:3000",
  "prefix": "/api",

  "auth": {
    "type": "api-key",
    "header": "X-API-Key",
    "envVar": "MYAPI_API_KEY"
  },

  "outputs": {
    "mcp": {
      "enabled": true,
      "path": "src/mcp/mcp.module.ts",
      "options": {
        "serverName": "myapi"
      }
    }
  }
}

2. Generate MCP module:

npx aiyard generate --output .

This creates src/mcp/mcp.module.ts - a complete NestJS module.

3. Import MCP module in your app:

// src/app.module.ts
import { Module } from '@nestjs/common';
import { McpModule } from './mcp/mcp.module';

@Module({
  imports: [
    // ... your existing modules
    McpModule,  // ✨ Add this
  ],
})
export class AppModule {}

4. (Optional) Customize MCP route prefix:

By default, MCP respects your global prefix. To serve MCP at /mcp instead of /api/mcp:

// src/main.ts
app.setGlobalPrefix('api', {
  exclude: ['mcp', 'mcp/health'], // Exclude MCP from global prefix
});

Now MCP is at /mcp while REST API stays at /api/*!

5. Start your NestJS app:

npm run start

Your MCP server is now running at http://localhost:3000/api/mcp!

Testing MCP Endpoints

List available tools:

curl -X POST http://localhost:3000/api/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  }'

Call a tool:

curl -X POST http://localhost:3000/api/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "users_findAll",
      "arguments": {}
    }
  }'

Health check:

curl -X POST http://localhost:3000/api/mcp/health

Authentication

If you configure auth in aiyard.json, the generated MCP module will:

  • Read the API key from environment variable
  • Add the auth header to all backend API calls
  • Validate requests before forwarding to your API

Example with auth:

{
  "auth": {
    "type": "api-key",
    "header": "X-API-Key",
    "envVar": "MYAPI_API_KEY"
  }
}

Set environment variable:

export MYAPI_API_KEY=your-secret-key

The MCP module will automatically include X-API-Key: your-secret-key in all requests to your backend.

How It Works

The generated MCP module:

  1. Embeds your API schema (routes, parameters, types) at generation time
  2. Exposes JSON-RPC 2.0 endpoints (/mcp for requests, /mcp/health for health checks)
  3. Translates MCP tool calls to HTTP requests to your NestJS API
  4. Includes JSDoc descriptions from your controller methods
  5. Handles authentication if configured

Environment Variables

Generated module reads these from environment:

  • {NAME}_URL - Backend API base URL (e.g., MYAPI_URL)
  • {AUTH.ENVVAR} - API key (e.g., MYAPI_API_KEY) if auth is configured

Override defaults:

export MYAPI_URL=https://api.production.com
export MYAPI_API_KEY=prod-secret-key

Claude Desktop Integration

Add to Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "myapi": {
      "command": "curl",
      "args": [
        "-X", "POST",
        "http://localhost:3000/api/mcp",
        "-H", "Content-Type: application/json",
        "-d", "@-"
      ]
    }
  }
}

Note: The HTTP-based MCP is best for integrations. For CLI usage, use the standalone stdio MCP server (coming soon).


🔍 What Gets Parsed

AIYard extracts everything from your NestJS controllers:

  • @Controller() - Base route path
  • @Get/@Post/@Patch/@Delete/@Put() - HTTP methods
  • @Param() - Path parameters
  • @Query() - Query parameters
  • @Body() - Request body DTOs
  • ✅ Return types - Response types
  • ✅ Enums - With value extraction
  • PartialType() - Mapped type support
  • ✅ JSDoc comments - For descriptions

🚀 Examples

Check the examples/ directory:

  • minimal.json - Simplest configuration
  • workspace.json - Multi-package workspace
  • unified.json - Single package
  • versioning.json - Independent version management

💻 Tech Stack

  • Parser: ts-morph (TypeScript AST)
  • Templates: Handlebars
  • CLI: Commander.js
  • SDK: Fetch API with TypeScript
  • Build: tsup + TypeScript
  • Runtime: Node.js 18+

🤝 Contributing

Contributions welcome! Feel free to:

  • Open issues for bugs or feature requests
  • Submit PRs for improvements
  • Share feedback and ideas

📄 License

MIT


⭐ Show Your Support

If AIYard saves you time, give it a star!

Ship faster. Build amazing products.