ProvenPin Install

libphonenumber-js parse, validate and format phone numbers: working versions (tested 2026-10-15)

Normalize user phone input to E.164 with libphonenumber-js max metadata: national and international formats, number type, extensions, validation, as-you-type formatting and extraction from text.

passed7 passed · 0 failed2026-10-15 20:09 UTC
Status
passed
Last passed
2026-10-15 20:09 UTC
Tests
7 / 7
Language
TypeScript
Runtime
node v22.23.3
pinned versions3
libphonenumber-js@1.13.15@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false
npm test
test environment
node v22.23.3
linux 6.17.0-1022-azure
x64
runner: github-actions 38799498972

The code

Exactly the files that passed. Your agent can pull them with get_recipe("libphonenumber-js-parse-format") (install), or see them on GitHub.

package.json
{
  "name": "libphonenumber-js-parse-format",
  "private": true,
  "type": "module",
  "scripts": {
    "test": "node --import tsx --test test/*.test.ts"
  },
  "dependencies": {
    "libphonenumber-js": "1.13.15"
  },
  "devDependencies": {
    "@types/node": "26.6.5",
    "tsx": "4.23.15"
  }
}
src/phone.ts
import { parsePhoneNumberFromString, isValidPhoneNumber, AsYouType, findPhoneNumbersInText, type CountryCode } from "libphonenumber-js/max";

export function normalize(input: string, defaultCountry: CountryCode = "US") {
  const p = parsePhoneNumberFromString(input, defaultCountry);
  if (!p || !p.isValid()) return null;
  return {
    e164: p.number,
    national: p.formatNational(),
    international: p.formatInternational(),
    country: p.country ?? null,
    type: p.getType() ?? null,
    uri: p.getURI(),
    ext: p.ext ?? null,
  };
}

export const valid = (s: string, c?: CountryCode) => isValidPhoneNumber(s, c);

export function typeAhead(digits: string, country: CountryCode) {
  const t = new AsYouType(country);
  return t.input(digits);
}

export const extract = (text: string, c: CountryCode = "US") => findPhoneNumbersInText(text, c).map((m) => m.number.number);
test/phone.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { normalize, valid, typeAhead, extract } from "../src/phone.ts";

test("normalizes US numbers to E.164 and formats", () => {
  const n = normalize("(907) 283-1234");
  assert.ok(n);
  assert.equal(n.e164, "+19072831234");
  assert.equal(n.national, "(907) 283-1234");
  assert.equal(n.international, "+1 907 283 1234");
  assert.equal(n.country, "US");
  assert.equal(n.uri, "tel:+19072831234");
});

test("international input ignores default country", () => {
  const n = normalize("+44 20 7946 0958", "US");
  assert.equal(n?.country, "GB");
  assert.equal(n?.e164, "+442079460958");
});

test("number type from max metadata", () => {
  assert.equal(normalize("+44 7400 123456")?.type, "MOBILE");
  assert.equal(normalize("+1 800 555 0199")?.type, "TOLL_FREE");
});

test("extensions are kept", () => {
  assert.equal(normalize("907-283-1234 ext. 42")?.ext, "42");
});

test("invalid numbers are rejected", () => {
  assert.equal(normalize("123"), null);
  assert.equal(valid("+1 555 0100"), false);
  assert.equal(valid("+1 907 283 1234"), true);
});

test("as-you-type formatting", () => {
  assert.equal(typeAhead("907283", "US"), "(907) 283");
  assert.equal(typeAhead("9072831234", "US"), "(907) 283-1234");
});

test("finds numbers in free text", () => {
  assert.deepEqual(extract("Call 907.283.1234 or +44 20 7946 0958 today"), ["+19072831234", "+442079460958"]);
});

Connect to ProvenPin

Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.

terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp

Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.