Developer docs

Developer docsArtist MCP

Artist MCP

The HQ Artist MCP is a remote Model Context Protocol server for managing an artist's show drafts. A compatible client connects over Streamable HTTP, signs in to an existing HQ account through OAuth 2.1, and can then read shows, create drafts, update drafts, and upload cover images.

The endpoint is:

https://api.b90hq.app/mcp

This is a separate authenticated surface from the public show catalog. The catalog is unauthenticated and read-only. Artist MCP uses browser consent and an OAuth access token for account-scoped dashboard show access.

What the server does

The server provides these operations for the authenticated artist:

  • Read the shows that the artist can manage, including dashboard fields, lineup, and review state.
  • Create an unpublished show draft.
  • Update an unpublished show draft.
  • Upload one source image and receive derived cover and thumbnail URLs.
  • Mark every mutation as an MCP change that needs review in the HQ dashboard.

What the server does not do

  • It does not publish a show.
  • It does not delete a show.
  • It does not edit an already-published show through MCP.
  • It does not provide direct Supabase database or Storage access.
  • It does not expose an API key or a long-lived personal access token.
  • It does not provide access to private RSVP identities, credentials, or unrelated user data.

After an MCP draft is ready, open the show in the HQ dashboard, check the details, and publish it there. Publishing from the dashboard clears the MCP review marker.

Requirements

You need all of the following:

  1. An existing HQ account that is an artist account.
  2. The account must have the artist role and the artist.show.add_own permission.
  3. An MCP client that supports remote Streamable HTTP servers, OAuth 2.1, Proof Key for Code Exchange (PKCE), and Dynamic Client Registration (DCR).
  4. A browser available for the client's HQ sign-in and consent step.

The MCP endpoint checks the authenticated OAuth client token as well as the HQ artist permission. Being signed in to HQ in another browser tab does not by itself authorize an MCP client.

Install a client connection

All clients below connect to the same remote endpoint. Use a client that supports the required remote Streamable HTTP and OAuth 2.1/DCR features. Do not paste a bearer token into a config file. The client opens the browser consent flow and stores its credentials in the client's own credential store.

Codex desktop or IDE

In Codex desktop, open Settings, select MCP servers, and select Add server. In the IDE extension, open the gear menu, select MCP servers, and select Add server. Enter hq-artist as the name, select Streamable HTTP, and enter https://api.b90hq.app/mcp as the URL. Save the server, restart the app or extension when prompted, and select Authenticate to complete the HQ browser consent flow.

Codex CLI

Run these commands in a terminal:

codex mcp add hq-artist --url https://api.b90hq.app/mcp
codex mcp login hq-artist

The first command adds the remote server. The second command opens the OAuth browser flow. Approve the requested HQ access after you sign in.

You can also add the server to Codex's TOML configuration:

[mcp_servers.hq-artist]
url = "https://api.b90hq.app/mcp"

After adding the TOML entry, start the client login flow for hq-artist. See the official Codex MCP documentation for client-specific configuration and login behavior.

Claude Code

Run these commands:

claude mcp add --transport http hq-artist https://api.b90hq.app/mcp
claude mcp login hq-artist

You can also start or manage the connection from a Claude Code session with /mcp. Complete the browser consent flow when Claude Code opens it. See the official Claude Code MCP documentation.

VS Code

Add the server to the workspace or user mcp.json file. VS Code uses the http server type for a remote Streamable HTTP endpoint:

{
  "servers": {
    "hq-artist": {
      "type": "http",
      "url": "https://api.b90hq.app/mcp"
    }
  }
}

Start the server from VS Code and complete the browser OAuth consent flow. See the official VS Code MCP documentation.

Other compatible clients

