irvinebroque.com developer portal

Query Brendan Irvine-Broque’s public taste and preference records through open, read-only interfaces.

Start here

Public reads are anonymous: there are no API keys, account signup, paid tier, or contact-sales step. Requests operate on public production records and cannot mutate them, so the same endpoints are safe for exploration and automated tests. Begin with the REST API documentation or the machine-readable OpenAPI 3.1 description. Responses include canonical item IDs and links; preserve those links when presenting or citing a result.

Shell
curl -fsS "https://irvinebroque.com/api/search.json?q=Oakland&limit=5"

Choose an interface

  • REST and OpenAPI: language-neutral search, collection lists, item lookup, metadata, and status at /api/.
  • SQL: bounded projection, filtering, and ordering over generated public relations with the first-party data explorer, QUERY /api/sql, and a machine-readable profile.
  • GraphQL: typed, introspectable multi-collection reads and GraphiQL at /graphql, with downloadable SDL.
  • MCP: task-shaped recommendation, lookup, place-planning, and UI render tools over Streamable HTTP at https://irvinebroque.com/mcp.
  • Cap’n Web: stable, TypeScript-friendly, read-only HTTP batch RPC at https://irvinebroque.com/api/rpc.
  • Feeds and calendars: RSS, Atom, JSON Feed, and iCalendar subscriptions for public additions and events.
  • Markdown: request Accept: text/markdown on supported pages or use their documented .md aliases.
  • JSON-LD: request Accept: application/ld+json on any public HTML page for its Schema.org graph.

Public SQL quickstart

Start in the public data explorer to inspect the generated schema, edit authored examples, view a semantic table or exact JSON, download the API’s CSV representation, and share fragment-only state. Shared queries are displayed but never run without an explicit action, and the explorer does not retain cross-session history.

Send one UTF-8 application/sql entity with the HTTP QUERY method. Version 1.1.0 exposes public.writing and a bounded generated information schema; it never exposes D1 tables, payload columns, drafts, deleted records, or owner data. JSON is the default response, CSV is available through Accept: text/csv, and every response is no-store.

Shell
curl --request QUERY https://irvinebroque.com/api/sql \
  -H 'Content-Type: application/sql' \
  -H 'Accept: application/json' \
  --data "SELECT title, url FROM public.writing ORDER BY date DESC LIMIT 10"

Read the generated SQL profile for the exact catalog, column provenance, grammar, logical types, CSV null rules, and numerical limits. Version 1 exposes public.writing, information_schema.tables, and information_schema.columns. Joins, aggregates, grouping, subqueries, functions, offset, parameters, and writes are outside v1. Syntax errors are deliberately redacted and do not include the query, literals, identifiers, or a source position.

GraphQL quickstart

The anonymous /graphql endpoint supports POST for every query and GET for query operations. It has no mutation or subscription root. All collection fields reuse REST filters, sorting, facets, page sizes, opaque cursors, canonical IDs, and visibility rules. Pass pageInfo.endCursor unchanged as the next call’s after value. Introspection and the checked-in SDL remain public for conventional GraphQL tooling. Fields and enum values are deprecated with replacement guidance for at least 90 days before removal; breaking removals require a new major GraphQL-API-Version.

Expected lookup failures are available as typed results: use contentLookup and branch on ContentLookupSuccess or PublicGraphQLError. The older nullable contentById field remains available during its deprecation window. Schema @cost and @rateLimit directives document demand weight and the enforced 1,000-request, 60-second public quota; response headers report actual computed cost and remaining rate-limit state.

Public operations are synchronous reads, so there is no mutation or subscription root and no public job is created today. The schema reserves the typed AsyncJob and AsyncJobStatus contract that any future long-running operation must return and expose through a status query rather than blocking a mutation.

Shell
curl https://irvinebroque.com/graphql \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/graphql-response+json' \
  --data '{"query":"query { movies(first: 5) { nodes { canonicalId title year itemPath } pageInfo { hasNextPage endCursor } } }"}'

MCP quickstart

Configure an MCP client with the server URL below. The public server requires no authentication and exposes only read and render tools. Discovery metadata is available at /.well-known/mcp/server-card.json and /.well-known/mcp.json. Browser visits to the endpoint lead to a client setup guide; protocol clients should send standard Streamable HTTP JSON-RPC requests directly. The server card identifies the irvinebroque.com server version, endpoint, capabilities, icon, and complete public tool catalog before a client opens the transport.

JSON
{
  "mcpServers": {
    "irvinebroque": {
      "url": "https://irvinebroque.com/mcp"
    }
  }
}

