telique-mcp 1.0.21 → 1.0.22

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.
@@ -1,3 +1,3 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  export declare function registerKnowledge(server: McpServer): void;
3
- export declare const TELIQUE_KNOWLEDGE = "# Telique Telecom API Knowledge Base\n\nYou have access to 13 Telique tools for querying live telecom data. NEVER guess carrier names, LRNs, routing data, or CNAM \u2014 always query the API.\n\n---\n\n## CRITICAL: LERG vs LSMS \u2014 Two Different Databases\n\n**LERG** = Static telecom infrastructure reference (updated monthly from iconectiv BIRRDS)\n- NPA-NXX assignments, switches, tandems, homing arrangements, rate centers, LATAs, carrier names by OCN\n- 27 tables. NO individual phone number data.\n- Query with: `lerg_query`, `lerg_complex_query`, `lerg_tandem`, `lerg_table_info`, or `graphql_query(service=\"lerg\")`\n\n**LSMS** = Live NPAC porting data (refreshed within minutes)\n- Phone number ownership (SPID), LRN assignments, porting state\n- NO routing infrastructure (no tandems, switches, homing, rate centers, OCN names)\n- Query with: `lrn_lookup`, `lrn_relationship_query`, or `graphql_query(service=\"lsms\")`\n\n| Data Type | Database | Tool |\n|-----------|----------|------|\n| Tandem switches | **LERG** | `lerg_tandem` |\n| Switch/CLLI details | **LERG** | `lerg_query` on lerg_7 |\n| Homing arrangements | **LERG** | `lerg_query` on lerg_7_sha |\n| NPA-NXX routing (OCN, switch, LATA, rate center) | **LERG** | `lerg_query` on lerg_6 |\n| Carrier/OCN names | **LERG** | `lerg_query` on lerg_1 |\n| Current TN ownership (SPID) | **LSMS** | `lrn_lookup` |\n| Current LRN for a TN | **LSMS** | `lrn_lookup` |\n| Porting state | **LSMS** | `lrn_relationship_query` or GraphQL |\n\n---\n\n## CRITICAL: Always Dip the LRN First\n\nA ported phone number's NPA-NXX often differs from its LRN's NPA-NXX. For ANY routing question about a specific phone number:\n\n1. **`lrn_lookup`** \u2192 get the LRN and SPID\n2. Extract NPA-NXX from the **LRN** (first 3 digits = NPA, next 3 = NXX)\n3. Use the **LRN's** NPA-NXX for all LERG lookups\n\n**Example:** TN 303-629-8301\n- WRONG: Look up 303-629 in LERG \u2192 returns the original carrier's data (before porting)\n- CORRECT: `lrn_lookup(\"3036298301\")` \u2192 LRN = 7207081999 \u2192 look up 720-708 in LERG \u2192 returns current carrier\n\nThis applies to: tandem, switch, LATA, OCN, rate center \u2014 anything keyed by NPA-NXX.\n\n### The Golden Pattern for Routing Questions\n\n```\nStep 1: lrn_lookup({phone_number}) \u2192 get LRN and SPID\nStep 2: Extract NPA (first 3 digits of LRN) and NXX (next 3 digits of LRN)\nStep 3: Use LRN's NPA-NXX for LERG queries:\n - lerg_tandem({npa, nxx}) \u2192 tandem switch\n - lerg_query(\"lerg_6\", ..., \"npa={npa}&nxx={nxx}\") \u2192 OCN, switch, LATA, rate center\n - lerg_query(\"lerg_7\", ..., \"switch={clli}\") \u2192 switch details\n```\n\nOr use `lookup_tn` for a quick consolidated view (dips LRN + CNAM + DNO + LERG in parallel).\n\n---\n\n## LERG Table Reference\n\n### Big 4 \u2014 Cover ~80% of Queries\n\n**lerg_1** \u2014 OCN/carrier directory\n- Fields: ocn_num, ocn_name, abbre_ocn_name, ocn_state, category, overall_ocn\n- Use: Look up carrier name by OCN. Join from lerg_6 `ocn` \u2192 lerg_1 `ocn_num`.\n- Example: `lerg_query(\"lerg_1\", \"ocn_num,ocn_name,ocn_state\", \"ocn_num=567G\")`\n\n**lerg_6** \u2014 NPA-NXX block assignments (the workhorse)\n- Fields: npa, nxx, block_id, lata, lata_name, loc_state, ocn, aocn, switch, sha_indicator, rc_abbre, rc_type, coc_type, eff_date, status\n- Has LATA + state + NPA + NXX + OCN + switch + rate center all on one row.\n- BLOCK=\"A\" = full NXX assignment; numeric 0-9 = thousands-block pooling.\n- Example: `lerg_query(\"lerg_6\", \"npa,nxx,loc_name,loc_state,ocn,switch,lata\", \"npa=720&nxx=708\")`\n\n**lerg_7_sha** \u2014 Switch homing arrangements\n- Fields: switch, sha_indicator, h_trm_d_tdm (FGD tandem), host, ocn\n- Match `switch` + `sha_indicator` from lerg_6 to find the correct tandem.\n- Or use `lerg_tandem` which does the join automatically.\n\n**lerg_12** \u2014 LRN registry\n- Fields: lrn, lata, lata_name, switch, ocn, status, eff_date\n- Which company/switch established each LRN.\n\n### Supporting Tables\n\n| Table | Purpose |\n|-------|---------|\n| lerg_3 | NPA (area code) metadata \u2014 state, effective date |\n| lerg_5 | NPA/LATA cross-reference \u2014 which NPAs exist in a LATA |\n| lerg_7 | Switch/CLLI details \u2014 address, coordinates, equipment type |\n| lerg_8 | Rate center details \u2014 geography, V&H coordinates |\n| lerg_8_loc | Localities (towns) \u2192 rate centers |\n| lerg_8_pst | ZIP codes \u2192 localities (US only) |\n| lerg_9 | Homing by tandem \u2014 \"top-down\" view of which NPA-NXXs subtend a tandem |\n| lerg_4 | SS7 point codes |\n| lerg_10 | NPA-NXX \u2192 operator services ATC |\n| lerg_11 | Locality \u2192 operator services ATC |\n| lerg_16 | IP capability by LRN |\n| lerg_17 | IP capability by NPA-NXX |\n\nUse `lerg_table_info` to list all tables or get the schema for a specific one.\n\n---\n\n## LERG Query Syntax\n\nREST queries use path-based filters with `&` joining multiple conditions:\n- Single filter: `lerg_query(\"lerg_1\", \"ocn_num,ocn_name\", \"ocn_state=CO\")`\n- Multiple filters: `lerg_query(\"lerg_6\", \"npa,nxx,loc_name,ocn\", \"npa=303&nxx=629\")`\n\nFor complex queries with JOINs and advanced operators, use `lerg_complex_query`:\n```json\n{\n \"table\": \"lerg_6\",\n \"fields\": [\"npa\", \"nxx\", \"ocn\", \"loc_name\"],\n \"filters\": [{\"field\": \"npa\", \"operator\": \"eq\", \"value\": 720}],\n \"join\": {\n \"table\": \"lerg_1\",\n \"on\": [{\"left_field\": \"ocn\", \"right_field\": \"ocn_num\"}],\n \"fields\": [\"ocn_name\", \"ocn_state\"]\n }\n}\n```\n\nFilter operators: eq, ne, gt, gte, lt, lte, like, in, isnull, isnotnull.\n\n---\n\n## CNAM (Caller Name)\n\n`cnam_lookup` queries TransUnion's LIDB for the caller name associated with a phone number.\n\n- Returns: calling_name (up to 15 chars), calling_name_status (available/unavailable), presentation_indicator (allowed/restricted)\n- Results cached server-side for 24 hours\n- \"WIRELESS CALLER\" typically means the carrier hasn't provisioned a specific CNAM entry\n\n---\n\n## DNO (Do Not Originate)\n\n`dno_check` checks if a phone number should never appear as a caller ID.\n\n- DNO numbers belong to entities that only receive calls (IRS, major banks, government agencies)\n- If is_dno=true, the number appearing as caller ID indicates **spoofing/fraud**\n- Supports prefix matching: 3-digit (NPA), 6-digit (NPA-NXX), 7-digit, or 10-digit patterns\n- Response includes: is_dno, matched_pattern, source\n\n---\n\n## RouteLink \u2014 Toll-Free Number Routing\n\n### ROR (Responsible Organization)\n`routelink_lookup` with lookup_type=\"ror\" returns the RespOrg managing a toll-free number.\n- RespOrg is a 5-char code (e.g., \"NEX01\", \"TZN99\")\n- Only needs the CRN (toll-free number), no ANI/LATA required\n\n### CIC (Carrier Identification Code)\n`routelink_lookup` with lookup_type=\"cic\" or \"cicror\" returns which carrier handles calls to a toll-free number FROM a specific caller (ANI) in a specific LATA.\n- Requires: crn, ani (10-digit), lata (3-digit)\n- Common carrier codes: 0288 (AT&T), 0222 (MCI/Verizon), 0333 (Sprint), 0432 (Lumen)\n\n### CPR (Call Processing Record)\n`routelink_cpr` retrieves the full routing decision tree for a toll-free number.\n\n**Decision tree node types:**\n| Node | Description |\n|------|-------------|\n| [LATA] | Routes by caller's LATA |\n| [NPA] | Routes by caller's area code |\n| [NXX] | Routes by caller's exchange |\n| [STATE] | Routes by caller's state |\n| [DAY_OF_WEEK] | Routes by day (1-7, Sunday=1) |\n| [TIME_OF_DAY] | Routes by time (15-min intervals) |\n| [PERCENT] | Percentage-based load balancing |\n| [ANI] | Routes by specific caller number |\n\n**Action types:** Carrier XXXX (4-digit carrier code), Template XXXXXXXXXX (reference to template CRN), Routing XXXXX, NMC X (Network Management Class)\n\n**Common patterns:**\n- Simple: All calls \u2192 one carrier \u2192 one termination number\n- Time-of-day: Business hours \u2192 office, after hours \u2192 answering service\n- Geographic: Different terminations per LATA/state/NPA\n- Percentage: Load balancing across call centers (e.g., 60%/40%)\n\nUse `expand=true` (default) to recursively resolve template references into their full decision trees.\n\n---\n\n## GraphQL\n\nUse `graphql_query` for complex queries not possible with REST:\n- Cross-table relationship joins (lerg6 \u2192 carrier, lerg6 \u2192 switchInfo, lerg7Sha \u2192 tandemSwitch)\n- Filtering by fields not in REST endpoints\n- Multiple related data points in one query\n\n### LERG GraphQL (`service=\"lerg\"`)\n\n**CRITICAL naming rules:**\n- **Query names** are camelCase: `lerg1`, `lerg6`, `lerg7Sha`, `lerg7ShaIns`, `lerg12`, etc.\n- **Return fields** MUST be camelCase: `ocnName`, `locState`, `locName`, `shaIndicator`, `hTrmDTdm`\n- **Filter field names** accept BOTH camelCase (`ocnName`) and snake_case (`ocn_name`)\n- **All values are strings** \u2014 even numeric fields. Always quote: `value: \"720\"` not `value: 720`\n- **LERG data is stored UPPERCASE** \u2014 LIKE patterns must use uppercase: `%VERIZON%` not `%verizon%`\n\n**FilterInput:**\n```graphql\n{ field: \"ocnName\", op: LIKE, value: \"%VERIZON%\" } # partial match (UPPERCASE!)\n{ field: \"npa\", op: EQ, value: \"720\" } # exact match\n{ field: \"npa\", op: IN, values: [\"212\", \"646\", \"917\"] } # IN uses 'values' (plural), not 'value'\n```\n\nOperators: EQ, NE, GT, GTE, LT, LTE, LIKE, IN, IS_NULL, IS_NOT_NULL\n\n**Predefined relationships** (avoid N+1 \u2014 use these instead of separate queries):\n- `lerg6` \u2192 `carrier` (joins to lerg1 via OCN), `switchInfo` (\u2192 lerg7), `homingArrangements` (\u2192 lerg7Sha)\n- `lerg7` \u2192 `carrier` (\u2192 lerg1)\n- `lerg7Sha` \u2192 `tandemSwitch` (\u2192 lerg7)\n\n**Example \u2014 find all Verizon OCNs:**\n```graphql\n{\n lerg1(\n filters: [{ field: \"ocnName\", op: LIKE, value: \"%VERIZON%\" }]\n pagination: { limit: 10 }\n ) {\n ocnNum\n ocnName\n ocnState\n category\n }\n}\n```\n\n**Example \u2014 NPA-NXX with carrier and switch in one query:**\n```graphql\n{\n lerg6(\n filters: [{ field: \"npa\", op: EQ, value: \"303\" }, { field: \"nxx\", op: EQ, value: \"629\" }]\n pagination: { limit: 1 }\n ) {\n npa nxx ocn locName switch\n carrier { ocnNum ocnName ocnState }\n switchInfo { switch eqpType swCity swState }\n homingArrangements {\n shaIndicator hTrmDTdm\n tandemSwitch { switch swCity swState }\n }\n }\n}\n```\n\n**dynamicJoin** \u2014 for arbitrary cross-table SQL joins (returns raw JSON):\n```graphql\n{\n dynamicJoin(input: {\n table: \"lerg_6\"\n fields: [\"npa\", \"nxx\", \"ocn\", \"locName\"]\n filters: [{ field: \"locName\", op: LIKE, value: \"%DENVER%\" }]\n join: {\n table: \"lerg_1\"\n fields: [\"ocnName\", \"category\"]\n on: [{ leftField: \"ocn\", rightField: \"ocnNum\" }]\n joinType: \"INNER\"\n }\n pagination: { limit: 10 }\n })\n}\n```\nNote: dynamicJoin uses snake_case table IDs (lerg_6, lerg_1, lerg_7_sha) and returns raw PostgreSQL column names.\n\n### LSMS GraphQL (`service=\"lsms\"`)\n\nThe LSMS GraphQL API is a **completely separate implementation** from LERG GraphQL. It has different query patterns, different field naming, and different schema design. Do NOT use LERG-style FilterInput syntax with LSMS.\n\n**5 tables:**\n\n| Table | ~Rows | Requires Filter? |\n|-------|-------|------------------|\n| subscriptionVersions | 514M | Yes (phoneNumber, lrn, or spid) |\n| numberBlocks | 751K | Yes (npanxxx, spid, or lrn) |\n| serviceProviders | 5.2K | No |\n| locationRoutingNumbers | 56K | No |\n| npanxx | 192K | No |\n\n**Query patterns (NOT FilterInput \u2014 uses typed named parameters):**\n```graphql\n# Single phone number lookup\n{ subscriptionVersion(phoneNumber: \"3036298301\") { phoneNumber lrn spid serviceProvider { name } } }\n\n# Paginated list by LRN (returns totalCount, hasMore)\n{ subscriptionVersionsByLrn(lrn: \"7207081999\", limit: 10) { totalCount hasMore items { phoneNumber spid } } }\n\n# Paginated list by SPID\n{ subscriptionVersionsBySpid(spid: \"567G\", limit: 10) { totalCount hasMore items { phoneNumber lrn } } }\n\n# Number block lookup\n{ numberBlock(npanxxx: \"3035551\") { npanxxx lrn spid serviceProvider { name } } }\n\n# List carriers\n{ serviceProviders(limit: 20) { spid name npacRegion } }\n\n# LRN metadata\n{ locationRoutingNumber(lrn: \"7207081999\") { lrn ocn switchInfo } }\n\n# NPA-NXX codes for a carrier\n{ npanxxBySpid(spid: \"567G\", limit: 50) { npa nxx effectiveTimestamp } }\n\n# Database statistics\n{ lsmsStats { activeSubscriptionVersions activeNumberBlocks totalServiceProviders } }\n```\n\n**Relationships** (use DataLoader batching \u2014 no N+1):\n- subscriptionVersion \u2192 `serviceProvider`, `lrnMetadata`\n- numberBlock \u2192 `serviceProvider`, `lrnMetadata`\n- npanxx \u2192 `serviceProvider`\n\n**Safety limits:** max 1000 results, depth 5, complexity 200, 10-second statement timeout\n**Auto-filters:** Soft-deletable records filter to is_active = true automatically\n**Note:** Phone numbers/LRNs are strings (stored as BIGINT but GraphQL Int is 32-bit). SPIDs are auto-trimmed.\n\n---\n\n## Key Telecom Concepts\n\n| Term | Definition |\n|------|-----------|\n| **LRN** | Location Routing Number \u2014 identifies the switch serving a ported number |\n| **SPID** | Service Provider ID \u2014 identifies the carrier that owns a number |\n| **OCN** | Operating Company Number \u2014 4-char carrier identifier (often same as SPID) |\n| **NPA** | Numbering Plan Area \u2014 3-digit area code |\n| **NXX** | Exchange code \u2014 next 3 digits after area code |\n| **LATA** | Local Access Transport Area \u2014 geographic region for call routing |\n| **CLLI** | Common Language Location Identifier \u2014 8-11 char switch/building code |\n| **CRN** | Call Routing Number \u2014 a toll-free number in RouteLink context |\n| **ROR/RespOrg** | Responsible Organization \u2014 entity managing a toll-free number's routing |\n| **CIC** | Carrier Identification Code \u2014 identifies which carrier handles a toll-free call |\n| **CPR** | Call Processing Record \u2014 routing decision tree for a toll-free number |\n| **Rate Center** | Geographic area defining local calling boundaries |\n| **Tandem** | A switching office that connects local switches to the long-distance network |\n| **Homing** | The relationship between a local switch and its tandem |\n\n---\n\n## Common Mistakes to Avoid\n\n1. **Never guess carrier names** \u2014 always query lerg_1 by OCN\n2. **Never skip the LRN dip** \u2014 ported TNs have different NPA-NXX than their LRN\n3. **Never look for tandem/switch data in LSMS** \u2014 LSMS has no infrastructure data\n4. **Never look for TN-level data in LERG** \u2014 LERG has no per-phone-number data\n5. **Never query LSMS subscriptionVersions without a filter** \u2014 514M rows will timeout\n6. **Always use the LRN's NPA-NXX** (not the TN's) for LERG routing lookups\n7. **GraphQL return fields must be camelCase** \u2014 `ocnName` not `ocn_name`, `locState` not `loc_state`\n8. **LIKE patterns must be UPPERCASE** \u2014 LERG data is uppercase, so `%VERIZON%` works but `%verizon%` returns nothing\n9. **IN operator uses `values` (plural)** \u2014 `{ field: \"npa\", op: IN, values: [\"212\", \"646\"] }` not `value`\n";
3
+ export declare const TELIQUE_KNOWLEDGE = "# Telique Telecom API Knowledge Base\n\nYou have access to 13 Telique tools for querying live telecom data. NEVER guess carrier names, LRNs, routing data, or CNAM \u2014 always query the API.\n\n---\n\n## CRITICAL: LERG vs LSMS \u2014 Two Different Databases\n\n**LERG** = Static telecom infrastructure reference (updated monthly from iconectiv BIRRDS)\n- NPA-NXX assignments, switches, tandems, homing arrangements, rate centers, LATAs, carrier names by OCN\n- 27 tables. NO individual phone number data.\n- Query with: `lerg_query`, `lerg_complex_query`, `lerg_tandem`, `lerg_table_info`, or `graphql_query(service=\"lerg\")`\n\n**LSMS** = Live NPAC porting data (refreshed within minutes)\n- Phone number ownership (SPID), LRN assignments, porting state\n- NO routing infrastructure (no tandems, switches, homing, rate centers, OCN names)\n- Query with: `lrn_lookup`, `lrn_relationship_query`, or `graphql_query(service=\"lsms\")`\n\n| Data Type | Database | Tool |\n|-----------|----------|------|\n| Tandem switches | **LERG** | `lerg_tandem` |\n| Switch/CLLI details | **LERG** | `lerg_query` on lerg_7 |\n| Homing arrangements | **LERG** | `lerg_query` on lerg_7_sha |\n| NPA-NXX routing (OCN, switch, LATA, rate center) | **LERG** | `lerg_query` on lerg_6 |\n| Carrier/OCN names | **LERG** | `lerg_query` on lerg_1 |\n| Current TN ownership (SPID) | **LSMS** | `lrn_lookup` |\n| Current LRN for a TN | **LSMS** | `lrn_lookup` |\n| Porting state | **LSMS** | `lrn_relationship_query` or GraphQL |\n\n---\n\n## CRITICAL: Always Dip the LRN First\n\nA ported phone number's NPA-NXX often differs from its LRN's NPA-NXX. For ANY routing question about a specific phone number:\n\n1. **`lrn_lookup`** \u2192 get the LRN and SPID\n2. Extract NPA-NXX from the **LRN** (first 3 digits = NPA, next 3 = NXX)\n3. Use the **LRN's** NPA-NXX for all LERG lookups\n\n**Example:** TN 303-629-8301\n- WRONG: Look up 303-629 in LERG \u2192 returns the original carrier's data (before porting)\n- CORRECT: `lrn_lookup(\"3036298301\")` \u2192 LRN = 7207081999 \u2192 look up 720-708 in LERG \u2192 returns current carrier\n\nThis applies to: tandem, switch, LATA, OCN, rate center \u2014 anything keyed by NPA-NXX.\n\n### The Golden Pattern for Routing Questions\n\n```\nStep 1: lrn_lookup({phone_number}) \u2192 get LRN and SPID\nStep 2: Extract NPA (first 3 digits of LRN) and NXX (next 3 digits of LRN)\nStep 3: Use LRN's NPA-NXX for LERG queries:\n - lerg_tandem({npa, nxx}) \u2192 tandem switch\n - lerg_query(\"lerg_6\", ..., \"npa={npa}&nxx={nxx}\") \u2192 OCN, switch, LATA, rate center\n - lerg_query(\"lerg_7\", ..., \"switch={clli}\") \u2192 switch details\n```\n\nOr use `lookup_tn` for a quick consolidated view (dips LRN + CNAM + DNO + LERG in parallel).\n\n---\n\n## LERG Table Reference\n\n### Big 4 \u2014 Cover ~80% of Queries\n\n**lerg_1** \u2014 OCN/carrier directory\n- Fields: ocn_num, ocn_name, abbre_ocn_name, ocn_state, category, overall_ocn\n- Use: Look up carrier name by OCN. Join from lerg_6 `ocn` \u2192 lerg_1 `ocn_num`.\n- Example: `lerg_query(\"lerg_1\", \"ocn_num,ocn_name,ocn_state\", \"ocn_num=567G\")`\n\n**lerg_6** \u2014 NPA-NXX block assignments (the workhorse)\n- Fields: npa, nxx, block_id, lata, lata_name, loc_state, ocn, aocn, switch, sha_indicator, rc_abbre, rc_type, coc_type, eff_date, status\n- Has LATA + state + NPA + NXX + OCN + switch + rate center all on one row.\n- BLOCK=\"A\" = full NXX assignment; numeric 0-9 = thousands-block pooling.\n- Example: `lerg_query(\"lerg_6\", \"npa,nxx,loc_name,loc_state,ocn,switch,lata\", \"npa=720&nxx=708\")`\n\n**lerg_7_sha** \u2014 Switch homing arrangements\n- Fields: switch, sha_indicator, h_trm_d_tdm (FGD tandem), host, ocn\n- Match `switch` + `sha_indicator` from lerg_6 to find the correct tandem.\n- Or use `lerg_tandem` which does the join automatically.\n\n**lerg_12** \u2014 LRN registry\n- Fields: lrn, lata, lata_name, switch, ocn, status, eff_date\n- Which company/switch established each LRN.\n\n### Supporting Tables\n\n| Table | Purpose |\n|-------|---------|\n| lerg_3 | NPA (area code) metadata \u2014 state, effective date |\n| lerg_5 | NPA/LATA cross-reference \u2014 which NPAs exist in a LATA |\n| lerg_7 | Switch/CLLI details \u2014 address, coordinates, equipment type |\n| lerg_8 | Rate center details \u2014 geography, V&H coordinates |\n| lerg_8_loc | Localities (towns) \u2192 rate centers |\n| lerg_8_pst | ZIP codes \u2192 localities (US only) |\n| lerg_9 | Homing by tandem \u2014 \"top-down\" view of which NPA-NXXs subtend a tandem |\n| lerg_4 | SS7 point codes |\n| lerg_10 | NPA-NXX \u2192 operator services ATC |\n| lerg_11 | Locality \u2192 operator services ATC |\n| lerg_16 | IP capability by LRN |\n| lerg_17 | IP capability by NPA-NXX |\n\nUse `lerg_table_info` to list all tables or get the schema for a specific one.\n\n---\n\n## LERG Query Syntax\n\nREST queries use path-based filters with `&` joining multiple conditions:\n- Single filter: `lerg_query(\"lerg_1\", \"ocn_num,ocn_name\", \"ocn_state=CO\")`\n- Multiple filters: `lerg_query(\"lerg_6\", \"npa,nxx,loc_name,ocn\", \"npa=303&nxx=629\")`\n\nFor complex queries with JOINs and advanced operators, use `lerg_complex_query`:\n```json\n{\n \"table\": \"lerg_6\",\n \"fields\": [\"npa\", \"nxx\", \"ocn\", \"loc_name\"],\n \"filters\": [{\"field\": \"npa\", \"operator\": \"eq\", \"value\": 720}],\n \"join\": {\n \"table\": \"lerg_1\",\n \"on\": [{\"left_field\": \"ocn\", \"right_field\": \"ocn_num\"}],\n \"fields\": [\"ocn_name\", \"ocn_state\"]\n }\n}\n```\n\nFilter operators: eq, ne, gt, gte, lt, lte, like, in, notin, isnull, isnotnull.\nUse `*` as the fields value to return all fields from a table.\nNote: REST `like` is **case-insensitive** (in-memory matching), unlike GraphQL LIKE which is case-sensitive (PostgreSQL).\n\n---\n\n## CNAM (Caller Name)\n\n`cnam_lookup` queries TransUnion's LIDB for the caller name associated with a phone number.\n\n- Returns: calling_name (up to 15 chars), calling_name_status (available/unavailable), presentation_indicator (allowed/restricted)\n- Results cached server-side for 24 hours\n- \"WIRELESS CALLER\" typically means the carrier hasn't provisioned a specific CNAM entry\n\n---\n\n## DNO (Do Not Originate)\n\n`dno_check` checks if a phone number should never appear as a caller ID.\n\n- DNO numbers belong to entities that only receive calls (IRS, major banks, government agencies)\n- If is_dno=true, the number appearing as caller ID indicates **spoofing/fraud**\n- Supports prefix matching: 3-digit (NPA), 6-digit (NPA-NXX), 7-digit, or 10-digit patterns\n- Response includes: is_dno, matched_pattern, source\n\n---\n\n## RouteLink \u2014 Toll-Free Number Routing\n\n### ROR (Responsible Organization)\n`routelink_lookup` with lookup_type=\"ror\" returns the RespOrg managing a toll-free number.\n- RespOrg is a 5-char code (e.g., \"NEX01\", \"TZN99\")\n- Only needs the CRN (toll-free number), no ANI/LATA required\n\n### CIC (Carrier Identification Code)\n`routelink_lookup` with lookup_type=\"cic\" or \"cicror\" returns which carrier handles calls to a toll-free number FROM a specific caller (ANI) in a specific LATA.\n- Requires: crn, ani (10-digit), lata (3-digit)\n- Common carrier codes: 0288 (AT&T), 0222 (MCI/Verizon), 0333 (Sprint), 0432 (Lumen)\n\n### CPR (Call Processing Record)\n`routelink_cpr` retrieves the full routing decision tree for a toll-free number.\n\n**Decision tree node types:**\n| Node | Description |\n|------|-------------|\n| [LATA] | Routes by caller's LATA |\n| [NPA] | Routes by caller's area code |\n| [NXX] | Routes by caller's exchange |\n| [STATE] | Routes by caller's state |\n| [DAY_OF_WEEK] | Routes by day (1-7, Sunday=1) |\n| [TIME_OF_DAY] | Routes by time (15-min intervals) |\n| [PERCENT] | Percentage-based load balancing |\n| [ANI] | Routes by specific caller number |\n\n**Action types:** Carrier XXXX (4-digit carrier code), Template XXXXXXXXXX (reference to template CRN), Routing XXXXX, NMC X (Network Management Class)\n\n**Common patterns:**\n- Simple: All calls \u2192 one carrier \u2192 one termination number\n- Time-of-day: Business hours \u2192 office, after hours \u2192 answering service\n- Geographic: Different terminations per LATA/state/NPA\n- Percentage: Load balancing across call centers (e.g., 60%/40%)\n\nUse `expand=true` (default) to recursively resolve template references into their full decision trees.\n\n---\n\n## GraphQL\n\nUse `graphql_query` for complex queries not possible with REST:\n- Cross-table relationship joins (lerg6 \u2192 carrier, lerg6 \u2192 switchInfo, lerg7Sha \u2192 tandemSwitch)\n- Filtering by fields not in REST endpoints\n- Multiple related data points in one query\n\n### LERG GraphQL (`service=\"lerg\"`)\n\n**CRITICAL naming rules:**\n- **Query names** are camelCase: `lerg1`, `lerg6`, `lerg7Sha`, `lerg7ShaIns`, `lerg12`, etc.\n- **Return fields** MUST be camelCase: `ocnName`, `locState`, `locName`, `shaIndicator`, `hTrmDTdm`\n- **Filter field names** accept BOTH camelCase (`ocnName`) and snake_case (`ocn_name`)\n- **All values are strings** \u2014 even numeric fields. Always quote: `value: \"720\"` not `value: 720`\n- **GraphQL LIKE is case-sensitive (SQL)** \u2014 LERG data is stored UPPERCASE, so patterns must be uppercase: `%VERIZON%` not `%verizon%`. (The REST `like` operator is case-insensitive \u2014 this only applies to GraphQL.)\n\n**FilterInput:**\n```graphql\n{ field: \"ocnName\", op: LIKE, value: \"%VERIZON%\" } # partial match (UPPERCASE!)\n{ field: \"npa\", op: EQ, value: \"720\" } # exact match\n{ field: \"npa\", op: IN, values: [\"212\", \"646\", \"917\"] } # IN uses 'values' (plural), not 'value'\n```\n\nOperators: EQ, NE, GT, GTE, LT, LTE, LIKE, IN, IS_NULL, IS_NOT_NULL\n\n**Predefined relationships** (avoid N+1 \u2014 use these instead of separate queries):\n- `lerg6` \u2192 `carrier` (joins to lerg1 via OCN), `switchInfo` (\u2192 lerg7), `homingArrangements` (\u2192 lerg7Sha)\n- `lerg7` \u2192 `carrier` (\u2192 lerg1)\n- `lerg7Sha` \u2192 `tandemSwitch` (\u2192 lerg7)\n\n**Example \u2014 find all Verizon OCNs:**\n```graphql\n{\n lerg1(\n filters: [{ field: \"ocnName\", op: LIKE, value: \"%VERIZON%\" }]\n pagination: { limit: 10 }\n ) {\n ocnNum\n ocnName\n ocnState\n category\n }\n}\n```\n\n**Example \u2014 NPA-NXX with carrier and switch in one query:**\n```graphql\n{\n lerg6(\n filters: [{ field: \"npa\", op: EQ, value: \"303\" }, { field: \"nxx\", op: EQ, value: \"629\" }]\n pagination: { limit: 1 }\n ) {\n npa nxx ocn locName switch\n carrier { ocnNum ocnName ocnState }\n switchInfo { switch eqpType swCity swState }\n homingArrangements {\n shaIndicator hTrmDTdm\n tandemSwitch { switch swCity swState }\n }\n }\n}\n```\n\n**dynamicJoin** \u2014 for arbitrary cross-table SQL joins (returns raw JSON):\n```graphql\n{\n dynamicJoin(input: {\n table: \"lerg_6\"\n fields: [\"npa\", \"nxx\", \"ocn\", \"locName\"]\n filters: [{ field: \"locName\", op: LIKE, value: \"%DENVER%\" }]\n join: {\n table: \"lerg_1\"\n fields: [\"ocnName\", \"category\"]\n on: [{ leftField: \"ocn\", rightField: \"ocnNum\" }]\n joinType: \"INNER\"\n }\n pagination: { limit: 10 }\n })\n}\n```\nNote: dynamicJoin uses snake_case table IDs (lerg_6, lerg_1, lerg_7_sha) and returns raw PostgreSQL column names (UPPERCASE with spaces, e.g., `\"OCN_NAME\"`, `\"LOC NAME\"`, `\"EFF DATE\"`), NOT GraphQL camelCase.\n\n### LSMS GraphQL (`service=\"lsms\"`)\n\nThe LSMS GraphQL API is a **completely separate implementation** from LERG GraphQL. It has different query patterns, different field naming, and different schema design. Do NOT use LERG-style FilterInput syntax with LSMS.\n\n**5 tables:**\n\n| Table | ~Rows | Requires Filter? |\n|-------|-------|------------------|\n| subscriptionVersions | 514M | Yes (phoneNumber, lrn, or spid) |\n| numberBlocks | 751K | Yes (npanxxx, spid, or lrn) |\n| serviceProviders | 5.2K | No |\n| locationRoutingNumbers | 56K | No |\n| npanxx | 192K | No |\n\n**Query patterns (NOT FilterInput \u2014 uses typed named parameters):**\n```graphql\n# Single phone number lookup\n{ subscriptionVersion(phoneNumber: \"3036298301\") { phoneNumber lrn spid serviceProvider { name } } }\n\n# Paginated list by LRN (returns totalCount, hasMore)\n{ subscriptionVersionsByLrn(lrn: \"7207081999\", limit: 10) { totalCount hasMore items { phoneNumber spid } } }\n\n# Paginated list by SPID\n{ subscriptionVersionsBySpid(spid: \"567G\", limit: 10) { totalCount hasMore items { phoneNumber lrn } } }\n\n# Number block lookup\n{ numberBlock(npanxxx: \"3035551\") { npanxxx lrn spid serviceProvider { name } } }\n\n# List carriers\n{ serviceProviders(limit: 20) { spid name npacRegion } }\n\n# LRN metadata\n{ locationRoutingNumber(lrn: \"7207081999\") { lrn ocn switchInfo } }\n\n# NPA-NXX codes for a carrier\n{ npanxxBySpid(spid: \"567G\", limit: 50) { npa nxx effectiveTimestamp } }\n\n# Database statistics\n{ lsmsStats { activeSubscriptionVersions activeNumberBlocks totalServiceProviders } }\n```\n\n**Relationships** (use DataLoader batching \u2014 no N+1):\n- subscriptionVersion \u2192 `serviceProvider`, `lrnMetadata`\n- numberBlock \u2192 `serviceProvider`, `lrnMetadata`\n- npanxx \u2192 `serviceProvider`\n\n**Safety limits:** max 1000 results, depth 5, complexity 200, 10-second statement timeout\n**Auto-filters:** Soft-deletable records filter to is_active = true automatically\n**Note:** Phone numbers/LRNs are strings (stored as BIGINT but GraphQL Int is 32-bit). SPIDs are auto-trimmed.\n\n---\n\n## Key Telecom Concepts\n\n| Term | Definition |\n|------|-----------|\n| **LRN** | Location Routing Number \u2014 identifies the switch serving a ported number |\n| **SPID** | Service Provider ID \u2014 identifies the carrier that owns a number |\n| **OCN** | Operating Company Number \u2014 4-char carrier identifier (often same as SPID) |\n| **NPA** | Numbering Plan Area \u2014 3-digit area code |\n| **NXX** | Exchange code \u2014 next 3 digits after area code |\n| **LATA** | Local Access Transport Area \u2014 geographic region for call routing |\n| **CLLI** | Common Language Location Identifier \u2014 8-11 char switch/building code |\n| **CRN** | Call Routing Number \u2014 a toll-free number in RouteLink context |\n| **ROR/RespOrg** | Responsible Organization \u2014 entity managing a toll-free number's routing |\n| **CIC** | Carrier Identification Code \u2014 identifies which carrier handles a toll-free call |\n| **CPR** | Call Processing Record \u2014 routing decision tree for a toll-free number |\n| **Rate Center** | Geographic area defining local calling boundaries |\n| **Tandem** | A switching office that connects local switches to the long-distance network |\n| **Homing** | The relationship between a local switch and its tandem |\n\n---\n\n## Common Mistakes to Avoid\n\n1. **Never guess carrier names** \u2014 always query lerg_1 by OCN\n2. **Never skip the LRN dip** \u2014 ported TNs have different NPA-NXX than their LRN\n3. **Never look for tandem/switch data in LSMS** \u2014 LSMS has no infrastructure data\n4. **Never look for TN-level data in LERG** \u2014 LERG has no per-phone-number data\n5. **Never query LSMS subscriptionVersions without a filter** \u2014 514M rows will timeout\n6. **Always use the LRN's NPA-NXX** (not the TN's) for LERG routing lookups\n7. **GraphQL return fields must be camelCase** \u2014 `ocnName` not `ocn_name`, `locState` not `loc_state`\n8. **GraphQL LIKE patterns must be UPPERCASE** \u2014 LERG data is uppercase in PostgreSQL, so `%VERIZON%` works but `%verizon%` returns nothing. (REST `like` is case-insensitive \u2014 this only applies to GraphQL.)\n9. **IN operator uses `values` (plural)** \u2014 `{ field: \"npa\", op: IN, values: [\"212\", \"646\"] }` not `value`\n";
package/dist/knowledge.js CHANGED
@@ -136,7 +136,9 @@ For complex queries with JOINs and advanced operators, use \`lerg_complex_query\
136
136
  }
137
137
  \`\`\`
138
138
 
139
- Filter operators: eq, ne, gt, gte, lt, lte, like, in, isnull, isnotnull.
139
+ Filter operators: eq, ne, gt, gte, lt, lte, like, in, notin, isnull, isnotnull.
140
+ Use \`*\` as the fields value to return all fields from a table.
141
+ Note: REST \`like\` is **case-insensitive** (in-memory matching), unlike GraphQL LIKE which is case-sensitive (PostgreSQL).
140
142
 
141
143
  ---
142
144
 
@@ -214,7 +216,7 @@ Use \`graphql_query\` for complex queries not possible with REST:
214
216
  - **Return fields** MUST be camelCase: \`ocnName\`, \`locState\`, \`locName\`, \`shaIndicator\`, \`hTrmDTdm\`
215
217
  - **Filter field names** accept BOTH camelCase (\`ocnName\`) and snake_case (\`ocn_name\`)
216
218
  - **All values are strings** — even numeric fields. Always quote: \`value: "720"\` not \`value: 720\`
217
- - **LERG data is stored UPPERCASE** LIKE patterns must use uppercase: \`%VERIZON%\` not \`%verizon%\`
219
+ - **GraphQL LIKE is case-sensitive (SQL)** — LERG data is stored UPPERCASE, so patterns must be uppercase: \`%VERIZON%\` not \`%verizon%\`. (The REST \`like\` operator is case-insensitive — this only applies to GraphQL.)
218
220
 
219
221
  **FilterInput:**
220
222
  \`\`\`graphql
@@ -280,7 +282,7 @@ Operators: EQ, NE, GT, GTE, LT, LTE, LIKE, IN, IS_NULL, IS_NOT_NULL
280
282
  })
281
283
  }
282
284
  \`\`\`
283
- Note: dynamicJoin uses snake_case table IDs (lerg_6, lerg_1, lerg_7_sha) and returns raw PostgreSQL column names.
285
+ Note: dynamicJoin uses snake_case table IDs (lerg_6, lerg_1, lerg_7_sha) and returns raw PostgreSQL column names (UPPERCASE with spaces, e.g., \`"OCN_NAME"\`, \`"LOC NAME"\`, \`"EFF DATE"\`), NOT GraphQL camelCase.
284
286
 
285
287
  ### LSMS GraphQL (\`service="lsms"\`)
286
288
 
@@ -364,6 +366,6 @@ The LSMS GraphQL API is a **completely separate implementation** from LERG Graph
364
366
  5. **Never query LSMS subscriptionVersions without a filter** — 514M rows will timeout
365
367
  6. **Always use the LRN's NPA-NXX** (not the TN's) for LERG routing lookups
366
368
  7. **GraphQL return fields must be camelCase** — \`ocnName\` not \`ocn_name\`, \`locState\` not \`loc_state\`
367
- 8. **LIKE patterns must be UPPERCASE** — LERG data is uppercase, so \`%VERIZON%\` works but \`%verizon%\` returns nothing
369
+ 8. **GraphQL LIKE patterns must be UPPERCASE** — LERG data is uppercase in PostgreSQL, so \`%VERIZON%\` works but \`%verizon%\` returns nothing. (REST \`like\` is case-insensitive — this only applies to GraphQL.)
368
370
  9. **IN operator uses \`values\` (plural)** — \`{ field: "npa", op: IN, values: ["212", "646"] }\` not \`value\`
369
371
  `;
