Skip to content
Documentation menu

Systems

callcharlie-ops Model Context Protocol server

A governance server whose read path is BigQuery and whose write path is one tool, one sink. The refusals are the product.

A second Model Context Protocol server, built after junelife-ops

and deliberately not a copy of it. That one governs a 388,000-page publishing estate, where the

expensive failure is an agent acting on a stale number. This one governs a corpus — 740,552 business records — where the expensive failure is different: an unbounded query, or an agent mailing a stranger about a website that is not theirs.

Same doctrine, different organ. The architecture follows the difference.

The architecture

callcharlie-ops architecture Agent seats call a governance Model Context Protocol (MCP) server on Cloud Run. The server reads BigQuery and the source-of-truth tree through named queries only, and writes to exactly one receipts sink. Writes are gated on a spoken word. The server returns its own caution about the data with every answer. ARCHITECTURE CALLCHARLIE-OPS AGENT SEATS Mini seat leads the website · owns the slug MacBook seat corpus · server · research HUMAN GATE Charlie's pen INGEST_GO · HYGIENE_GO RESEED_GO · DMARC_GO no tool files a ruling, edits the ledger, or fires a build MODEL CONTEXT PROTOCOL SERVER CALLCHARLIE-OPS Python · Cloud Run · Google Cloud Platform (GCP) keyless · 0 stored credentials Named queries only no free-form SQL path Dry-run first bytes recorded · 2 GB cap Named parameters input never becomes SQL text 14 tools · 1 estate writer measured 2026-08-30 THE CORPUS BIGQUERY · agent_ready 11 tables · read-only 740,552 places 618,107 with a website SOURCE-OF-TRUTH vendor bytes, hash-verified 87 quotes · 0 failures RECEIPTS SINK the only place a tool may write tool calls read read write returns its own caution: the URL on record is wrong two times in five Every read is a tool call. Nothing is fetched by a seat directly, and no seat holds a credential.

Read down, write once. Seats call tools; tools run named queries against BigQuery; exactly one tool writes, and only to a receipts sink. No tool files a ruling, edits the ledger, or fires a build — not by policy an agent is asked to respect, but as a capability it does not have.

What makes this one different from junelife-ops

The data source is a bill, not a repository

junelife-ops reads an estate it owns. This one reads a 48.9 GB public table and a derived

corpus of 740,552 places across 11 tables. An unfenced query here is not a mistake, it is an invoice.

So the read path is constrained three ways, and each one is structural rather than advisory:

  • Named queries only. There is no free-form SQL path. Seventeen queries exist, each with its own description and its own reason to exist. This is what makes the byte cap a promise — a cap you can route around with arbitrary SQL is not a cap.
  • Every query dry-runs before it runs, and the estimated bytes are recorded and returned in the response. The caller sees the cost of the answer alongside the answer.
  • A hard byte ceiling of 2 GB per call, enforced before execution.

The inputs are strangers’ data, so the parameters are never SQL

place_site_check takes a business name. That name travels as a BigQuery named parameter and never becomes SQL text. A non-match returns a clean empty result carrying a finding — not an error, and not a silence.

The tool answers with its own doubt

The corpus records a website for each business, taken from Overture. It is wrong roughly two times in five — directories, aggregators and other companies’ sites sit in that field. So the tool that returns it says so, in the response, every time:

The URL comes from Overture and is wrong roughly two times in five — directories, aggregators and other businesses’ sites appear in it. Do not mail it to anyone as evidence about their own site.

That caution is not documentation. It is a field in the payload, because documentation is not present at the moment somebody acts.

Measured example. A barber shop on Staten Island, booking_dependent, carries exactly one website on record: a shipping company’s domain. A name-matching script passes it — both strings share a word. The tool returns the row and the caution together, and a human reading it sees in one line what a regex could not.

Absence has two names, and they are not the same

A verdict of NOT_TIERED means the business sits outside the crawled fleet, so nothing was ever fetched. NOT_CRAWLED means it is in the fleet and was never fetched. Neither means “no defects.” They are reported as distinct states rather than collapsed into an empty field, because a reader who cannot tell them apart will read both as clean.

The same discipline governs the flags: where no page was fetched, the has_* flags are suppressed rather than returned as zero — four zeros would read as four defects where there is one absence.

The catalog reads itself

bq_catalog returns every backing table’s schema read live from INFORMATION_SCHEMA on each call, never a copy baked into the deployed image.

The reason is a failure that had already happened: a seat needed a column name, the catalog it held was stale, and the work stopped. A catalog shipped inside an image ages silently, and the first symptom is a blocked seat. Reading it live costs a round trip and removes the failure mode.

Authorization, and what a public endpoint means

The endpoint is public; the application does the enforcing. OAuth 2.1 with PKCE and dynamic client registration — the client registers itself, so there is no secret to paste anywhere. An unauthenticated call receives a 401 carrying the discovery pointer, not a 200 and not a bare 403.

Authentication to Google is keyless, on workload identity federation. Zero stored service-account keys, marker-verified rather than assumed — a cure that came out of an incident, not a preference.

Where it is honest about itself

The server reports, in every response, that the registers it serves are an image snapshot taken at build time, not main — so a ruling filed after the image was built reads as absent, and the response says to re-check against git before concluding anything is unfiled.

A server that quietly served stale governance would be the most dangerous component in the estate. This one announces its own staleness in the payload, and that line is not removable without changing the code.

navigate openesc close