skills/legal/contracts-query-enhancer/SKILL.md
Enhance contract search queries with contextual understanding, mapping natural language to contract fields, obligation types, clause categories, and CLM-specific terminology
npx skillsauth add happy-technologies-llc/happy-servicenow-skills contracts-query-enhancerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill transforms natural language contract search queries into precise ServiceNow queries, enabling users to find contracts without knowing table structures or encoded query syntax. It covers:
When to use:
contract_reader, contract_manager, sn_clm.user, or admincom.snc.contract_management (Contract Management) or com.sn_clm (Contract Lifecycle Management)| Natural Language Term | ServiceNow Field | Table | Encoded Query Example | |----------------------|------------------|-------|----------------------| | "vendor", "supplier", "provider" | vendor | ast_contract | vendor.nameLIKE[term] | | "expires", "expiring", "end date" | ends | ast_contract | ends<javascript:gs.daysAgoEnd(-90) | | "value", "cost", "amount", "price" | total_cost | ast_contract | total_cost>100000 | | "auto-renew", "automatic renewal" | auto_renew | ast_contract | auto_renew=true | | "notice period", "opt-out window" | notice_period | ast_contract | notice_period<=60 | | "governing law", "jurisdiction" | governing_law | ast_contract | governing_lawLIKE[state] | | "obligation", "commitment" | short_description | clm_obligation | short_descriptionLIKE[term] | | "type", "category" | contract_type | ast_contract | contract_type=[value] | | "active", "current", "in force" | state | ast_contract | state=active | | "department", "business unit" | department | ast_contract | department=[sys_id] |
| Natural Language | Encoded Query |
|-----------------|---------------|
| "expiring this month" | endsBETWEENjavascript:gs.beginningOfThisMonth()@javascript:gs.endOfThisMonth() |
| "expiring next quarter" | endsBETWEENjavascript:gs.beginningOfNextQuarter()@javascript:gs.endOfNextQuarter() |
| "expiring in 90 days" | ends<=javascript:gs.daysAgoEnd(-90)^ends>=javascript:gs.daysAgoEnd(0) |
| "signed last year" | startsBETWEENjavascript:gs.beginningOfLastYear()@javascript:gs.endOfLastYear() |
| "renewed in 2025" | renewal_dateBETWEEN2025-01-01@2025-12-31 |
| "overdue obligations" | due_date<javascript:gs.now()^state!=complete |
Analyze the user's search intent and identify the components:
Map user terms to ServiceNow choice values and references.
Resolve contract type values:
Tool: SN-Query-Table
Parameters:
table_name: sys_choice
query: name=ast_contract^element=contract_type
fields: value,label
limit: 50
Using REST API:
GET /api/now/table/sys_choice?sysparm_query=name=ast_contract^element=contract_type&sysparm_fields=value,label&sysparm_limit=50
Resolve vendor by name:
Tool: SN-Query-Table
Parameters:
table_name: core_company
query: nameLIKE[search_term]^vendor=true
fields: sys_id,name,vendor_type
limit: 10
Combine resolved values into an optimized encoded query.
Example: "Show me all SaaS contracts with Acme expiring in the next 6 months worth over $100K"
Step-by-step translation:
contract_type=saas (resolved from sys_choice)vendor=[acme_sys_id] (resolved from core_company)ends<=javascript:gs.daysAgoEnd(-180)^ends>=javascript:gs.daysAgoEnd(0)total_cost>100000Combined query:
Tool: SN-Query-Table
Parameters:
table_name: ast_contract
query: contract_type=saas^vendor=[acme_sys_id]^ends<=javascript:gs.daysAgoEnd(-180)^ends>=javascript:gs.daysAgoEnd(0)^total_cost>100000^active=true
fields: sys_id,number,short_description,vendor,contract_type,starts,ends,total_cost,auto_renew,notice_period,state
limit: 50
order_by: ends
Run the query and verify results match the user's intent.
Using REST API:
GET /api/now/table/ast_contract?sysparm_query=contract_type=saas^vendor=[acme_sys_id]^ends<=javascript:gs.daysAgoEnd(-180)^ends>=javascript:gs.daysAgoEnd(0)^total_cost>100000^active=true&sysparm_fields=sys_id,number,short_description,vendor,contract_type,starts,ends,total_cost,auto_renew,notice_period&sysparm_limit=50&sysparm_display_value=true
Alternatively, use Natural Language Search for fuzzy matching:
Tool: SN-Natural-Language-Search
Parameters:
table_name: ast_contract
query: "SaaS contracts with Acme expiring in the next 6 months worth over 100 thousand dollars"
limit: 25
Translate obligation-related queries to CLM table searches.
Example: "Find all vendor data protection obligations due this quarter"
Tool: SN-Query-Table
Parameters:
table_name: clm_obligation
query: obligation_type=vendor_obligation^short_descriptionLIKEdata protection^due_dateBETWEENjavascript:gs.beginningOfThisQuarter()@javascript:gs.endOfThisQuarter()^active=true
fields: sys_id,short_description,contract,obligation_type,responsible_party,due_date,state,compliance_status,clause_reference
limit: 50
Offer follow-up queries to refine results:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var suggestions = {
original_query: 'SaaS contracts with Acme expiring next 6 months over $100K',
results_found: 0,
refinement_suggestions: [],
broadening_suggestions: []
};
// Count results
var ga = new GlideAggregate('ast_contract');
ga.addQuery('contract_type', 'saas');
ga.addQuery('active', true);
ga.addQuery('ends', '<=', gs.daysAgoEnd(-180));
ga.addQuery('ends', '>=', gs.daysAgoEnd(0));
ga.addQuery('total_cost', '>', 100000);
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) suggestions.results_found = parseInt(ga.getAggregate('COUNT'));
if (suggestions.results_found > 20) {
suggestions.refinement_suggestions = [
'Add department filter: "...in the Engineering department"',
'Narrow timeline: "...expiring in the next 30 days"',
'Add renewal filter: "...with auto-renewal enabled"'
];
} else if (suggestions.results_found == 0) {
suggestions.broadening_suggestions = [
'Remove value filter: "SaaS contracts with Acme expiring in 6 months"',
'Expand vendor: "SaaS contracts expiring in 6 months over $100K"',
'Expand type: "All contracts with Acme expiring in 6 months"'
];
}
gs.info('QUERY SUGGESTIONS:\n' + JSON.stringify(suggestions, null, 2));
description: "CLM: Generate query refinement suggestions based on result count"
Save frequently used query patterns for reuse:
Tool: SN-Create-Record
Parameters:
table_name: sys_filter
fields:
title: "Contracts Expiring Next 90 Days - Auto Renew"
table: ast_contract
filter: active=true^auto_renew=true^ends<=javascript:gs.daysAgoEnd(-90)^ends>=javascript:gs.daysAgoEnd(0)
| Operation | MCP Tool | REST Endpoint | |-----------|----------|---------------| | Query Contracts | SN-Query-Table | GET /api/now/table/ast_contract | | Fuzzy Search | SN-Natural-Language-Search | N/A | | Resolve Choice Values | SN-Query-Table | GET /api/now/table/sys_choice | | Resolve Vendors | SN-Query-Table | GET /api/now/table/core_company | | Query Obligations | SN-Query-Table | GET /api/now/table/clm_obligation | | Aggregate Counts | SN-Execute-Background-Script | POST /api/now/table/sys_trigger | | Discover Fields | SN-Discover-Table-Schema | GET /api/now/table/sys_dictionary |
sysparm_display_value=true for user-facing results so reference fields show names^OR or ^NQ (new query) operators; test complex boolean logic carefullysysparm_limit to all queries; use indexed fields (number, sys_id, state) as primary filters^, =, and date expressionsSymptom: Natural language query translates correctly but returns zero results
Cause: Choice value mismatch (e.g., "saas" vs "SaaS" vs "software_as_a_service")
Solution: Always resolve choice values from sys_choice first:
Tool: SN-Query-Table
Parameters:
table_name: sys_choice
query: name=ast_contract^element=contract_type^labelLIKESaaS
fields: value,label
limit: 5
Symptom: "Expiring next quarter" returns contracts from the wrong time period
Cause: JavaScript date functions use server timezone; gs.daysAgoEnd() counts from today
Solution: Verify server timezone with gs.getProperty('glide.sys.date.format'). Use BETWEEN with explicit quarter boundaries for precision.
Symptom: Vendor lookup returns no results for a known vendor
Cause: The vendor name in core_company may differ from common usage (e.g., "International Business Machines" vs "IBM")
Solution: Use nameLIKE with partial match, or search across name, stock_symbol, and u_dba_name fields.
User Query: "Show me all contracts that auto-renew and need notice within 60 days"
Enhanced Query:
Tool: SN-Query-Table
Parameters:
table_name: ast_contract
query: active=true^auto_renew=true^endsRELATIVELE@dayAgo@-60^endsRELATIVEGE@dayAgo@0
fields: number,short_description,vendor,ends,notice_period,total_cost,contract_administrator
limit: 50
order_by: ends
User Query: "Find contracts with European vendors that don't mention GDPR"
Enhanced Query (two-step):
Tool: SN-Query-Table
Parameters:
table_name: ast_contract
query: active=true^vendor.country=Germany^ORvendor.country=France^ORvendor.country=Netherlands^terms_and_conditionsNOT LIKEGDPR
fields: number,short_description,vendor,governing_law,total_cost,ends
limit: 50
User Query: "What is our total contract spend with technology vendors expiring this year?"
Enhanced Query:
Tool: SN-Execute-Background-Script
Parameters:
script: |
var ga = new GlideAggregate('ast_contract');
ga.addQuery('active', true);
ga.addQuery('vendor.vendor_type', 'technology');
ga.addQuery('ends', 'BETWEEN', gs.beginningOfThisYear() + '@' + gs.endOfThisYear());
ga.addAggregate('SUM', 'total_cost');
ga.addAggregate('COUNT');
ga.query();
if (ga.next()) {
gs.info('Total contracts: ' + ga.getAggregate('COUNT'));
gs.info('Total spend: $' + ga.getAggregate('SUM', 'total_cost'));
}
description: "CLM: Calculate total spend for technology vendors with contracts expiring this year"
legal/contract-metadata-extraction - Extract metadata from contract recordslegal/contract-analysis - Full contract risk analysislegal/contract-obligation-extraction - Obligation discovery and trackinglegal/legal-request-triage - Triage contract review requestsitsm/natural-language-queries - General natural language query techniquestesting
Manage supplier onboarding, qualification, performance monitoring, and offboarding with auditable lifecycle controls
tools
Identify emerging risks, prioritize intake signals, and route candidates into formal GRC risk assessment workflows
documentation
Screen inbound documents for completeness, policy risk, and routing readiness before extraction or case workflows
testing
Generate concise task summaries with status, timeline, blockers, SLA risk, and recommended next actions