Add a remote Streamable HTTP server with the URL https://api.b90hq.app/mcp. The exact config shape depends on the client. Select HTTP or Streamable HTTP, not a local process command such as stdio. Then start the client's OAuth 2.1 login flow. A compatible client must support:

  • Protected-resource metadata discovery.
  • OAuth 2.1 authorization with PKCE.
  • Dynamic Client Registration.
  • A browser-based consent redirect.
  • Bearer tokens bound to the MCP resource URL.

If a client only accepts an API key, a manually entered bearer token, or a local stdio command, it cannot connect to this server.

These instructions target Codex, Claude Code, VS Code, and compatible clients that expose remote MCP configuration. They do not describe arbitrary local MCP configuration in ChatGPT web.

The endpoint uses the OAuth protected-resource flow:

  1. The client discovers the protected-resource metadata from the endpoint's 401 challenge. The metadata identifies the MCP resource and the HQ OAuth authorization server. The path-aware metadata URL is https://api.b90hq.app/.well-known/oauth-protected-resource/mcp; the root https://api.b90hq.app/.well-known/oauth-protected-resource is also available.
  2. The client registers itself dynamically when required by its OAuth implementation.
  3. The client opens a browser and sends you to HQ sign-in and consent. PKCE binds the authorization response to the client that started the flow.
  4. After consent, the client receives an OAuth access token for the resource https://api.b90hq.app/mcp.
  5. The client sends that token as a bearer token over HTTPS for subsequent MCP requests.
  6. HQ validates the token, its audience, the OAuth-client claim, and the artist permission before it exposes the tool catalog.

The OAuth scopes advertised by the protected-resource metadata are openid and profile. The resource audience is also required. A normal HQ session token, a token issued for another resource, or a token copied from another client is rejected.

Consent grants this client access to the show-management tools for the signed-in artist. Review the client name and requested access before approving. The server does not publish or delete shows as a side effect of consent.

Remove a local connection

Remove the server entry and the client-side credentials when you no longer want to use a client:

codex mcp logout hq-artist
codex mcp remove hq-artist
claude mcp logout hq-artist
claude mcp remove hq-artist

For VS Code, remove the hq-artist object from mcp.json, then remove the saved MCP account or OAuth credential through the VS Code account or MCP controls. Other clients provide an equivalent remove-server and sign-out or clear-credentials action.

Removing a local connection does not delete shows. It also does not claim to revoke every token on the server. If a client offers a provider account-revocation control, use that control as well. To stop using the integration immediately, remove the connection and clear the client's stored credentials.

Safe workflows

Treat create, update, and upload as write operations. Ask the client to show the proposed values and ask for an explicit confirmation before each write. Use a read-before-write sequence so the client does not overwrite changes made in the dashboard or by another tool.

Inspect existing shows

Example prompt:

Use list_shows to inspect my shows. Group the results into published and unpublished shows, and
show the show ID, date, venue, lineup, review marker, and current image URLs. Do not change anything.

Use get_show with the returned UUID before editing one show.

Create a draft

Example prompt:

Create an HQ show draft for 2026-10-10 at The Venue in Berlin for my artist UUID
00000000-0000-4000-8000-000000000001. Use Europe/Berlin, the exact event times I provide, and this
lineup. First show me the complete create payload and wait for my confirmation. After confirmation,
call create_show_draft only. Do not publish the show.

The response includes a new show ID and status: "review". The show remains unpublished.

Update a draft

Example prompt:

Get show 00000000-0000-4000-8000-000000000002 first. Change only the title and web URL in the
existing unpublished draft. Show me the complete resulting values and wait for confirmation before
calling update_show_draft. Do not change the lineup or publish the show.

An update accepts a partial patch. The server merges it with the current show, so omitted fields stay unchanged. If you provide artist_playtimes, the supplied array replaces the full lineup.

Upload a cover

Example prompt:

Get show 00000000-0000-4000-8000-000000000002 first and confirm that it is unpublished. Upload the
attached JPEG as its cover only after I confirm. Use the image content object and report both
resulting image URLs. Do not publish the show.

