LLM Artifact Types Implementation Guide
This chapter provides detailed implementation documentation for the built-in AI/ML artifact types (MODEL_SCHEMA and PROMPT_TEMPLATE) in Apicurio Registry. It covers the internal architecture, TypeScript implementation details, JSON Schema backing, MCP integration, and how to customize or extend these types.
-
For usage documentation, see [registry-artifact-types_registry]
-
For creating custom artifact types, see the Custom Artifact Type Example
Architecture Overview
Apicurio Registry implements the LLM artifact types using a script-based architecture that allows artifact type logic to be written in TypeScript and executed within the Java runtime.
Script-Based Artifact Type System
The LLM artifact types are implemented using the following components:
| Component | Description |
|---|---|
TypeScript Implementation |
Located in |
Bundled JavaScript |
The TypeScript is transpiled and bundled using esbuild into JavaScript files stored in |
Configuration File |
|
Java Bridge |
|
Prompt Rendering Service |
Java service ( |
Configuration File Structure
The LLM artifact types are configured in app/src/main/resources/llm-artifact-types/default-artifact-types-config.json:
{
"includeStandardArtifactTypes": true,
"artifactTypes": [
{
"artifactType": "MODEL_SCHEMA",
"name": "Model Schema",
"description": "AI/ML model input/output schema definitions and metadata for LLM governance",
"contentTypes": ["application/json", "application/x-yaml"],
"scriptLocation": "llm-artifact-types/model-schema-artifact-type.js",
"contentAccepter": { "type": "script" },
"contentCanonicalizer": { "type": "script" },
"contentValidator": { "type": "script" },
"compatibilityChecker": { "type": "script" },
"contentDereferencer": { "type": "script" },
"referenceFinder": { "type": "script" }
},
{
"artifactType": "PROMPT_TEMPLATE",
"name": "Prompt Template",
"description": "Version-controlled prompt templates with variable schemas for LLMOps",
"contentTypes": ["application/x-yaml", "application/json", "text/x-prompt-template"],
"scriptLocation": "llm-artifact-types/prompt-template-artifact-type.js",
"contentAccepter": { "type": "script" },
"contentCanonicalizer": { "type": "script" },
"contentValidator": { "type": "script" },
"compatibilityChecker": { "type": "script" },
"contentDereferencer": { "type": "script" },
"referenceFinder": { "type": "script" }
}
]
}
Handler Functions
Each artifact type implements the following handler functions:
| Function | Purpose |
|---|---|
|
Determines if content matches this artifact type based on structure and content type. |
|
Validates content structure, required fields, and constraint violations. |
|
Checks backward compatibility between existing and proposed versions. |
|
Normalizes content for consistent hashing and comparison. |
|
Locates |
|
Resolves |
|
Rewrites |
|
Ensures all references are properly mapped. |
JSON Schema Backing and Industry Standards
Apicurio Registry provides formal JSON Schemas for PROMPT_TEMPLATE and MODEL_SCHEMA artifact types, aligned with industry standards for interoperability.
Schema Locations
The JSON Schemas are located in app/llm-artifact-types-src/schemas/:
| File | Description |
|---|---|
|
Schema for PROMPT_TEMPLATE artifacts, aligned with Microsoft Prompty |
|
Schema for MODEL_SCHEMA artifacts, aligned with Google Model Card Toolkit |
PROMPT_TEMPLATE Schema - Prompty Alignment
The PROMPT_TEMPLATE schema is aligned with the Microsoft Prompty specification for interoperability with the Azure AI ecosystem.
| Apicurio Field | Prompty Field | Notes |
|---|---|---|
|
|
Primary identifier |
|
|
Both names supported |
|
|
Both names supported |
|
Body content |
{{variable}} syntax |
|
|
Model configuration |
|
|
Contributors list |
|
|
Categorization |
templateId: sentiment-analysis
name: Sentiment Analysis Prompt
version: "1.0.0"
description: Analyzes customer sentiment
# Prompty-aligned fields
authors:
- Apicurio Team
tags:
- sentiment
- nlp
templateFormat: mustache # or jinja2, handlebars
# Model configuration (Prompty-aligned)
model:
api: chat
parameters:
temperature: 0.3
max_tokens: 500
template: |
Analyze the sentiment of: {{customerMessage}}
variables:
customerMessage:
type: string
required: true
MODEL_SCHEMA Schema - Model Card Toolkit Alignment
The MODEL_SCHEMA is aligned with the Google Model Card Toolkit for governance and compliance use cases.
| Apicurio Field | Model Card Field | Notes |
|---|---|---|
|
|
Model identifier |
|
|
Model provider |
|
|
Version info |
|
|
I/O schema |
|
|
I/O schema |
|
|
Full metadata |
|
|
Metrics |
|
|
Ethics, limitations |
{
"modelId": "claude-3-opus",
"provider": "anthropic",
"version": "2024-02",
"input": { "type": "object" },
"output": { "type": "object" },
"modelDetails": {
"name": "Claude 3 Opus",
"overview": "Advanced multimodal AI assistant",
"owners": [{"name": "Anthropic"}],
"licenses": [{"identifier": "Proprietary"}]
},
"considerations": {
"users": ["Developers", "Enterprises"],
"limitations": ["May produce inaccurate information"],
"ethicalConsiderations": [
{"name": "Bias", "mitigationStrategy": "Regular auditing"}
]
}
}
MCP (Model Context Protocol) Integration
Apicurio Registry integrates with the Model Context Protocol (MCP) to expose PROMPT_TEMPLATE artifacts as MCP prompts, enabling AI agents to discover and use prompt templates.
MCP Extension in PROMPT_TEMPLATE
Prompt templates can include an mcp section to enable MCP protocol integration:
templateId: support-chat
name: Support Chat Prompt
template: |
Answer the question: {{question}}
variables:
question:
type: string
required: true
# MCP integration
mcp:
enabled: true
name: support-chat
description: Answer customer support questions
arguments:
- name: question
description: The customer question
required: true
| Field | Required | Description |
|---|---|---|
|
No |
Set to |
|
No |
MCP prompt name (defaults to |
|
No |
MCP prompt description (defaults to template description) |
|
No |
MCP arguments (auto-derived from |
MCP Server Module
The mcp/ module provides an MCP Server that exposes registry prompts:
| Component | Description |
|---|---|
|
MCP Server exposing prompt templates as MCP prompts |
|
Parses and renders prompt templates |
MCP Tools and Prompts
The MCP Server exposes these tools and prompts:
| Tool | Description |
|---|---|
|
List all MCP-enabled prompt templates in the registry |
|
Get and render an MCP-enabled prompt with arguments |
|
Render any prompt template (MCP-enabled or not) |
| Prompt | Description |
|---|---|
|
Interactive prompt for rendering templates from the registry |
Using the MCP Server
To run the MCP Server:
cd mcp
../mvnw quarkus:dev
The MCP Server connects to the registry and exposes prompts via the MCP protocol, enabling AI agents (Claude, ChatGPT, etc.) to discover and use registry-stored prompts.
MCP Architecture
┌─────────────────────────────────────────────────────────────┐
│ Apicurio Registry │
├─────────────────────────────────────────────────────────────┤
│ PROMPT_TEMPLATE Artifacts (with mcp.enabled: true) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ MCP Server Module │
│ - list_mcp_prompts(): List MCP-enabled templates │
│ - get_mcp_prompt(): Render template with arguments │
│ - render_prompt_template(): Render any template │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AI Agent / LLM │
│ (Claude, ChatGPT, etc.) │
│ │
│ MCP Protocol: │
│ - prompts/list → Discover available prompts │
│ - prompts/get → Get prompt content with arguments │
└─────────────────────────────────────────────────────────────┘
PROMPT_TEMPLATE Implementation Details
The PROMPT_TEMPLATE artifact type is implemented in app/llm-artifact-types-src/src/PromptTemplateArtifactType.ts.
Schema Structure
interface PromptTemplate {
$schema?: string; // Schema URL for identification
templateId: string; // Required: unique identifier
name?: string; // Human-readable name
description?: string; // Template description
version?: string; // Semantic version
template: string; // Required: template text with {{variable}} placeholders
// Prompty-aligned fields
authors?: string[]; // Contributors list
tags?: string[]; // Categorization tags
templateFormat?: string; // 'mustache' | 'jinja2' | 'handlebars'
model?: ModelConfiguration; // Model configuration
// Input/output definitions (Prompty: inputs/outputs)
variables?: Record<string, VariableSchema>; // Variable definitions
inputs?: Record<string, VariableSchema>; // Alias for variables (Prompty)
outputSchema?: Record<string, unknown>; // Expected output structure
outputs?: Record<string, unknown>; // Alias for outputSchema (Prompty)
// Extensions
metadata?: Record<string, unknown>; // Custom metadata
mcp?: MCPConfiguration; // MCP integration
}
interface VariableSchema {
type?: string; // string, integer, number, boolean, array, object
required?: boolean; // Whether variable is required
default?: unknown; // Default value
description?: string; // Variable description
enum?: unknown[]; // Allowed values
minimum?: number; // Minimum value for numeric types
maximum?: number; // Maximum value for numeric types
$ref?: string; // Reference to external schema
}
interface ModelConfiguration {
api?: string; // 'chat' | 'completion'
configuration?: {
type?: string; // Provider type
azure_deployment?: string;
azure_endpoint?: string;
};
parameters?: {
temperature?: number;
max_tokens?: number;
top_p?: number;
};
response?: string; // 'first' | 'all'
}
interface MCPConfiguration {
enabled?: boolean; // Expose via MCP protocol
name?: string; // MCP prompt name
description?: string; // MCP prompt description
arguments?: MCPArgument[]; // MCP arguments
}
Content Detection
The acceptsContent() function identifies PROMPT_TEMPLATE content by checking:
-
Content type is
application/json,application/x-yaml, ortext/x-prompt-template -
Schema identifier:
$schemafield containsprompt-template -
Required fields: presence of both
templateIdandtemplatefields
Validation Rules
The validate() function enforces:
-
templateIdmust be a non-empty string -
templatemust be a non-empty string -
All
{{variable}}placeholders in template must have corresponding definitions invariables -
Variable types must be valid:
string,integer,number,boolean,array,object -
Numeric constraints (
minimum,maximum) must be numbers -
Enum values must be arrays
Compatibility Rules
| Change Type | Allowed | Reason |
|---|---|---|
Add new optional variable |
Yes |
Existing consumers can ignore new variables |
Remove unused variable (not in template) |
Yes |
No impact on template rendering |
Remove used variable (in template) |
No |
Breaks template rendering |
Change variable type |
No |
Existing variable values may become invalid |
Make optional variable required |
No |
Existing consumers may not provide value |
Narrow enum values (remove options) |
No |
Existing valid values may become invalid |
Change template text (same variables) |
Yes |
Variable contract remains intact |
Remove output schema property |
No |
Consumer expectations may be violated |
Template Variable Extraction
Variables are extracted from template text using regex pattern \{\{(\w+)\}\}:
function extractTemplateVariables(template: string): string[] {
const regex = /\{\{(\w+)\}\}/g;
const variables: string[] = [];
let match;
while ((match = regex.exec(template)) !== null) {
if (!variables.includes(match[1])) {
variables.push(match[1]);
}
}
return variables;
}
MODEL_SCHEMA Implementation Details
The MODEL_SCHEMA artifact type is implemented in app/llm-artifact-types-src/src/ModelSchemaArtifactType.ts.
Schema Structure
interface ModelSchema {
$schema?: string; // Schema URL for identification
modelId: string; // Required: unique model identifier
provider?: string; // Provider name (OpenAI, Anthropic, etc.)
version?: string; // Model version
input?: Record<string, unknown>; // Input schema (JSON Schema format)
output?: Record<string, unknown>; // Output schema (JSON Schema format)
metadata?: Record<string, unknown>; // Custom metadata (capabilities, etc.)
definitions?: Record<string, unknown>; // Reusable schema definitions
// Model Card Toolkit-aligned sections
modelDetails?: ModelDetails; // Comprehensive model metadata
modelParameters?: ModelParameters; // Architecture and data info
quantitativeAnalysis?: QuantitativeAnalysis; // Performance metrics
considerations?: Considerations; // Ethics, limitations, users
// MLflow compatibility
signature?: MLflowSignature; // MLflow model signature
}
interface ModelDetails {
name?: string;
overview?: string;
owners?: Array<{name: string; contact?: string}>;
version?: {name: string; date?: string};
licenses?: Array<{identifier?: string; custom_text?: string}>;
references?: Array<{reference: string}>;
citations?: Array<{style?: string; citation: string}>;
regulatoryRequirements?: string[];
}
interface Considerations {
users?: string[];
useCases?: string[];
limitations?: string[];
tradeoffs?: string[];
ethicalConsiderations?: Array<{name: string; mitigationStrategy?: string}>;
fairnessConsiderations?: Array<{name: string; mitigationStrategy?: string}>;
}
Content Detection
The acceptsContent() function identifies MODEL_SCHEMA content by checking:
-
Content type is
application/jsonorapplication/x-yaml -
Schema identifier:
$schemafield containsmodel-schema -
Required fields: presence of
modelIdand at least one ofinputoroutput
Validation Rules
The validate() function enforces:
-
modelIdmust be a non-empty string -
At least one of
inputoroutputmust be defined -
inputandoutputmust be objects (JSON Schema format) -
metadatamust be an object if provided
Compatibility Rules
| Change Type | Allowed | Reason |
|---|---|---|
Add optional input property |
Yes |
Existing consumers can ignore new fields |
Add required input property |
No |
Existing consumers won’t provide new required fields |
Remove input property |
No |
Consumers may still send removed properties |
Change input property type |
No |
Existing data may become invalid |
Add output property |
Yes |
Consumers can ignore new fields in response |
Remove output property |
No |
Consumers may depend on removed properties |
Change output property type |
No |
Consumer parsing may break |
Remove input schema entirely |
No |
Contract changes fundamentally |
Remove output schema entirely |
No |
Contract changes fundamentally |
Prompt Rendering Service
Apicurio Registry provides a server-side prompt rendering service for PROMPT_TEMPLATE artifacts.
REST API Endpoint
POST /apis/registry/v3/groups/{groupId}/artifacts/{artifactId}/versions/{versionExpression}/render
{
"variables": {
"variableName": "value",
"anotherVariable": 123
}
}
{
"rendered": "The fully rendered template with substituted values",
"groupId": "default",
"artifactId": "my-prompt",
"version": "1.0",
"validationErrors": []
}
Java Implementation
The PromptRenderingService class (app/src/main/java/io/apicurio/registry/services/PromptRenderingService.java) provides:
| Feature | Description |
|---|---|
Content Parsing |
Automatically parses YAML or JSON prompt templates |
Variable Substitution |
Replaces |
Type Validation |
Validates variable values match their defined types |
Required Field Checking |
Reports missing required variables as validation errors |
Enum Validation |
Ensures values are within allowed enum options |
Range Validation |
Validates numeric values against minimum/maximum constraints |
Validation Error Response
When validation fails, the response includes validation errors:
{
"rendered": "Template with {{missing}} placeholder intact",
"groupId": "default",
"artifactId": "my-prompt",
"version": "1.0",
"validationErrors": [
{
"variableName": "missing",
"message": "Required variable is missing"
},
{
"variableName": "count",
"message": "Value 200 is greater than maximum 100"
}
]
}
Customizing LLM Artifact Types
You can modify the built-in LLM artifact types or use them as templates for custom types.
Source Code Location
| File | Purpose |
|---|---|
|
PROMPT_TEMPLATE implementation |
|
MODEL_SCHEMA implementation |
|
Shared utility functions (parsing, reference handling) |
|
Build configuration and dependencies |
Building Modified Types
To modify and rebuild the LLM artifact types:
# Navigate to source directory
cd app/llm-artifact-types-src
# Install dependencies
npm install
# Build both artifact types
npm run build
# Or build individually
npm run build-prompt-template
npm run build-model-schema
The build process:
-
Transpiles TypeScript to JavaScript using
tsc -
Bundles with dependencies using
esbuild -
Outputs to
app/src/main/resources/llm-artifact-types/
Using the artifact-type-builtins Package
The @apicurio/artifact-type-builtins npm package provides:
-
TypeScript type definitions for all request/response interfaces
-
Utility functions (
info(),debug()) for logging -
IDE autocomplete support
npm install @apicurio/artifact-type-builtins
import type {
ContentAccepterRequest,
ContentValidatorRequest,
ContentValidatorResponse,
CompatibilityCheckerRequest,
CompatibilityCheckerResponse
} from '@apicurio/artifact-type-builtins';
export function validate(request: ContentValidatorRequest): ContentValidatorResponse {
// Implementation
}
Shared Utilities
The shared-utils.ts file provides reusable functions:
| Function | Purpose |
|---|---|
|
Parse JSON or YAML content based on content type |
|
Recursively sort object keys for canonicalization |
|
Find all external |
|
Replace |
|
Rewrite |
External Configuration
To override the built-in LLM artifact types at deployment time:
-
Create a custom configuration file:
{ "includeStandardArtifactTypes": true, "artifactTypes": [ { "artifactType": "PROMPT_TEMPLATE", "scriptLocation": "/custom/my-prompt-template.js", // ... other configuration } ] } -
Mount the configuration and JavaScript files
-
Set the environment variable:
APICURIO_ARTIFACT_TYPES_CONFIG_FILE=/path/to/config.json
-
For a complete custom artifact type example, see the TOML Custom Artifact Type Example
-
For the npm package documentation, see @apicurio/artifact-type-builtins on npm
