easy_api_generator 0.6.0 copy "easy_api_generator: ^0.6.0" to clipboard
easy_api_generator: ^0.6.0 copied to clipboard

Build Runner generator that creates MCP server code from @tool annotations.

easy_api_generator #

easy_api

Build Runner generator that creates MCP server code, REST API servers, and OpenAPI 3.0 specs from annotated Dart classes.

Processes Dart code annotated with the easy_api_annotations package to produce any combination of a ready-to-run MCP server (.mcp.dart), MCP metadata (.mcp.json), a Shelf-based REST server (.openapi.dart), and an OpenAPI 3.0 specification (.openapi.json):

  • @Server — configures transport (stdio/HTTP), port/address, code mode, and which artifacts to generate.
  • @Tool — exposes a method as an MCP tool and/or REST endpoint, with optional custom naming, icons, and code-mode controls.
  • @Parameter (optional) — attaches rich metadata to individual parameters (titles, descriptions, examples, validation, sensitivity, external aliases). The generator infers parameter info from Dart types by default, so you only need @Parameter when you want richer metadata than the Dart signature already expresses.

Migration note: @Mcp is still available as a deprecated typedef for backward compatibility. New code should use @Server.

Installation #

easy_api_generator is a build-time tool, so it belongs under dev_dependencies. Only easy_api_annotations is needed at runtime:

dependencies:
  easy_api_annotations: ^0.6.0

dev_dependencies:
  build_runner: ^2.4.0
  easy_api_generator: ^0.6.0

Usage #

  1. Annotate your functions with @Server and @Tool:
import 'package:easy_api_annotations/mcp_annotations.dart';

@Server(transport: McpTransport.stdio)
class MyServer {
  @Tool(description: 'Create a new user')
  Future<bool> createUser(String name, String email) async {
    // Implementation here
    return true;
  }
}

HTTP Transport Configuration #

For HTTP transport, you can customize the port and bind address:

@Server(
  transport: McpTransport.http,
  port: 8080,           // Default: 3000
  address: '0.0.0.0',   // Default: '127.0.0.1' (loopback)
)
class MyServer {
  @Tool(description: 'Create a new user')
  Future<bool> createUser(String name, String email) async {
    // Implementation here
    return true;
  }
}

Note: Use address: '0.0.0.0' to listen on all network interfaces (useful for Docker containers or remote access).

REST API Specification Generation #

Set generateRest: true on @Server to generate a Shelf-based REST API server (.openapi.dart) plus a matching OpenAPI 3.0 specification (.openapi.json). REST generation is independent of MCP generation — you can generate REST only, MCP only, or both.

REST + MCP (both artifacts)

@Server(
  transport: McpTransport.http,
  port: 8080,
  generateRest: true,        // Adds .openapi.dart + .openapi.json
  // generateMcp defaults to true, so .mcp.dart is also produced
)
class MyApi {
  @Tool(description: 'Create a new user')
  Future<User> createUser({
    required String name,
    required String email,
  }) async { ... }

  @Tool(description: 'Get user by ID')
  Future<User> getUser({required int id}) async { ... }

  @Tool(description: 'List all users')
  Future<List<User>> listUsers() async { ... }
}

REST only (no MCP server)

When you only need a REST API, set generateMcp: false so the MCP artifacts are skipped. The transport and port options are MCP-specific and are not required in REST-only mode — configure the REST server's host/port when you run .openapi.dart instead.

@Server(
  generateMcp: false,   // Skip .mcp.dart / .mcp.json
  generateRest: true,   // Produce .openapi.dart + .openapi.json
)
class MyApi {
  @Tool(description: 'Create a new user')
  Future<User> createUser({
    required String name,
    required String email,
  }) async { ... }

  @Tool(description: 'Get user by ID')
  Future<User> getUser({required int id}) async { ... }

  @Tool(description: 'List all users')
  Future<List<User>> listUsers() async { ... }
}

Either configuration generates a .openapi.json with RESTful endpoints:

{
  "openapi": "3.0.3",
  "paths": {
    "/users": {
      "post": {
        "summary": "Create a new user",
        "operationId": "createUser",
        "requestBody": { ... },
        "responses": { "201": { ... } }
      },
      "get": {
        "summary": "List all users",
        "operationId": "listUsers",
        "responses": { "200": { ... } }
      }
    },
    "/users/{id}": {
      "get": {
        "summary": "Get user by ID",
        "operationId": "getUser",
        "parameters": [{ "name": "id", "in": "path" }],
        "responses": { "200": { ... }, "404": { ... } }
      }
    }
  }
}

Features:

  • ✅ RESTful endpoint mapping (POST for create, GET for list/get, PATCH for update, DELETE for remove)
  • ✅ Resource-based URL patterns (/users, /users/{id})
  • ✅ Request/response schemas with validation
  • ✅ Proper HTTP status codes (200, 201, 204, 400, 404)
  • ✅ Parameter metadata from @Parameter annotations
  • ✅ Compatible with Swagger UI, API gateways, and code generators