The upload returns a new thumbnail_url and cover_url. Review the result in the dashboard with the show details, then publish from the dashboard when the content is ready.

Tool reference

The server registers exactly five tools. Successful tool results contain one MCP text content item whose text is a JSON value. Clients often parse that text and display it as structured data.

ToolRead/writeIdempotency and effect
list_showsReadSafe to repeat. Lists the authenticated artist's dashboard-visible shows.
get_showReadSafe to repeat for one show UUID. Returns show_not_found when the show is not readable.
create_show_draftWriteNot idempotent at the tool boundary. Each invocation creates a new command ID; repeat only after checking for a prior success. Duplicate-match protection applies unless force_create is true.
update_show_draftWriteSemantically idempotent. It applies the requested values to the selected show and returns the same show ID; repeating the same patch is safe after a read. It always leaves the show in review.
upload_show_coverWriteNot idempotent. Each invocation creates fresh derived image objects and URLs; repeat only when a new upload is intended.

The MCP schema does not expose an Idempotency-Key input. The service creates an internal UUID for each invocation. A network timeout can therefore leave a write uncertain; call get_show or list_shows before retrying a create or upload.

list_shows

Input: no fields.

Success: a JSON array of dashboard show objects. The list is scoped to shows that the authenticated artist can manage and is ordered by event date and start time.

get_show

Input:

FieldTypeRequiredConstraints
show_idUUID stringYesMust identify a show that the authenticated artist can read.

Success: one dashboard show object. If the UUID is unknown, deleted, or outside the artist's scope, the tool returns show_not_found.

create_show_draft

Input: all fields in the create and update field reference, except show_id. Required fields are marked there. The force_create field defaults to false.

Success:

{
  "id": "00000000-0000-4000-8000-000000000003",
  "status": "review"
}

The returned ID is the new show UUID. Creates always set published to false, mark the record as MCP and Needs review, and do not attach to an existing show.

update_show_draft

Input: show_id plus any non-empty subset of the update fields. Every field is optional except show_id; omitted values remain unchanged. The force_create field is not accepted for updates.

Success:

{
  "id": "00000000-0000-4000-8000-000000000002",
  "status": "review"
}

The update replaces the supplied values, keeps the show unpublished, and marks it for dashboard review. A published show cannot be edited by this tool.

upload_show_cover

Input:

FieldTypeRequiredConstraints
show_idUUID stringYesMust identify an unpublished show that the artist can edit.
imageobjectPreferred{ "type": "image", "data": "<base64>", "mimeType": "image/jpeg" }.
image_base64stringLegacy alternativeBase64 source data. Use with mime_type.
mime_typestringWith legacy inputOne of image/jpeg, image/png, image/webp, or image/gif.

Send the image object when the client supports MCP image content. Do not send both forms with conflicting data. The image object and the legacy fields are accepted as base64 input because the remote endpoint receives JSON.

Success:

{
  "id": "00000000-0000-4000-8000-000000000002",
  "status": "review",
  "thumbnail_url": "https://cdn.example.com/thumbnail.jpg",
  "cover_url": "https://cdn.example.com/cover.jpg"
}

The URLs are fresh public show-covers objects. The show remains unpublished and marked for review.

Create and update fields

The create schema exposes every field accepted by the dashboard show editor. create_show_draft requires the fields marked “Create”; update_show_draft accepts the fields marked “Update” as optional patch fields. The names are snake_case exactly as shown.

