Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Added


- **`c1i mcp classifiers`: manage MCP gateway AI governance.** This renames
the former guardrails concept in c1i's user-facing CLI. List, inspect,
create, update, and delete named classifiers; bind them to enforcement
targets; inspect and instantiate templates; splice a template rule; manage
the singleton agent policy; and manage or search tool gates. Nested policy
and rule updates use JSON files and an explicit update mask so an ordered
cascade is never replaced accidentally.
- **`c1i access-reviews`: manage access-review campaigns and reports.**
List and inspect campaigns, create and update a campaign from validated JSON
input, list generated reports, and request JSON, CSV, or XLSX report
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,51 @@ c1i mcp bindings delete --app-id <id> --connector-id <id> --toolset-id <tid> -
c1i mcp bindings by-tools --app-id <id> --connector-id <id> --tool-id <id> [--tool-id <id> ...] # --tool-id max 32
c1i mcp bindings history --app-id <id> --connector-id <id> (--toolset-id <tid> | --tool-id <id>) [--page-size N] [--limit N]

# Classifiers (AI governance for MCP gateways)
c1i mcp classifiers list [--page-size N] [--page-token TOKEN] [--limit N]
c1i mcp classifiers get <classifier-id>
c1i mcp classifiers create --body-file classifier.json
c1i mcp classifiers update <classifier-id> --body-file classifier.json --update-mask <camelCase-fields>
c1i mcp classifiers delete <classifier-id>
c1i mcp classifiers bindings list [--page-size N] [--page-token TOKEN] [--limit N]
c1i mcp classifiers bindings create --body-file binding.json
c1i mcp classifiers bindings delete <binding-id>
c1i mcp classifiers templates list [--latest-only] [--page-size N] [--page-token TOKEN] [--limit N]
c1i mcp classifiers templates get <template-id> [--template-version <version>]
c1i mcp classifiers templates instantiate <template-id> [--body-file params.json]
c1i mcp classifiers templates add-rule <classifier-id> <template-id> [--body-file params.json]
c1i mcp classifiers policy show
c1i mcp classifiers policy update --body-file policy.json --update-mask <camelCase-fields>
c1i mcp classifiers tool-gates list [--page-size N] [--page-token TOKEN] [--limit N]
c1i mcp classifiers tool-gates search [--query <text>] [--page-size N] [--page-token TOKEN] [--limit N]
c1i mcp classifiers tool-gates get <tool-gate-id>
c1i mcp classifiers tool-gates create --body-file tool-gate.json
c1i mcp classifiers tool-gates update <tool-gate-id> --body-file tool-gate.json --update-mask <camelCase-fields>
c1i mcp classifiers tool-gates delete <tool-gate-id>

