This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
# Build
dotnet build
# Run all tests
dotnet test
# Run a single test project
dotnet test tests/AgentRegistry.Api.Tests/
# Run a specific test by name
dotnet test --filter "FullyQualifiedName~RegistrationTests.Register_WithValidRequest"
# Run the API (requires Postgres + Redis via user secrets)
dotnet run --project src/MarimerLLC.AgentRegistry.Api
# Add a migration after domain/EF model changes
dotnet ef migrations add <Name> -p src/MarimerLLC.AgentRegistry.Infrastructure -s src/MarimerLLC.AgentRegistry.Api
# Apply migrations
dotnet ef database update -p src/MarimerLLC.AgentRegistry.Infrastructure -s src/MarimerLLC.AgentRegistry.ApiFour-layer clean architecture. No layer may reference a layer above it.
Domain → Application → Infrastructure
→ Api (references Application, not Infrastructure directly)
AgentRegistry.Domain— pure value objects and entities (Agent,Endpoint,Capability,ApiKey). No external dependencies. IDs are strongly-typed structs wrappingGuid(e.g.AgentId,EndpointId).AgentRegistry.Application— use-case logic inAgentService. Defines interfacesIAgentRepository,ILivenessStore,IApiKeyServicethat Infrastructure implements. Exceptions (NotFoundException,ForbiddenException) are declared here.AgentRegistry.Infrastructure— EF Core (Postgres via Npgsql) for agent/key persistence; Redis for endpoint liveness TTLs.AgentRegistryDbContextmaps strongly-typed IDs viaValueConverter.AgentRegistry.Api— ASP.NET Core 10 minimal APIs. Composed inProgram.cs. Protocol adapters live underProtocols/(A2A, MCP, ACP), each with a*Mapperclass and endpoint registration.
Liveness is split across two stores. Agent identity and capabilities live in Postgres. Endpoint liveness is purely Redis TTL keys (endpoint:liveness:{endpointId}). Discovery queries Postgres first, then does a single batched Redis check to filter live endpoints. The two liveness models are:
Ephemeral— TTL expires automatically; agent callsPOST /agents/{id}/endpoints/{eid}/renewon each invocation.Persistent— agent callsPOST /agents/{id}/endpoints/{eid}/heartbeat; grace period is 2.5×heartbeatIntervalSeconds.
Protocol metadata round-trips as raw JSON. Protocol-specific fields (A2A skill schemas, MCP tool descriptors, ACP content types) are stored in Endpoint.ProtocolMetadata as a JSONB column. Mappers serialize in and deserialize out — nothing protocol-specific reaches the domain model.
Authentication uses a "Smart" policy scheme. X-Api-Key header → ApiKeyAuthenticationHandler (validates against Postgres hash, sets registry_scope claim). Authorization: Bearer → standard JwtBearer. The registry does not issue JWTs; it validates tokens from an external IdP. Auth policies (AdminOnly, AgentOrAdmin) in RegistryPolicies.cs check registry_scope claim or roles claim.
Central Package Management is enabled (Directory.Packages.props). Add <PackageVersion> entries there; use <PackageReference> without Version in project files. tests/Directory.Build.props chains to the root and auto-includes GitHubActionsTestLogger + enables UseMicrosoftTestingPlatformRunner for all test projects.
Integration tests use WebApplicationFactory<Program> (AgentRegistryFactory). Infrastructure dependencies are replaced with in-memory fakes:
InMemoryAgentRepository— thread-safeConcurrentDictionaryInMemoryLivenessStore— in-memory equivalent of Redis TTL storeFakeApiKeyService— exposesFakeApiKeyService.AdminKeyandFakeApiKeyService.AgentKeyconstants
Use factory.CreateAdminClient() or factory.CreateAgentClient() to get pre-authenticated HttpClient instances. Call factory.Reset() in Dispose() to clear state between tests. Tests are IClassFixture<AgentRegistryFactory>.
Application-layer tests use Rocks (source-generator mocks) to mock IAgentRepository and ILivenessStore.
- Add a
Protocols/<Name>/folder underMarimerLLC.AgentRegistry.Apiwith:Models/— request/response records matching the protocol's wire format<Name>AgentManifestMapper.cs(or equivalent) — bidirectional mapping to/from domain types usingEndpoint.ProtocolMetadatafor protocol-specific fields<Name>Endpoints.cs— minimal API route registrations with.WithTags(),.WithSummary(),.WithDescription(),.Produces<T>(), and.ProducesProblem()on every route
- Call
Map<Name>Endpoints()fromProgram.cs - Add integration tests under
tests/AgentRegistry.Api.Tests/Protocols/<Name>/