@@ -141,11 +141,58 @@ export function registerLergTools(server, client) {
141
141
  const result = await client.post("/v1/telique/lerg/query", body);
142
142
  return formatResponse(result);
143
143
  });
144
- server.tool("lerg_tandem", "Look up tandem routing information for a given NPA-NXX. Returns the tandem switch and routing path for calls to a specific area code and exchange. Uses SQL JOINs across LERG tables (lerg_6, lerg_7, lerg_7_sha) for comprehensive routing data.", {
145
- npa: z.string().regex(/^\d{3}$/).describe("3-digit area code (NPA)"),
146
- nxx: z.string().regex(/^\d{3}$/).describe("3-digit exchange code (NXX)"),
147
- }, async ({ npa, nxx }) => {
148
- const result = await client.get("/v1/telique/lerg/tandem", { npa, nxx });
144
+ server.tool("lerg_tandem", "Look up tandem routing information. Query by NPA+NXX, switch CLLI, tandem CLLI, or carrier name pattern. Returns tandem switch, OCN, LATA, and routing path via SQL JOINs across lerg_6, lerg_7_sha, and lerg_1.", {
145
+ npa: z
146
+ .string()
147
+ .regex(/^\d{3}$/)
148
+ .optional()
149
+ .describe("3-digit area code (NPA) — use with nxx"),
150
+ nxx: z
151
+ .string()
152
+ .regex(/^\d{3}$/)
153
+ .optional()
154
+ .describe("3-digit exchange code (NXX) — use with npa"),
155
+ switch_clli: z
156
+ .string()
157
+ .optional()
158
+ .describe("Switch CLLI code (e.g. DNVRCOMADS0)"),
159
+ tandem: z
160
+ .string()
161
+ .optional()
162
+ .describe("Tandem CLLI code — reverse lookup to find what subtends it"),
163
+ name: z
164
+ .string()
165
+ .optional()
166
+ .describe("Carrier name pattern with % wildcard (e.g. %VERIZON%). Case-insensitive."),
167
+ limit: z
168
+ .number()
169
+ .int()
170
+ .min(1)
171
+ .max(10000)
172
+ .default(100)
173
+ .describe("Max results (default 100)"),
174
+ offset: z
175
+ .number()
176
+ .int()
177
+ .min(0)
178
+ .default(0)
179
+ .describe("Pagination offset (default 0)"),
180
+ }, async ({ npa, nxx, switch_clli, tandem, name, limit, offset }) => {
181
+ const params = {
182
+ limit,
183
+ offset,
184
+ };
185
+ if (npa)
186
+ params.npa = npa;
187
+ if (nxx)
188
+ params.nxx = nxx;
189
+ if (switch_clli)
190
+ params.switch = switch_clli;
191
+ if (tandem)
192
+ params.tandem = tandem;
193
+ if (name)
194
+ params.name = name;
195
+ const result = await client.get("/v1/telique/lerg/tandem", params);
149
196
  return formatResponse(result);
150
197
  });
151
198
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "telique-mcp",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "description": "MCP server for Telique telecom APIs (RouteLink, LRN, CNAM, LERG)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",