OSP logo
os://protocol

Registry

Registration and discovery of resources in the system — the kernel interface for managing what is available and finding it by criteria.

This interface is experimental — no production implementation exists yet. The API surface may change.

Overview

Registry is the kernel's service directory: it registers and discovers any type of resource in the system. The interface is generic (Registry<T>) so it works uniformly for agents, skills, MCP servers, or any other resource type. The RegistryContext/RegistryActions split operates on named registries (e.g., "agents", "skills"), allowing a single implementation to manage multiple resource namespaces. This pattern is analogous to Google A2A Agent Cards, Microsoft AutoGen Registry, AGENTS.md/AAIF Agent Discovery, and the npm Registry.

TypeScript API

import type {
  RegistryEntry,
  RegistryContext,
  RegistryActions,
  Registry,
} from 'osprotocol/system/registry'

RegistryEntry

A single record stored in the registry.

interface RegistryEntry<T = unknown> {
  name: string
  description: string
  resource: T
  metadata?: Record<string, unknown>
}
FieldTypeDescription
namestringUnique identifier within its registry
descriptionstringHuman-readable summary
resourceTThe actual resource payload (agent, skill, server, etc.)
metadataRecord<string, unknown>Optional arbitrary metadata

RegistryContext

Read-only access to named registries. Used during the gather phase to inspect what is available.

interface RegistryContext {
  get<T = unknown>(registry: string, name: string): Promise<RegistryEntry<T> | null>
  list<T = unknown>(registry: string): Promise<RegistryEntry<T>[]>
}
MethodDescription
get(registry, name)Fetch a single entry by name from a named registry
list(registry)List all entries in a named registry

RegistryActions

Write access to named registries. Used during the act phase to mutate what is registered.

interface RegistryActions {
  register<T = unknown>(registry: string, entry: RegistryEntry<T>): Promise<void>
  unregister(registry: string, name: string): Promise<boolean>
}
MethodDescription
register(registry, entry)Add or update an entry in a named registry
unregister(registry, name)Remove an entry; returns true if it existed

Registry

The full provider interface for a single typed registry. Combines all operations and adds find for criteria-based lookup — this method is only available at the provider level, not on RegistryContext or RegistryActions.

interface Registry<T = unknown> {
  register(entry: RegistryEntry<T>): Promise<void>
  unregister(name: string): Promise<boolean>
  get(name: string): Promise<RegistryEntry<T> | null>
  list(): Promise<RegistryEntry<T>[]>
  find(criteria: Partial<T>): Promise<RegistryEntry<T>[]>
}
MethodDescription
register(entry)Add or update an entry
unregister(name)Remove an entry; returns true if it existed
get(name)Fetch a single entry by name
list()List all entries
find(criteria)Search entries by matching fields on the resource payload

Named Registries

RegistryContext and RegistryActions accept a registry string as the first parameter. This means one implementation can manage multiple independent namespaces:

// Same context, different namespaces
const agent = await ctx.get('agents', 'summarizer-v2')
const skill = await ctx.get('skills', 'web-search')
const server = await ctx.get('mcp-servers', 'filesystem')

The full Registry<T> interface, by contrast, is typed per resource type and manages a single namespace — you would instantiate one Registry<AgentDefinition> for agents and a separate Registry<SkillDefinition> for skills.

Usage Examples

Register an agent

await actions.register('agents', {
  name: 'summarizer-v2',
  description: 'Summarizes long documents into structured output.',
  resource: agentDefinition,
  metadata: { version: '2.0.0', owner: 'platform-team' },
})

Discover resources by criteria

Using a typed Registry<AgentDefinition>, find all agents that match a partial resource shape:

const agentRegistry: Registry<AgentDefinition> = getAgentRegistry()

const matches = await agentRegistry.find({
  domain: 'summarization',
  supportsStreaming: true,
})

Use named registries to cross-reference resources

async function resolveSkillsForAgent(
  ctx: RegistryContext,
  agentName: string,
): Promise<RegistryEntry[]> {
  const agentEntry = await ctx.get('agents', agentName)
  if (!agentEntry) return []

  const requiredSkills: string[] = agentEntry.metadata?.skills as string[] ?? []
  return Promise.all(
    requiredSkills.map((skillName) => ctx.get('skills', skillName)),
  ).then((results) => results.filter(Boolean) as RegistryEntry[])
}

Integration

  • Installer — installs capabilities that are then registered, making them available for discovery
  • MCP Client — discovers MCP servers through the registry before establishing connections
  • Env — environment-aware discovery uses registry lookups scoped to the current runtime context