Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ mcp-data-platform/
│ ├── connid/ # Connection identity: the instance a connection is stored under, the name a call binds it by, the toolkit serving it, and which half of the config owns it — one Resolver, distinct types
│ ├── connview/ # Builds the list_connections view (configured + discovered)
│ ├── contenttype/ # Media-type detection and normalization for every content write path
│ ├── database/ # Database utilities (migrate/ = golang-migrate runner + 118 embedded SQL migrations)
│ ├── database/ # Database utilities (migrate/ = golang-migrate runner + 119 embedded SQL migrations)
│ ├── embedding/ # Text embedding generation for memory vector search
│ ├── indexjobs/ # Postgres-backed, source-kind-agnostic background indexer
│ ├── knowledge/ # Unified read path for platform knowledge (federation/ = live toolkit registry adapter)
Expand Down Expand Up @@ -224,7 +224,7 @@ mcp-data-platform/
├── internal/ # Non-exported implementation (not part of the supported library surface)
│ ├── admin/ # Admin-API seams built only by pkg/admin: auditapi/ (events + metrics), callapi/ (the call catalog + its review actions), catalogapi/ (OpenAPI spec bundles + embedding jobs), connoauthapi/ (connection OAuth, unified + legacy per-kind), notifyapi/ (notification delivery history + status counts), settingsapi/ (SMTP + review-queue-alert settings REST) — extracted by #1078
│ ├── httpjson/ # RFC 9457 Problem Details responder + admin list-query param parsing, shared by the admin/portal decomposition seams (#1078)
│ ├── httpserver/ # HTTP composition root: mux/route assembly (MCP streamable+SSE, OAuth, admin/portal/resources/gateway/observability REST, portal UI), CORS, drain/shutdown sequencing — extracted from main.go (#895). Subpackages are the adapters it mounts: accessgate/, attachhttp/, datahubapi/, gatewayhttp/, health/, httpauth/, mentionhttp/, notifyhttp/ (self-scoped notification prefs), scripthttp/ (managed-script review + the approval action), sources/, unsubhttp/ (no-login unsubscribe + its tokens), versionhttp/ (#1076, #1080)
│ ├── httpserver/ # HTTP composition root: mux/route assembly (MCP streamable+SSE, OAuth, admin/portal/resources/gateway/observability REST, portal UI), CORS, drain/shutdown sequencing — extracted from main.go (#895). Subpackages are the adapters it mounts: accessgate/, attachhttp/, datahubapi/, gatewayhttp/, health/, httpauth/, mentionhttp/, notifyhttp/ (self-scoped notification prefs), scripthttp/ (managed-script admin + portal routes, including the administrator's owner transfer), sources/, unsubhttp/ (no-login unsubscribe + its tokens), versionhttp/ (#1076, #1080)
│ ├── sqltables/ # The one lexical extractor of the tables a SQL statement reads (enrichment + call targets)
│ ├── pglisten/ # Shared LISTEN adapter: one goroutine per pg_notify channel waking the workers registered on it (notification delivery, managed-script runs)
│ ├── notification/ # Notification delivery layers built only by internal/platform/notifydelivery, extracted by #1080: notifyprefs/ (preference persistence), notifyqueue/ (queue persistence + LISTEN wakeup), notifyrender/ (branded templates), notifysend/ (SMTP transport), notifyworker/ (send worker)
Expand Down Expand Up @@ -410,9 +410,12 @@ calls:
Authoring needs no configuration and is available wherever there is a database.
A saved script runs: `run_script` and a schedule execute the latest saved
version, presenting the roles its author held at the save, and the persona
filter authorizes every call at run time. The knobs are how long the record of
a run is kept, whether this replica executes runs at all, and which bucket
destinations a script's output may be delivered to.
filter authorizes every call at run time. A script is personal: its owner sees
it, edits it, runs it, and schedules it, administrators do all four on every
script, and an administrator can move a script to another owner (which
re-captures the run identity from the administrator making the move). The knobs
are how long the record of a run is kept, whether this replica executes runs at
all, and which bucket destinations a script's output may be delivered to.

