Globals


Instance Methods

buildProtectedResourceMetadata()

RFC 9728 protected resource metadata.

authorization_servers is intentionally omitted: Choreo Planer issues MCP access tokens through its own POST /auth/mcp-token endpoint and does not operate an OAuth 2.0 authorization server. RFC 9728 marks the parameter OPTIONAL for exactly this case.

getUserFromRequest()

Extract user info from MCP request handler extra.

The AuthInfo.extra field carries userId, ownerIds, and isAdmin set by the JwtTokenVerifier during authentication.

formatError()

Format an error into an MCP tool result.

Returns a consistent error response shape that the MCP SDK expects.

conditionalBearerAuth()

Enforce bearer auth only for methods not in PUBLIC_MCP_METHODS.

GET (stream resume) and DELETE (session teardown) always require a prior authenticated session, so they stay gated. For POST we inspect the JSON-RPC method and skip auth for the public capability handshake.

errorHandlingMiddleWare( err: Error, req: Request, res: Response, next: function, )

Middleware to send an appropriate status code response depending on the occurring error.

  • NotFoundError: status code 404
  • LineupConflictError: status code 409

Parameters

  • err (Error) — Error to handle and to log
  • req (Request) — Incoming request object
  • res (Response) — Outgoing response object
  • next (function) — Next handler function (not called)

loggerMiddleWare(req: Request, res: Response, next: function)

Middleware to log every incoming request

Parameters

  • req (Request) — Incoming request object
  • res (Response) — Outgoing response object
  • next (function) — Next handler function

errorLoggingMiddleWare( err: Error, req: Request, res: Response, next: function, )

Middleware to handle all errors

Parameters

  • err (Error) — Error to handle and to log
  • req (Request) — Incoming request object
  • res (Response) — Outgoing response object
  • next (function) — Next handler function (not called)

assertUsableMember(MemberId: string): Promise.<Member>

Load the member a position is being assigned to and assert it is usable. Centralizes the existence check shared by position create, find-or-create, and member reassignment so a position never references a missing member.

Parameters

  • MemberId (string) — The member being placed.

Returns

  • Promise.<Member> — The loaded member.

Throws

  • NotFoundError — If no member exists with the given ID.

canRead(ownerId, actingUserId, isAdmin)

Check if the acting user has read access to resources owned by the given ownerId.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise - True if the user has read access

canWrite(ownerId, actingUserId, isAdmin)

Check if the acting user has write access to resources owned by the given ownerId. Coaches and assistants have write access.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise - True if the user has write access

canDelete(ownerId, actingUserId, isAdmin)

Check if the acting user has delete access to resources owned by the given ownerId. Only coaches have delete access.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise - True if the user has delete access

checkReadAccess(ownerId, actingUserId, isAdmin)

Verify the acting user has read access to resources owned by the given ownerId. Throws AccessDeniedError if access is denied.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise

Throws

  • AccessDeniedError if access is denied

checkWriteAccess(ownerId, actingUserId, isAdmin)

Verify the acting user has write access to resources owned by the given ownerId. Throws AccessDeniedError if access is denied.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise

Throws

  • AccessDeniedError if access is denied

checkDeleteAccess(ownerId, actingUserId, isAdmin)

Verify the acting user has delete access to resources owned by the given ownerId. Throws AccessDeniedError if access is denied.

Parameters

  • ownerId — The ID of the owner whose resources are being accessed
  • actingUserId — The ID of the user attempting to access the resource
  • isAdmin — Whether the acting user is an admin (admin has full access)

Returns

  • Promise

Throws

  • AccessDeniedError if access is denied

filterAccessibleOwnerIds(ownerIds, actingUserId, isAdmin)

Filter ownerIds to only include those the acting user has read access to. This prevents false negatives when checking access for multiple owners.

Parameters

  • ownerIds — Array of owner IDs to filter
  • actingUserId — The acting user ID
  • isAdmin — Whether the acting user is an admin

Returns

  • Promise<string[]> - Filtered array of owner IDs the user can access

isPostgres(): boolean

Returns

  • boolean — True when the active database dialect is PostgreSQL, which supports the advisory locks used to serialize concurrent guarded mutations.

acquireLock( transaction: Transaction, choreoId: string, ): Promise.<void>

