The tool registry is the single source of truth for every tool the harness can invoke. NeitherDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/joseluis-dev/harness-ai/llms.txt
Use this file to discover all available pages before exploring further.
harness-core nor the Python runtime contain a list of MCP servers, CLI commands, or HTTP adapters. Instead, every tool — from read-only SQL queries to external notification dispatch — is represented as a row in the tools table within harness_auth_db. When an agent needs a tool, the gateway queries the registry by technical_name, evaluates the row’s state, risk_level, and required_scopes, and then delegates authorization to mcp-auth-service. Adding, deprecating, or disabling a tool requires only a database update — no redeployment of harness-core.
Core Principles
No hardcoded lists
Neither
harness-core nor the runtime enumerate tools in code. The available set is the set of state = 'active' rows in tool_registry. Any if tool_name == '...' branch in core is an automatic PR rejection.Registry is single source of truth
Tools resolved at runtime come from the persisted registry. A tool registered only in memory (without a database row) does not survive a restart. The last persisted row always wins.
Seeds are migrations
The nine Phase 0.b tools are inserted via Alembic migration
0002_tool_registry_seed, not application startup code. Seeds are versioned, idempotent (ON CONFLICT DO NOTHING), and part of the normal migration history.Each row declares its authorization
The registry row states what scopes a tool requires and what
risk_level it carries. mcp-auth-service crosses these against the client’s token scopes. The tool itself does not make authorization decisions.Logical Schema
Thetools table lives in harness_auth_db. The schema below is the canonical logical definition as implemented in Alembic migration 0001_init for harness_auth_db:
The
operations JSONB array enables per-operation scope granularity. For example, list_views requires sql:list_views while execute_query requires sql:read. mcp-auth-service evaluates the operation-level scope, not only the global required_scopes.Initial Seeds (Phase 0.b)
The following nine tools are inserted by Alembic migration0002_tool_registry_seed against harness_auth_db. All rows land with state = 'active', environment = 'dev', version = '0.1.0', timeout_ms = 30000, and created_by = 'phase-0b-seed'. The seed is idempotent (ON CONFLICT (technical_name) DO NOTHING).
technical_name | type | transport | risk_level | required_scopes | Human confirm |
|---|---|---|---|---|---|
mcp-readonly-sql | mcp_http | oauth | READ_ONLY | sql:read, sql:list_views, sql:describe_view | No |
mcp-identity-connector | mcp_http | oauth | READ_SENSITIVE | identity:read | No |
mcp-document-registry | mcp_http | oauth | READ_ONLY | documents:read | No |
mcp-expedientes | mcp_http | oauth | READ_SENSITIVE | expedientes:read | No |
mcp-reportes | mcp_http | oauth | READ_ONLY | reportes:run | No |
mcp-notificaciones | mcp_http | oauth | EXTERNAL_SIDE_EFFECT | external | Yes |
cli-pdf-tools | cli | command | READ_ONLY | documents:read | No |
cli-ocr-tools | cli | command | READ_SENSITIVE | documents:read | No |
cli-office-converter | cli | command | WRITE | write | Yes |
dev: mcp-readonly-sql at http://mcp-readonly-sql:9003, mcp-identity-connector at http://mcp-identity-connector:9001, mcp-document-registry at http://mcp-document-registry:9004, mcp-expedientes at http://mcp-expedientes:9005, mcp-reportes at http://mcp-reportes:9007, mcp-notificaciones at http://mcp-notificaciones:9006.
Resolution and Authorization Flow
The following eight steps describe the full lifecycle from agent request to audited result:Agent requests a tool by name
The agent (or workflow) declares the tool it needs using its
technical_name, for example mcp-readonly-sql.tool_gateway queries tool_registry
The gateway fetches the row from
harness_auth_db: technical_name, state, environment, required_scopes, risk_level, transport, endpoint, config, and requires_human_confirmation.mcp-auth-service issues a scoped token
The gateway requests a Client Credentials token with the minimum required scope (e.g.,
sql:read). mcp-auth-service validates that the requesting client’s allowed_scopes include the requested scope and that oauth_clients.state = 'active'.gateway invokes the tool endpoint
The gateway sends the tool call with
Authorization: Bearer <token> in the header. No token is placed in query strings or the request body.auth-sdk validates the token
The MCP server (or CLI wrapper) calls
auth-sdk to verify the JWT signature and expiration, confirm the scope covers the operation, and check that both tool_registry.state and oauth_clients.state are active.risk_level gate
If the operation’s
risk_level requires human confirmation (DESTRUCTIVE, ADMINISTRATIVE, or EXTERNAL_SIDE_EFFECT with the flag set), the MCP responds 202 Accepted with an approval_id. Execution is held until an administrator approves via the admin panel.Result is normalized
The tool response is normalized to the standard result contract before being returned to the agent.
mcp-readonly-sql Configuration
The mcp-readonly-sql seed row carries a full config JSONB block that declares SQL safety controls and connection profile constraints. No connection string or secret is embedded — credentials are resolved from MCP_READONLY_SQL_DATABASE_URL at runtime. The admin panel edits this block as a declarative policy; the MCP server consumes it as a contract, not as its own authorization.
Versioning and Deprecation
Every tool row carries aversion field. The state machine for tool lifecycle is:
- A tool in
state = 'active'is fully operational. - A tool in
state = 'deprecated'continues to serve clients that already have authorization, but the admin panel flags it as requiring migration. New clients are directed to the replacement row. - A tool in
state = 'disabled'is rejected before reaching the MCP server. The harness returnscode = TOOL_NOT_ALLOWEDimmediately.
version. The old row transitions to deprecated. The two rows coexist during the migration window.
Declarative Restrictions
Tool restrictions are declared in the registry row and enforced by the runtime — not by the MCP server itself.| Field | Applies to | Enforcement |
|---|---|---|
network_restrictions.egress_allowlist | All tool types | Runtime blocks all outbound traffic not explicitly listed. Default: block all. |
network_restrictions.ingress_allowlist | mcp_http, http_api | Runtime restricts inbound connections to declared sources. |
filesystem_restrictions.read_allowlist | cli | CLI Tool Runner validates paths against the allowlist before invoking the binary. |
filesystem_restrictions.write_allowlist | cli | CLI Tool Runner validates write targets before invoking the binary. |
execution_restrictions.cpu | cli | Translated to container resource limits on the execution container. |
execution_restrictions.memory | cli | Translated to container resource limits on the execution container. |
execution_restrictions.no_network | cli | Container is started with --network none when set to true. |
execution_restrictions.sandbox | cli | Additional sandboxing flags applied to the execution container. |
required_env_vars | All tool types | Runtime fails fast at startup if any declared variable is absent from the environment. |
Audit Record
Every significant change to atool_registry row is recorded in audit_auth_events. The minimum audit fields per action are:
| Action | Audited fields |
|---|---|
| Create tool | created_by, version, risk_level, required_scopes, state |
Change state | before.state, after.state, actor_id, reason |
Change risk_level | before.risk_level, after.risk_level, actor_id, justification |
Change required_scopes | before.required_scopes, after.required_scopes, actor_id |
| Deprecate | version, migrated_to (new technical_name if applicable), sunset_at |
Persistence and Isolation
- The authoritative
tool_registrylives inharness_auth_db.mcp-auth-serviceis its sole writer for authorization-plane mutations. harness_dbmay hold a read-only cache or materialized view of registry data for operational queries, but it must never make authorization decisions, modifystate, changerisk_level, or alterrequired_scopes. All such changes originate inharness_auth_db.- Migrations and seeds for
tool_registryare applied via theinfra/postgres/auth-migrationsdirectory (alembic_version_auth), never viainfra/postgres/migrations(alembic_version).