FieldTypeCreateUpdateConstraints and meaning
titlestringRequiredOptionalAt least 1 and at most 500 characters.
event_datestringRequiredOptionalCalendar date in YYYY-MM-DD format.
start_timestringRequiredOptionalNon-empty ISO 8601 timestamp with an explicit offset or Z.
end_timestringRequiredOptionalNon-empty ISO 8601 timestamp with an explicit offset or Z.
is_playtime_tbabooleanOptional, default falseOptionalMarks the top-level playtime as to be announced.
timezonestringRequiredOptionalNon-empty timezone identifier, such as Europe/Berlin; at most 100 characters.
location_namestringRequiredOptionalNon-empty venue name; at most 500 characters.
location_citystringRequiredOptionalNon-empty city; at most 200 characters.
location_countrystringRequiredOptionalCountry code with 1 or 2 characters; use the two-letter country code, such as DE.
latitudenumberRequiredOptionalFinite decimal from -90 to 90.
longitudenumberRequiredOptionalFinite decimal from -180 to 180.
venue_place_idstring or nullOptionalOptionalAt most 500 characters. Omit or send null when no place ID exists.
web_urlstring or nullOptionalOptionalValid URL, at most 2,000 characters. Omit or send null when absent.
thumbnail_urlstring or nullOptionalOptionalValid URL, at most 2,000 characters. This is a custom image URL; omit or send null when absent.
artist_idUUID stringRequiredOptionalOwner or primary artist UUID. For an artist account, this normally is the authenticated artist UUID.
community_idUUID string or nullOptionalOptionalCommunity UUID or null. The artist must be allowed to use the selected community.
artist_playtimesarrayRequiredOptionalAt least one lineup row. On update, replaces the complete lineup.
force_createbooleanOptional, default falseNot acceptedIf true, create a separate show even when duplicate candidates are found.

Lineup and playtimes

Each artist_playtimes item has this schema:

FieldTypeRequiredConstraints and meaning
artist_idUUID stringYesArtist UUID for this lineup row.
community_idUUID string or nullNoCommunity UUID for this artist's row, or null.
start_timestringYesNon-empty ISO 8601 timestamp.
end_timestringYesNon-empty ISO 8601 timestamp.
is_playtime_tbabooleanNo, default falseMarks this artist's playtime as to be announced.

The array must contain at least one row. The service stores the lineup rows as the source for the show's aggregate start, end, and TBA values. On update, submit the complete lineup if you want to change it; omitting the array keeps the current lineup.

When is_playtime_tba is true, still send usable fallback start_time and end_time values. HQ uses those values for ordering and scheduling, while clients should display “TBA” instead of presenting them as confirmed artist times.

Dates, timestamps, coordinates, and IDs

  • event_date is a date without a time zone. Use the local event calendar date.
  • start_time, end_time, and every lineup timestamp are ISO 8601 instants. Include an offset, for example 2026-10-10T22:00:00+02:00, or use Z for UTC.
  • timezone is the event's IANA time zone. It provides the local context for the date and display.
  • latitude and longitude are separate decimal numbers. Latitude is north/south and longitude is east/west. Do not send a PostgreSQL point string in the mutation input.
  • Every artist_id, community_id, and show_id must be a UUID. A missing optional community is represented by omission or null, not an empty string.
  • If an artist has exactly one eligible owned community and community_id is omitted on create, HQ can resolve that community automatically. Send the UUID explicitly when the choice matters.

Nullable fields and partial updates

On create, venue_place_id, web_url, thumbnail_url, and community_id may be omitted and are stored as null. A lineup row's community_id is also nullable. On update, an omitted field keeps its current value; sending null clears a nullable field. Do not use an empty string as a substitute for null.

artist_playtimes is different: if supplied, the array replaces the full lineup and must contain at least one item. Keep the current array when you only want to change title, venue, URL, or another shell field.

Show response fields

list_shows and get_show return dashboard-shaped objects with snake_case keys. The response can include unpublished shows and review metadata, so do not expose it as a public catalog response.

