Skip to main content
Glama

Update a project

update_project

Update a project's name, privacy, rewards or settings. Only the fields you pass change. Passing script replaces the whole script atomically and requires an inactive project. To open later, set delivery.opening to {mode: "scheduled", time} on an inactive project and do NOT call open_project: the project opens itself at time and posts its invite then, so confirm with the human as you would before open_project. delivery.closing the same way closes it at time.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "project_id": "<project_id>"
    },
    "name": "update_project"
  }
}

Input Schema

NameRequiredDescriptionDefault
nameNo
scriptNo
rewardsNo
deliveryNo
settingsNo
project_idYesProject id, from list_projects or create_project.
presentationNo
privacy_modeNo
scoring_enabledNo
max_completes_per_userNo

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • addedInput schema / $defs / BlockSchema / properties / upload
      Added value: +{
      +  "anyOf": [
      +    {
      +      "$ref": "#/$defs/UploadSchema"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null
      +}
    • addedInput schema / $defs / UploadSchema
      Added value: +{
      +  "description": "A `file_upload` block's rules. Every field is optional: images and documents,\none file, required, up to the community's plan limit.",
      +  "properties": {
      +    "categories": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "title": "Categories"
      +    },
      +    "max_bytes": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "title": "Max Bytes"
      +    },
      +    "max_files": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "title": "Max Files"
      +    },
      +    "min_files": {
      +      "anyOf": [
      +        {
      +          "type": "integer"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "title": "Min Files"
      +    }
      +  },
      +  "title": "UploadSchema",
      +  "type": "object"
      +}
  2. Changed29 schema fields changed
    • addedInput schema / $defs / AudienceRequest / description
      Added value: +"Deprecated: use `delivery.entry_points`. Who may answer the project without an\ninvitation, and where they answer.\n\nStill read and written. It cannot describe a project whose Discord post answers in\nDiscord while its web link is on (it reads back as `private` + `discord`); sending\nback what you read leaves such a project as it is. Never send it together with\n`entry_points`."
    • addedInput schema / $defs / AudienceRequest / properties / participation / description
      Added value: +"`private`: with `response_channel` `discord`, members of the community's Discord server answer from the Discord post. With `response_channel` `web` the project also gets a web link (`delivery.share_url`) that asks respondents to sign in: with Discord, or with one of `verified_source_ids` when set (only those check membership). `open_web`: public web link, anyone who has it can answer. An `open_web` link identifies only respondents already signed in to Subo with Discord, members of the server or not: they can be rewarded (XP, roles, achievements) when rewards are on, and everyone else answers unidentified and cannot be. Set `settings.privacy_mode` to `anonymous` to identify nobody; privacy applies to the whole project, the Discord post included."
    • addedInput schema / $defs / AudienceRequest / properties / required_role_ids / description
      Added value: +"Discord role snowflakes; a member needs at least one of them (any-of). Empty means no role is required. Checked wherever Subo checks someone is in the Discord server: pressing the Discord post, and signing in with Discord on the web link. Not on an `open_web` link, and not for Twitch or YouTube sources. Invitations from journeys and campaigns skip this check."
    • addedInput schema / $defs / AudienceRequest / properties / response_channel / description
      Added value: +"Where a member who presses the Discord post answers. `discord`: in Discord (a thread or a DM, in the order `answer_surfaces` sets), and the project has no web link. `web`: the post hands each member who presses it a personal link, so they answer on the web as themselves; the project also gets a web link. Ignored with `open_web`, which always answers on the web."
    • addedInput schema / $defs / AudienceRequest / properties / verified_source_ids / description
      Added value: +"Audience source ids (from GET /communities/{communityId}/sources) a respondent may verify with on the web link, any-of: a Discord source admits members of that server, a Twitch source its followers, a YouTube source its subscribers. Requires participation `private` and response_channel `web`. Omit to keep the current sources; without any, the link asks for a Discord sign-in."
    • addedInput schema / $defs / ClosingRequest / properties / mode / description
      Added value: +"`manual`: the project closes when you close it. `scheduled`: it closes itself at `time`."
    • addedInput schema / $defs / ClosingRequest / properties / time / description
      Added value: +"Required when mode is `scheduled`. The project closes itself at this time."
    • addedInput schema / $defs / DeliveryRequest / properties / answer_surfaces / description
      Added value: +"Ordered fallback chain of surfaces a Convo is attempted on, first to last. `null` means no override: the project follows Subo's default rules, which are resolved at press time and can differ between two posts of the same project in different channels. Send `null` to clear an override; an empty array is rejected."
    • addedInput schema / $defs / DeliveryRequest / properties / audience / description
      Added value: +"Deprecated, use `entry_points`. Who may answer without an invitation, and where they answer. Never send it together with `entry_points`."
    • addedInput schema / $defs / DeliveryRequest / properties / clear_conversation / description
      Added value: +"Purge the respondent's thread transcript once they COMPLETE the Convo. Never fires on a timeout: a timed-out Convo is resumable and the respondent returns to it. Defaults to false. Only honored on a thread: Subo can bulk-delete in a thread but only its own messages in a DM, so a project with this on AND a `dm` step in `answer_surfaces` fails script validation and will not open. A web Convo carries no durable transcript, so the flag is inert there."
    • addedInput schema / $defs / DeliveryRequest / properties / closing / description
      Added value: +"When the project closes."
    • addedInput schema / $defs / DeliveryRequest / properties / entry_points
      Added value: +{
      +  "anyOf": [
      +    {
      +      "items": {
      +        "$ref": "#/$defs/EntryPointRequest"
      +      },
      +      "type": "array"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "The ways into the project, each with its own rule: the Discord post and the web link, on independently. Send only the ones you change. A member invited by a journey or a campaign is admitted by that invitation, whatever these say; GET project lists those as read-only `invitation` entries.",
      +  "title": "Entry Points"
      +}
    • addedInput schema / $defs / DeliveryRequest / properties / invitation / description
      Added value: +"The Discord post: where it goes and how its embed looks."
    • addedInput schema / $defs / DeliveryRequest / properties / opening / description
      Added value: +"When the project opens."
    • addedInput schema / $defs / EmbedConfigRequest / properties / info_display / description
      Added value: +"`embed`: project info shows inside the invitation embed; `button`: behind a button."
    • addedInput schema / $defs / EmbedConfigRequest / properties / rows / description
      Added value: +"Per-row visibility toggles for the invitation embed (open map, e.g. status/privacyMode/roles/xpReward)"
    • addedInput schema / $defs / EntryPointRequest
      Added value: +{
      +  "description": "One way into the project. Send only the entry points you change; each is matched\non `type`, and an omitted field keeps its value.",
      +  "properties": {
      +    "answers_on": {
      +      "anyOf": [
      +        {
      +          "enum": [
      +            "discord",
      +            "web"
      +          ],
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`discord_post` only. Where a member who presses the post answers. `discord`: in Discord (thread or DM, in the order `answer_surfaces` sets). `web`: the post hands that member a personal link and they answer on the web as themselves, whatever the web link says. Polls answer in Discord only.",
      +      "title": "Answers On"
      +    },
      +    "enabled": {
      +      "anyOf": [
      +        {
      +          "type": "boolean"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`discord_post`: on when the project has a post channel. `false` clears `delivery.invitation.post_channel_id`; `true` needs that channel, already set or sent in the same request. `web_link`: turns the link on or off. With the link off, members who press the Discord post still answer on the web when its `answers_on` is `web`.",
      +      "title": "Enabled"
      +    },
      +    "required_role_ids": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`discord_post` only, but it applies to every Discord check: a member needs at least one of these role snowflakes (any-of) to start from the post, and to sign in with Discord on a `verified` web link. Empty means no role is required. Not checked on an `anyone` link, nor for Twitch or YouTube sources.",
      +      "title": "Required Role Ids"
      +    },
      +    "sign_in_with": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "enum": [
      +              "discord",
      +              "twitch",
      +              "google",
      +              "steam"
      +            ],
      +            "type": "string"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`web_link` only, with `who` `signed_in`: the platforms a respondent may sign in with (any Subo supports; nothing to connect first). `google` is a Google account, not a YouTube channel; `steam` only once Subo's Steam key is set. Omit to keep the current list; a new `signed_in` link defaults to `discord`. Rewards (XP, roles, achievements) reach only respondents who sign in with Discord.",
      +      "title": "Sign In With"
      +    },
      +    "type": {
      +      "description": "`discord_post`: the invitation posted in a Discord channel, answered by members of the community's server. `web_link`: the project's link (`url` on read), which works anywhere. `invitation` entries (journeys and campaigns) are read-only.",
      +      "enum": [
      +        "discord_post",
      +        "web_link"
      +      ],
      +      "title": "Type",
      +      "type": "string"
      +    },
      +    "verified_source_ids": {
      +      "anyOf": [
      +        {
      +          "items": {
      +            "type": "integer"
      +          },
      +          "type": "array"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`web_link` only, implies `who` `verified`. Audience source ids (from GET /communities/{communityId}/sources): a Discord source admits members of that server, a Twitch source its followers, a YouTube source its subscribers. Omit to keep the current sources.",
      +      "title": "Verified Source Ids"
      +    },
      +    "who": {
      +      "anyOf": [
      +        {
      +          "enum": [
      +            "anyone",
      +            "signed_in",
      +            "verified"
      +          ],
      +          "type": "string"
      +        },
      +        {
      +          "type": "null"
      +        }
      +      ],
      +      "default": null,
      +      "description": "`web_link` only. `anyone`: anyone who has the link answers; only respondents already signed in to Subo with Discord are identified (members of the server or not) and can be rewarded, everyone else answers unidentified. Set `settings.privacy_mode` to `anonymous` to identify nobody. `signed_in`: a respondent signs in with one of `sign_in_with` and is named; nothing about them is checked (no server, no roles). `verified`: a respondent signs in and must verify with one of `verified_source_ids` (any-of); a Discord source is checked live, with the post's `required_role_ids`. Turning the link on without `who` uses `verified` with the community's Discord server.",
      +      "title": "Who"
      +    }
      +  },
      +  "required": [
      +    "type"
      +  ],
      +  "title": "EntryPointRequest",
      +  "type": "object"
      +}
    • addedInput schema / $defs / InvitationRequest / properties / answer_button_color / description
      Added value: +"Discord button style for the answer button (1 primary, 2 secondary, 3 success, 4 danger). Premium only."
    • addedInput schema / $defs / InvitationRequest / properties / answer_button_label / description
      Added value: +"Custom label for the answer/vote button. Premium only."
    • addedInput schema / $defs / InvitationRequest / properties / border_color / description
      Added value: +"6-digit hex color without '#' for the embed border when the project is open (e.g. `e1287e`). A leading '#' is accepted and removed; anything else is a 400."
    • addedInput schema / $defs / InvitationRequest / properties / border_color_closed / description
      Added value: +"6-digit hex color without '#' for the embed border when the project is closed (e.g. `745399`). A leading '#' is accepted and removed. Premium only."
    • addedInput schema / $defs / InvitationRequest / properties / embed_config / description
      Added value: +"Invitation embed layout: whether project info shows inline (`embed`) or behind a button, plus per-row visibility toggles"
    • addedInput schema / $defs / InvitationRequest / properties / footer / description
      Added value: +"Text shown below the invitation embed. Premium only."
    • addedInput schema / $defs / InvitationRequest / properties / image_url / description
      Added value: +"Image URL shown in the invitation embed"
    • addedInput schema / $defs / InvitationRequest / properties / message / description
      Added value: +"Custom invitation message (Markdown)"
    • addedInput schema / $defs / InvitationRequest / properties / post_channel_id / description
      Added value: +"Discord channel snowflake where the invitation embed is posted. For a project answered on the web (`open_web`, or `private` with `response_channel` `web`) it is the only place the invitation is posted: `null` or an empty string means post nowhere, and respondents come in through `delivery.share_url`. A project answered in Discord needs it to open. On update and open: omit to leave it, send `null` or an empty string to clear it."
    • addedInput schema / $defs / InvitationRequest / properties / thumbnail_url / description
      Added value: +"Thumbnail image URL shown in the invitation embed"
    • addedInput schema / $defs / OpeningRequest / properties / mode / description
      Added value: +"`manual`: the project opens when you open it. `scheduled`: it opens itself at `time`."
    • addedInput schema / $defs / OpeningRequest / properties / time / description
      Added value: +"Required when mode is `scheduled`. The project opens itself at this time (UTC unless the value carries an offset) and posts its invitation then; do not also call open. Set it on an inactive project."
  3. Changed11 schema fields changed
    • changedInput schema / $defs / ActionConfigSchema / properties / fire_scope / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "respondent",
      +      "session"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / AudienceRequest / properties / participation / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "private",
      +      "open_web"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / AudienceRequest / properties / response_channel / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "discord",
      +      "web"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / BlockOptionSchema / properties / anchor_position / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "const": "last",
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / ClosingRequest / properties / mode / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "manual",
      +      "scheduled"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / ContinueSchema / properties / after / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "pause",
      +      "click"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / ContinueSchema / properties / pause / anyOf
      Previous value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "minimum": 0,
      +    "type": "integer"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / EmbedConfigRequest / properties / info_display / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "embed",
      +      "button"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / InvitationRequest / properties / answer_button_color / anyOf
      Previous value: -[
      -  {
      -    "type": "integer"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "maximum": 4,
      +    "minimum": 1,
      +    "type": "integer"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / OpeningRequest / properties / mode / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "manual",
      +      "scheduled"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
    • changedInput schema / $defs / SettingsRequest / properties / poll_results_mode / anyOf
      Previous value: -[
      -  {
      -    "type": "string"
      -  },
      -  {
      -    "type": "null"
      -  }
      -]New value: +[
      +  {
      +    "enum": [
      +      "public",
      +      "voters_only",
      +      "hidden"
      +    ],
      +    "type": "string"
      +  },
      +  {
      +    "type": "null"
      +  }
      +]
  4. Changed2 schema fields changed
    • addedInput schema / $defs / BlockSchema / properties / text_analysis
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "boolean"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "title": "Text Analysis"
      +}
    • addedInput schema / $defs / SettingsRequest / properties / text_analysis
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "boolean"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "title": "Text Analysis"
      +}
  5. First observed

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the annotations, the description explains partial-update semantics ('Only the fields you pass change'), atomic whole-script replacement, the inactive-project precondition, scheduled open/close timing, and the human-confirmation expectation. These are material behavioral details for a non-idempotent mutation tool.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose and then behavior, with no filler. The scheduling paragraph is dense and long but earns its space by preventing an erroneous open_project call.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 10-parameter mutation tool with no output schema, the description covers the riskiest operations (script replacement and scheduled opening/closing) but omits broad guidance on several top-level fields and does not describe the response or error behavior. It is adequate but not complete for the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With only project_id documented in the top-level schema, the description supplies important meaning for script (atomic replacement requiring an inactive project) and delivery.opening/closing (scheduled self-open/close and invite posting). However, it leaves many top-level parameters such as presentation, scoring_enabled, max_completes_per_user, and privacy_mode without added explanation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names the verb 'Update' and the resource 'project' and enumerates the main editable areas (name, privacy, rewards, settings), so the tool's function is immediately clear. It does not explicitly contrast with sibling mutation tools such as update_block or update_community, but the resource label makes the distinction obvious.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It provides an explicit precondition for script replacement ('requires an inactive project') and a clear when-not/alternative for scheduling: set delivery.opening and do NOT call open_project. It does not cover when to choose update_project over create_project or other update tools, so guidance is strong but not exhaustive.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.