Federation API¶
Base URL: /api/v1
Authentication: - Authorization: Bearer <token>
Content type: - Content-Type: application/json
Scope Requirements¶
GET /federationsGET /federations/{federation_id}- Requires
federations-read POST /federationsPUT /federations/{federation_id}DELETE /federations/{federation_id}POST /federations/{federation_id}/sync- Requires
federations-write POST /federation/agentcore/runtime/sync- Requires
system-ops
Common Enums¶
providerType¶
Current implementation note: - aws_agentcore is the only supported federation provider in this version - azure_ai_foundry is still reserved in the enum for compatibility, but create, update, and sync currently return 501 Not Implemented
federation status¶
federation syncStatus¶
lastSync.status uses the same enum. It represents the latest sync attempt snapshot for the federation, so it may also be pending or syncing while a job is in progress.
federation jobType¶
federation job status¶
Runtime Auth Model¶
Each discovered runtime (MCP server or A2A agent) carries its own data-plane authentication configuration. This is inferred automatically during discovery and stored on the resource, not on the federation.
Why per-resource, not per-federation¶
A single federation can discover runtimes with different authorizer configurations:
- runtime A → IAM
- runtime B → JWT
- runtime C → IAM
Placing runtimeAccess on the federation would force all runtimes to share one auth mode, which is incorrect. The correct model is:
- Federation = control plane (how to discover runtimes)
- MCP server / A2A agent = data plane (how to call that specific runtime)
runtimeAccess shape on resources¶
config.runtimeAccess stored on each discovered resource:
IAM mode:
JWT mode:
{
"config": {
"runtimeAccess": {
"mode": "jwt",
"jwt": {
"discoveryUrl": "https://issuer.example.com",
"audiences": ["jarvis-services"]
}
}
}
}
discoveryUrl is the OIDC well-known endpoint of the runtime's authorizer. The JWT iss claim sent to the runtime is derived from discoveryUrl (scheme + host), not from the local auth server URL. audiences comes from the runtime's allowedAudience configuration.
providerConfig.runtimeAccess is not supported¶
Passing runtimeAccess inside providerConfig will be rejected:
{
"detail": {
"error": "invalid_request",
"message": "providerConfig.runtimeAccess is not supported"
}
}
Sync Strategy¶
Each sync run uses a version-aware, minimum-write strategy to maintain MongoDB and Weaviate consistency.
Three sync scenarios¶
| Scenario | Condition | MongoDB | Weaviate |
|---|---|---|---|
| First sync | Resource not in MongoDB | INSERT | Full build |
| Version unchanged | runtimeVersion same as stored | Skip write | Check version; skip if up to date, repair if stale |
| Version changed | runtimeVersion differs | UPDATE | Delete + full rebuild |
MCP servers and A2A agents are evaluated independently.
Sync summary fields¶
The summary object returned in lastSync and dry-run responses:
| Field | Description |
|---|---|
discoveredMcpServers | Total MCP runtimes returned by discovery |
discoveredAgents | Total A2A runtimes returned by discovery |
createdMcpServers | Newly inserted into MongoDB |
updatedMcpServers | Updated in MongoDB due to version change |
deletedMcpServers | Removed from MongoDB (no longer discovered) |
unchangedMcpServers | MongoDB skipped; version unchanged |
skippedMcpServers | MCP servers skipped due to serverName conflict with another federation |
createdAgents | Newly inserted into MongoDB |
updatedAgents | Updated in MongoDB due to version change |
deletedAgents | Removed from MongoDB (no longer discovered) |
unchangedAgents | MongoDB skipped; version unchanged |
skippedAgents | A2A agents skipped due to path conflict with another federation |
vectorSyncFailedMcpServers | Changed MCP resources persisted to MongoDB but not fully imported because their vector rebuild failed. Returned by lastSync; not present in dry-run responses. |
vectorSyncFailedAgents | Changed A2A resources persisted to MongoDB but not fully imported because their vector rebuild failed. Returned by lastSync; not present in dry-run responses. |
errors | Count of recorded per-resource enrichment and vector-sync failures |
errorMessages | List of recorded per-resource error strings |
An enrichment-failed resource is not classified as created, updated, or unchanged. It is not written to MongoDB or Weaviate. If the same runtime already has a healthy MongoDB document, that last-known-good document is preserved and is not treated as stale merely because enrichment failed.
Federation stats fields¶
The stats object returned by create, list, update, and detail responses currently contains:
| Field | Description |
|---|---|
mcpServerCount | MCP runtimes discovered during the latest completed sync |
agentCount | A2A runtimes discovered during the latest completed sync |
toolCount | Live total of tools on MCP servers currently persisted for the federation |
importedTotal | Resources from the latest sync that completed enrichment, Mongo apply, and any required changed-resource vector sync |
unimportedTotal | Latest-sync residual: (mcpServerCount + agentCount) - importedTotal |
The following invariant holds for every completed sync:
toolCount is intentionally a live inventory value; the other four values describe the latest completed sync run. A future contract revision may separate inventory and per-run statistics into different objects. Until that revision is implemented, clients must use the semantics above.
Weaviate sync behavior¶
Weaviate is a secondary search index rebuilt from MongoDB state. It runs after the MongoDB transaction commits.
- Changed or missing resources: delete existing Weaviate docs for that runtime, then full rebuild.
- Unchanged resources (version same in MongoDB): compare Weaviate version. If Weaviate is already up to date, skip. If stale or missing, rebuild without deleting first.
- Changed-resource Weaviate failures: recorded in
errorMessagesandvectorSyncFailedMcpServers/vectorSyncFailedAgents. They reduceimportedTotalfor this run but do not roll back MongoDB. - Repair-only Weaviate failures: recorded in
errorMessages, but do not reduceimportedTotalbecause the unchanged resource was already imported by this run.
1. Create Federation¶
POST /federations
Request Body¶
{
"providerType": "aws_agentcore",
"displayName": "AgentCore Prod",
"description": "Production federation",
"tags": ["prod", "aws"],
"providerConfig": {
"region": "us-east-1",
"assumeRoleArn": "arn:aws:iam::123456789012:role/demo"
}
}
AWS resourceTagsFilter API shape example:
Request Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
providerType | string | Yes | aws_agentcore is supported. azure_ai_foundry currently returns 501 Not Implemented. |
displayName | string | Yes | 1-200 chars |
description | string \| null | No | Federation description |
tags | string[] | No | UI classification tags |
providerConfig | object | No | Federation-level control-plane config only. For aws_agentcore, create may omit region and assumeRoleArn. Those fields become required during update and sync. resourceTagsFilter is optional; when provided, sync only imports runtimes whose AWS tags fully match every key:value pair. providerConfig.runtimeAccess is not supported and will be rejected. |
UI note for AWS: - The form may let users type env:production, team:platform - The frontend must convert that into providerConfig.resourceTagsFilter as a JSON object - The backend does not accept the raw comma-separated string
Success Response¶
Status: 201 Created
Create only stores the federation definition. It does not trigger a sync job.
{
"id": "federation_demo_id",
"providerType": "aws_agentcore",
"displayName": "AgentCore Prod",
"description": "Production federation",
"tags": ["prod", "aws"],
"status": "active",
"syncStatus": "idle",
"syncMessage": null,
"providerConfig": {
"region": "us-east-1",
"assumeRoleArn": "arn:aws:iam::123456789012:role/demo"
},
"stats": {
"mcpServerCount": 0,
"agentCount": 0,
"toolCount": 0,
"importedTotal": 0,
"unimportedTotal": 0
},
"lastSync": null,
"recentJobs": [],
"createdBy": "user_demo_id",
"updatedBy": "user_demo_id",
"createdAt": "2026-03-26T07:20:00Z",
"updatedAt": "2026-03-26T07:20:10Z"
}
Error Responses¶
400 Bad Request
{
"detail": {
"error": "invalid_request",
"message": "Unsupported federation provider type: some_provider"
}
}
401 Unauthorized
403 Forbidden
2. List Federations¶
GET /federations
Query Parameters¶
| Param | Type | Required | Description |
|---|---|---|---|
providerType | string | No | Filter by provider |
syncStatus | string | No | Filter by sync status |
tag | string | No | Single tag filter |
tags | string[] | No | Multi-tag filter |
query | string | No | Search display name / description |
page | number | No | Default 1 |
per_page | number | No | Default 20, max 100 |
Compatibility notes: - keyword is still accepted as a deprecated alias for query - pageSize is still accepted as a deprecated alias for per_page
Response note: - lastSync reflects the latest sync attempt snapshot for the federation - when a sync job is queued or running, lastSync.status may be pending or syncing - if a sync attempt fails before the apply phase, lastSync is still updated to that failed attempt instead of preserving the older success snapshot
Success Response¶
Status: 200 OK
{
"federations": [
{
"id": "federation_demo_id",
"providerType": "aws_agentcore",
"displayName": "AgentCore Prod",
"description": "Production federation",
"tags": ["prod", "aws"],
"status": "active",
"syncStatus": "success",
"syncMessage": null,
"stats": {
"mcpServerCount": 2,
"agentCount": 1,
"toolCount": 14,
"importedTotal": 3,
"unimportedTotal": 0
},
"lastSync": {
"jobId": "job_demo_id",
"jobType": "full_sync",
"status": "success",
"startedAt": "2026-03-26T07:20:00Z",
"finishedAt": "2026-03-26T07:20:10Z",
"summary": {
"discoveredMcpServers": 2,
"discoveredAgents": 1,
"createdMcpServers": 2,
"updatedMcpServers": 0,
"deletedMcpServers": 0,
"unchangedMcpServers": 0,
"skippedMcpServers": 0,
"createdAgents": 1,
"updatedAgents": 0,
"deletedAgents": 0,
"unchangedAgents": 0,
"skippedAgents": 0,
"vectorSyncFailedMcpServers": 0,
"vectorSyncFailedAgents": 0,
"errors": 0,
"errorMessages": []
}
},
"createdAt": "2026-03-26T07:20:00Z",
"updatedAt": "2026-03-26T07:20:10Z"
}
],
"pagination": {
"total": 1,
"page": 1,
"perPage": 20,
"totalPages": 1
}
}
3. Get Federation Detail¶
GET /federations/{federation_id}
Path Parameters¶
| Param | Type | Required | Description |
|---|---|---|---|
federation_id | string | Yes | Mongo ObjectId |
Success Response¶
Status: 200 OK
Response shape is the same as POST /federations.
Error Responses¶
404 Not Found
4. Update Federation¶
PUT /federations/{federation_id}
Request Body¶
{
"displayName": "AgentCore Prod Updated",
"description": "Updated description",
"tags": ["prod", "aws", "core"],
"providerConfig": {
"region": "us-east-1",
"assumeRoleArn": "arn:aws:iam::123456789012:role/demo"
},
"syncAfterUpdate": true
}
AWS resourceTagsFilter API shape example:
{
"providerConfig": {
"region": "us-east-1",
"assumeRoleArn": "arn:aws:iam::123456789012:role/demo",
"resourceTagsFilter": {
"env": "production",
"team": "platform"
}
}
}
Request Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
displayName | string | Yes | 1-200 chars |
description | string \| null | No | Description |
tags | string[] | No | UI tags |
providerConfig | object | No | Provider-level control-plane config. For aws_agentcore, both region and assumeRoleArn are required. providerConfig.runtimeAccess is not supported and will be rejected. |
syncAfterUpdate | boolean | No | Default false. When true and providerConfig changed, a full sync is triggered after the update. |
Success Response¶
Status: 200 OK
Response shape is the same as POST /federations.
Error Responses¶
400 Bad Request
{
"detail": {
"error": "invalid_request",
"message": "AWS AgentCore federation requires providerConfig.region, providerConfig.assumeRoleArn"
}
}
404 Not Found
409 Conflict
{
"detail": {
"error": "conflict",
"message": "Federation in status 'deleting' cannot be updated"
}
}
501 Not Implemented
{
"detail": {
"error": "not_implemented",
"message": "Azure AI Foundry federation sync is not implemented yet"
}
}
502 Bad Gateway
{
"detail": {
"error": "external_service_error",
"message": "Failed to list AgentCore runtimes in us-east-1: Token has expired and refresh failed"
}
}
5. Sync Federation¶
POST /federations/{federation_id}/sync
Triggers a full sync. Requires the calling user to have EDIT permission on the federation.
Request Body¶
Request Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
dryRun | boolean | No | When true, perform discovery and diff only. No job is created and no data is written. Default false. |
reason | string \| null | No | Manual trigger reason |
For aws_agentcore, sync validates the stored federation config before discovery starts. Both providerConfig.region and providerConfig.assumeRoleArn must be present.
Runtime auth (runtimeAccess) is not read from providerConfig. It is inferred from each runtime's authorizerConfiguration during discovery and stored per resource.
Sync Flow (dryRun=false)¶
1. Validate federation config
2. Discover runtimes from AgentCore control plane
- For each runtime: infer auth mode (IAM or JWT) from authorizerConfiguration
- Enrich each runtime (fetch tools, agent card) using its own auth
3. Apply mutations in a single MongoDB transaction
- New runtimes → INSERT
- Version changed → UPDATE
- Version unchanged → skip MongoDB write (log only)
- Stale runtimes (no longer discovered) → DELETE
- Enrichment failed → record error and do not create/update the resource
4. Rebuild Weaviate search index (outside transaction)
- Changed/deleted/missing runtimes → delete + rebuild
- Unchanged runtimes → check Weaviate version; rebuild only if stale
- Isolate and record failures per runtime
5. Fold vector outcomes into the apply summary
6. Build federation stats and lastSync from the final per-resource outcomes
7. Mark the federation and job:
- `success` when no resources were discovered
- `success` when at least one discovered resource was fully imported
- `failed` when resources were discovered but none were fully imported
Success Response¶
Status: 202 Accepted
For dryRun=false, the endpoint creates a pending job and schedules the sync as a background task. The response is the initial job snapshot; it does not wait for discovery, Mongo apply, vector sync, or terminal status.
{
"id": "job_demo_id",
"federationId": "federation_demo_id",
"jobType": "full_sync",
"status": "pending",
"phase": "queued",
"startedAt": null,
"finishedAt": null,
"error": null
}
Poll the job until it reaches success or failed:
Example terminal response:
{
"id": "job_demo_id",
"federationId": "federation_demo_id",
"jobType": "full_sync",
"status": "success",
"phase": "completed",
"startedAt": "2026-03-26T07:21:00Z",
"finishedAt": "2026-03-26T07:21:05Z",
"error": null
}
The federation detail/list response is the authoritative source for the final stats, syncMessage, and lastSync snapshot.
Partial Success Result¶
When some runtimes fail but at least one runtime is fully imported, both the job and federation are marked success. Failure information is deliberately retained:
- the initial POST still returns
202with a pending job - the terminal job returned by the polling endpoint has
status="success",phase="completed", and a non-nullerror - the federation has
syncStatus="success"and a non-nullsyncMessage lastSync.statusissuccesslastSync.summarycontains failure counters and messages- failed enrichment resources are not persisted or vector-synced
{
"id": "job_demo_id",
"federationId": "federation_demo_id",
"jobType": "full_sync",
"status": "success",
"phase": "completed",
"startedAt": "2026-03-26T07:21:00Z",
"finishedAt": "2026-03-26T07:21:08Z",
"error": "A2A agent pharmacy_fraud_a2a: enrichment failed: 403 Forbidden"
}
The federation detail will contain corresponding per-run stats and lastSync data:
{
"syncStatus": "success",
"syncMessage": "A2A agent pharmacy_fraud_a2a: enrichment failed: 403 Forbidden",
"stats": {
"mcpServerCount": 0,
"agentCount": 3,
"toolCount": 0,
"importedTotal": 2,
"unimportedTotal": 1
},
"lastSync": {
"status": "success",
"summary": {
"discoveredMcpServers": 0,
"discoveredAgents": 3,
"createdMcpServers": 0,
"updatedMcpServers": 0,
"deletedMcpServers": 0,
"unchangedMcpServers": 0,
"skippedMcpServers": 0,
"createdAgents": 2,
"updatedAgents": 0,
"deletedAgents": 0,
"unchangedAgents": 0,
"skippedAgents": 0,
"vectorSyncFailedMcpServers": 0,
"vectorSyncFailedAgents": 0,
"errors": 1,
"errorMessages": [
"A2A agent pharmacy_fraud_a2a: enrichment failed: 403 Forbidden"
]
}
}
}
If discovery returns one or more resources but importedTotal == 0, the job, federation, and lastSync are marked failed. If discovery returns zero resources, the sync is a successful empty run rather than a failure.
Dry-Run Success Response¶
Status: 200 OK
{
"dryRun": true,
"providerType": "aws_agentcore",
"providerConfig": {
"region": "us-east-1",
"assumeRoleArn": "arn:aws:iam::123456789012:role/TestRole"
},
"summary": {
"discoveredMcpServers": 2,
"discoveredAgents": 1,
"createdMcpServers": 0,
"updatedMcpServers": 1,
"deletedMcpServers": 0,
"unchangedMcpServers": 1,
"skippedMcpServers": 0,
"createdAgents": 0,
"updatedAgents": 0,
"deletedAgents": 0,
"unchangedAgents": 1,
"skippedAgents": 0,
"errors": 0,
"errorMessages": []
},
"message": null
}
When dryRun=true: - no FederationSyncJob is created - federation syncStatus, lastSync, stats, and child resources are unchanged - provider discovery and runtime enrichment operate on temporary in-memory resources only - runtime type transitions such as MCP → A2A or A2A → MCP are previewed in the diff only; persisted child resources are not mutated during discovery - vector sync is not executed
Error Responses¶
400 Bad Request
{
"detail": {
"error": "invalid_request",
"message": "AWS AgentCore federation requires providerConfig.region, providerConfig.assumeRoleArn"
}
}
403 Forbidden
404 Not Found
409 Conflict
{
"detail": {
"error": "conflict",
"message": "Federation in sync status 'syncing' cannot start a new sync"
}
}
409 Conflict
{
"detail": {
"error": "conflict",
"message": "Federation in status 'disabled' cannot be synced"
}
}
501 Not Implemented
{
"detail": {
"error": "not_implemented",
"message": "Azure AI Foundry federation sync is not implemented yet"
}
}
502 Bad Gateway
{
"detail": {
"error": "external_service_error",
"message": "Failed to list AgentCore runtimes in us-east-1: Token has expired and refresh failed"
}
}
6. Delete Federation¶
DELETE /federations/{federation_id}
Success Response¶
Status: 200 OK
Error Responses¶
404 Not Found
409 Conflict
{
"detail": {
"error": "conflict",
"message": "Federation in status 'disabled' cannot be deleted"
}
}
7. Manual AgentCore Runtime Sync¶
This is the legacy manual AgentCore runtime sync endpoint, not federation CRUD.
POST /federation/agentcore/runtime/sync
Request Body¶
{
"dryRun": false,
"awsRegion": "us-east-1",
"runtimeArn": "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/demo"
}
Request Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
dryRun | boolean | No | Preview only, default false |
awsRegion | string \| null | No | Optional region override. If omitted, uses the configured default AgentCore region. |
runtimeArn | string \| null | No | Optional single runtime sync. If omitted, scans all AgentCore runtimes in the selected region. |
Success Response¶
Status: 200 OK
{
"runtime_filter_count": 1,
"discovered": {
"mcp_servers": 1,
"a2a_agents": 0,
"skipped_runtimes": 0
},
"created": {
"mcp_servers": 1,
"a2a_agents": 0
},
"updated": {
"mcp_servers": 0,
"a2a_agents": 0
},
"deleted": {
"mcp_servers": 0,
"a2a_agents": 0
},
"skipped": {
"mcp_servers": 0,
"a2a_agents": 0
},
"errors": [],
"mcp_servers": [
{
"action": "created",
"server_name": "demo-runtime",
"server_id": "server_demo_id",
"changes": ["new server"],
"error": null,
"agent_name": null,
"agent_id": null
}
],
"a2a_agents": [],
"skipped_runtimes": [],
"duration_seconds": 0.52
}
Error Responses¶
400 Bad Request
403 Forbidden
500 Internal Server Error
Frontend Notes¶
tagsis used for federation list classification and filtering.providerConfigstores provider-level control-plane configuration only. It does not store runtime auth.assumeRoleArnbelongs to AWS federationproviderConfig. It controls control-plane access for this federation and must not be stored on MCP servers or A2A agents.providerConfig.runtimeAccessis not supported and will be rejected with400. Runtime auth is inferred per discovered resource during sync.- Creating a federation does not trigger a sync job automatically.
- For
aws_agentcore, create may save an incomplete provider config, but update and sync require bothproviderConfig.regionandproviderConfig.assumeRoleArn. - For
aws_agentcore,providerConfig.resourceTagsFilteris applied during sync as an AND filter. A runtime is imported only if all configured tag key:value pairs match its AWS resource tags. - The UI-friendly string form
env:production, team:platformmust be converted by the frontend into{ "env": "production", "team": "platform" }. toolCountis returned in federation stats and can be displayed directly.unimportedTotalis returned by the backend. The current backend-only branch does not yet add the corresponding frontend type or fourth statistics tile; that UI work is a follow-up.POST /federations/{federation_id}/syncwithdryRun=falsereturns202 Acceptedwith the initial pending job. PollGET /federations/{federation_id}/jobs/{job_id}for terminal status, then fetch the federation detail for final stats and lastSync.POST /federations/{federation_id}/syncwithdryRun=trueexecutes inline and returns200 OKwith the preview; it creates no job.unchangedMcpServersandunchangedAgentsin the summary mean the runtime was discovered but its version matched what is already stored — MongoDB was not written. Weaviate consistency is still checked and repaired if stale.- Per-resource enrichment errors (e.g. IAM 403, JWT 401) do not abort sibling resources. Failed resources are not persisted or vector-synced. If at least one resource is fully imported, the overall job remains
successwhilejob.error,syncMessage, andsummary.errorMessagesretain the failures. azure_ai_foundryremains in the provider enum for compatibility but is not yet implemented.