Skip to content

5.13-b Write tools, non-destructive (phase 2) #7712

Description

@andypalmi

Parent: #7682 (5.13 Broker and MQTT (special review))
Tool file: new forge/ee/lib/mcp/tools/broker.js

readOnlyHint: false, destructiveHint: false. Contingent on a product decision to allow broker mutation over MCP. None of these mint credentials, so #7513 does not block them. Sibling broker lifecycle transitions are consolidated into a single action tool; topic create/update touch broker state but not credentials.

Tool Endpoint(s) Scope Annotation
platform_broker_lifecycle_action (action: start|stop|suspend) broker lifecycle routes broker:credentials:edit write
platform_create_broker_topic POST /teams/:teamId/brokers/:brokerId/topics broker:topics:write write
platform_update_broker_topic PUT /teams/:teamId/brokers/:brokerId/topics/:topicId broker:topics:write write

Design notes:

  • Only the topic create route (POST /topics) is dual-gated (agent/broker token scope OR user broker:topics:write). The update route (PUT /topics/:topicId) uses a plain needsPermission('broker:topics:write') and is not dual-gated; its path is a single :topicId param, not a wildcard. The handler only updates metadata.
  • Broker lifecycle (start/stop/suspend) maps to PUT /brokers/:brokerId/start|stop|suspend; it touches broker state but does not mint credentials, so it is not in the Scoped PATs - Block Broker Credential Issuance for PAT-Authenticated Requests #7513 class; it remains contingent on the broker-mutation decision.
  • The whole surface is gated by the enterprise license tier and the per-team teamBroker feature flag.

Scopes to allow-list + access-tag (#7445, only if writes approved):
broker:topics:write (write), broker:credentials:edit (write).

Tool definitions (description + zod inputSchema):

platform_broker_lifecycle_action: {
  description: 'Applies a lifecycle transition (start, stop, or suspend) to the broker agent.',
  inputSchema: z.object({
    teamId: z.string().describe('team hashid'),
    brokerId: z.string().describe("broker id: either the literal 'team-broker' or a 3rd-party broker hashid"),
    action: z.enum(['start', 'stop', 'suspend']).describe('lifecycle transition to apply to the broker agent')
  })
},
platform_create_broker_topic: {
  description: 'Creates one or more topics for a broker. Accepts a single topic object instead of an array.',
  inputSchema: z.object({
    teamId: z.string().describe('team hashid'),
    brokerId: z.string().describe("broker id: either the literal 'team-broker' or a 3rd-party broker hashid"),
    topics: z.array(z.object({
      topic: z.string().describe('MQTT topic string'),
      type: z.any().optional().describe('inferred payload schema/type; stored as inferredSchema'),
      metadata: z.record(z.any()).optional().describe('arbitrary topic metadata (e.g. description, schema)')
    })).describe('Topics to store')
  })
},
platform_update_broker_topic: {
  description: 'Updates a broker topic. Only the topic metadata is updated.',
  inputSchema: z.object({
    teamId: z.string().describe('team hashid'),
    brokerId: z.string().describe("broker id: either the literal 'team-broker' or a 3rd-party broker hashid"),
    topicId: z.string().describe('topic hashid'),
    metadata: z.record(z.any()).describe('replacement topic metadata')
  })
}

Tests (only if writes approved):

  • The action tool routes each enum value to the correct lifecycle route.
  • Read-only PAT rejected for every tool here.
  • Feature-disabled team returns the descriptive gate error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    storyA user-oriented description of a feature

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions