Skip to content

5.12-b Write tools, non-destructive (phase 2) #7709

Description

@andypalmi

Parent: #7681 (5.12 Team data and packages)
Tool file: new forge/ee/lib/mcp/tools/teamData.js

readOnlyHint: false, destructiveHint: false. One tool per endpoint; these siblings do not share a single action concept, so they are not consolidated.

Tool Endpoint Scope Annotation
platform_create_database_table POST /teams/:teamId/databases/:databaseId/tables team:database:create write
platform_create_library_entry POST /storage/library/:libraryId/* library:entry:create write
platform_update_git_token PUT /teams/:teamId/git/tokens/:tokenId team:git:tokens:edit write

Design notes:

  • Plan-gated (enabled per team type/plan, not per user role): tables, npm, gitIntegration, shared-library. If the team's plan does not include the feature the route returns a 404 (Not Found - not available on team), independent of the PAT's permissions. The tool should surface this as a clear "feature not enabled for this team" message rather than passing through a bare "not found", which an agent would misread as a missing resource.
  • update_git_token edits an existing token record; it does not mint or return a new credential (the minting POST is excluded, see 5.12-d).

Tool definitions (description + zod inputSchema):

platform_create_database_table: {
  description: 'Creates a new table in a database. Both name and columns are required to create the table.',
  inputSchema: z.object({
    teamId: z.string().describe('team hashid'),
    databaseId: z.string().describe('database hashid'),
    name: z.string().describe('name for the new table'),
    columns: z.array(z.object({
      name: z.string().describe('column name'),
      type: z.string().describe('column data type (free-form string; the API declares no enum)'),
      nullable: z.boolean().optional().describe('whether the column allows NULL'),
      default: z.string().nullable().optional().describe('default value, or null for none'),
      generated: z.boolean().optional().describe('whether the column value is generated'),
      maxLength: z.number().nullable().optional().describe('maximum length, or null for unbounded')
    })).describe('column definitions for the new table')
  })
},
platform_create_library_entry: {
  description: 'Creates an entry in the team shared library. type is required and a missing type is rejected with a 400 error. When body is an object it is JSON-stringified before storage.',
  inputSchema: z.object({
    libraryId: z.string().describe('shared-library hashid (the team hashid)'),
    path: z.string().describe('Library entry path'),
    type: z.string().describe('entry type (e.g. flows, functions)'),
    body: z.any().describe('entry contents'),
    meta: z.record(z.any()).optional().describe('arbitrary metadata stored with the entry')
  })
},
platform_update_git_token: {
  description: 'Updates an existing git token. name is the only editable field: the update only changes the token name and never mints or returns a credential.',
  inputSchema: z.object({
    teamId: z.string().describe('team hashid'),
    tokenId: z.string().describe('git token hashid'),
    name: z.string().describe('new name for the git token')
  })
}

Tests:

  • Read-only PAT rejected for every tool here.
  • Feature-disabled team returns the descriptive gate error.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

storyA user-oriented description of a feature

Type

No type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions