@integrity-labs/cloud-broker 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -18
- package/dist/index.js +15 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# `@integrity-labs/cloud-broker`
|
|
2
2
|
|
|
3
|
-
MCP server for the Augmented ephemeral cloud-access broker. Exposes the agent-facing tools (`
|
|
3
|
+
MCP server for the Augmented ephemeral cloud-access broker. Exposes the agent-facing tools (`aws_request_access`, `aws_poll_grant`, `aws_release_access`, `aws_describe_scope`, `aws_preview_request`, `aws_get_credentials`) that mint, poll, and release scoped, TTL-bounded cloud credentials for a single task.
|
|
4
4
|
|
|
5
|
-
**v1 ships AWS support** (STS AssumeRole under the hood; pair with the `aws-cli` toolkit or any AWS SDK as the consumer). GCP, Azure, and Cloudflare land in this same package as
|
|
5
|
+
**v1 ships AWS support** (STS AssumeRole under the hood; pair with the `aws-cli` toolkit or any AWS SDK as the consumer). All tools are namespaced `aws_*` so GCP, Azure, and Cloudflare can land in this same package as `gcp_*` / `azure_*` / `cf_*` siblings without colliding (ENG-4782).
|
|
6
6
|
|
|
7
7
|
See the [PRD](../../docs/prds/aws-ephemeral-access.md) and the [toolkit doc](../../docs/toolkits/aws.md) for the full design.
|
|
8
8
|
|
|
@@ -10,18 +10,18 @@ See the [PRD](../../docs/prds/aws-ephemeral-access.md) and the [toolkit doc](../
|
|
|
10
10
|
|
|
11
11
|
| Tool | Purpose |
|
|
12
12
|
|---|---|
|
|
13
|
-
| `
|
|
14
|
-
| `
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
13
|
+
| `aws_describe_scope` | Returns the team's resolved policy ceiling for an account — what the agent is allowed to ask for. Free, idempotent. |
|
|
14
|
+
| `aws_preview_request` | Dry-run a candidate request: `auto_approve` / `route_to_approver` / `hard_deny`. Writes nothing. |
|
|
15
|
+
| `aws_request_access` | Mint or queue a grant. On `auto_approve` you get `secret_ref`; on `route_to_approver` you get `pending` and a `grant_id`. |
|
|
16
|
+
| `aws_poll_grant` | Single-shot status check. Escape hatch — the broker pushes resolution via direct-chat. |
|
|
17
|
+
| `aws_get_credentials` | Fetch the AWS_* values for an active grant. Call after `aws_request_access` returns active or after the resolution-notification arrives. |
|
|
18
|
+
| `aws_release_access` | Voluntarily release a grant before TTL. Idempotent. |
|
|
18
19
|
|
|
19
20
|
## Environment
|
|
20
21
|
|
|
21
22
|
| Var | Purpose |
|
|
22
23
|
|---|---|
|
|
23
24
|
| `AGT_HOST` | Augmented API base URL (e.g. `https://api.augmented.example`). |
|
|
24
|
-
| `AGT_TEAM_SLUG` | Team slug for the `X-Team-Slug` header. |
|
|
25
25
|
| `AGT_TOKEN` | Pre-provisioned JWT (preferred). If set, used until expiry. |
|
|
26
26
|
| `AGT_API_KEY` | Host API key (`tlk_…`). Used to refresh the JWT via `/host/exchange` when `AGT_TOKEN` is missing or expired. |
|
|
27
27
|
|
|
@@ -33,10 +33,10 @@ An agent that needs to read one S3 object:
|
|
|
33
33
|
|
|
34
34
|
```jsonc
|
|
35
35
|
// 1. Optional: inspect the envelope before guessing.
|
|
36
|
-
|
|
36
|
+
aws_describe_scope({ account_id: "123456789012" })
|
|
37
37
|
|
|
38
38
|
// 2. Optional: dry-run.
|
|
39
|
-
|
|
39
|
+
aws_preview_request({
|
|
40
40
|
account_id: "123456789012",
|
|
41
41
|
actions: ["s3:GetObject", "s3:ListBucket"],
|
|
42
42
|
resources: ["arn:aws:s3:::reports/*", "arn:aws:s3:::reports"],
|
|
@@ -46,7 +46,7 @@ preview_request({
|
|
|
46
46
|
// → { "would": "auto_approve", "reason": null }
|
|
47
47
|
|
|
48
48
|
// 3. Mint.
|
|
49
|
-
|
|
49
|
+
aws_request_access({
|
|
50
50
|
agent_id: "11111111-1111-4111-8111-111111111111",
|
|
51
51
|
run_id: "22222222-2222-4222-8222-222222222222",
|
|
52
52
|
account_id: "123456789012",
|
|
@@ -58,21 +58,24 @@ request_access({
|
|
|
58
58
|
})
|
|
59
59
|
// → { "grant_id": "...", "status": "active", "secret_ref": "secret_ref://aws/runs/<run_id>/<grant_id>", "expires_at": "..." }
|
|
60
60
|
|
|
61
|
-
// 4.
|
|
62
|
-
|
|
61
|
+
// 4. Fetch the AWS_* values and use them.
|
|
62
|
+
aws_get_credentials({ grant_id: "..." })
|
|
63
|
+
// → { "grant_id": "...", "expires_at": "...", "credentials": { "access_key_id": "...", "secret_access_key": "...", "session_token": "..." } }
|
|
64
|
+
// Bash: AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... aws s3 cp s3://reports/today.csv -
|
|
63
65
|
|
|
64
66
|
// 5. (Optional) release early.
|
|
65
|
-
|
|
67
|
+
aws_release_access({ grant_id: "..." })
|
|
66
68
|
```
|
|
67
69
|
|
|
68
|
-
If `
|
|
70
|
+
If `aws_request_access` returns `status: "pending"`, a human approver was paged. The broker pushes the resolution to you via direct-chat — save the `grant_id`, return control, and resume when the inbound message arrives. On `denied`, the `denial_reason` field explains why. `aws_poll_grant` is an escape hatch for explicit re-checks.
|
|
69
71
|
|
|
70
72
|
## Running locally
|
|
71
73
|
|
|
72
74
|
```bash
|
|
73
75
|
pnpm --filter @integrity-labs/cloud-broker build
|
|
74
76
|
AGT_HOST=http://api.agt.localhost:1355 \
|
|
75
|
-
|
|
77
|
+
AGT_AGENT_ID=<your-agent-uuid> \
|
|
78
|
+
AGT_RUN_ID=<your-run-uuid> \
|
|
76
79
|
AGT_TOKEN=$YOUR_JWT \
|
|
77
80
|
node packages/cloud-broker/dist/index.js
|
|
78
81
|
```
|
|
@@ -81,5 +84,6 @@ The server speaks MCP over stdio. Wire it into your runtime adapter's `.mcp.json
|
|
|
81
84
|
|
|
82
85
|
## Notes
|
|
83
86
|
|
|
84
|
-
- The MCP response strips inline credentials from `
|
|
85
|
-
- Per PRD §6.3 v1 ships TTL-bounded revocation only. `
|
|
87
|
+
- The MCP response strips inline credentials from `aws_request_access` — only the `secret_ref` pointer reaches the LLM. The runtime adapter resolves the pointer at AWS-SDK call time (or via `aws_get_credentials`) so credentials never enter the agent's transcript.
|
|
88
|
+
- Per PRD §6.3 v1 ships TTL-bounded revocation only. `aws_release_access` marks the grant `revoked` in the broker DB and tears down the `secret_ref`, but in-flight STS sessions remain valid in AWS until their TTL. v1.1 closes that gap with the `aws:TokenIssueTime` hard-revoke pattern.
|
|
89
|
+
- **0.6.0 hard-renamed the tools to the `aws_*` namespace (ENG-4782).** Agents on cloud-broker ≤ 0.5.0 will see "tool not found" errors from the broker until they're re-provisioned with the new tool names. Re-run `agt agent provision` for any agent that has the cloud-broker MCP wired in.
|
package/dist/index.js
CHANGED
|
@@ -21214,8 +21214,8 @@ var getCredentialsShape = getCredentialsSchema.shape;
|
|
|
21214
21214
|
// package.json
|
|
21215
21215
|
var package_default = {
|
|
21216
21216
|
name: "@integrity-labs/cloud-broker",
|
|
21217
|
-
version: "0.
|
|
21218
|
-
description: "Cloud Access Broker \u2014 MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (
|
|
21217
|
+
version: "0.6.0",
|
|
21218
|
+
description: "Cloud Access Broker \u2014 MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (aws_request_access, aws_poll_grant, aws_release_access, aws_describe_scope, aws_preview_request, aws_get_credentials \u2014 STS AssumeRole under the hood); GCP, Azure, and Cloudflare land alongside in the same package as the broker grows.",
|
|
21219
21219
|
type: "module",
|
|
21220
21220
|
bin: {
|
|
21221
21221
|
"cloud-broker": "./dist/index.js"
|
|
@@ -21261,17 +21261,12 @@ var AGT_AGENT_ID = process.env.AGT_AGENT_ID;
|
|
|
21261
21261
|
var AGT_RUN_ID = process.env.AGT_RUN_ID ?? "";
|
|
21262
21262
|
var AGT_TOKEN = process.env.AGT_TOKEN ?? "";
|
|
21263
21263
|
var AGT_API_KEY = process.env.AGT_API_KEY ?? "";
|
|
21264
|
-
if (!AGT_HOST || !AGT_AGENT_ID || !AGT_TOKEN && !AGT_API_KEY) {
|
|
21264
|
+
if (!AGT_HOST || !AGT_AGENT_ID || !AGT_RUN_ID || !AGT_TOKEN && !AGT_API_KEY) {
|
|
21265
21265
|
console.error(
|
|
21266
|
-
"cloud-broker: missing env vars. Need AGT_HOST, AGT_AGENT_ID, and one of AGT_TOKEN or AGT_API_KEY."
|
|
21266
|
+
"cloud-broker: missing env vars. Need AGT_HOST, AGT_AGENT_ID, AGT_RUN_ID, and one of AGT_TOKEN or AGT_API_KEY."
|
|
21267
21267
|
);
|
|
21268
21268
|
process.exit(1);
|
|
21269
21269
|
}
|
|
21270
|
-
if (!AGT_RUN_ID) {
|
|
21271
|
-
console.error(
|
|
21272
|
-
"cloud-broker: AGT_RUN_ID is not set. request_access will require run_id in the args; describe_scope / get_credentials / release_access still work without it."
|
|
21273
|
-
);
|
|
21274
|
-
}
|
|
21275
21270
|
if (process.env.AGT_TEAM_SLUG) {
|
|
21276
21271
|
console.error(
|
|
21277
21272
|
"cloud-broker: AGT_TEAM_SLUG is set but no longer required (the API derives team from agent_id). Safe to remove from .mcp.json env block."
|
|
@@ -21280,7 +21275,7 @@ if (process.env.AGT_TEAM_SLUG) {
|
|
|
21280
21275
|
var broker = new BrokerClient({
|
|
21281
21276
|
host: AGT_HOST,
|
|
21282
21277
|
agentId: AGT_AGENT_ID,
|
|
21283
|
-
// ENG-4746: thread AGT_RUN_ID through so
|
|
21278
|
+
// ENG-4746: thread AGT_RUN_ID through so aws_request_access can default it.
|
|
21284
21279
|
// Empty string is treated as absent — the broker client only fills runId
|
|
21285
21280
|
// on requestAccess if a non-empty value is provided.
|
|
21286
21281
|
runId: AGT_RUN_ID || void 0,
|
|
@@ -21299,8 +21294,8 @@ var server = new McpServer({
|
|
|
21299
21294
|
version: package_default.version
|
|
21300
21295
|
});
|
|
21301
21296
|
server.tool(
|
|
21302
|
-
"
|
|
21303
|
-
"Return the team's resolved AWS policy ceiling for an account: max_ttl_seconds, allowed_regions, auto_approved_actions, and the action denylist. Call this before
|
|
21297
|
+
"aws_describe_scope",
|
|
21298
|
+
"Return the team's resolved AWS policy ceiling for an account: max_ttl_seconds, allowed_regions, auto_approved_actions, and the action denylist. Call this before aws_request_access if you're not sure what you're allowed to ask for \u2014 it's free, idempotent, and writes nothing.",
|
|
21304
21299
|
describeScopeShape,
|
|
21305
21300
|
async (args) => {
|
|
21306
21301
|
try {
|
|
@@ -21312,7 +21307,7 @@ server.tool(
|
|
|
21312
21307
|
}
|
|
21313
21308
|
);
|
|
21314
21309
|
server.tool(
|
|
21315
|
-
"
|
|
21310
|
+
"aws_preview_request",
|
|
21316
21311
|
'Dry-run a candidate request. Returns one of "auto_approve", "route_to_approver", or "hard_deny" without dispatching anything or writing audit_log. Useful for letting the agent self-tighten its scope before triggering a human approval.',
|
|
21317
21312
|
previewRequestShape,
|
|
21318
21313
|
async (args) => {
|
|
@@ -21325,8 +21320,8 @@ server.tool(
|
|
|
21325
21320
|
}
|
|
21326
21321
|
);
|
|
21327
21322
|
server.tool(
|
|
21328
|
-
"
|
|
21329
|
-
'Request scoped, TTL-bounded AWS credentials for the current task. agent_id and run_id are optional \u2014 the broker fills them from the host MCP env (AGT_AGENT_ID / AGT_RUN_ID). source_context is optional but you SHOULD pass it whenever the request was triggered by an inbound channel message: extract { channel_type, channel_id, thread_ts? } from the `<channel>` tag in the conversation that triggered this. Slack: channel_type="slack", channel_id=tag\'s `channel`, thread_ts=tag\'s `thread_ts`. Telegram: channel_type="telegram", channel_id=tag\'s `chat_id`. Direct-chat: channel_type="direct-chat", channel_id=tag\'s `session_id`. Without source_context the resolution notification dead-ends in your direct-chat instead of threading back to the original conversation. Returns { grant_id, status, secret_ref?, expires_at?, denial_reason?, notification_status?, notification_failure_reason? }. status="active" means credentials are ready, use them now. status="denied" means the request was rejected (denial_reason explains). status="pending" means approval is still outstanding \u2014 DO NOT poll. The broker pushes the resolution to you via direct-chat the moment a human approves or denies, so save the grant_id, return control, and resume when the inbound message arrives. The notification body will include an "Original conversation:" line naming the channel/thread to reply in \u2014 complete the user\'s task there, not in direct-chat. Only check notification_status if you need to flag a setup issue to the user: "sent" means a human was paged; "failed" or "not_attempted" means no human was paged (typically channel_not_found because the approval-bot is not a member of the configured channel) \u2014 quote grant_id + notification_failure_reason and recommend manual escalation.
|
|
21323
|
+
"aws_request_access",
|
|
21324
|
+
'Request scoped, TTL-bounded AWS credentials for the current task. agent_id and run_id are optional \u2014 the broker fills them from the host MCP env (AGT_AGENT_ID / AGT_RUN_ID). source_context is optional but you SHOULD pass it whenever the request was triggered by an inbound channel message: extract { channel_type, channel_id, thread_ts? } from the `<channel>` tag in the conversation that triggered this. Slack: channel_type="slack", channel_id=tag\'s `channel`, thread_ts=tag\'s `thread_ts`. Telegram: channel_type="telegram", channel_id=tag\'s `chat_id`. Direct-chat: channel_type="direct-chat", channel_id=tag\'s `session_id`. Without source_context the resolution notification dead-ends in your direct-chat instead of threading back to the original conversation. Returns { grant_id, status, secret_ref?, expires_at?, denial_reason?, notification_status?, notification_failure_reason? }. status="active" means credentials are ready, use them now. status="denied" means the request was rejected (denial_reason explains). status="pending" means approval is still outstanding \u2014 DO NOT poll. The broker pushes the resolution to you via direct-chat the moment a human approves or denies, so save the grant_id, return control, and resume when the inbound message arrives. The notification body will include an "Original conversation:" line naming the channel/thread to reply in \u2014 complete the user\'s task there, not in direct-chat. Only check notification_status if you need to flag a setup issue to the user: "sent" means a human was paged; "failed" or "not_attempted" means no human was paged (typically channel_not_found because the approval-bot is not a member of the configured channel) \u2014 quote grant_id + notification_failure_reason and recommend manual escalation. aws_poll_grant exists as an escape hatch for explicit re-checks but the autonomous flow does not need it.',
|
|
21330
21325
|
requestAccessShape,
|
|
21331
21326
|
async (args) => {
|
|
21332
21327
|
try {
|
|
@@ -21339,8 +21334,8 @@ server.tool(
|
|
|
21339
21334
|
}
|
|
21340
21335
|
);
|
|
21341
21336
|
server.tool(
|
|
21342
|
-
"
|
|
21343
|
-
"Single-shot status check for a grant. The broker pushes resolution updates to you via direct-chat automatically \u2014 you do NOT need to poll in the normal flow. Use this only as an escape hatch: explicit re-check after a notification, or if you suspect a notification was lost (e.g. you got `
|
|
21337
|
+
"aws_poll_grant",
|
|
21338
|
+
"Single-shot status check for a grant. The broker pushes resolution updates to you via direct-chat automatically \u2014 you do NOT need to poll in the normal flow. Use this only as an escape hatch: explicit re-check after a notification, or if you suspect a notification was lost (e.g. you got `aws_request_access` returning pending more than ~5 minutes ago and have heard nothing). Returns { grant_id, status, secret_ref?, expires_at?, denial_reason? }.",
|
|
21344
21339
|
pollGrantShape,
|
|
21345
21340
|
async (args) => {
|
|
21346
21341
|
try {
|
|
@@ -21352,7 +21347,7 @@ server.tool(
|
|
|
21352
21347
|
}
|
|
21353
21348
|
);
|
|
21354
21349
|
server.tool(
|
|
21355
|
-
"
|
|
21350
|
+
"aws_release_access",
|
|
21356
21351
|
"Voluntarily release a grant before its TTL expires. Idempotent \u2014 safe to call on already-revoked or already-expired grants. Returns { grant_id, status }.",
|
|
21357
21352
|
releaseAccessShape,
|
|
21358
21353
|
async (args) => {
|
|
@@ -21365,8 +21360,8 @@ server.tool(
|
|
|
21365
21360
|
}
|
|
21366
21361
|
);
|
|
21367
21362
|
server.tool(
|
|
21368
|
-
"
|
|
21369
|
-
"Fetch the AWS credentials for an active grant. Call this AFTER
|
|
21363
|
+
"aws_get_credentials",
|
|
21364
|
+
"Fetch the AWS credentials for an active grant. Call this AFTER aws_request_access returns active (auto-approve) OR after the resolution-notification arrives (route_to_approver). Returns { grant_id, expires_at, credentials: { access_key_id, secret_access_key, session_token } }. Use them by prefixing your bash invocation, e.g. `AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_SESSION_TOKEN=... aws ec2 describe-instances`. The grant must still be active and unexpired \u2014 409 if already released, 410 if past expires_at. Safe to call multiple times within the TTL window; the credentials don't change. Call aws_release_access when done.",
|
|
21370
21365
|
getCredentialsShape,
|
|
21371
21366
|
async (args) => {
|
|
21372
21367
|
try {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@integrity-labs/cloud-broker",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Cloud Access Broker — MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Cloud Access Broker — MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (aws_request_access, aws_poll_grant, aws_release_access, aws_describe_scope, aws_preview_request, aws_get_credentials — STS AssumeRole under the hood); GCP, Azure, and Cloudflare land alongside in the same package as the broker grows.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cloud-broker": "./dist/index.js"
|