@qinisolabs/qiniso 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ <div align="center">
2
+
3
+ <img src="https://qinisolabs.github.io/qiniso/logo.svg" width="96" height="96" alt="Qiniso" />
4
+
5
+ # Qiniso
6
+
7
+ **The deterministic fact-verification layer for AI agents.**
8
+
9
+ *Verified, trustworthy data tools for AI agents. "Qiniso" means "truth" in Zulu.*
10
+
11
+ [Website](https://qinisolabs.github.io/qiniso/) · [GitHub](https://github.com/qinisolabs/qiniso) · [MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=qiniso)
12
+
13
+ </div>
14
+
15
+ ---
16
+
17
+ Agents confidently emit IBANs, phone numbers, domains, VAT numbers and crypto addresses that are subtly — and silently — **wrong**. Qiniso checks the structured facts an agent produces against **checksums and curated authoritative data**, so a bad value is caught instead of trusted.
18
+
19
+ > On arbitrary identifiers, a frontier LLM validates them **wrong ~91% of the time, cold and silently. Qiniso: 0%.**
20
+
21
+ ## Install
22
+
23
+ ```bash
24
+ npm i @qinisolabs/qiniso
25
+ ```
26
+
27
+ ## Use as a library
28
+
29
+ Every check is a typed function — validate locally in one pass, no MCP required:
30
+
31
+ ```ts
32
+ import { validateIban, validateVat, validatePhone } from "@qinisolabs/qiniso";
33
+
34
+ validateIban("GB82 WEST 1234 5698 7654 32");
35
+ // { valid: true, country: "United Kingdom", checkDigits: "82", ... }
36
+
37
+ validateVat("DE136695976"); // { valid: true, country: "Germany", ... }
38
+ validatePhone("020 7946 0123", "GB"); // { valid: true, e164: "+442079460123", ... }
39
+ ```
40
+
41
+ ## Use as an MCP server (in Claude and other agents)
42
+
43
+ No install needed — add the hosted endpoint as a custom connector:
44
+
45
+ ```
46
+ https://qiniso.qinisolabs.workers.dev/mcp
47
+ ```
48
+
49
+ Or run it locally over stdio: `npx -p @qinisolabs/qiniso qiniso-mcp`.
50
+
51
+ ## What it verifies — 34 tools across 8 domains
52
+
53
+ | Domain | Checks |
54
+ | --- | --- |
55
+ | **Identifiers** | IBAN, payment card (Luhn + brand), ISBN-13, VIN, GTIN/UPC/EAN barcodes (+ GS1 country) |
56
+ | **Web / network** | TLD & domain (IANA root zone), IP, UUID, URL, email |
57
+ | **Finance** | ISIN, CUSIP, SEDOL, LEI, US ABA routing |
58
+ | **Crypto** | Ethereum (EIP-55), Bitcoin (Base58Check / Bech32) addresses |
59
+ | **National & tax IDs** | Brazil CPF/CNPJ, South Africa ID, Spain DNI/NIE, India Aadhaar, EU/UK VAT |
60
+ | **Academic** | ISBN-10, ISSN, ORCID |
61
+ | **Locale** | Phone (global), date parsing, currency, holidays (~200 countries), UK VAT-by-date |
62
+ | **Addresses** | UK/US address parsing |
63
+
64
+ ## What it is *not*
65
+
66
+ - **Not a live-data provider** — it verifies facts you give it; it does not return the current time, weather, or live FX.
67
+ - **Not a credential sink** — it never asks for secrets or keys.
68
+ - **Not a registration check** — it validates a VAT number's checksum, not whether it is live-registered (VIES); it confirms a TLD is real, not that a domain is registered.
69
+
70
+ ## License
71
+
72
+ Apache-2.0
@@ -418,6 +418,217 @@ function validateVin(input) {
418
418
  return result;
419
419
  }
420
420
 
421
+ // ../identifiers/dist/data/gs1-prefixes.json
422
+ var gs1_prefixes_default = {
423
+ _comment: "Curated GS1 prefix \u2192 issuing organisation/country map. Ranges are on the 3-digit GS1 prefix (000\u2013999) of the GTIN-13 representation. A prefix indicates where the number was ASSIGNED, not where the product was made. Special ranges (restricted distribution, coupons, Bookland) are labelled. Maintained by Qiniso.",
424
+ ranges: [
425
+ { start: 0, end: 19, country: "United States & Canada" },
426
+ { start: 20, end: 29, country: "Restricted distribution (in-store)" },
427
+ { start: 30, end: 39, country: "United States (drugs, NDC)" },
428
+ { start: 40, end: 49, country: "Restricted distribution (in-store)" },
429
+ { start: 50, end: 59, country: "Coupons (United States)" },
430
+ { start: 60, end: 139, country: "United States & Canada" },
431
+ { start: 200, end: 299, country: "Restricted distribution (in-store)" },
432
+ { start: 300, end: 379, country: "France & Monaco" },
433
+ { start: 380, end: 380, country: "Bulgaria" },
434
+ { start: 383, end: 383, country: "Slovenia" },
435
+ { start: 385, end: 385, country: "Croatia" },
436
+ { start: 387, end: 387, country: "Bosnia and Herzegovina" },
437
+ { start: 389, end: 389, country: "Montenegro" },
438
+ { start: 390, end: 390, country: "Kosovo" },
439
+ { start: 400, end: 440, country: "Germany" },
440
+ { start: 450, end: 459, country: "Japan" },
441
+ { start: 460, end: 469, country: "Russia" },
442
+ { start: 470, end: 470, country: "Kyrgyzstan" },
443
+ { start: 471, end: 471, country: "Taiwan" },
444
+ { start: 474, end: 474, country: "Estonia" },
445
+ { start: 475, end: 475, country: "Latvia" },
446
+ { start: 476, end: 476, country: "Azerbaijan" },
447
+ { start: 477, end: 477, country: "Lithuania" },
448
+ { start: 478, end: 478, country: "Uzbekistan" },
449
+ { start: 479, end: 479, country: "Sri Lanka" },
450
+ { start: 480, end: 480, country: "Philippines" },
451
+ { start: 481, end: 481, country: "Belarus" },
452
+ { start: 482, end: 482, country: "Ukraine" },
453
+ { start: 483, end: 483, country: "Turkmenistan" },
454
+ { start: 484, end: 484, country: "Moldova" },
455
+ { start: 485, end: 485, country: "Armenia" },
456
+ { start: 486, end: 486, country: "Georgia" },
457
+ { start: 487, end: 487, country: "Kazakhstan" },
458
+ { start: 488, end: 488, country: "Tajikistan" },
459
+ { start: 489, end: 489, country: "Hong Kong" },
460
+ { start: 490, end: 499, country: "Japan" },
461
+ { start: 500, end: 509, country: "United Kingdom" },
462
+ { start: 520, end: 521, country: "Greece" },
463
+ { start: 528, end: 528, country: "Lebanon" },
464
+ { start: 529, end: 529, country: "Cyprus" },
465
+ { start: 530, end: 530, country: "Albania" },
466
+ { start: 531, end: 531, country: "North Macedonia" },
467
+ { start: 535, end: 535, country: "Malta" },
468
+ { start: 539, end: 539, country: "Ireland" },
469
+ { start: 540, end: 549, country: "Belgium & Luxembourg" },
470
+ { start: 560, end: 560, country: "Portugal" },
471
+ { start: 569, end: 569, country: "Iceland" },
472
+ { start: 570, end: 579, country: "Denmark, Faroe Islands & Greenland" },
473
+ { start: 590, end: 590, country: "Poland" },
474
+ { start: 594, end: 594, country: "Romania" },
475
+ { start: 599, end: 599, country: "Hungary" },
476
+ { start: 600, end: 601, country: "South Africa" },
477
+ { start: 603, end: 603, country: "Ghana" },
478
+ { start: 604, end: 604, country: "Senegal" },
479
+ { start: 608, end: 608, country: "Bahrain" },
480
+ { start: 609, end: 609, country: "Mauritius" },
481
+ { start: 611, end: 611, country: "Morocco" },
482
+ { start: 613, end: 613, country: "Algeria" },
483
+ { start: 615, end: 615, country: "Nigeria" },
484
+ { start: 616, end: 616, country: "Kenya" },
485
+ { start: 617, end: 617, country: "Cameroon" },
486
+ { start: 618, end: 618, country: "Ivory Coast" },
487
+ { start: 619, end: 619, country: "Tunisia" },
488
+ { start: 620, end: 620, country: "Tanzania" },
489
+ { start: 621, end: 621, country: "Syria" },
490
+ { start: 622, end: 622, country: "Egypt" },
491
+ { start: 623, end: 623, country: "Brunei" },
492
+ { start: 624, end: 624, country: "Libya" },
493
+ { start: 625, end: 625, country: "Jordan" },
494
+ { start: 626, end: 626, country: "Iran" },
495
+ { start: 627, end: 627, country: "Kuwait" },
496
+ { start: 628, end: 628, country: "Saudi Arabia" },
497
+ { start: 629, end: 629, country: "United Arab Emirates" },
498
+ { start: 630, end: 630, country: "Qatar" },
499
+ { start: 631, end: 631, country: "Namibia" },
500
+ { start: 640, end: 649, country: "Finland" },
501
+ { start: 690, end: 699, country: "China" },
502
+ { start: 700, end: 709, country: "Norway" },
503
+ { start: 729, end: 729, country: "Israel" },
504
+ { start: 730, end: 739, country: "Sweden" },
505
+ { start: 740, end: 740, country: "Guatemala" },
506
+ { start: 741, end: 741, country: "El Salvador" },
507
+ { start: 742, end: 742, country: "Honduras" },
508
+ { start: 743, end: 743, country: "Nicaragua" },
509
+ { start: 744, end: 744, country: "Costa Rica" },
510
+ { start: 745, end: 745, country: "Panama" },
511
+ { start: 746, end: 746, country: "Dominican Republic" },
512
+ { start: 750, end: 750, country: "Mexico" },
513
+ { start: 754, end: 755, country: "Canada" },
514
+ { start: 759, end: 759, country: "Venezuela" },
515
+ { start: 760, end: 769, country: "Switzerland & Liechtenstein" },
516
+ { start: 770, end: 771, country: "Colombia" },
517
+ { start: 773, end: 773, country: "Uruguay" },
518
+ { start: 775, end: 775, country: "Peru" },
519
+ { start: 777, end: 777, country: "Bolivia" },
520
+ { start: 778, end: 779, country: "Argentina" },
521
+ { start: 780, end: 780, country: "Chile" },
522
+ { start: 784, end: 784, country: "Paraguay" },
523
+ { start: 786, end: 786, country: "Ecuador" },
524
+ { start: 789, end: 790, country: "Brazil" },
525
+ { start: 800, end: 839, country: "Italy, San Marino & Vatican City" },
526
+ { start: 840, end: 849, country: "Spain & Andorra" },
527
+ { start: 850, end: 850, country: "Cuba" },
528
+ { start: 858, end: 858, country: "Slovakia" },
529
+ { start: 859, end: 859, country: "Czech Republic" },
530
+ { start: 860, end: 860, country: "Serbia" },
531
+ { start: 865, end: 865, country: "Mongolia" },
532
+ { start: 867, end: 867, country: "North Korea" },
533
+ { start: 868, end: 869, country: "Turkey" },
534
+ { start: 870, end: 879, country: "Netherlands" },
535
+ { start: 880, end: 880, country: "South Korea" },
536
+ { start: 883, end: 883, country: "Myanmar" },
537
+ { start: 884, end: 884, country: "Cambodia" },
538
+ { start: 885, end: 885, country: "Thailand" },
539
+ { start: 888, end: 888, country: "Singapore" },
540
+ { start: 890, end: 890, country: "India" },
541
+ { start: 893, end: 893, country: "Vietnam" },
542
+ { start: 896, end: 896, country: "Pakistan" },
543
+ { start: 899, end: 899, country: "Indonesia" },
544
+ { start: 900, end: 919, country: "Austria" },
545
+ { start: 930, end: 939, country: "Australia" },
546
+ { start: 940, end: 949, country: "New Zealand" },
547
+ { start: 950, end: 950, country: "GS1 Global Office" },
548
+ { start: 955, end: 955, country: "Malaysia" },
549
+ { start: 958, end: 958, country: "Macau" },
550
+ { start: 977, end: 977, country: "Serial publications (ISSN)" },
551
+ { start: 978, end: 979, country: "Bookland (ISBN / ISMN)" },
552
+ { start: 980, end: 980, country: "Refund receipts" },
553
+ { start: 981, end: 984, country: "Coupons (common currency area)" },
554
+ { start: 990, end: 999, country: "Coupons" }
555
+ ]
556
+ };
557
+
558
+ // ../identifiers/dist/gtin.js
559
+ var RANGES = gs1_prefixes_default.ranges;
560
+ var LENGTH_TYPE = {
561
+ 8: "GTIN-8",
562
+ 12: "GTIN-12",
563
+ 13: "GTIN-13",
564
+ 14: "GTIN-14"
565
+ };
566
+ function gtinCheckDigit(dataDigits) {
567
+ if (!/^\d+$/.test(dataDigits)) {
568
+ throw new Error("gtinCheckDigit expects digits only.");
569
+ }
570
+ let sum = 0;
571
+ for (let i = 0; i < dataDigits.length; i++) {
572
+ const d = dataDigits.charCodeAt(dataDigits.length - 1 - i) - 48;
573
+ sum += i % 2 === 0 ? d * 3 : d;
574
+ }
575
+ return String((10 - sum % 10) % 10);
576
+ }
577
+ function toBase13Prefix(normalized) {
578
+ const len = normalized.length;
579
+ if (len === 14)
580
+ return normalized.slice(1, 4);
581
+ if (len === 13)
582
+ return normalized.slice(0, 3);
583
+ if (len === 12)
584
+ return ("0" + normalized).slice(0, 3);
585
+ return normalized.slice(0, 3);
586
+ }
587
+ function gs1Country(prefix3) {
588
+ const n = Number(prefix3);
589
+ if (!Number.isInteger(n))
590
+ return null;
591
+ for (const r of RANGES)
592
+ if (n >= r.start && n <= r.end)
593
+ return r.country;
594
+ return null;
595
+ }
596
+ function validateGtin(input) {
597
+ const normalized = input.replace(/[\s-]/g, "");
598
+ const result = {
599
+ input,
600
+ normalized,
601
+ valid: false,
602
+ type: null,
603
+ checkDigit: null,
604
+ gs1Prefix: null,
605
+ gs1Country: null,
606
+ errors: []
607
+ };
608
+ if (!/^\d+$/.test(normalized)) {
609
+ result.errors.push("A GTIN/barcode must contain digits only (after removing spaces/hyphens).");
610
+ return result;
611
+ }
612
+ const type = LENGTH_TYPE[normalized.length];
613
+ if (!type) {
614
+ result.errors.push(`Length ${normalized.length} is not a GTIN \u2014 expected 8 (EAN-8), 12 (UPC-A), 13 (EAN-13) or 14 (GTIN-14) digits.`);
615
+ return result;
616
+ }
617
+ result.type = type;
618
+ const prefix3 = toBase13Prefix(normalized);
619
+ result.gs1Prefix = prefix3;
620
+ result.gs1Country = gs1Country(prefix3);
621
+ const expected = gtinCheckDigit(normalized.slice(0, -1));
622
+ result.checkDigit = expected;
623
+ const actual = normalized[normalized.length - 1];
624
+ if (expected !== actual) {
625
+ result.errors.push(`Check digit failed: expected ${expected}, got ${actual}.`);
626
+ return result;
627
+ }
628
+ result.valid = true;
629
+ return result;
630
+ }
631
+
421
632
  // ../network/dist/data/tlds.json
422
633
  var tlds_default = ["aaa", "aarp", "abb", "abbott", "abbvie", "abc", "able", "abogado", "abudhabi", "ac", "academy", "accenture", "accountant", "accountants", "aco", "actor", "ad", "ads", "adult", "ae", "aeg", "aero", "aetna", "af", "afl", "africa", "ag", "agakhan", "agency", "ai", "aig", "airbus", "airforce", "airtel", "akdn", "al", "alibaba", "alipay", "allfinanz", "allstate", "ally", "alsace", "alstom", "am", "amazon", "americanexpress", "americanfamily", "amex", "amfam", "amica", "amsterdam", "analytics", "android", "anquan", "anz", "ao", "aol", "apartments", "app", "apple", "aq", "aquarelle", "ar", "arab", "aramco", "archi", "army", "arpa", "art", "arte", "as", "asda", "asia", "associates", "at", "athleta", "attorney", "au", "auction", "audi", "audible", "audio", "auspost", "author", "auto", "autos", "aw", "aws", "ax", "axa", "az", "azure", "ba", "baby", "baidu", "banamex", "band", "bank", "bar", "barcelona", "barclaycard", "barclays", "barefoot", "bargains", "baseball", "basketball", "bauhaus", "bayern", "bb", "bbc", "bbt", "bbva", "bcg", "bcn", "bd", "be", "beats", "beauty", "beer", "berlin", "best", "bestbuy", "bet", "bf", "bg", "bh", "bharti", "bi", "bible", "bid", "bike", "bing", "bingo", "bio", "biz", "bj", "black", "blackfriday", "blockbuster", "blog", "bloomberg", "blue", "bm", "bms", "bmw", "bn", "bnpparibas", "bo", "boats", "boehringer", "bofa", "bom", "bond", "boo", "book", "booking", "bosch", "bostik", "boston", "bot", "boutique", "box", "br", "bradesco", "bridgestone", "broadway", "broker", "brother", "brussels", "bs", "bt", "build", "builders", "business", "buy", "buzz", "bv", "bw", "by", "bz", "bzh", "ca", "cab", "cafe", "cal", "call", "calvinklein", "cam", "camera", "camp", "canon", "capetown", "capital", "capitalone", "car", "caravan", "cards", "care", "career", "careers", "cars", "casa", "case", "cash", "casino", "cat", "catering", "catholic", "cba", "cbn", "cbre", "cc", "cd", "center", "ceo", "cern", "cf", "cfa", "cfd", "cg", "ch", "chanel", "channel", "charity", "chase", "chat", "cheap", "chintai", "christmas", "chrome", "church", "ci", "cipriani", "circle", "cisco", "citadel", "citi", "citic", "city", "ck", "cl", "claims", "cleaning", "click", "clinic", "clinique", "clothing", "cloud", "club", "clubmed", "cm", "cn", "co", "coach", "codes", "coffee", "college", "cologne", "com", "commbank", "community", "company", "compare", "computer", "comsec", "condos", "construction", "consulting", "contact", "contractors", "cooking", "cool", "coop", "corsica", "country", "coupon", "coupons", "courses", "cpa", "cr", "credit", "creditcard", "creditunion", "cricket", "crown", "crs", "cruise", "cruises", "cu", "cuisinella", "cv", "cw", "cx", "cy", "cymru", "cyou", "cz", "dad", "dance", "data", "date", "dating", "datsun", "day", "dclk", "dds", "de", "deal", "dealer", "deals", "degree", "delivery", "dell", "deloitte", "delta", "democrat", "dental", "dentist", "desi", "design", "dev", "dhl", "diamonds", "diet", "digital", "direct", "directory", "discount", "discover", "dish", "diy", "dj", "dk", "dm", "dnp", "do", "docs", "doctor", "dog", "domains", "dot", "download", "drive", "dtv", "dubai", "dupont", "durban", "dvag", "dvr", "dz", "earth", "eat", "ec", "eco", "edeka", "edu", "education", "ee", "eg", "email", "emerck", "energy", "engineer", "engineering", "enterprises", "epson", "equipment", "er", "ericsson", "erni", "es", "esq", "estate", "et", "eu", "eurovision", "eus", "events", "exchange", "expert", "exposed", "express", "extraspace", "fage", "fail", "fairwinds", "faith", "family", "fan", "fans", "farm", "farmers", "fashion", "fast", "fedex", "feedback", "ferrari", "ferrero", "fi", "fidelity", "fido", "film", "final", "finance", "financial", "fire", "firestone", "firmdale", "fish", "fishing", "fit", "fitness", "fj", "fk", "flickr", "flights", "flir", "florist", "flowers", "fly", "fm", "fo", "foo", "food", "football", "ford", "forex", "forsale", "forum", "foundation", "fox", "fr", "free", "fresenius", "frl", "frogans", "frontier", "ftr", "fujitsu", "fun", "fund", "furniture", "futbol", "fyi", "ga", "gal", "gallery", "gallo", "gallup", "game", "games", "gap", "garden", "gay", "gb", "gbiz", "gd", "gdn", "ge", "gea", "gent", "genting", "george", "gf", "gg", "ggee", "gh", "gi", "gift", "gifts", "gives", "giving", "gl", "glass", "gle", "global", "globo", "gm", "gmail", "gmbh", "gmo", "gmx", "gn", "godaddy", "gold", "goldpoint", "golf", "goodyear", "goog", "google", "gop", "got", "gov", "gp", "gq", "gr", "grainger", "graphics", "gratis", "green", "gripe", "grocery", "group", "gs", "gt", "gu", "gucci", "guge", "guide", "guitars", "guru", "gw", "gy", "hair", "hamburg", "hangout", "haus", "hbo", "hdfc", "hdfcbank", "health", "healthcare", "help", "helsinki", "here", "hermes", "hiphop", "hisamitsu", "hitachi", "hiv", "hk", "hkt", "hm", "hn", "hockey", "holdings", "holiday", "homedepot", "homegoods", "homes", "homesense", "honda", "horse", "hospital", "host", "hosting", "hot", "hotels", "hotmail", "house", "how", "hr", "hsbc", "ht", "hu", "hughes", "hyatt", "hyundai", "ibm", "icbc", "ice", "icu", "id", "ie", "ieee", "ifm", "ikano", "il", "im", "imamat", "imdb", "immo", "immobilien", "in", "inc", "industries", "infiniti", "info", "ing", "ink", "institute", "insurance", "insure", "int", "international", "intuit", "investments", "io", "ipiranga", "iq", "ir", "irish", "is", "ismaili", "ist", "istanbul", "it", "itau", "itv", "jaguar", "java", "jcb", "je", "jeep", "jetzt", "jewelry", "jio", "jll", "jm", "jmp", "jnj", "jo", "jobs", "joburg", "jot", "joy", "jp", "jpmorgan", "jprs", "juegos", "juniper", "kaufen", "kddi", "ke", "kerryhotels", "kerryproperties", "kfh", "kg", "kh", "ki", "kia", "kids", "kim", "kindle", "kitchen", "kiwi", "km", "kn", "koeln", "komatsu", "kosher", "kp", "kpmg", "kpn", "kr", "krd", "kred", "kuokgroup", "kw", "ky", "kyoto", "kz", "la", "lacaixa", "lamborghini", "lamer", "land", "landrover", "lanxess", "lasalle", "lat", "latino", "latrobe", "law", "lawyer", "lb", "lc", "lds", "lease", "leclerc", "lefrak", "legal", "lego", "lexus", "lgbt", "li", "lidl", "life", "lifeinsurance", "lifestyle", "lighting", "like", "lilly", "limited", "limo", "lincoln", "link", "live", "living", "lk", "llc", "llp", "loan", "loans", "locker", "locus", "lol", "london", "lotte", "lotto", "love", "lpl", "lplfinancial", "lr", "ls", "lt", "ltd", "ltda", "lu", "lundbeck", "luxe", "luxury", "lv", "ly", "ma", "madrid", "maif", "maison", "makeup", "man", "management", "mango", "map", "market", "marketing", "markets", "marriott", "marshalls", "mattel", "mba", "mc", "mckinsey", "md", "me", "med", "media", "meet", "melbourne", "meme", "memorial", "men", "menu", "merck", "merckmsd", "mg", "mh", "miami", "microsoft", "mil", "mini", "mint", "mit", "mitsubishi", "mk", "ml", "mlb", "mls", "mm", "mma", "mn", "mo", "mobi", "mobile", "moda", "moe", "moi", "mom", "monash", "money", "monster", "mormon", "mortgage", "moscow", "moto", "motorcycles", "mov", "movie", "mp", "mq", "mr", "ms", "msd", "mt", "mtn", "mtr", "mu", "museum", "music", "mv", "mw", "mx", "my", "mz", "na", "nab", "nagoya", "name", "navy", "nba", "nc", "ne", "nec", "net", "netbank", "netflix", "network", "neustar", "new", "news", "next", "nextdirect", "nexus", "nf", "nfl", "ng", "ngo", "nhk", "ni", "nico", "nike", "nikon", "ninja", "nissan", "nissay", "nl", "no", "nokia", "norton", "now", "nowruz", "nowtv", "np", "nr", "nra", "nrw", "ntt", "nu", "nyc", "nz", "obi", "observer", "office", "okinawa", "olayan", "olayangroup", "ollo", "om", "omega", "one", "ong", "onl", "online", "ooo", "open", "oracle", "orange", "org", "organic", "origins", "osaka", "otsuka", "ott", "ovh", "pa", "page", "panasonic", "paris", "pars", "partners", "parts", "party", "pay", "pccw", "pe", "pet", "pf", "pfizer", "pg", "ph", "pharmacy", "phd", "philips", "phone", "photo", "photography", "photos", "physio", "pics", "pictet", "pictures", "pid", "pin", "ping", "pink", "pioneer", "pizza", "pk", "pl", "place", "play", "playstation", "plumbing", "plus", "pm", "pn", "pnc", "pohl", "poker", "politie", "porn", "post", "pr", "praxi", "press", "prime", "pro", "prod", "productions", "prof", "progressive", "promo", "properties", "property", "protection", "pru", "prudential", "ps", "pt", "pub", "pw", "pwc", "py", "qa", "qpon", "quebec", "quest", "racing", "radio", "re", "read", "realestate", "realtor", "realty", "recipes", "red", "redumbrella", "rehab", "reise", "reisen", "reit", "reliance", "ren", "rent", "rentals", "repair", "report", "republican", "rest", "restaurant", "review", "reviews", "rexroth", "rich", "richardli", "ricoh", "ril", "rio", "rip", "ro", "rocks", "rodeo", "rogers", "room", "rs", "rsvp", "ru", "rugby", "ruhr", "run", "rw", "rwe", "ryukyu", "sa", "saarland", "safe", "safety", "sakura", "sale", "salon", "samsclub", "samsung", "sandvik", "sandvikcoromant", "sanofi", "sap", "sarl", "sas", "save", "saxo", "sb", "sbi", "sbs", "sc", "scb", "schaeffler", "schmidt", "scholarships", "school", "schule", "schwarz", "science", "scot", "sd", "se", "search", "seat", "secure", "security", "seek", "select", "sener", "services", "seven", "sew", "sex", "sexy", "sfr", "sg", "sh", "shangrila", "sharp", "shell", "shia", "shiksha", "shoes", "shop", "shopping", "shouji", "show", "si", "silk", "sina", "singles", "site", "sj", "sk", "ski", "skin", "sky", "skype", "sl", "sling", "sm", "smart", "smile", "sn", "sncf", "so", "soccer", "social", "softbank", "software", "sohu", "solar", "solutions", "song", "sony", "soy", "spa", "space", "sport", "spot", "sr", "srl", "ss", "st", "stada", "staples", "star", "statebank", "statefarm", "stc", "stcgroup", "stockholm", "storage", "store", "stream", "studio", "study", "style", "su", "sucks", "supplies", "supply", "support", "surf", "surgery", "suzuki", "sv", "swatch", "swiss", "sx", "sy", "sydney", "systems", "sz", "tab", "taipei", "talk", "taobao", "target", "tatamotors", "tatar", "tattoo", "tax", "taxi", "tc", "tci", "td", "tdk", "team", "tech", "technology", "tel", "temasek", "tennis", "teva", "tf", "tg", "th", "thd", "theater", "theatre", "tiaa", "tickets", "tienda", "tips", "tires", "tirol", "tj", "tjmaxx", "tjx", "tk", "tkmaxx", "tl", "tm", "tmall", "tn", "to", "today", "tokyo", "tools", "top", "toray", "toshiba", "total", "tours", "town", "toyota", "toys", "tr", "trade", "trading", "training", "travel", "travelers", "travelersinsurance", "trust", "trv", "tt", "tube", "tui", "tunes", "tushu", "tv", "tvs", "tw", "tz", "ua", "ubank", "ubs", "ug", "uk", "unicom", "university", "uno", "uol", "ups", "us", "uy", "uz", "va", "vacations", "vana", "vanguard", "vc", "ve", "vegas", "ventures", "verisign", "versicherung", "vet", "vg", "vi", "viajes", "video", "vig", "viking", "villas", "vin", "vip", "virgin", "visa", "vision", "viva", "vivo", "vlaanderen", "vn", "vodka", "volvo", "vote", "voting", "voto", "voyage", "vu", "wales", "walmart", "walter", "wang", "wanggou", "watch", "watches", "weather", "weatherchannel", "webcam", "weber", "website", "wed", "wedding", "weibo", "weir", "wf", "whoswho", "wien", "wiki", "williamhill", "win", "windows", "wine", "winners", "wme", "woodside", "work", "works", "world", "wow", "ws", "wtc", "wtf", "xbox", "xerox", "xihuan", "xin", "xn--11b4c3d", "xn--1ck2e1b", "xn--1qqw23a", "xn--2scrj9c", "xn--30rr7y", "xn--3bst00m", "xn--3ds443g", "xn--3e0b707e", "xn--3hcrj9c", "xn--3pxu8k", "xn--42c2d9a", "xn--45br5cyl", "xn--45brj9c", "xn--45q11c", "xn--4dbrk0ce", "xn--4gbrim", "xn--54b7fta0cc", "xn--55qw42g", "xn--55qx5d", "xn--5su34j936bgsg", "xn--5tzm5g", "xn--6frz82g", "xn--6qq986b3xl", "xn--80adxhks", "xn--80ao21a", "xn--80aqecdr1a", "xn--80asehdb", "xn--80aswg", "xn--8y0a063a", "xn--90a3ac", "xn--90ae", "xn--90ais", "xn--9dbq2a", "xn--9et52u", "xn--9krt00a", "xn--b4w605ferd", "xn--bck1b9a5dre4c", "xn--c1avg", "xn--c2br7g", "xn--cck2b3b", "xn--cckwcxetd", "xn--cg4bki", "xn--clchc0ea0b2g2a9gcd", "xn--czr694b", "xn--czrs0t", "xn--czru2d", "xn--d1acj3b", "xn--d1alf", "xn--e1a4c", "xn--eckvdtc9d", "xn--efvy88h", "xn--fct429k", "xn--fhbei", "xn--fiq228c5hs", "xn--fiq64b", "xn--fiqs8s", "xn--fiqz9s", "xn--fjq720a", "xn--flw351e", "xn--fpcrj9c3d", "xn--fzc2c9e2c", "xn--fzys8d69uvgm", "xn--g2xx48c", "xn--gckr3f0f", "xn--gecrj9c", "xn--gk3at1e", "xn--h2breg3eve", "xn--h2brj9c", "xn--h2brj9c8c", "xn--hxt814e", "xn--i1b6b1a6a2e", "xn--imr513n", "xn--io0a7i", "xn--j1aef", "xn--j1amh", "xn--j6w193g", "xn--jlq480n2rg", "xn--jvr189m", "xn--kcrx77d1x4a", "xn--kprw13d", "xn--kpry57d", "xn--kput3i", "xn--l1acc", "xn--lgbbat1ad8j", "xn--mgb9awbf", "xn--mgba3a3ejt", "xn--mgba3a4f16a", "xn--mgba7c0bbn0a", "xn--mgbaam7a8h", "xn--mgbab2bd", "xn--mgbah1a3hjkrd", "xn--mgbai9azgqp6j", "xn--mgbayh7gpa", "xn--mgbbh1a", "xn--mgbbh1a71e", "xn--mgbc0a9azcg", "xn--mgbca7dzdo", "xn--mgbcpq6gpa1a", "xn--mgberp4a5d4ar", "xn--mgbgu82a", "xn--mgbi4ecexp", "xn--mgbpl2fh", "xn--mgbt3dhd", "xn--mgbtx2b", "xn--mgbx4cd0ab", "xn--mix891f", "xn--mk1bu44c", "xn--mxtq1m", "xn--ngbc5azd", "xn--ngbe9e0a", "xn--ngbrx", "xn--node", "xn--nqv7f", "xn--nqv7fs00ema", "xn--nyqy26a", "xn--o3cw4h", "xn--ogbpf8fl", "xn--otu796d", "xn--p1acf", "xn--p1ai", "xn--pgbs0dh", "xn--pssy2u", "xn--q7ce6a", "xn--q9jyb4c", "xn--qcka1pmc", "xn--qxa6a", "xn--qxam", "xn--rhqv96g", "xn--rovu88b", "xn--rvc1e0am3e", "xn--s9brj9c", "xn--ses554g", "xn--t60b56a", "xn--tckwe", "xn--tiq49xqyj", "xn--unup4y", "xn--vermgensberater-ctb", "xn--vermgensberatung-pwb", "xn--vhquv", "xn--vuq861b", "xn--w4r85el8fhu5dnra", "xn--w4rs40l", "xn--wgbh1c", "xn--wgbl6a", "xn--xhq521b", "xn--xkc2al3hye2a", "xn--xkc2dl3a5ee0h", "xn--y9a3aq", "xn--yfro4i67o", "xn--ygbi2ammx", "xn--zfr164b", "xxx", "xyz", "yachts", "yahoo", "yamaxun", "yandex", "ye", "yodobashi", "yoga", "yokohama", "you", "youtube", "yt", "yun", "za", "zappos", "zara", "zero", "zip", "zm", "zone", "zuerich", "zw"];
423
634
 
@@ -1540,6 +1751,13 @@ var TOOLS = [
1540
1751
  argDescription: "The 17-character VIN to validate.",
1541
1752
  run: (v) => validateVin(v)
1542
1753
  },
1754
+ {
1755
+ name: "validate_gtin",
1756
+ description: "USE THIS to verify a product barcode \u2014 GTIN/EAN/UPC \u2014 before trusting it, instead of guessing the check digit. Handles EAN-8, UPC-A (12), EAN-13 and GTIN-14, verifies the GS1 mod-10 check digit, and returns the barcode type plus the GS1 prefix's issuing country (e.g. 50 = UK, 690-699 = China, 978 = Bookland/ISBN). Validates structure only \u2014 it does NOT confirm the barcode maps to a real, registered product.",
1757
+ argName: "gtin",
1758
+ argDescription: "The barcode digits (EAN-8/UPC-A/EAN-13/GTIN-14); spaces and hyphens are ignored.",
1759
+ run: (v) => validateGtin(v)
1760
+ },
1543
1761
  {
1544
1762
  name: "validate_tld",
1545
1763
  description: "USE THIS to check whether a top-level domain is real before trusting a domain or link \u2014 do NOT guess whether a TLD like .zip, .corp, .crypto or .web exists. Checks the suffix against the authoritative IANA root-zone list (kept current). Returns valid:false for TLDs that are not actually delegated.",
@@ -1761,7 +1979,7 @@ var TOOLS = [
1761
1979
  runArgs: (a) => validateVat(a.vat ?? "", a.country)
1762
1980
  }
1763
1981
  ];
1764
- var SERVER_INFO = { name: "qiniso", version: "0.1.0" };
1982
+ var SERVER_INFO = { name: "qiniso", version: "0.2.0" };
1765
1983
  var DEFAULT_PROTOCOL = "2025-06-18";
1766
1984
  function argList(t) {
1767
1985
  return t.args ?? [{ name: t.argName, description: t.argDescription }];
@@ -1814,7 +2032,8 @@ function handleRpc(msg) {
1814
2032
  result = {
1815
2033
  protocolVersion: params?.protocolVersion ?? DEFAULT_PROTOCOL,
1816
2034
  capabilities: { tools: {} },
1817
- serverInfo: { ...SERVER_INFO, websiteUrl: PUBLIC_BASE, icons: ICONS }
2035
+ serverInfo: { ...SERVER_INFO, websiteUrl: PUBLIC_BASE, icons: ICONS },
2036
+ instructions: "Qiniso deterministically verifies structured facts (identifiers, locale, finance, crypto, national/tax IDs). Each tool validates ONE value per call. For validating MANY values (e.g. a CSV or spreadsheet column), looping these tools one-by-one is slow; the same checks are available as the npm library `@qinisolabs/qiniso` (same functions: validateIban, validateVat, validatePhone, \u2026) for one-pass bulk validation in your own environment. For phone numbers, pass the number's ACTUAL country in `region` (e.g. GB for a UK number) \u2014 never reuse an unrelated country field; 'valid' means well-formed for that country's numbering plan, not that the line is live."
1818
2037
  };
1819
2038
  break;
1820
2039
  case "tools/list":
@@ -1848,6 +2067,9 @@ export {
1848
2067
  validateIsbn13,
1849
2068
  vinCheckDigit,
1850
2069
  validateVin,
2070
+ gtinCheckDigit,
2071
+ gs1Country,
2072
+ validateGtin,
1851
2073
  isKnownTld,
1852
2074
  knownTldCount,
1853
2075
  validateTld,
package/dist/http.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  LOGO_SVG,
4
4
  handleRpc
5
- } from "./chunk-TDQYIG3R.js";
5
+ } from "./chunk-I6MBL33Q.js";
6
6
 
7
7
  // src/http.ts
8
8
  import { createServer } from "http";
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@ interface ToolSpec {
25
25
  declare const TOOLS: ToolSpec[];
26
26
  declare const SERVER_INFO: {
27
27
  readonly name: "qiniso";
28
- readonly version: "0.1.0";
28
+ readonly version: "0.2.0";
29
29
  };
30
30
  declare function listTools(): {
31
31
  name: string;
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ import {
4
4
  callTool,
5
5
  detectBrand,
6
6
  formatMoney,
7
+ gs1Country,
8
+ gtinCheckDigit,
7
9
  handleRpc,
8
10
  ibanCheckDigits,
9
11
  isHoliday,
@@ -31,6 +33,7 @@ import {
31
33
  validateDomain,
32
34
  validateEmail,
33
35
  validateEthAddress,
36
+ validateGtin,
34
37
  validateIban,
35
38
  validateIp,
36
39
  validateIsbn10,
@@ -51,13 +54,15 @@ import {
51
54
  verhoeffGenerate,
52
55
  verhoeffValid,
53
56
  vinCheckDigit
54
- } from "./chunk-TDQYIG3R.js";
57
+ } from "./chunk-I6MBL33Q.js";
55
58
  export {
56
59
  SERVER_INFO,
57
60
  TOOLS,
58
61
  callTool,
59
62
  detectBrand,
60
63
  formatMoney,
64
+ gs1Country,
65
+ gtinCheckDigit,
61
66
  handleRpc,
62
67
  ibanCheckDigits,
63
68
  isHoliday,
@@ -85,6 +90,7 @@ export {
85
90
  validateDomain,
86
91
  validateEmail,
87
92
  validateEthAddress,
93
+ validateGtin,
88
94
  validateIban,
89
95
  validateIp,
90
96
  validateIsbn10,
package/dist/server.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  SERVER_INFO,
4
4
  TOOLS
5
- } from "./chunk-TDQYIG3R.js";
5
+ } from "./chunk-I6MBL33Q.js";
6
6
 
7
7
  // src/server.ts
8
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
package/package.json CHANGED
@@ -1,8 +1,17 @@
1
1
  {
2
2
  "name": "@qinisolabs/qiniso",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "mcpName": "io.github.qinisolabs/qiniso",
5
5
  "description": "The deterministic fact-verification layer for AI agents — verify identifiers, locale and more against authoritative ground truth.",
6
+ "homepage": "https://qinisolabs.github.io/qiniso/",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/qinisolabs/qiniso.git",
10
+ "directory": "packages/qiniso"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/qinisolabs/qiniso/issues"
14
+ },
6
15
  "type": "module",
7
16
  "main": "dist/index.js",
8
17
  "types": "dist/index.d.ts",
@@ -33,7 +42,13 @@
33
42
  "agents",
34
43
  "verification",
35
44
  "validation",
36
- "deterministic"
45
+ "deterministic",
46
+ "barcode",
47
+ "gtin",
48
+ "upc",
49
+ "ean",
50
+ "iban",
51
+ "vat"
37
52
  ],
38
53
  "license": "Apache-2.0",
39
54
  "author": "Qiniso",