NineA is a local capability runtime for AI agents. It turns integrations into capabilities that a human or agent can search and run from the current project.
Plan 9 made system resources feel uniform by exposing them through 9P. NineA borrows that idea for agent systems: integrations should be mounted into a workspace behind a small, regular interface instead of taught to every agent one by one.
For agents today, Skills are the filesystem-based first-class instruction
format. NineA therefore exposes one shared gateway Skill, using-ninea, and
lets the runtime handle capability discovery, contracts, credentials,
approvals, and execution. The Skill stays stable; the mounted capabilities can
change with the workspace.
brew install gopact-ai/tap/nineaAlready installed? Update with brew upgrade gopact-ai/tap/ninea.
In any project, save this as weather.yaml. If an agent is translating API
documentation, it can read the embedded contract before the first integration
or gateway Skill exists:
9a connect --guide http --jsonThen create:
version: 1
name: weather
description: Read current weather for coordinates.
type: http
services:
forecast:
baseURL: https://api.open-meteo.com
capabilities:
current-weather:
description: Read current temperature and wind.
service: forecast
method: GET
path: /v1/forecast
request:
query:
latitude: "{{ input.latitude }}"
longitude: "{{ input.longitude }}"
current: temperature_2m,wind_speed_10m
inputSchema:
type: object
required: [latitude, longitude]
additionalProperties: false
properties:
latitude: {type: number}
longitude: {type: number}
outputSchema:
type: object
required: [temperature_2m, wind_speed_10m]
properties:
temperature_2m: {type: number}
wind_speed_10m: {type: number}
hooks:
afterResponse:
- transform:
language: jq
expression: .body.currentThen run:
9a connect weather.yaml
9a search weather/current-weather --json
9a run weather/current-weather \
--input '{"latitude":31.2,"longitude":121.5}'connect prints 9a search <integration> --json to list every connected
capability. Use an exact <integration>/<capability> search from that result to
inspect its input and output contracts. NineA saves the editable source at
.9a/integrations/weather.yaml; re-running it updates the same integration.
Version 1 HTTP manifests require an explicit inputSchema and outputSchema
for every capability and workflow, so agents never have to infer a contract
from request templates.
- Integration — one external system, such as a weather API.
- Capability — one action, such as
current-weather. - Workspace — the project whose agent can use those capabilities.
NineA prepares a workspace automatically and installs one using-ninea
gateway Skill shared by every integration.
9a search weather
9a run weather/current-weather --input @request.json
cat request.json | 9a run weather/current-weather
9a status
9a doctor
9a disconnect weatherstatus reports whether integrations are ready and prints the next action for
missing credentials or broken state. disconnect removes the active
integration but keeps .9a/integrations/weather.yaml. Add --json to data
commands for machine-readable output.
Capabilities that may change an upstream system require explicit approval:
9a run orders/create-order --input @order.json --json
# After the user approves this exact input, reuse data.approvalToken:
9a run orders/create-order --input @order.json \
--approve "$APPROVAL_TOKEN"The approval token binds the capability revision and exact JSON input. It can be used once, expires after 10 minutes, and is invalidated when the local daemon restarts. Changing either bound value also requires a new preflight and explicit approval.
Manifests name credential aliases, never secret values. Store a declared value through stdin or the hidden terminal prompt:
9a secret set private-api.api-token
printf '%s' "$API_TOKEN" | 9a secret set private-api.api-tokenValues go to the operating system credential store (system keyring) and are scoped to the current workspace. The same reference can hold a different value in another workspace. See the authenticated HTTP example.
Connect a local MCP server or a remote A2A agent without writing YAML:
9a connect mcp --name local-tools -- /absolute/path/to/server
9a connect a2a --name research-agent https://agent.example.comFor a bearer-authenticated A2A agent, use an
A2A manifest and store its declared credential
with 9a secret set.
The using-ninea gateway Skill teaches an agent to search for capabilities and
call 9a run. Capability descriptions are loaded only when needed instead of
being added to the prompt at startup.
The manifest is an Agent-to-NineA contract, not a form the user must learn.
Give an agent API documentation or an OpenAPI file and ask it to create an
integration and connect it. On a fresh workspace it starts with
9a connect --guide http --json; successful connection then projects the shared
gateway Skill.
Searching and checking status have no upstream side effect. During normal
capability use, run is the execution boundary, and mutating actions require
an approval token. All MCP and A2A runs require approval. Integration YAML is
strictly decoded: unknown fields, duplicate keys, aliases, multiple documents,
unsafe remote HTTP URLs, and oversized input are rejected.
MCP servers and executable hooks are local code running with your operating system account's privileges. Use a sandbox or separate account when that trust boundary is too broad. See Security.
See the documentation index for guides and reference material.
go test -count=1 ./...NineA is available under the MIT License.