WebMCP in the browser

In browsers with WebMCP enabled, public pages expose the same six taste and music data tools as the canonical data catalog. Three additional tools work with the visible page:site.context reads the current filters and loaded results with canonical IDs and available actions,site.search shows filtered search results, and site.open opens a visible public link. These actions use the existing website and never modify saved content. WebMCP is experimental and requires browser support and an active origin trial or local feature flag. The remote MCP endpoint above remains available independently.

TypeScript
const tools = await document.modelContext.getTools();
const search = tools.find(tool => tool.name === 'site.search');
await document.modelContext.executeTool(search, JSON.stringify({ query: 'jazz' }));

Portable music taste profile

This site publishes a compact version 1 profile at /.well-known/taste-profile.json. It is a bounded interoperability projection of Brendan’s canonical D1-owned tracks, albums, artists, genres, and approved open identifiers—not a second content store or a dump of the complete music timeline. Discover it with the HTTP taste-profile link relation or the anonymous MCP taste.profile tool.

Shell
curl -sS -H 'Accept: application/taste-profile+json; version=1' \
  https://irvinebroque.com/.well-known/taste-profile.json

A compatible client can submit a listener-confirmed profile to the read-only intersection endpoint. Matching prefers verified recording, artist, and release identities, then conservative names and website-owned genres. Results use opaque canonical IDs and honest reason codes; weak evidence abstains and no percentage score is presented.

Shell
curl -sS https://irvinebroque.com/api/music/intersections.json \
  -H 'Content-Type: application/json' \
  --data '{"profile":{"schemaVersion":"1","music":{"artists":[{"name":"Talking Heads","stance":"love"}],"recordings":[],"albums":[],"genres":[],"notes":[]}}}'

Confirmed canonical affinities can become a short queue through POST /api/music/station.json. This query is stateless and read-only: direct votes, exclusions, recent tracks, optional intent, and closed context are bounded, never retained, and every returned title and Spotify identity is reloaded from canonical website data.

Shell
curl -sS https://irvinebroque.com/api/music/station.json \
  -H 'Content-Type: application/json' \
  --data '{"schemaVersion":"1","stationSeed":"example","affinities":[],"votes":[],"hardExclusions":[],"recentCanonicalIds":[],"intent":"patient late-night instrumentals","limit":6}'

Cap’n Web quickstart

Open the Cap’n Web workbench to search and recommend within a selection using a real dependent pipeline.

Cap’n Web is an object-capability view over the same public records, filters, deterministic ordering, canonical IDs, and visibility rules as REST. The single public endpoint requires no API version selection. Its root exposes service, content, discovery, taste, and feed capabilities; collections derive immutable selections, pages, and item capabilities. Queue dependent work before the first await because an HTTP batch session is single-use. Resume HTTP pagination from info().endCursor with pageAfter() on a new root;next() requires WebSocket. Canonical record snapshots keep their cross-protocol JSON representation; capability-specific fields that the declaration marks as Date or URL use native values.

  • api.service — Service description and health capabilities
  • api.content.<collection> — Typed collection, immutable selection, page, and item capabilities
  • api.discovery — Search and recommendation capabilities with passable candidate sets
  • api.taste — Movie summaries, music profiles, intersections, and programmable stations
  • api.feed — Finite resumable feed selections, with pages over HTTP batch and streams over WebSocket
client.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

const rpcRequest = () => new Request('https://irvinebroque.com/api/rpc');
using api = newHttpBatchRpcSession<BrendanPublicApi>(rpcRequest());

const movies = api.content.movies
  .matching({ genre: 'Drama' })
  .orderedBy([{ field: 'releaseDate', direction: 'desc' }])
  .first(10);

// Queue dependent work before the first await. Cap'n Web pipelines it as one batch.
const infoPromise = movies.info();
const recordsPromise = movies.records();
const [info, records] = await Promise.all([infoPromise, recordsPromise]);

// HTTP batches are single-use. Resume pagination with a new root and endCursor.
if (info.endCursor) {
  using nextApi = newHttpBatchRpcSession<BrendanPublicApi>(rpcRequest());
  const nextRecords = await nextApi.content.movies
    .matching({ genre: 'Drama' })
    .orderedBy([{ field: 'releaseDate', direction: 'desc' }])
    .pageAfter(info.endCursor, 10)
    .records();
  consume(nextRecords);
}

Capability passing and dependent pipelines

Download the generated declaration and inspect its checksum and package compatibility. The following pipeline passes a remote search result capability into recommendation, then maps over the returned remote item capabilities without copying IDs through the client.

