/portaljs-add-dcat
Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.
> /plugin marketplace add datopian/portaljs > /plugin install portaljs@datopian-portaljs
How it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/portaljs-add-dcat
Context preview
What this command does when you run it.
Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.
Command definition
portaljs-add-dcat.mddescription: Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking.
allowed-tools: Read, Write, Edit, Bash, WebFetch
/portaljs-add-dcat
Turn an existing PortalJS (`portaljs-catalog`) portal into a **harvestable** data catalog: emit standards-compliant **DCAT** metadata feeds so external catalogs and government data portals (data.europa.eu, data.gov, national portals) can harvest its datasets automatically.
PortalJS is Frictionless-native (a dataset is a Data Package — see `/portaljs-define-schema`); **DCAT is the serialization + harvest layer on top** (`lib/metadata/dcat.ts` + `lib/metadata/dcat-profiles.ts`). This skill selects one or more DCAT **application profiles**, maps every dataset's metadata to them, and writes static feed files at build so they harvest on **any** host (static Cloudflare Pages, a CDN, a Worker) — no runtime.
What it produces
Wired into `predev`/`prebuild` via `scripts/generate-dcat.ts`, so the feeds are always fresh:
| File | What | |------|------| | `public/catalog.jsonld` · `catalog.ttl` · `catalog.rdf` | **Canonical** feed (first profile) in JSON-LD, Turtle, RDF/XML — the stable autodiscovery targets | | `public/catalog.<profile>.{jsonld,ttl,rdf}` | One feed per configured profile × serialization (e.g. `catalog.dcat-ap.ttl`) | | `public/catalog-feeds.json` | Machine-readable index of every emitted feed | | `<link rel="alternate" type="application/ld+json" href="/catalog.jsonld">` in `_document.tsx` | Autodiscovery — how harvesters find the feed |
Config lives in **`dcat.config.json`** at the portal root (created/updated by this skill). All feeds regenerate from `datasets.json` + this config.
Supported profiles
Profiles are a **pluggable registry** (`lib/metadata/dcat-profiles.ts`) — national profiles are config/data, not hardcoded, and multiple can be emitted at once.
| id | Profile | Notes | |----|---------|-------| | `dcat-3` | DCAT 3 (W3C) | Default. `conformsTo` DCAT-3. | | `dcat-2` | DCAT 2 (W3C) | Same core subset stamped as DCAT-2. | | `dcat-ap` | DCAT-AP (data.europa.eu) | EU profile: adds foaf/vcard, publisher, contactPoint, `dcat:theme` (EU data-theme vocab). | | `dcat-us` | DCAT-US 3.0 (data.gov) | US federal / Project Open Data: adds publisher, contactPoint, `dct:accessLevel`. | | `geodcat-ap` | GeoDCAT-AP (spatial / INSPIRE) | DCAT-AP + spatial coverage: `dct:spatial` (bbox/geometry as `gsp:wktLiteral`) + `dcat:spatialResolutionInMeters` from each dataset's `dcat.spatial` (or the catalog-wide `spatial` config). | | `croissant` | Croissant (MLCommons / schema.org) | ML-dataset metadata: a schema.org `DataCatalog` of Croissant `Dataset`s with `cr:FileObject` distributions + `cr:RecordSet`/`cr:Field` mapped from the Frictionless Table Schema. **JSON-LD only.** | | `dcat-ap-se` · `dcat-ap-ch` · `dcat-ap-de` | National (Sweden / Switzerland / Germany) | DCAT-AP + national `conformsTo`; examples of the plug mechanism. |
**Add another national profile** without code: `registerDcatProfile(makeNationalProfile({ id, label, conformsTo, context }))` in a small module the app loads, then list its id in `dcat.config.json`. See `lib/metadata/README.md`.
**GeoDCAT-AP spatial input.** Per dataset, add `"dcat": { "spatial": { "bbox": "POLYGON((…))" } }` (or `"geometry"`, `"uri"`, `"spatialResolutionInMeters"`) to `datasets.json`; or set a catalog-wide `"spatial"` in `dcat.config.json`. WKT strings are serialized as `gsp:wktLiteral`. A dataset with no spatial coverage is still GeoDCAT-AP-conformant.
**Croissant** describes datasets for ML tooling (schema.org JSON-LD). It reads the Frictionless Table Schema to emit `cr:RecordSet`/`cr:Field`, and stamps a `sha256` on each `cr:FileObject` from the local `public/data/<file>` bytes (mlcroissant requires a checksum on a hosted file; remote-only files are emitted without one). Because a Croissant document describes a single dataset, the feed is a `DataCatalog` whose `dataset[]` entries are each a standalone Croissant `Dataset`.
Required input — ask, don't error
- **Portal directory** — path to the portal project (defaults to current directory).
- **Profiles** — which to emit (default: `dcat-3`). For national-portal harvesting the
user wants `dcat-ap` (EU) or `dcat-us` (US) + optionally a national profile.
- **Publisher + contact** — DCAT-AP and DCAT-US **require** `dct:publisher` and
`dcat:contactPoint`. If the user picks one of those profiles, ask for the publishing organization (name + homepage) and a contact (name + email). Without them the feed is emitted but **fails conformance** — the generator will say what's missing.
- **Site URL** — the portal's public origin (e.g. `https://data.example.org`), so feed
links are absolute. Without it links are root-relative (fine only for same-origin harvest). Reuse `SITE_URL` if already set for `/portaljs-deploy`.
If profiles beyond `dcat-3` are chosen and publisher/contact are missing, ask (one focused prompt) and wait:
DCAT-AP / DCAT-US require a publisher and a contact point. I need:
1. Publishing organization: name + homepage URL
2. Contact: name + email
3. Public site URL (Enter to use root-relative links)
You can say "skip" to emit the feed anyway — it will be flagged as non-conformant.
Steps
1. Gather input from `$ARGUMENTS` (interview if thin)
Extract: `PORTAL_DIR` (default `.`), `PROFILES` (default `["dcat-3"]`), `SITE_URL`, `PUBLISHER` (name/homepage), `CONTACT` (fn/email), `LICENSE` (default dataset license URI), `THEMES` (EU data-theme URIs), `LANGUAGES`, `ACCESS_LEVEL` (DCAT-US, default `public`).
2. Validate the portal directory
Confirm `PORTAL_DIR/datasets.json`, `PORTAL_DIR/package.json`, and `PORTAL_DIR/lib/metadata/` exist. If `lib/metadata/` is missing, the portal predates the metadata c
Read more
description: Make a PortalJS portal harvestable by national/EU/US open-data portals — emit standards-compliant DCAT catalog feeds (DCAT 2/3, DCAT-AP, DCAT-US, national profiles) in JSON-LD, Turtle, and RDF/XML at build, with autodiscovery and per-profile conformance checking. allowed-tools: Read, Write, Edit, Bash, WebFetch
/portaljs-add-dcat
Turn an existing PortalJS (`portaljs-catalog`) portal into a **harvestable** data catalog: emit standards-compliant **DCAT** metadata feeds so external catalogs and government data portals (data.europa.eu, data.gov, national portals) can harvest its datasets automatically.
PortalJS is Frictionless-native (a dataset is a Data Package — see `/portaljs-define-schema`); **DCAT is the serialization + harvest layer on top** (`lib/metadata/dcat.ts` + `lib/metadata/dcat-profiles.ts`). This skill selects one or more DCAT **application profiles**, maps every dataset's metadata to them, and writes static feed files at build so they harvest on **any** host (static Cloudflare Pages, a CDN, a Worker) — no runtime.
What it produces
Wired into `predev`/`prebuild` via `scripts/generate-dcat.ts`, so the feeds are always fresh:
| File | What | |------|------| | `public/catalog.jsonld` · `catalog.ttl` · `catalog.rdf` | **Canonical** feed (first profile) in JSON-LD, Turtle, RDF/XML — the stable autodiscovery targets | | `public/catalog.<profile>.{jsonld,ttl,rdf}` | One feed per configured profile × serialization (e.g. `catalog.dcat-ap.ttl`) | | `public/catalog-feeds.json` | Machine-readable index of every emitted feed | | `<link rel="alternate" type="application/ld+json" href="/catalog.jsonld">` in `_document.tsx` | Autodiscovery — how harvesters find the feed |
Config lives in **`dcat.config.json`** at the portal root (created/updated by this skill). All feeds regenerate from `datasets.json` + this config.
Supported profiles
Profiles are a **pluggable registry** (`lib/metadata/dcat-profiles.ts`) — national profiles are config/data, not hardcoded, and multiple can be emitted at once.
| id | Profile | Notes | |----|---------|-------| | `dcat-3` | DCAT 3 (W3C) | Default. `conformsTo` DCAT-3. | | `dcat-2` | DCAT 2 (W3C) | Same core subset stamped as DCAT-2. | | `dcat-ap` | DCAT-AP (data.europa.eu) | EU profile: adds foaf/vcard, publisher, contactPoint, `dcat:theme` (EU data-theme vocab). | | `dcat-us` | DCAT-US 3.0 (data.gov) | US federal / Project Open Data: adds publisher, contactPoint, `dct:accessLevel`. | | `geodcat-ap` | GeoDCAT-AP (spatial / INSPIRE) | DCAT-AP + spatial coverage: `dct:spatial` (bbox/geometry as `gsp:wktLiteral`) + `dcat:spatialResolutionInMeters` from each dataset's `dcat.spatial` (or the catalog-wide `spatial` config). | | `croissant` | Croissant (MLCommons / schema.org) | ML-dataset metadata: a schema.org `DataCatalog` of Croissant `Dataset`s with `cr:FileObject` distributions + `cr:RecordSet`/`cr:Field` mapped from the Frictionless Table Schema. **JSON-LD only.** | | `dcat-ap-se` · `dcat-ap-ch` · `dcat-ap-de` | National (Sweden / Switzerland / Germany) | DCAT-AP + national `conformsTo`; examples of the plug mechanism. |
**Add another national profile** without code: `registerDcatProfile(makeNationalProfile({ id, label, conformsTo, context }))` in a small module the app loads, then list its id in `dcat.config.json`. See `lib/metadata/README.md`.
**GeoDCAT-AP spatial input.** Per dataset, add `"dcat": { "spatial": { "bbox": "POLYGON((…))" } }` (or `"geometry"`, `"uri"`, `"spatialResolutionInMeters"`) to `datasets.json`; or set a catalog-wide `"spatial"` in `dcat.config.json`. WKT strings are serialized as `gsp:wktLiteral`. A dataset with no spatial coverage is still GeoDCAT-AP-conformant.
**Croissant** describes datasets for ML tooling (schema.org JSON-LD). It reads the Frictionless Table Schema to emit `cr:RecordSet`/`cr:Field`, and stamps a `sha256` on each `cr:FileObject` from the local `public/data/<file>` bytes (mlcroissant requires a checksum on a hosted file; remote-only files are emitted without one). Because a Croissant document describes a single dataset, the feed is a `DataCatalog` whose `dataset[]` entries are each a standalone Croissant `Dataset`.
Required input — ask, don't error
- **Portal directory** — path to the portal project (defaults to current directory).
- **Profiles** — which to emit (default: `dcat-3`). For national-portal harvesting the
user wants `dcat-ap` (EU) or `dcat-us` (US) + optionally a national profile.
- **Publisher + contact** — DCAT-AP and DCAT-US **require** `dct:publisher` and
`dcat:contactPoint`. If the user picks one of those profiles, ask for the publishing organization (name + homepage) and a contact (name + email). Without them the feed is emitted but **fails conformance** — the generator will say what's missing.
- **Site URL** — the portal's public origin (e.g. `https://data.example.org`), so feed
links are absolute. Without it links are root-relative (fine only for same-origin harvest). Reuse `SITE_URL` if already set for `/portaljs-deploy`.
If profiles beyond `dcat-3` are chosen and publisher/contact are missing, ask (one focused prompt) and wait:
DCAT-AP / DCAT-US require a publisher and a contact point. I need: 1. Publishing organization: name + homepage URL 2. Contact: name + email 3. Public site URL (Enter to use root-relative links) You can say "skip" to emit the feed anyway — it will be flagged as non-conformant.
Steps
1. Gather input from `$ARGUMENTS` (interview if thin)
Extract: `PORTAL_DIR` (default `.`), `PROFILES` (default `["dcat-3"]`), `SITE_URL`, `PUBLISHER` (name/homepage), `CONTACT` (fn/email), `LICENSE` (default dataset license URI), `THEMES` (EU data-theme URIs), `LANGUAGES`, `ACCESS_LEVEL` (DCAT-US, default `public`).
2. Validate the portal directory
Confirm `PORTAL_DIR/datasets.json`, `PORTAL_DIR/package.json`, and `PORTAL_DIR/lib/metadata/` exist. If `lib/metadata/` is missing, the portal predates the metadata c
🌀 AI-native framework for building data portals. Scaffold a full portal from a brief and load datasets in minutes with agentic skills — any backend (CKAN, GitHub, Frictionless).
Repo: datopian/portaljs
Other commands on portaljs.
- /add-chart
Renamed → /portaljs-add-chart. This alias will be removed next minor release.
Open command - /add-dataset
Renamed → /portaljs-add-dataset. This alias will be removed next minor release.
Open command - /add-map
Renamed → /portaljs-add-map. This alias will be removed next minor release.
Open command - /add-resource
Renamed → /portaljs-add-resource. This alias will be removed next minor release.
Open command - /arcgis-to-portaljs
Migrate a whole ArcGIS Hub site (opendata.arcgis.com or a Hub Premium custom domain) into a PortalJS Arc portal end-to-end. Harvests the Hub /data.json (DCAT-US) inventory, exports every FeatureService layer through the ArcGIS REST query API (resultOffset paging), converts each
Open command - /architect
Renamed → /portaljs-architect. This alias will be removed next minor release.
Open command