```yaml
scripts:
Expand Down
10 changes: 5 additions & 5 deletions dev/seed.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1845,15 +1845,15 @@ DELETE FROM script_versions WHERE script_id IN (

INSERT INTO scripts (
id, name, display_name, description, source_code, params,
scope, personas, owner_email, tags, enabled, status, version, created_at, updated_at
owner_email, tags, enabled, status, version, created_at, updated_at
) VALUES
(
'e1e1e1e1-0000-4000-8000-000000000001',
'daily-sales-report', 'Daily Sales Report',
'Yesterday''s sales by region, exported for the morning review.',
E'rows = platform.query(\n connection="acme",\n sql="SELECT region, sum(amount) AS revenue FROM warehouse.public.sales WHERE sale_date = :d GROUP BY region",\n params={"d": run.params["report_date"]},\n)["rows"]\n\nplatform.export(name="daily-sales", rows=rows, format="csv")\nprint("wrote %d regions for %s" % (len(rows), run.params["report_date"]))\n',
'[{"name":"report_date","type":"date","description":"The business date to report on; the schedule pins it to the fire time.","required":true}]'::jsonb,
'global', '{}', 'analyst@example.com', '{sales,reporting}', true, 'active', 2,
'analyst@example.com', '{sales,reporting}', true, 'active', 2,
NOW() - interval '40 days', NOW() - interval '30 days'
),
(
Expand All @@ -1862,7 +1862,7 @@ INSERT INTO scripts (
'Accounts with no orders since a cutoff date, for the retention review.',
E'rows = platform.query(\n connection="acme",\n sql="SELECT account_id, last_order_at FROM warehouse.public.accounts WHERE last_order_at < :cutoff",\n params={"cutoff": run.params["cutoff"]},\n)["rows"]\n\nplatform.export(name="dormant-accounts", rows=rows, format="csv")\n',
'[{"name":"cutoff","type":"date","description":"Accounts idle since this date.","required":true}]'::jsonb,
'personal', '{}', 'analyst@example.com', '{retention}', true, 'active', 1,
'analyst@example.com', '{retention}', true, 'active', 1,
NOW() - interval '3 days', NOW() - interval '3 days'
),
(
Expand All @@ -1871,13 +1871,13 @@ INSERT INTO scripts (
'Row counts and max load timestamps per warehouse table.',
E'rows = platform.query(\n connection="acme",\n sql="SELECT table_name, row_count, max_loaded_at FROM warehouse.public.table_stats",\n)["rows"]\n\nplatform.export(name="freshness", rows=rows, format="csv")\n',
'[]'::jsonb,
'global', '{}', 'admin@example.com', '{operations}', true, 'active', 5,
'admin@example.com', '{operations}', true, 'active', 5,
NOW() - interval '60 days', NOW() - interval '21 days'
)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name, display_name = EXCLUDED.display_name,
description = EXCLUDED.description, source_code = EXCLUDED.source_code,
params = EXCLUDED.params, scope = EXCLUDED.scope, personas = EXCLUDED.personas,
params = EXCLUDED.params,
owner_email = EXCLUDED.owner_email, tags = EXCLUDED.tags,
enabled = EXCLUDED.enabled, status = EXCLUDED.status, version = EXCLUDED.version,
updated_at = EXCLUDED.updated_at;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/admin-admin-script-runs-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/admin-admin-scripts-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-detail-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-documentation-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-dry-run-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-run-log-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-run-now-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-runs-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-source-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-script-versions-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-scripts-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/dark/user-scripts-empty-dark.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/admin-admin-script-runs-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/admin-admin-scripts-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-detail-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-dry-run-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-run-log-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-run-now-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-runs-light.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/screenshots/light/user-script-source-light.webp
Binary file modified docs/images/screenshots/light/user-scripts-empty-light.webp
Binary file modified docs/images/screenshots/light/user-scripts-light.webp
10 changes: 5 additions & 5 deletions docs/llms-full.txt

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/llms.txt

Large diffs are not rendered by default.

38 changes: 25 additions & 13 deletions docs/scripts/running.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,24 @@ as it does for a person, and the persona filter decides which connections the
run may reach — at run time, with no script-side allowlist. Narrowing a
persona's rules takes effect on the script's next run.

Who may save is the edit rule: a non-admin edits only their own personal
scripts, and editing a `global` or `persona`-scoped script is an
administrator's action. A personal script is its owner's to delete outright;
`manage_script delete` refuses a shared script in favour of deprecating it,
because it may be executing on a schedule for somebody else.
Who may save is the edit rule: a script is one person's, so its owner saves it
and so does an administrator. Deleting it is the same rule, and it takes the
script's schedule and history with it — nobody else could see it, run it, or
notice it go.

An administrator can move a script to another owner, from the script's page in
the portal (`PUT /api/v1/portal/scripts/{id}/owner`). Ownership is the whole of
what a script is, so the transfer hands over what its owner sees, edits, runs,
and schedules, all at once — including its history: the new owner reads the run
records and dry-run accounts the previous owner produced, whose logs are free
text those runs printed. That is the reason a transfer is an administrator's
action and not an owner's to give away.

The move is recorded as a new version authored by the administrator making it,
and from then on a run presents THAT administrator's roles: moving a script to
an administrator is how it comes to run with an administrator's reach. It is
refused when the receiving owner already keeps a script of the same name, and
it is recorded in the audit log like any other administrative write.

### Where output may go

Expand Down Expand Up @@ -190,8 +203,9 @@ own editor, and the edit crosses the same gate a `manage_script update` crosses
is the version that runs from then on. The save says so, and says instead when
the script is disabled or retired and nothing will execute it. The route is
`PUT /api/v1/portal/scripts/{id}/source`, restricted to the script's owner and
to administrators, and it edits the SOURCE only — scope, personas, status, and
the parameter contract are structured decisions the tool owns.
to administrators, and it edits the SOURCE only — the status and the parameter
contract are structured decisions the tool owns, and the owner is the
administrator's transfer.

The source is parsed before anything is stored, so code that cannot run is
refused at the keyboard rather than at the next fire.
Expand Down Expand Up @@ -291,12 +305,10 @@ pressed the button.

### Who sets a cadence

The owner of a script sets its cadence, at every scope, and so does an
administrator. That is a different rule from the one that governs editing: what
a script DOES is confined to a personal script unless an administrator changes
it. When it runs carries no authority to confine — the run gate and the persona
filter are re-read at every fire — so the owner of a shared report re-times or
pauses it without asking anyone.
The owner of a script sets its cadence, and so does an administrator: it is the
same rule reading and editing answer to. A cadence carries no authority of its
own — the run gate and the persona filter are re-read at every fire — so
re-timing a script reaches nothing the script could not already reach.

The portal asks for a cadence in the terms a person has it in — hourly, daily,
weekdays, chosen days, a day of the month, a time, a zone — and derives the cron
Expand Down
Loading
Loading