Ectomancer.SchemaIntrospection (Ectomancer v1.7.0)

Copy Markdown View Source

Compile-time Ecto schema introspection for generating MCP tools.

This module provides utilities to read Ecto schema metadata at compile time and use it to generate MCP-compatible tool definitions.

Example

schema_info = Ectomancer.SchemaIntrospection.analyze(MyApp.Accounts.User)
# Returns: %{
#   fields: [:id, :email, :name, :inserted_at, :updated_at],
#   types: %{id: :id, email: :string, name: :string, ...},
#   associations: [%{field: :posts, cardinality: :many, related: MyApp.Blog.Post}],
#   primary_key: [:id]
# }

Supported Ecto Types

  • :string - String values
  • :integer - Integer values
  • :float / :decimal - Number values
  • :boolean - Boolean values
  • :date - Date values
  • :time / :time_usec - Time values
  • :naive_datetime / :naive_datetime_usec - Naive datetime values
  • :utc_datetime / :utc_datetime_usec - UTC datetime values
  • Ecto.UUID - UUID values
  • {:array, inner} - Array values
  • :map - Map/object values
  • Embeds - Embedded schemas

Summary

Functions

Analyzes an Ecto schema module and returns its metadata.

Returns associations suitable for nested creation (has_many and has_one). Excludes belongs_to (handled via FK), many_to_many, and through associations.

Returns true if the module is an Ecto schema.

Gets field information including type and nullable status.

Gets all associations for a schema module.

Returns the primary key field(s) for a schema.

Detects if a schema has a soft-delete field (e.g., deleted_at, archived_at).

Converts an Ecto type to a human-readable string representation.

Returns all fields except associations and primary key fields.

Functions

analyze(schema_module)

@spec analyze(module()) :: %{
  fields: [atom()],
  types: %{required(atom()) => any()},
  associations: [%{field: atom(), cardinality: atom(), related: module()}],
  primary_key: [atom()],
  embedded: boolean()
}

Analyzes an Ecto schema module and returns its metadata.

Parameters

  • schema_module - The Ecto schema module to analyze

Returns

A map containing:

  • :fields - List of field names (atoms)
  • :types - Map of field names to their Ecto types
  • :associations - List of association information
  • :primary_key - List of primary key field names
  • :embedded - Boolean indicating if this is an embedded schema

Examples

iex> Ectomancer.SchemaIntrospection.analyze(MyApp.Accounts.User)
%{
  fields: [:id, :email, :name, :role, :inserted_at, :updated_at],
  types: %{
    id: :id,
    email: :string,
    name: :string,
    role: :string,
    inserted_at: :utc_datetime,
    updated_at: :utc_datetime
  },
  associations: [
    %{field: :posts, cardinality: :many, related: MyApp.Blog.Post}
  ],
  primary_key: [:id],
  embedded: false
}

associations_for_create(schema_module)

@spec associations_for_create(module()) :: [
  %{field: atom(), cardinality: atom(), related: module(), type: atom()}
]

Returns associations suitable for nested creation (has_many and has_one). Excludes belongs_to (handled via FK), many_to_many, and through associations.

ecto_schema?(module)

@spec ecto_schema?(module()) :: boolean()

Returns true if the module is an Ecto schema.

Examples

iex> Ectomancer.SchemaIntrospection.ecto_schema?(MyApp.Accounts.User)
true

iex> Ectomancer.SchemaIntrospection.ecto_schema?(String)
false

field_info(schema_module, field)

@spec field_info(module(), atom()) :: %{type: any(), nullable: boolean()}

Gets field information including type and nullable status.

Examples

iex> Ectomancer.SchemaIntrospection.field_info(MyApp.Accounts.User, :email)
%{type: :string, nullable: false}

get_associations(schema_module)

@spec get_associations(module()) :: [
  %{field: atom(), cardinality: atom(), related: module()}
]

Gets all associations for a schema module.

Returns a list of maps with association metadata.

primary_key(schema_module)

@spec primary_key(module()) :: [atom()]

Returns the primary key field(s) for a schema.

Examples

iex> Ectomancer.SchemaIntrospection.primary_key(MyApp.Accounts.User)
[:id]

iex> Ectomancer.SchemaIntrospection.primary_key(MyApp.CompositeKeyModel)
[:org_id, :user_id]

soft_delete_field(schema_module)

@spec soft_delete_field(module()) :: atom() | nil

Detects if a schema has a soft-delete field (e.g., deleted_at, archived_at).

Returns the field name (atom) if found, nil otherwise. Only datetime-type fields are considered valid soft-delete markers.

Examples

iex> Ectomancer.SchemaIntrospection.soft_delete_field(MyApp.Accounts.User)
:deleted_at

iex> Ectomancer.SchemaIntrospection.soft_delete_field(MyApp.Blog.Post)
nil

type_to_string(type)

@spec type_to_string(any()) :: String.t()

Converts an Ecto type to a human-readable string representation.

Examples

iex> Ectomancer.SchemaIntrospection.type_to_string(:string)
"string"

iex> Ectomancer.SchemaIntrospection.type_to_string({:array, :string})
"array of string"

iex> Ectomancer.SchemaIntrospection.type_to_string(Ecto.UUID)
"uuid"

writable_fields(schema_module)

@spec writable_fields(module()) :: [atom()]

Returns all fields except associations and primary key fields.

Useful for generating create/update forms.

Examples

iex> Ectomancer.SchemaIntrospection.writable_fields(MyApp.Accounts.User)
[:email, :name, :role]