@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.
Files changed (3) hide show
  1. package/README.md +22 -18
  2. package/dist/index.js +15 -20
  3. 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 (`request_access`, `poll_grant`, `release_access`, `describe_scope`, `preview_request`) that mint, poll, and release scoped, TTL-bounded cloud credentials for a single task.
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 the broker grows; the AWS-shaped tools below are the v1 contract and per-cloud namespacing arrives with the second cloud.
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
- | `describe_scope` | Returns the team's resolved policy ceiling for an account — what the agent is allowed to ask for. Free, idempotent. |
14
- | `preview_request` | Dry-run a candidate request: `auto_approve` / `route_to_approver` / `hard_deny`. Writes nothing. |
15
- | `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 to poll. |
16
- | `poll_grant` | Single-shot status check. Back-off ~10s between polls. |
17
- | `release_access` | Voluntarily release a grant before TTL. Idempotent. |
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
- describe_scope({ account_id: "123456789012" })
36
+ aws_describe_scope({ account_id: "123456789012" })
37
37
 
38
38
  // 2. Optional: dry-run.
39
- preview_request({
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
- request_access({
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. Use the AWS CLI / SDK as normal — the runtime resolves the secret_ref to env vars at exec time.
62
- // Bash: aws s3 cp s3://reports/today.csv -
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
- release_access({ grant_id: "..." })
67
+ aws_release_access({ grant_id: "..." })
66
68
  ```
67
69
 
68
- If `request_access` returns `status: "pending"`, a human approver was paged. Call `poll_grant` every ~10s until status leaves `pending`. On `denied`, the `denial_reason` field explains why.
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
- AGT_TEAM_SLUG=demo \
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 `request_access` — only the `secret_ref` pointer reaches the LLM. The runtime adapter resolves the pointer at AWS-SDK call time so credentials never enter the agent's transcript.
85
- - Per PRD §6.3 v1 ships TTL-bounded revocation only. `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.
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.5.1",
21218
- description: "Cloud Access Broker \u2014 MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (request_access, poll_grant, release_access, describe_scope, preview_request \u2014 STS AssumeRole under the hood); GCP, Azure, and Cloudflare land alongside in the same package as the broker grows.",
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 request_access can default it.
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
- "describe_scope",
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 request_access if you're not sure what you're allowed to ask for \u2014 it's free, idempotent, and writes nothing.",
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
- "preview_request",
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
- "request_access",
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. poll_grant exists as an escape hatch for explicit re-checks but the autonomous flow does not need it.',
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
- "poll_grant",
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 `request_access` returning pending more than ~5 minutes ago and have heard nothing). Returns { grant_id, status, secret_ref?, expires_at?, denial_reason? }.",
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
- "release_access",
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
- "get_credentials",
21369
- "Fetch the AWS credentials for an active grant. Call this AFTER 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 release_access when done.",
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.5.1",
4
- "description": "Cloud Access Broker — MCP server that mints scoped, TTL-bounded cloud credentials per agent task. v1 ships AWS support (request_access, poll_grant, release_access, describe_scope, preview_request — STS AssumeRole under the hood); GCP, Azure, and Cloudflare land alongside in the same package as the broker grows.",
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"