# Gateway (verify end to end: list and invoke tools over the live MCP gateway)
c1i mcp gateway list-tools [--full] [--gateway-url <url>]
c1i mcp gateway call <tool-name> [--args '{"k":"v"}'] [--gateway-url <url>]
```

**`mcp classifiers`** manages the AI-governance API introduced for MCP
gateways. Named classifiers are reusable ordered rule cascades; bindings attach
one to an `AGENT` or `GATEWAY` target. `policy` is the separate, tenant-wide
agent policy. A rule with an empty `celCondition` matches every tool call, and
rules execute in order, so read the current object before changing `rules`.

`create` accepts a resource JSON object: `Classifier` for classifiers,
`ClassifierBinding` for bindings, and the full create request for tool gates.
`classifiers update` must include the classifier's required `displayName`,
even when the mask changes another field; read, edit, and resubmit the current
object. Every update requires `--update-mask`; include `rules` only when
deliberately replacing the complete cascade. `templates get` shows required
parameter keys. `instantiate` and `add-rule` accept an optional
JSON object with `templateVersion`, `params`, and, for `add-rule`, `ruleIndex`
or `insertIndex`; the positional IDs always select the template and
classifier. A tool gate's `filter` must select either `builtInPattern` or
`celExpression`, not both.

**Auth for `register` / `update-credentials`:** convenience flags cover the simple methods — `--auth none`, `--auth bearer-token --bearer-token TOKEN`, `--auth custom-header --header-name NAME --header-value VALUE`, `--auth basic-auth --basic-auth-username USER --basic-auth-password PASS`. For OAuth2 / AWS SigV4 / Google service-account auth, pass the full config object via `--hosted-config-file` / `--external-config-file` (JSON file, or `-` for stdin) — generate a ready-to-edit skeleton with `--print-config-template --auth <method> [--type hosted]` instead of hand-writing it. Secrets are sealed server-side; reads only ever return `*_configured` booleans, never the values. `--token-sharing shared|per-user` sets the server's token-sharing mode (case-insensitive; `per_user`/`peruser` are also accepted). Per the register help, `per-user` is only valid with `oauth2` in authorization-code or passthrough mode, `bearerToken`, `customHeader`, or `basicAuth`. Note that a read-back can legitimately differ from what you sent: the backend may store a *resolved* OAuth2 grant such as `..._MODE_AUTHORIZATION_CODE` in place of the input mode, so that is a normal round-trip, not a bug. `--source-app-id` names the source app for a connector-backed HOSTED server. `--data-sensitivity`, `--tool-prefix`, `--require-tool-approval` and `--user-id` (repeatable — sets the connector's integration owners) can all be set at `register` time, not only via `update`.

`mcp tools approve` is the standard post-registration step: newly discovered tools (from `register` or `resync-tools`) start in `PENDING_REVIEW`, and an admin approves them for the gateway to proxy calls. It takes one or more tool ids — the API has no batch approve, so each id is a separate request, but one invocation covers a whole toolset (pipe `mcp tools search --app-id <id> --connector-id <id> --state pending --fields id | jq -r .id`). History endpoints return records newest-first.
Expand Down
18 changes: 10 additions & 8 deletions cmd/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,11 @@ applies either way.
Access-profile lifecycle, requestable and visibility entitlements, and bundle
automation are first-class under `access-profiles`; use its nested `--help` to
choose a command. Use `entitlements proxy-bindings` for directional
entitlement-to-entitlement links, and `access-reviews` for campaign lists,
lifecycle calls, and reports.
entitlement-to-entitlement links, `access-reviews` for campaign lists,
lifecycle calls, and reports, and `mcp classifiers` for AI-governance
classifiers, bindings, templates, the singleton agent policy, and tool gates.
Classifier creates and updates take JSON because their nested, ordered rules
must be preserved; read first and use the explicit `--update-mask` for updates.

The cobra tree never drifts from what's implemented. Step down it with
`--help` at each level:
Expand All @@ -143,12 +146,11 @@ A few wire conventions if you build a raw request: GET endpoints take
`page_size`/`page_token` as snake_case query params; POST search endpoints
take `pageSize`/`pageToken` (camelCase) in the body; response pagination is
always `nextPageToken`. List/search responses wrap items under `"list"` —
except the MCP admin endpoints (`mcp_tools`, `mcp_toolsets`,
`tool_bindings`), which use a resource-named key (`"tools"`, `"profiles"`,
`"bindings"`) instead. `--paginate` unwraps whichever field it finds, but pass
`--list-key <field>` to name it yourself rather than hand-rolling the loop when
auto-detection picks the wrong array. GET and DELETE refuse a body by default;
the few endpoints that need one on DELETE (e.g. `remove-membership`) want
except the MCP admin endpoints, which use resource-named keys (`"tools"`,
`"profiles"`, `"bindings"`, `"classifiers"`, or `"templates"`). `--paginate`
unwraps whichever field it finds, but pass `--list-key <field>` to name it
yourself rather than hand-rolling the loop when auto-detection picks the wrong
array. GET and DELETE refuse a body by default; the few endpoints that need
`--allow-delete-body`. The UI's "campaign" is the API's access review — a
campaign ID from a URL is the access review `id` directly, and the UI's "access
profile" is the API's catalog: `c1i access-profiles list`, `/api/v1/catalogs`, whose
Expand Down
35 changes: 35 additions & 0 deletions cmd/get_unwrap_guard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,41 @@ func getUnwrapCases() []getUnwrapCase {
payloadPath: []string{"profile"},
wantKeys: []string{"id", "displayName"},
},
{
name: "mcp classifiers get",
cmd: mcpClassifiersGetCmd,
args: []string{"classifier-1"},
idKey: "id",
body: `{"classifier":{"id":"classifier-1","displayName":"Observe"}}`,
payloadPath: []string{"classifier"},
wantKeys: []string{"id", "displayName"},
},
{
name: "mcp classifiers templates get",
cmd: mcpClassifierTemplatesGetCmd,
args: []string{"template-1"},
idKey: "id",
body: `{"template":{"id":"template-1","displayName":"Balanced"}}`,
payloadPath: []string{"template"},
wantKeys: []string{"id", "displayName"},
},
{
name: "mcp classifiers tool-gates get",
cmd: mcpClassifierToolGatesGetCmd,
args: []string{"gate-1"},
idKey: "id",
body: `{"toolGate":{"id":"gate-1","displayName":"Destructive"}}`,
payloadPath: []string{"toolGate"},
wantKeys: []string{"id", "displayName"},
},
{
name: "mcp classifiers policy show",
cmd: mcpClassifierPolicyShowCmd,
idKey: "agentId",
body: `{"policy":{"agentId":"agent-1","defaultOutcome":"AGENT_CLASSIFIER_RULE_OUTCOME_ALLOWED"}}`,
payloadPath: []string{"policy"},
wantKeys: []string{"agentId", "defaultOutcome"},
},
{
name: "mcp toolsets get-by-entitlement",
cmd: mcpToolsetsGetByEntitlementCmd,
Expand Down
Loading
Loading