Take a transaction-scoped PostgreSQL advisory lock keyed to one choreography, so concurrent guarded mutations on the same choreo run one at a time. No-op on SQLite, which serializes writers and is only used by the sequential test suite.

Parameters

  • transaction (Transaction) — The transaction the lock is scoped to; it is released automatically when the transaction ends.
  • choreoId (string) — The choreography whose mutations are serialized.

Returns

  • Promise.<void>

snapshotLineupConflicts( choreoIds: Array.<string>, transaction: Transaction, ): Promise.<LineupConflictSnapshot>

Snapshot every lineup consistency violation currently present across the given choreographies: overlapping lineups, members in two overlapping lineups, and duplicate positions. Runs plain SELECTs inside the caller's transaction so the result reflects uncommitted changes made earlier in the same transaction.

Parameters

  • choreoIds (Array.<string>) — Choreographies to inspect. An empty array yields an empty snapshot without querying.
  • transaction (Transaction) — The transaction the queries run in.

Returns

  • Promise.<LineupConflictSnapshot> — The violations found, as canonical key sets suitable for diffing against another snapshot.

describeNewViolations( before: LineupConflictSnapshot, after: LineupConflictSnapshot, ): Array.<string>

Diff two snapshots and return the violation keys present in after but not in before — i.e. the conflicts a mutation newly introduced. Pre-existing violations appear in both and are omitted, which is what grandfathers them.

Parameters

  • before (LineupConflictSnapshot) — Snapshot taken before the mutation.
  • after (LineupConflictSnapshot) — Snapshot taken after the mutation.

Returns

  • Array.<string> — Canonical keys of the newly introduced violations; empty when the mutation introduced none.

withLineupConflictGuard( choreoIds: Array.<(string|null|undefined)>, work: function, ): Promise.<T>

Run a mutation inside a transaction that enforces lineup consistency.

The guard takes an advisory lock per choreography (so concurrent writers run one at a time), snapshots existing violations, runs work, snapshots again, and commits only if work introduced no new overlap, member conflict, or duplicate position. Violations that already existed before work are preserved in both snapshots and therefore allowed (stateless grandfathering), so legacy data stays editable as long as the edit does not make it worse.

work must perform its writes on the ambient transaction via getTransaction so they are included in the "after" snapshot and roll back together when a conflict is detected. When choreoIds contains no usable id, work runs directly with no transaction, lock, or snapshot.

Parameters

  • choreoIds (Array.<(string|null|undefined)>) — Choreographies affected by the mutation; null/undefined/duplicate entries are filtered and the rest are locked in sorted order to avoid deadlocks.
  • work (function) — The mutation to run under the guard.

Returns

  • Promise.<T> — The value returned by work.

Throws

  • LineupConflictError — If work introduces a new lineup overlap, member conflict, or duplicate position; the transaction is rolled back.

runInTransaction( transaction: Transaction, work: function, ): Promise.<T>

Run work with transaction bound as the ambient transaction.

While the returned promise is pending, any code it calls can retrieve the same transaction via getTransaction without it being threaded through every function signature. Used by the lineup conflict guard so a mutation, its before/after conflict snapshots, and its writes all share one connection and roll back together.

Parameters

  • transaction (Transaction) — The transaction to expose to work.
  • work (function) — The callback run inside the transaction context.

Returns

  • Promise.<T> — The resolved value of work.

getTransaction(): Transaction | undefined

Get the transaction bound by the nearest enclosing runInTransaction.

Returns

  • Transaction | undefined — The ambient transaction, or undefined when called outside any runInTransaction scope (the normal, unguarded path).

Instance Fields

FeatureFlagKey

Keys for available feature flags used by the application.

Other

PUBLIC_MCP_METHODS

JSON-RPC methods that an agent may call before authenticating.

The capability handshake (initialize, tools/list, ping) is left public so scanners and agents can discover the server's WebMCP capabilities and enumerate tools without a bearer token. Everything else — notably tools/call and all resources/* and prompts/* reads — stays behind requireBearerAuth, which answers with a 401 and the RFC 9728 WWW-Authenticate pointer to the protected-resource metadata.

router

router

router

TOKEN_SECRET: string

Secret to encode the JWTs with

JWT_EXPIRES_IN: string

Description string for expiration time of JWTs

defaultColors: Array.<string>

Set of web-safe HEX color codes