Multiple MCP accounts and resource sets
Most personal agents do not need an MCP resource set. Start with the first useful result, and open the detailed model only when the workflow needs more than one account, shared access, or an external-customer mapping.
Start simple - one personal account
A new user begins in one implicit personal space. The first Gmail or other
toolkit connection becomes that toolkit's obvious default connection. Give
its numeric configured-tool ID directly to one agent through available_tools,
run the agent, freeze an accepted run, and deploy it through the normal agent
lifecycle. The user does not need to create or name an MCP resource set,
group, slot, grant, principal, revision, or mapping.
The MCP path is intentionally short:
search_tools({"query":"read my Gmail inbox"})
# If Gmail is not configured, send the hosted setup link returned by FlyMyAI.
# Programmatic setup may call add_tool({"mcp_tool":"gmail"}) without an alias.
list_configured_tools({"mcp_tool":"gmail","alias":"default","page_size":100})
create_agent({
"name":"Inbox summary",
"user_prompt":"Summarize unread messages and return a short action list.",
"available_tools":[123],
"mcp_access_mode":"legacy"
})
run_agent({
"agent_id":"<same agent UUID>",
"variables":{},
"operation_key":"inbox-summary-run-<uuid>"
})
Continue by refining and freezing that same agent, then follow Create, Freeze, and Embed One Agent if it will be deployed. This default path uses the same exact connection IDs, authority checks, freeze snapshot, and runtime selection model described below. It can grow into advanced configuration without rebuilding the agent or reconnecting the first account.
Expand only when the workflow needs it
Introduce aliases and resource sets when one toolkit name is no longer enough to identify the account that should act.
For example, one agent can read a support Gmail inbox, compare it with a sales Gmail inbox, and archive selected messages through a third Gmail account. Each mailbox is one exact connection instance. A named resource set groups those instances into one reusable MCP resource set.
The identities stay separate:
| Object | What it identifies | Stable reference |
|---|---|---|
| Toolkit | Which operations exist, such as Gmail actions | mcp_tool slug |
| Connection instance | Which authenticated account or remote MCP server acts | public_id |
| MCP resource set | A named collection of exact connections | resource-set public_id and revision |
| Agent grant | Which agent may use the set | mcp_resource_set_ids |
| Agent group | A flat group of agents that share set grants | agent-group public_id |
| Runtime binding | Which exact connection fills a frozen slot | connection public_id |
Aliases such as support and sales are labels for people. Authorization,
freezing, dispatch, refresh, revocation, and customer isolation use persisted
IDs. Never select an account by email, display name, toolkit slug, resource-set
name, list position, or a database first row.
Advanced owner workflow
Set the exact REST environment before using the examples. Released production uses the value shown here; a release candidate must supply its own root and must not fall back to production:
export FLYMYAI_AGENTS_API_ROOT=https://backend.flymy.ai/api/v1/agents
: "${FLYMYAI_AGENTS_API_ROOT:?Set the exact Agents API root}"
case "$FLYMYAI_AGENTS_API_ROOT" in */api/v1/agents) ;; *) exit 1 ;; esac
BASE="${FLYMYAI_AGENTS_API_ROOT%/}"
auth=(-H "X-API-KEY: $FLYMYAI_API_KEY")
: "${MCP_RESOURCE_SET_CREATE_KEY:?Persist one key for the exact owner-set create body}"
: "${AGENT_GROUP_CREATE_KEY:?Persist one key for the exact group create body}"
The same flow is available through the FlyMyAI Agents MCP gateway. The MCP tool names are included after the REST example.
1. Create exact connection instances
Create each catalog connection with a unique alias:
support=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data '{"mcp_tool":"gmail","alias":"support"}' \
"$BASE/tools/")
sales=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data '{"mcp_tool":"gmail","alias":"sales"}' \
"$BASE/tools/")
archive=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data '{"mcp_tool":"gmail","alias":"archive"}' \
"$BASE/tools/")
SUPPORT_ID=$(jq -er '.public_id' <<<"$support")
SALES_ID=$(jq -er '.public_id' <<<"$sales")
ARCHIVE_ID=$(jq -er '.public_id' <<<"$archive")
Each response includes:
id- the numeric compatibility ID used by legacy direct tool attachment;public_id- the stable UUID used by resource sets and cross-system code;mcp_tool- the toolkit definition;alias- the normalized human label;is_configured,next_configuration_step,connection_status,connection_status_reason,connection_status_checked_at,connection_status_valid_until,connection_status_reason_code, andconnect_url, without returning stored secrets to an MCP client.
Follow the returned setup or connect URL for each exact row. Do not ask a user to paste an API key, OAuth token, password, or refresh token into chat.
Calling POST /tools/ without alias addresses the legacy alias default.
That request remains idempotent and preserves old single-connection behavior.
Use an explicit non-default alias whenever another instance is intended.
Aliases and resource-set slots use only ASCII letters, digits, underscores,
and hyphens. The API rejects spaces, punctuation, and arbitrary authority
fields instead of normalizing them into a different identity.
One owner may retain at most 25 connection rows for one toolkit, including
inactive rows. Replaying an existing alias remains idempotent at the limit.
Delete an unused row to free capacity; merely setting is_active:false keeps
its exact identity and therefore does not free a slot.
Through MCP, the equivalent calls are:
add_tool({"mcp_tool":"gmail","alias":"support"})
add_tool({"mcp_tool":"gmail","alias":"sales"})
add_tool({"mcp_tool":"gmail","alias":"archive"})
list_configured_tools({"mcp_tool":"gmail","page_size":100})
For an unconfigured row, the current gateway returns guidance containing the
hosted https://app.flymy.ai/mcp-configs#<slug>?connection_id=<public UUID>
link and the exact alias, public UUID, and numeric compatibility ID. A
slug-only link is used only before an exact row exists. It does not return a field named
setup_url. After the user finishes, match that exact alias and public_id in
list_configured_tools. Require is_configured:true; connected is ready,
setup_required or reconnect_required needs the hosted link,
verification_pending needs a bounded live read, and
temporarily_unavailable must be surfaced rather than bypassed.
Revoke one exact connection with delete_tool and that row's numeric id
(REST DELETE /api/v1/agents/tools/{id}/) after matching its public_id and
alias. Revocation is per connection: local authority closes atomically with
a durable provider revocation for that row only, sibling aliases keep
working, and a later dispatch on the revoked row fails before any provider
call.
Retain every returned public_id. Listing by toolkit slug alone is not enough
when several rows share the same toolkit.
list_configured_tools returns exactly one bounded cursor page shaped as
{next_cursor, previous_cursor, results}. Filter by exact mcp_tool and,
when one named row is wanted, exact alias. Pass a non-null next_cursor
back unchanged as cursor; never assume the first page contains every
connection instance. The endpoint does not expose a fuzzy query filter.
2. Create a named MCP resource set
An owner resource set can contain the owner's catalog connections and custom MCP servers:
resource_set=$(curl -fsS -X POST "${auth[@]}" \
-H "Idempotency-Key: $MCP_RESOURCE_SET_CREATE_KEY" \
-H 'Content-Type: application/json' \
--data '{
"name":"Inbox operations",
"description":"Support, sales, and archive mailboxes",
"management_mode":"flymyai"
}' \
"$BASE/mcp-resource-sets/")
RESOURCE_SET_ID=$(jq -er '.public_id' <<<"$resource_set")
RESOURCE_SET_REVISION=$(jq -er '.revision' <<<"$resource_set")
For MCP, pass the same durable identity as the required operation_key on
create_mcp_resource_set: a caller-owned string of 1-255 printable ASCII
characters with no leading or trailing spaces. No client, SDK, or server
generates a fallback key. Persist the exact body and key before the first
dispatch. If the response is lost, replay the identical body with that same
key. A changed body with the old key returns 409; a new key could create a
second resource set and is not a recovery strategy.
Omitting principal_id creates an owner-authority set. Do not put a customer
integration_connection in an owner set. Customer mappings use a set rooted
at one exact ExternalPrincipal, described below.
management_mode describes what the saved set represents:
flymyaiwith noprincipal_idis an owner resource set granted to owner agents;customerwith an exactprincipal_idis a customer-managed named mapping whose opaque set ID is stored and supplied by the customer's backend.
A FlyMyAI-managed embedded binding is not a principal resource set. It uses
saved ConnectionBinding rows, and the deployment run omits
resource_set_id and connections. Do not create a principal set with
management_mode: "flymyai" and do not send the deprecated
external_principal_id field.
3. Replace membership atomically
Membership is an atomic full replacement protected by optimistic concurrency.
Send the current revision as expected_revision:
members=$(jq -nc \
--arg support "$SUPPORT_ID" \
--arg sales "$SALES_ID" \
--arg archive "$ARCHIVE_ID" \
--argjson revision "$RESOURCE_SET_REVISION" \
'{
expected_revision:$revision,
members:[
{
resource_type:"user_mcp_tool",
resource_id:$support,
slot:"support_read",
allowed_actions:["GMAIL_SEARCH_EMAILS"],
position:0
},
{
resource_type:"user_mcp_tool",
resource_id:$support,
slot:"support_send",
allowed_actions:["GMAIL_SEND_EMAIL"],
position:1
},
{
resource_type:"user_mcp_tool",
resource_id:$sales,
slot:"sales_mailbox",
allowed_actions:[],
position:2
},
{
resource_type:"user_mcp_tool",
resource_id:$archive,
slot:"archive_mailbox",
allowed_actions:[],
position:3
}
]
}')
resource_set=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data "$members" \
"$BASE/mcp-resource-sets/$RESOURCE_SET_ID/replace-members/")
RESOURCE_SET_REVISION=$(jq -er '.revision' <<<"$resource_set")
The member input contract is:
| Field | Meaning |
|---|---|
resource_type | user_mcp_tool, custom_mcp_server, or integration_connection |
resource_id | Stable public UUID of the exact connection or server |
slot | Logical role in the workflow |
allowed_actions | Optional least-authority action ceiling; omission or an empty list means no additional set-level ceiling, not deny all |
position | Stable display and resolution order, defaulting to request order |
Member identity is the exact (resource_type, resource_id, slot) tuple. One
exact resource may appear in several distinct logical slots, as the support
connection does above. Each slot keeps its own allowed_actions ceiling, so a
read slot does not inherit the send slot's authority. Repeating the same tuple
is invalid.
A 409 response with code: "stale_revision" means another writer changed
the set. Reload the current representation, review the new membership, and
submit a new full replacement with its revision. Do not overwrite it blindly.
Metadata edits use the same CAS boundary. MCP update_mcp_resource_set
requires both the stable resource_set_id and current expected_revision,
even when only name, description, or status changes. A missing revision is a
validation error; HTTP 409 requires reload and intentional review.
A resource set supports at most 100 members and one runtime slot supports at most 25 connections. Members pooled inside one slot must use one toolkit and one action ceiling. Different slots remain independent even when they point to the same exact resource.
One scoped agent execution admits at most 100 MCP resource sets, 500 effective connector bindings, 100 logical slots, 5,000 allowed-action entries, and 1 MiB of UTF-8 allowed-action text. An oversized authority graph fails closed before provider dispatch.
4. Grant the resource set to one agent
Pass stable resource-set UUIDs when creating or updating the agent:
agent=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data "$(jq -nc --arg set "$RESOURCE_SET_ID" '{
name:"Cross-mailbox brief",
user_prompt:(
"Compare the support and sales mailboxes, then move approved messages " +
"through the archive mailbox. Return a structured summary."
),
mcp_resource_set_ids:[$set],
mcp_access_mode:"scoped",
output_schema:{
type:"object",
properties:{summary:{type:"string"}},
required:["summary"],
additionalProperties:false
}
}')" \
"$BASE/tasks/")
AGENT_ID=$(jq -er '.uuid' <<<"$agent")
Through MCP:
create_agent({
"name":"Cross-mailbox brief",
"user_prompt":"Compare support and sales, then archive approved messages.",
"mcp_resource_set_ids":["<resource-set public UUID>"],
"mcp_access_mode":"scoped"
})
When converting an existing personal agent from its direct
available_tools attachment to logical resource slots, discover the live
update_agent request schema with MCP tools/list first. The current schema
requires agent_id and accepts the same optional attachment fields as
create_agent. Include the existing default connection in the new set when it
must remain available, then replace the old direct attachment explicitly:
update_agent({
"agent_id":"<same agent UUID>",
"available_tools":[],
"mcp_resource_set_ids":["<resource-set public UUID>"],
"mcp_access_mode":"scoped"
})
get_agent({"agent_id":"<same agent UUID>"})
An omitted attachment field is unchanged; an explicit empty array clears that
relation. Verify that available_tools is empty and
mcp_resource_set_ids contains exactly the intended set. Direct attachments
and set grants otherwise form a union, which can preserve unintended owner
authority. Clear available_custom_mcp_servers only when those legacy server
attachments are also being replaced. Never copy an owner connection ID into a
customer mapping.
The mutable owner runtime receives the union of legacy direct attachments,
direct resource-set grants, and active agent-group grants. Exact connection
members are deduplicated only when the complete
(resource_type, resource_id, slot) tuple repeats across grants. Distinct slot
memberships for the same connection remain separate with their own action
ceilings. A foreign-owner set or connection fails closed.
5. Share the resource set with a flat agent group
Use an agent group when several agents need the same resource sets:
curl -fsS -X POST "${auth[@]}" \
-H "Idempotency-Key: $AGENT_GROUP_CREATE_KEY" \
-H 'Content-Type: application/json' \
--data "$(jq -nc --arg agent "$AGENT_ID" --arg set "$RESOURCE_SET_ID" '{
name:"Inbox agents",
description:"Agents allowed to use the inbox operations resource set",
is_active:true,
agent_ids:[$agent],
resource_set_ids:[$set]
}')" \
"$BASE/agent-groups/"
The MCP equivalents are create_agent_group, update_agent_group,
get_agent_group, and list_agent_groups. Supplying agent_ids or
resource_set_ids on an update replaces that full assignment list atomically.
Omitting a list leaves it unchanged.
create_agent_group requires its own caller-owned operation_key; an
uncertain response is retried only with the identical body and original key.
list_configured_tools, list_mcp_resource_sets,
list_mcp_resource_set_members, and list_agent_groups return one compact
cursor envelope shaped exactly as {next_cursor, previous_cursor, results}. Request
page_size from 1-100, treat cursor as an opaque, nonblank printable string
of at most 1024 Unicode code points, and pass next_cursor unchanged to
continue. Resource-set and agent-group list query is at most 256 printable
characters; the member list accepts only its set ID, page size, and cursor;
configured-tool list uses only exact mcp_tool and alias filters. Do not
translate cursors to offsets or stop after the first page when the continuation
is non-null.
Agent groups are flat in this release. They cannot contain groups. A child or subagent does not inherit a parent's connector authority merely because the parent invoked it. Add that agent to the group or grant the set directly.
Exact selection at runtime
FlyMyAI exposes one action schema per toolkit action, not one duplicated schema
per connected account. If exactly one granted connection can execute an action,
legacy implicit selection is preserved. If several can execute it, the action
schema requires _flymyai_connection.
Pass an allowed connection public_id:
{
"_flymyai_connection": "22222222-2222-4222-8222-222222222222",
"query": "after:2026/08/01"
}
For a direct MCP execute_tool call outside an agent run, pass the exact
configured-tool public UUID as connection_id. Omission is supported only
while one eligible connection is unambiguous; it must never select an account
by row order. The direct call still requires its caller-owned stable
operation_key and exact action schema.
Read the exact enum and descriptions from the runtime schema. Never substitute the alias or choose the first row. Missing, foreign, revoked, expired, stale, or no-longer-granted selectors fail before provider dispatch.
Connected-but-unattached discovery can auto-activate a toolkit only when one eligible connection exists. An ambiguous toolkit must be attached or grouped explicitly.
Freeze and publish without losing account identity
Freeze only an accepted completed execution of the same logical agent. New frozen versions capture effective resource-set membership, exact owner connection IDs, logical slots, action ceilings, and source set revisions.
Changing a member, slot, action ceiling, group membership, or set revision can change the next version fingerprint. An older immutable version does not change. Do not rely on editing an alias after freeze to retarget an old release.
Adding an account without rebuilding means keeping the same agent UUID: update that agent, accept a new run, and freeze a new immutable version. Stage that version on the same stable deployment ID. Neither the old version nor an incompatible saved customer mapping is mutated in place.
Continue with Create, Freeze, and Embed One Agent for the complete run, freeze, version, preflight, publish, and rollback flow.
Product-customer mapping modes
external_user_id resolves one product customer's ExternalPrincipal. It
does not identify a Gmail account, choose a resource set, create a FlyMyAI
user, or switch billing. FlyMyAI still bills the deployment owner.
Choose the mapping mode explicitly:
| Mode | What your backend sends on deployment run | Where the mapping lives |
|---|---|---|
| FlyMyAI-managed binding | Neither resource_set_id nor connections | Saved ConnectionBinding rows |
| Customer-managed named mapping | resource_set_id and its saved resource_set_revision | Stable opaque mapping ID in your backend, exact members in FlyMyAI |
| One-off explicit mapping | connections keyed by slot | Canonical request stored by your backend |
resource_set_id and connections are mutually exclusive. Supplying both is
a validation error. resource_set_revision is valid only with
resource_set_id. The backend still accepts an omitted named-mapping revision
for compatibility, but the public safe workflow requires both saved values so
a stale mapping fails closed.
The names describe who stores the run selector. A FlyMyAI-managed run uses
saved bindings and no principal resource-set ID. A customer-managed named run
uses a principal resource set created with management_mode: "customer" and
principal_id; the customer's backend stores and supplies that set ID.
Bootstrap the customer principal through connect-session
GET /deployments/{deployment_id}/access/ is read-only. It can show the
deployment requirements and existing customer state, but it never creates an
ExternalPrincipal. The first mutating bootstrap is
POST /deployments/{deployment_id}/connect-session/ with the exact
external_user_id and one connection_required slot from the published
deployment:
: "${SLOT:?Use a connection_required slot from deployment access}"
session=$(curl -fsS -X POST "${auth[@]}" \
-H 'Content-Type: application/json' \
--data "$(jq -nc \
--arg user "$EXTERNAL_USER_ID" \
--arg slot "$SLOT" \
'{external_user_id:$user,slot:$slot}')" \
"$BASE/deployments/$DEPLOYMENT_ID/connect-session/")
jq '{redirect_url,expires_at}' <<<"$session"
That POST resolves or creates the principal and creates a short-lived hosted
authorization session. Treat it as a single-dispatch write: persist the
canonical deployment, customer, and slot before calling, and reconcile an
ambiguous response before creating another session. Redirect the authenticated
customer to redirect_url. After authorization, poll the read-only access
route for active, unexpired connections:
curl -fsS -G "${auth[@]}" \
--data-urlencode "external_user_id=$EXTERNAL_USER_ID" \
"$BASE/deployments/$DEPLOYMENT_ID/access/"
Only after that mutating bootstrap should the owner query the principal list. Require exactly one result:
principals=$(curl -fsS -G "${auth[@]}" \
--data-urlencode "deployment=$DEPLOYMENT_ID" \
--data-urlencode "external_user_id=$EXTERNAL_USER_ID" \
"$BASE/external-principals/")
PRINCIPAL_ID=$(jq -er '
(.results // .) as $rows |
if ($rows|length)==1 then $rows[0].public_id else error("expected one principal") end
' <<<"$principals")
Create a customer-managed named mapping
Create a principal-authority set:
: "${CUSTOMER_RESOURCE_SET_CREATE_KEY:?Persist one key for this exact customer mapping create}"
customer_set=$(curl -fsS -X POST "${auth[@]}" \
-H "Idempotency-Key: $CUSTOMER_RESOURCE_SET_CREATE_KEY" \
-H 'Content-Type: application/json' \
--data "$(jq -nc --arg p "$PRINCIPAL_ID" '{
name:"Primary inbox mapping",
description:"Mapping stored by the customer backend",
management_mode:"customer",
principal_id:$p
}')" \
"$BASE/mcp-resource-sets/")
Replace its members with exact integration_connection public UUIDs belonging
to that same principal. Cross-principal IDs are rejected. Store the returned
resource-set public_id and revision next to your own customer and deployment
record. The ID is opaque and does not expose credentials.
Run the stable deployment with that saved mapping:
curl -fsS -X POST "${auth[@]}" \
-H "Idempotency-Key: $PERSISTED_DEPLOYMENT_RUN_KEY" \
-H 'Content-Type: application/json' \
--data "$(jq -nc \
--arg user "$EXTERNAL_USER_ID" \
--arg set "$CUSTOMER_RESOURCE_SET_ID" \
--argjson revision "$CUSTOMER_RESOURCE_SET_REVISION" \
'{
external_user_id:$user,
variables:{},
resource_set_id:$set,
resource_set_revision:$revision
}')" \
"$BASE/deployments/$DEPLOYMENT_ID/run/"
The runtime revalidates set authority and every exact connection at the immutable snapshot boundary. A stale revision, revoked connection, wrong principal, or slot mismatch fails closed.
For FlyMyAI-managed bindings, omit both mapping fields. For a one-off mapping,
send connections instead:
{
"external_user_id": "customer_42",
"variables": {},
"connections": {
"support_mailbox": "<integration-connection public UUID>",
"archive_mailbox": ["<integration-connection public UUID>"]
}
}
Each connections value is either one integration-connection public UUID or
an array of UUIDs for a pooled slot. Use only slot names from the frozen access
requirements, honor each slot's cardinality, and include every required slot
when the request is intended to be a complete explicit mapping. An omitted
slot is not an explicit empty value: current runtime behavior continues to its
saved binding for that slot. Every supplied UUID is revalidated against the
resolved external principal; an owner connection, a different customer's
connection, an unknown slot, or invalid cardinality fails before provider
dispatch. The request contains IDs only, never provider credentials.
Derive external_user_id from the authenticated product session and keep the
builder key on your server. Persist the mapping ID, revision, canonical run
body, idempotency key, execution ID, and settled price in your own ledger.
See Per-customer MCP Access for hosted connection sessions, readiness polling, isolation proof, billing, and cleanup.
Public API shapes
A resource-set list row is a compact summary containing:
public_id, name, description, status, management_mode, authority_type,
principal_id, revision, member_count, created_at, updated_at
It does not nest members. A resource-set detail/create/update/replace
response contains:
public_id, name, description, status, management_mode, authority_type,
principal_id, revision, member_count, members, created_at, updated_at
Every member contains:
public_id, resource_type, resource_id, toolkit_slug, alias, display_name,
slot, allowed_actions, position, created_at, updated_at
An agent-group response contains:
public_id, name, description, is_active, agent_ids, resource_set_ids,
created_at, updated_at
All new cross-system references are public UUIDs. Numeric IDs remain only on legacy compatibility routes and fields.
MCP and SDK paths
Use the implemented lifecycle boundary:
| Stage | Implemented surface |
|---|---|
| Author and test | MCP create_agent, run_agent, and get_run |
| Freeze and owner-test | MCP freeze_agent, get_compilation, and run_frozen |
| Exact owner connections | MCP add_tool, update_tool, and list_configured_tools |
| Resource sets and groups | The MCP tools listed below |
| Publish a deployment | Agents REST POST /deployments/, /preflight/, and /publish/ |
| Bootstrap and inspect a customer | Agents REST POST /deployments/{deployment_id}/connect-session/ and read-only GET /deployments/{deployment_id}/access/ |
| Run for a customer | Agents REST POST /deployments/{deployment_id}/run/ |
The Agents MCP gateway's resource-set and group surface is:
- exact account creation through
add_tool(mcp_tool, alias)and bounded discovery throughlist_configured_tools(mcp_tool, alias, page_size, cursor); list_mcp_resource_sets,get_mcp_resource_set,create_mcp_resource_set,update_mcp_resource_set, anddelete_mcp_resource_set;- paginated
list_mcp_resource_set_membersfor large membership reads; - revision-checked
replace_mcp_resource_set_members; list_agent_groups,get_agent_group,create_agent_group,update_agent_group, anddelete_agent_group;mcp_resource_set_idsoncreate_agentandupdate_agent.
Deployment publication, hosted customer connection setup, principal reads, and
customer runs remain on the Agents REST API. The MCP gateway can author, test,
freeze, and owner-test the same agent before that REST handoff. Always use the
tool list returned by MCP tools/list as the source of truth and do not turn a
REST operation into an invented MCP call.
SDK versions that expose resource scopes mirror the same REST objects through
typed mcp_resource_sets and agent_groups resources. Feature-detect those
namespaces in the installed package. If they are absent, use the canonical REST
calls above instead of inventing SDK methods. The currently released SDK's
deployment limitations are documented in the lifecycle guide.
Rollout and compatibility
- Additional aliases and resource-scope mutations are feature-gated during the
staged rollout. A workspace that is not enabled can continue using the
defaultconnection and can receive a clear mutation-disabled error. - Existing single-connection agents keep their explicit
defaultcompatibility projection. They are not silently reassigned to another row. - Disabling a resource set stops its grants without deleting connection credentials. Deleting a set removes its membership and grants, not the underlying connections.
- Sets and agent groups are not nested in this release. Cross-owner sharing and organization RBAC are not implied by matching names.
- A rollback disables new creation but must keep reading and executing existing exact rows and sets. Never collapse several aliases back into one row.
- Deploy backend expansion and backward-compatible readers before enabling multi-alias writers or the new clients. Before disabling those readers or rolling database state back across the alias constraint, run the server's read-only alias downgrade preflight and require a clean result. If it reports non-default aliases or scoped state, keep the compatible backend in service; do not drop or rewrite customer mappings to force a rollback.
Before promotion, run the clean-assistant lifecycle gate early. It must create
one logical agent, use exact resource scopes, freeze one accepted run, publish
one deployment, and stop without receiving customer IDs in its prompt. The
trusted harness must then provision two different external_user_id bindings
and prove they remain on the same agent, version, deployment, and billed owner
while receiving distinct principals, executions, and connector state.