{
  "id": "00000000-0000-4000-8000-000000000002",
  "title": "HQ at the venue",
  "event_date": "2026-10-10",
  "start_time": "2026-10-10T20:00:00.000Z",
  "end_time": "2026-10-11T02:00:00.000Z",
  "is_playtime_tba": false,
  "timezone": "Europe/Berlin",
  "location_name": "The Venue",
  "location_city": "Berlin",
  "location_country": "DE",
  "location_coordinates": "(13.405,52.52)",
  "venue_place_id": null,
  "web_url": "https://joinhq.app/show/example",
  "thumbnail_url": "https://cdn.example.com/thumbnail.jpg",
  "cover_url": "https://cdn.example.com/cover.jpg",
  "community_thumbnail_url": null,
  "community_cover_url": null,
  "total_rsvps": 0,
  "artist_id": "00000000-0000-4000-8000-000000000001",
  "artist_playtimes": [
    {
      "artist_id": "00000000-0000-4000-8000-000000000001",
      "display_name": "Artist name",
      "username": "artist",
      "avatar_url": null,
      "avatar_thumb_url": null,
      "community_id": null,
      "community_name": null,
      "start_time": "2026-10-10T20:00:00.000Z",
      "end_time": "2026-10-10T22:00:00.000Z",
      "is_playtime_tba": false,
      "sort_order": 0
    }
  ],
  "owner_display_name": "Artist name",
  "owner_username": "artist",
  "owner_avatar_url": null,
  "owner_avatar_thumb_url": null,
  "published": false,
  "created_at": "2026-08-01T12:00:00.000Z",
  "updated_at": "2026-08-01T12:00:00.000Z",
  "booking_api_provider": null,
  "booking_api_external_id": null,
  "booking_api_imported_at": null,
  "booking_api_needs_review": true,
  "review_source": "mcp"
}

The response fields are:

FieldTypeNullableMeaning
idUUID stringNoStable show ID.
titlestringNoShow title.
event_dateYYYY-MM-DD stringNoEvent calendar date.
start_time, end_timeISO timestamp stringsNoAggregate show time window.
is_playtime_tbabooleanNoAggregate TBA state.
timezone, location_name, location_city, location_countrystringNoEvent time zone and venue fields.
location_coordinatesstringYesCoordinates in the database point representation, when available.
venue_place_id, web_url, thumbnail_url, cover_urlstringYesVenue, URL, and image fields.
community_thumbnail_url, community_cover_urlstringYesRelated community image fields.
total_rsvpsintegerNoCurrent RSVP count.
artist_idUUID stringNoShow owner or primary artist field.
artist_playtimesarrayNoFull lineup with profile, community, and playtime values.
owner_display_name, owner_username, owner_avatar_url, owner_avatar_thumb_urlstringYesPublic owner profile fields, when available.
publishedbooleanNoWhether the dashboard has published the show.
created_at, updated_atISO timestamp stringsYesCreation and last-update times, when present.
booking_api_provider, booking_api_external_id, booking_api_imported_atstring or timestampYesBooking import metadata, when a show came from a booking provider.
booking_api_needs_reviewbooleanNoWhether dashboard review is required.
review_sourcestringYesReview source, normally mcp, aboss, or null.

Nullable response values are returned as JSON null. The artist_playtimes array is returned as an array and can be empty for a shell-only imported draft. Each lineup item uses the fields in the lineup table above plus profile fields and sort_order.

Cover image requirements

upload_show_cover accepts JPEG, PNG, WebP, or GIF source data. The decoded source must be no larger than 6 MiB. The schema also bounds the base64 input before decoding. Send a real base64 value, not a data URL with a data:image/...;base64, prefix.

Preferred image input:

{
  "show_id": "00000000-0000-4000-8000-000000000002",
  "image": {
    "type": "image",
    "data": "<base64 image bytes>",
    "mimeType": "image/jpeg"
  }
}

Legacy-compatible input:

{
  "show_id": "00000000-0000-4000-8000-000000000002",
  "image_base64": "<base64 image bytes>",
  "mime_type": "image/jpeg"
}

HQ validates the MIME type and image bytes, applies the source image's rotation metadata, and derives two centered JPEG crops:

  • A dashboard cover at 1200 x 900 pixels.
  • A dashboard thumbnail at 1200 x 630 pixels.