Parameter Annotations (Optional) #

Use @Parameter to provide rich metadata for tool parameters:

@Server(transport: McpTransport.stdio)
class MyServer {
  @Tool(description: 'Create a new user')
  Future<bool> createUser({
    @Parameter(
      title: 'Full Name',
      description: 'The user\'s full name',
      example: 'John Doe',
    )
    required String name,
    
    @Parameter(
      title: 'Email Address',
      description: 'A valid email address',
      example: 'john@example.com',
      pattern: r'^[\w\.-]+@[\w\.-]+\.\w+$',
    )
    required String email,
    
    @Parameter(
      title: 'Age',
      description: 'User age in years',
      minimum: 0,
      maximum: 150,
      example: 25,
    )
    int? age,
  }) async {
    // Implementation here
    return true;
  }
}

The @Parameter annotation is optional - by default, the generator extracts parameter information from Dart types and method signatures. Use it when you need:

  • Human-readable titles and descriptions
  • Example values to guide users
  • Validation constraints (min/max, patterns, enum values)
  • To mark sensitive data (passwords, API keys)

Custom Tool Names #

By default, the generator uses method names as tool names. You can customize this using:

1. The name parameter on @Tool:

@Server(transport: McpTransport.stdio)
class UserService {
  @Tool(
    name: 'user_create',  // Custom tool name
    description: 'Creates a new user',
  )
  Future<User> createUser(String name, String email) async { ... }
}

2. The toolPrefix parameter on @Server (applies to all tools in the class):

@Server(transport: McpTransport.stdio, toolPrefix: 'user_service_')
class UserService {
  @Tool(description: 'Create user')
  Future<User> createUser() async { ... }  // Tool name: user_service_createUser
  
  @Tool(description: 'Delete user')
  Future<void> deleteUser(String id) async { ... }  // Tool name: user_service_deleteUser
}

3. The autoClassPrefix parameter on @Server (automatically uses class name):

@Server(transport: McpTransport.stdio, autoClassPrefix: true)
class UserService {
  @Tool(description: 'Create user')
  Future<User> createUser() async { ... }  // Tool name: UserService_createUser
  
  @Tool(description: 'Delete user')
  Future<void> deleteUser(String id) async { ... }  // Tool name: UserService_deleteUser
}

You can also combine autoClassPrefix with toolPrefix:

@Server(transport: McpTransport.stdio, autoClassPrefix: true, toolPrefix: 'api_')
class UserService {
  @Tool(description: 'Create user')
  Future<User> createUser() async { ... }  // Tool name: api_UserService_createUser
}

This is useful for:

  • Avoiding naming collisions when aggregating tools from multiple files
  • Organizing tools by domain (e.g., user_, order_, admin_)
  • Creating more descriptive names for MCP clients
  1. Run the generator:
dart run build_runner build

This generates:

  • my_server.mcp.dart - Complete MCP server (stdio or HTTP)

Optional: To also generate a .mcp.json metadata file, set generateJson: true in the @Server annotation:

@Server(
  transport: McpTransport.stdio,
  generateJson: true,  // Generates my_server.mcp.json
)
class MyServer { ... }

Features #

  • AST-based parsing - Uses dart:analyzer for reliable annotation detection
  • Two transport modes - stdio (JSON-RPC) and HTTP (Shelf-based) servers
  • Configurable HTTP server - Customize port and bind address via @Server annotation
  • Automatic JSON-Schema generation - Maps Dart types to proper JSON Schema
  • Rich parameter metadata - Use @Parameter annotation for titles, descriptions, validation
  • Optional parameter support - Handles named and optional positional parameters
  • Doc comment extraction - Uses function doc comments when @Tool.description not provided
  • Dynamic method dispatch - Generated _dispatch function routes to actual tool methods

Example #

See the example directory in the workspace root for a complete working example that demonstrates usage of both packages together.

Generated Server Capabilities #

The generated MCP server supports:

  • initialize - Standard MCP initialization
  • tools/list - Returns list of available tools with schemas
  • tools/call - Executes the requested tool with provided arguments

Contributing #

Contributions are welcome! Please read the CONTRIBUTING.md guide at the root of the workspace for setup instructions, development workflow, coding standards, testing expectations, and the pull-request checklist before opening a PR.

License #

MIT License - see LICENSE for details.

Support #

If you find this package useful, consider supporting its development:

Buy Me A Coffee
1
likes
0
points
544
downloads

Publisher

verified publishercdavis.ca

Weekly Downloads

Build Runner generator that creates MCP server code from @tool annotations.

Repository (GitHub)
View/report issues

Topics

#mcp #code-generation #build-runner #model-context-protocol #annotations

Funding

Consider supporting this project:

buymeacoffee.com

License

unknown (license)

Dependencies

analyzer, build, source_gen

More

Packages that depend on easy_api_generator