# Capabilities

Capabilities are metadata tags attached to service registrations. They exist so you can inspect what a service claims to support without instantiating it.

That sounds minor until you start building:

- settings UIs
- extension manifests
- plugin discovery panels
- slot inspectors
- routing logic based on declared behavior

At that point, you definitely want to know what a service claims before you pay the cost of constructing it.

This page comes after services and settings. Once a plugin has something useful to expose, capabilities make that usefulness discoverable to the host app.

## The key idea

Capabilities live on the registration wrapper, not on the service instance.

That means you can inspect them through `resolveRaw(...)` without constructing the service.

```dart
const tooling = Namespace('tooling');
final wrapper = context.registry.resolveRaw(tooling('formatter'));
final caps = wrapper.capabilities;
```

That makes capabilities safe for discovery code. The host can walk the registry without accidentally starting services, opening sockets, or triggering lazy singletons too early.

## A capability can be literally anything

`Capability` is an empty abstract base class. You subclass it to describe whatever facts about a slot matter to your app. The runtime does not interpret the fields, does not validate them, and does not know which subclasses exist. It just stores them.

That is the point. You are not registering against a fixed schema Plugin Kit defined for you. You are stapling arbitrary tags onto a slot and reading them back later on your own terms.

```dart
extractRegion(capabilitiesSnippets, 'capability-define')
```

Any of those is a real capability. All three can coexist on the same registration. The runtime treats them identically.

## Attaching capabilities at registration

Pass a set of capabilities when you register the service.

```dart
extractRegion(extensionManifestSource, '09-extension-manifest-formatter-capability')
```

Later, the host reads them back:

```dart
final wrapper = context.registry.resolveRaw(const ServiceId('formatter'));
final cap = wrapper.capabilities.getOfType<FormatterCapability>();
```

No instance of `SqlFormatter` was ever created.

## Capability lookup helpers

`CapabilityLookup` adds two small but useful helpers on `Set<Capability>`.

- `hasType<T>()`
- `getOfType<T>()`

```dart
extractRegion(capabilitiesSnippets, 'capability-has-type-is-slow')
```

Cleaner than manual casting, and clearer at the call site.

```dart
extractRegion(capabilitiesSnippets, 'capability-resolve-raw-wrapper')
```

## Discovery APIs

| API | What it gives you |
|---|---|
| `resolveRaw(...)` | the current winning wrapper for one slot |
| `listCapabilitiesOfNamespace(...)` | all capabilities seen under a namespace |

Both return data without instantiating services. Combine them with your own `hasType<T>()` / `getOfType<T>()` reads to build whatever discovery surface your app needs.

## When capabilities are the right tool

Capabilities work best when they describe stable facts about a slot.

**Good examples**

- supported languages or file formats
- supported runtimes
- feature tier ("free", "pro", "internal")
- whether the slot is slow or safe to call on the UI thread

**Less good examples**

- ephemeral runtime state
- data that only makes sense after the service has started

Rule of thumb: if the answer depends on a running instance, it belongs on the service. If the answer should be discoverable before instantiation, it belongs in a capability.

## Related reading

[Service Registry](https://plugin-kit.saad-ardati.dev/concepts/service-registry/)
  [Configuration](https://plugin-kit.saad-ardati.dev/concepts/configuration/)
  [Settings and Overrides](https://plugin-kit.saad-ardati.dev/guides/settings/)