KDCube
← Our Journal

An app explicitly publishes a provider for a namespace — task, mem, cnv, parts. An agent in a different app wants to use it. The two were built separately, deployed separately, and neither knows where the other runs. The thing that connects them is the named-services discovery service: a small registry where a consumer asks "which published provider can answer this task request?" and gets back an answer — without anyone hardcoding an address.

That one move — resolve by namespace, not by location — is what makes the ecosystem composable. (For the full realm-connection picture, see the Deep blog "Your app as a service provider in the agentic network"; this Short is only about the find.)

The registry: a directory of published providers

The discovery service is a small directory of explicitly published providers, scoped to one tenant and project. Its core is an index keyed by namespace: each namespace name points to the set of providers that publish it. Normally one dedicated app owns a realm. Multiple providers may share a namespace only when the partition is intentional and the request can select among them by operation, ref, or object kind.

discovery table  (per tenant / project)
  namespace:parts   →  { inventory.parts }
  namespace:task    →  { task.issue }
  namespace:mem     →  { sdk.memory }

Nothing here is an address. The index maps a name to who — the where is carried separately, in each provider's record, and only handed to a caller once discovery has resolved the name.

How providers publish: reconcile on load

A provider decorator describes a contract; it does not publish that provider. The owner app explicitly contributes the provider to its current registry. On load, the app reconciles that complete registry with discovery. Each provider record says what it serves — its namespaces, operations, refs, object kinds, and provider_id — and how to reach it — the endpoint: the transport, the app package it lives in, and the provider handle.

provider record
  namespaces:   parts
  provider_id:  inventory.parts
  endpoint:     transport · app package · provider

Current provider records are upserted and indexed under every namespace they serve. Records that the same app published before but omitted from its current registry are withdrawn; publishing an empty registry withdraws all of that app's old providers. Another app's records are untouched. This prevents a reusable mixin or an old deployment from becoming an accidental second owner.

How consumers discover: resolve by namespace

A consumer app does not hardcode a provider location. In config it declares that it consumes namespace parts. At call time discovery reads the namespace:parts candidates, selects a provider that supports the request, and returns its endpointeven though that provider lives in a different app package.

consumer: "I consume namespace `parts`"
        │
        ▼
discovery.resolve(namespace="parts", operation="object.search")
        │   read namespace:parts → eligible inventory.parts → its record
        ▼
endpoint  →  transport · the publishing app package · provider
        │
        ▼
the agent calls the generic named-service operation on the realm
it never had to locate

The consumer never learned an address, a host, or which app package owns parts. It learned a name, and discovery did the rest. Move the provider to a different app, re-deploy it, let the owner publish its current registry — the consumer config doesn't change, because it was never pointing at a place.

Why this is the composable seam

PUBLISH → RECONCILE → RESOLVE App A — provider inventory.parts publishes current set reconciles snapshot Discovery registry keyed by namespace · per tenant/project namespace:parts → { inventory.parts } namespace:task → { task.issue } namespace:mem → { sdk.memory } reads parts row App B — consumer declares it consumes parts resolve("parts") ENDPOINT RETURNED transport · app package app-package boundary the resolve path crosses it — by name, not address publish current state · resolve by name discovery hands back an eligible place.
Publish current state, resolve by name: discovery hands back the eligible place across the app-package boundary.

Hardcoding a provider's location couples two apps forever: you cannot move, split, or replace the provider without editing every consumer. Discovery breaks that coupling. An owner app publishes its current state; consumers resolve the current provider at request time — same tenant and project, across app packages.

Discovery is routing, not exposure. A REST, MCP, Data Bus, or local adapter still defines how the call crosses the boundary, and that surface still applies its own authentication and authorization. Discovery only removes the hardcoded location.

Since then: the record grew a second reader

The published spec's metadata now also carries the realm's HUMAN layer — presentation (purpose, works-with, human labels per operation/action), object-kind one-liners, and connected-account requirements for provider-backed realms. The capability picker resolves the very same discovery record and renders it as the realm's service card: one provider spec, two readers.

And a realm's agent consumers now enter per agent: whether the caller is an external MCP client or an agent hosted in a KDCube app, it acts under the user's delegated-by grant for that specific agent, checked at the attempt — with the raw claim tokens (mail:read, slack:post) as the consent language everywhere the user reads or grants them. The provider does not implement that delegation gate; the boundary is the platform's.

Documentation on GitHub

The live docs behind this entry:

KDCube Journal · Entry № 2 · 26.06.2026