The source format does not determine the output format. Both returned image URLs point to JPEG derivatives. A corrupt image, unsupported MIME type, malformed base64 value, or source larger than 6 MiB is rejected before storage.

Draft, review, and publication rules

Every create, update, and cover upload carries the mcp review source through the service command. The database records review_source: "mcp" and booking_api_needs_review: true. The dashboard shows the MCP and Needs review markers.

  • A create always produces published: false.
  • An update can change only a show that is still unpublished. It leaves the show unpublished.
  • A cover upload can change only a show that is still unpublished. It leaves the show unpublished.
  • Dashboard publishing is the only publication path for these MCP drafts.
  • Dashboard publishing clears the MCP review marker.
  • MCP has no publish or delete tool, and a client cannot bypass this restriction by calling the ordinary /v1/* API with its OAuth client token.

Errors and troubleshooting

MCP tool failures contain an error object in the text content. Input schema failures can be reported directly by the client before a tool runs. The most useful server error codes are:

Code or responseMeaningWhat to do
401 invalid access tokenThe token is absent, expired, not an OAuth-client token, or is for another resource.Use the client's MCP login flow again. Do not paste a manual bearer token. Check that the URL is exactly https://api.b90hq.app/mcp.
403 forbiddenThe signed-in HQ user is not an eligible artist or lacks artist.show.add_own.Sign in with the artist account that manages the shows, or ask an HQ administrator to grant the required artist permission.
503 MCP show routes are unavailableThe hosted MCP route is not activated.Contact the HQ operator. This is a server configuration issue, not a client payload issue.
show_not_foundThe show UUID is unknown, deleted, or outside the authenticated artist's scope.Call list_shows, copy the exact UUID, and read it again before updating or uploading.
show_operation_failed for a published showThe service rejected an MCP update or cover change because the show is published (mcp_show_published_forbidden).Make edits in the dashboard. MCP cannot reopen or mutate a published show.
show_match_foundA create request matches an existing show by venue, date, time, and title.Call list_shows and inspect likely matches. Create a separate show only when that is intended, then repeat with force_create: true.
invalid_show_imageThe image object, MIME type, base64 value, or decoded image is invalid.Use a JPEG, PNG, WebP, or GIF with valid base64 and no data-URL prefix.
show_image_too_largeThe decoded source is larger than 6 MiB.Resize or recompress the source and retry after confirming the show is still unpublished.
show_operation_failedA service command or permission check failed without a more specific mapped code.Read the show again, check the payload and permissions, and inspect the dashboard for a partial result before retrying.

OAuth or login does not open

Confirm that the client supports remote Streamable HTTP, OAuth 2.1, PKCE, and DCR. Remove the local server entry and add it again with the exact endpoint. Then start codex mcp login hq-artist, claude mcp login hq-artist, or the equivalent client login action. A client that asks for a static API key is not compatible with this endpoint.

The client discovers no tools

Check that the client uses the remote HTTP transport and has completed OAuth consent. The endpoint does not expose tools until the token is valid and the user passes the artist permission check. A tool list that includes publish or delete is not the HQ Artist MCP catalog; reconnect to the exact HQ endpoint and report the client version to the HQ operator.

A write timed out

Do not immediately repeat create_show_draft or upload_show_cover. First call list_shows or get_show to check whether the operation succeeded. The MCP tool input has no client idempotency key, and uploads intentionally create fresh image URLs.

A duplicate warning is unexpected

Call list_shows, inspect likely matches, and compare the venue, date, times, and lineup. The public tool error does not include the candidate rows. The default force_create: false protects against creating the same event twice. Use force_create: true only after an explicit confirmation that a separate event is intended.

A draft is not visible in the public catalog

This is expected. MCP creates and updates remain unpublished until a user reviews and publishes them in the HQ dashboard. The public /v1/* show catalog returns published shows only.