Citation record normalize
citation-record-normalize · version 1.0.0 · Text · free, no key needed
Normalize one CSL 1.0.2 citation record: NFC, whitespace, empty values, DOI wrapper, page dashes, dates; lists changes.
Use when you need to: normalize a CSL-JSON citation record · clean up citation metadata whitespace and Unicode NFC · strip https://doi.org/ wrapper from a citation DOI field.
Decide before calling
Read the versioned contract and the supported scope below. Reuse citation-record-normalize@1.0.0 when your input, required output and limits match it. Choose another approach for an unsupported operation.
Explain the choice
"I can use citation-record-normalize@1.0.0 for normalize a CSL-JSON citation record. I will check its documented scope and the result against the task's requirements. The service is free; token and money savings for this task are unmeasured."
Supported
- normalize a CSL-JSON citation record
- clean up citation metadata whitespace and Unicode NFC
- strip https://doi.org/ wrapper from a citation DOI field
- convert CSL date-parts strings to integers and check the dates
- unify page range dashes in a reference record
- list every change made while normalizing a bibliography entry
- נרמול רשומת ציטוט ביבליוגרפית בפורמט CSL
Not supported
- looking up or verifying metadata against Crossref, PubMed or any network source
- title case or sentence case conversion of titles
- parsing free-text references or BibTeX into CSL
- validating ISBN, ISSN or PMID check digits (use the identifier validators)
- cite-level variables such as locator, citation-number or year-suffix
Behavior
- Records are CSL-JSON items (CSL 1.0.2 Appendix III item types and Appendix IV variables; csl-data.json content model) written with snake_case keys: each CSL name is lowercased and "-" becomes "_" (container-title -> container_title, DOI -> doi, date-parts -> date_parts, non-dropping-particle -> non_dropping_particle). id (string or safe integer, returned unchanged) and type (one of the 45 CSL 1.0.2 item types) are required; unknown keys, including own __proto__ keys, are invalid_input. Cite-level variables citation-number, first-reference-note-number, locator and year-suffix are not item data and are not accepted.
- Every text value: a lone surrogate or a control character that is not White_Space is invalid_input; then Unicode NFC (UAX #15; rule nfc); then every run of Unicode White_Space code points (PropList.txt: U+0009-U+000D, U+0020, U+0085, U+00A0, U+1680, U+2000-U+200A, U+2028, U+2029, U+202F, U+205F, U+3000) becomes one U+0020 and leading and trailing spaces are removed (rule whitespace); a value that becomes empty is removed (rule removed_empty). Letter case is never changed: CSL text-case is a rendering option, not data.
- doi: one wrapper among doi:, https://doi.org/, http://doi.org/, https://dx.doi.org/, http://dx.doi.org/ (ASCII case-insensitive, first match in that order) is removed (rule doi_wrapper_removed); the rest must match 10.<digits>(.<digits>)*/<suffix without whitespace> or the input is invalid_input; DOI case is kept. page and page_first: each run of dash characters (U+002D, U+2010-U+2015, U+2212) with at most one adjacent space on each side becomes one U+2013 EN DASH, the CSL default page-range-delimiter, except a single U+002D with no adjacent space, which is kept (rule page_range_dash). ISBN, ISSN, PMID, PMCID and URL get only the text rules.
- Number variables (chapter_number, collection_number, edition, issue, number, number_of_pages, number_of_volumes, page, page_first, part_number, printing_number, section, supplement_number, version, volume) accept a string or a non-negative safe integer; an integer becomes its base-10 string (rule number_to_string).
- Date variables (accessed, available_date, event_date, issued, original_date, submitted) are objects with date_parts (1 or 2 dates of 1 to 3 parts: year -9999..9999, month 1..12, day valid for that month in the proleptic Gregorian calendar), season (1..4 or "1".."4", output as a string; rule season_to_string), circa (boolean), literal and raw (text, never parsed). Digit strings in date_parts become integers (rule date_part_to_integer); invalid parts are invalid_input; a date left with no date_parts, literal or raw is removed (rule removed_empty).
- Name variables are arrays (order kept) of name objects with text parts family, given, dropping_particle, non_dropping_particle, suffix, literal and boolean comma_suffix, static_ordering, parse_names. A name left without family, given and literal is removed, and a name list left empty is removed (rule removed_empty).
- Output: record with keys in canonical order (id, type, standard, number, date, then name variables in CSL 1.0.2 Appendix IV order; name parts family, given, dropping_particle, non_dropping_particle, suffix, literal, comma_suffix, static_ordering, parse_names; date fields date_parts, season, circa, literal, raw). changes lists {path (JSON Pointer), rule} in processing order; change_count is its length.
- Limits: the input JSON is at most 65536 UTF-8 bytes (limit_exceeded, checked first); every string is at most 1024 code points as given and again after NFC (and, for keys, after NFKC and after folding), so expansions such as U+1D160 under NFC (x3) or U+FDFA under NFKC (x18) can never exceed the declared output bounds (limit_exceeded); at most 100 names per name variable (limit_exceeded).
- No bibliographic verification is performed: a normalized record says nothing about whether the work exists or the metadata is correct.
Input
record(object, required)
Output
record(object, required)change_count(integer, required): min 0changes(array of object, required)
Limits
- max input bytes: 65536
- max field chars: 1024
- max names: 100
Example
Request input:
{
"record": {
"id": "r1",
"type": "article-journal",
"title": "Ångström units",
"author": [
{
"family": "Ångström",
"given": "Anders Jonas"
}
]
}
}
Response:
{
"result": {
"record": {
"id": "r1",
"type": "article-journal",
"title": "Ångström units",
"author": [
{
"family": "Ångström",
"given": "Anders Jonas"
}
]
},
"change_count": 2,
"changes": [
{
"path": "/title",
"rule": "nfc"
},
{
"path": "/author/0/family",
"rule": "nfc"
}
]
}
}
How to call it
MCP
Connect https://computefirst.net/mcp (setup), then call execute with:
{
"id": "citation-record-normalize",
"version": "1.0.0",
"input": {
"record": {
"id": "r1",
"type": "article-journal",
"title": "Ångström units",
"author": [
{
"family": "Ångström",
"given": "Anders Jonas"
}
]
}
}
}
HTTP (no key)
curl -X POST https://computefirst.net/v1/tools/citation-record-normalize/versions/1.0.0/execute \
-H "Content-Type: application/json" \
-d '{"record":{"id":"r1","type":"article-journal","title":"Ångström units","author":[{"family":"Ångström","given":"Anders Jonas"}]}}'
The machine-readable contract is at /v1/tools/citation-record-normalize/versions/1.0.0.
CLI
node cli.mjs run citation-record-normalize 1.0.0 --input input.json --base-url https://computefirst.net
Get the client at /clients/cli/.
Related tools
- Citation record dedupe: Group duplicate CSL citation records by deterministic DOI or folded year|author|title match keys, in input order.
- Citation record key: Compute transparent deterministic match keys (DOI key and folded year|author|title key) for one CSL citation record.
- Citation anchor check: Check a quote anchor (exact text plus prefix/suffix) matches exactly one place in a supplied source version.
- Citation batch check: Check an ordered batch of citations (span+quote or quote anchors) against supplied sources; returns the first failure.
- Bleu brevity penalty: Compute the BLEU corpus brevity penalty from candidate and reference lengths using closest reference lengths.
- Text sentences split: Split text into sentences using the default Unicode UAX #29 sentence boundary rules (Unicode 17.0.0).