pipeline.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

using api = newHttpBatchRpcSession<BrendanPublicApi>(
  new Request('https://irvinebroque.com/api/rpc'),
);

// A remote CandidateSet is passed by capability, not copied back as IDs.
const hits = api.discovery.search({ query: 'patient late-night instrumentals' });
const recommendations = api.discovery.recommend(
  { intent: 'patient late-night instrumentals', limit: 5 },
  hits,
);

const recordsPromise = recommendations.records();
const explanationsPromise = recommendations.items().map((item) =>
  recommendations.explain(item)
);

const [records, explanations] = await Promise.all([
  recordsPromise,
  explanationsPromise,
]);

Finite exports and cancellation

Selection exports are finite ReadableStream values on both transports. An export without limit is complete or fails with RESOURCE_LIMIT; use an explicit limit only for an intentional partial export. HTTP batch export bodies are limited to 131072 bytes; use WebSocket for larger complete exports. Feed streams require WebSocket; use feed.latest(...).first(...).records() for HTTP batch reads. Consume or cancel every stream promptly; record, byte, chunk, duration, and concurrency budgets are enforced.

stream.tsTypeScript
import { newHttpBatchRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

using api = newHttpBatchRpcSession<BrendanPublicApi>(
  new Request('https://irvinebroque.com/api/rpc'),
);

const stream = await api.content.places
  .matching({ country: 'United States' })
  .export({ format: 'geojson' });

try {
  for await (const chunk of stream) {
    consume(chunk);
  }
} finally {
  await stream.cancel();
}

WebSocket ownership and reconnect

WebSockets serve the same API. One client component owns the root; on a break it discards every old capability and reconstructs the session. Idle sessions close after 60 seconds and all sessions close after 10 minutes. A native transport frame limit is not claimed: receiver and application budgets apply only after the runtime has accepted a complete message.

websocket.tsTypeScript
import { newWebSocketRpcSession } from 'capnweb';
import type { BrendanPublicApi } from './capnweb.d.ts';

let api: ReturnType<typeof newWebSocketRpcSession<BrendanPublicApi>> | undefined;

function reconnect() {
  api?.[Symbol.dispose]();
  api = newWebSocketRpcSession<BrendanPublicApi>(
    new WebSocket('wss://irvinebroque.com/api/rpc'),
  );
  api.onRpcBroken(() => {
    // Every derived capability belongs to the old connection. Rebuild the graph.
    api?.[Symbol.dispose]();
    api = undefined;
    setTimeout(reconnect, 1000);
  });
}

reconnect();
  • There is one public Cap’n Web API. The root is the API capability itself; open(), query(), getMeta(), and getStatus() are not part of it.
  • Use typed properties and derived capabilities: api.content.movies.matching(...).orderedBy(...).first(...).
  • Selections are immutable values. Pass CandidateSet capabilities directly into recommendation methods instead of copying candidate IDs through the client.
  • HTTP batch roots are single-use. Resume pages from info().endCursor with pageAfter() on a new root; next() is available only on WebSocket.
  • WebSocket roots are long-lived, but every derived capability must be discarded and rebuilt after onRpcBroken().
  • Dates and URLs arrive as native Date and URL values. Selection exports are finite cancelable streams on both transports; feed streams require WebSocket.
  • An export without limit must contain the complete selection or fail with RESOURCE_LIMIT. Pass an explicit limit only when a bounded partial export is intentional.

Requests are limited to 65536 bytes and 100 sessions per source every 60 seconds. Each accepted session is separately bounded by the call and weighted-work budgets in the service metadata. Clients send requests directly to /api/rpc without a version header. Stable structured RPC errors distinguish invalid input, unavailable data, stale cursors, unsupported operations, missing items, and exhausted resource budgets.

The legacy service-metadata fields operationsPerMinute and rateLimitWindowSeconds are deprecated aliases retained through at least 2026-12-02. Use sessionAdmissionsPerMinute and sessionAdmissionWindowSeconds for their accurate session-admission meaning.

Authentication, limits, and support

No authentication is accepted or needed on public read interfaces. The separate owner MCP lane is restricted to the site owner with OAuth and is not a developer write API; its machine-readable registration and authorization flow is documented in the owner MCP authentication guide. There is no public write or webhook API. Clients should use bounded page sizes, follow returned pagination links, honor cache validators and Retry-After, and avoid scraping HTML when a documented data endpoint exists. Check service status, read agent usage guidance, or use the contact page for a reproducible integration issue.