@ansvar/us-regulations-mcp 1.0.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/LICENSE +190 -0
- package/README.md +275 -0
- package/data/.gitkeep +0 -0
- package/data/regulations.db +0 -0
- package/data/seed/applicability/rules.json +74 -0
- package/data/seed/mappings/ccpa-nist-csf.json +144 -0
- package/data/seed/mappings/hipaa-nist-800-53.json +377 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/adapters/california-leginfo.d.ts +72 -0
- package/dist/ingest/adapters/california-leginfo.d.ts.map +1 -0
- package/dist/ingest/adapters/california-leginfo.js +270 -0
- package/dist/ingest/adapters/california-leginfo.js.map +1 -0
- package/dist/ingest/adapters/ecfr.d.ts +76 -0
- package/dist/ingest/adapters/ecfr.d.ts.map +1 -0
- package/dist/ingest/adapters/ecfr.js +355 -0
- package/dist/ingest/adapters/ecfr.js.map +1 -0
- package/dist/ingest/adapters/regulations-gov.d.ts +47 -0
- package/dist/ingest/adapters/regulations-gov.d.ts.map +1 -0
- package/dist/ingest/adapters/regulations-gov.js +91 -0
- package/dist/ingest/adapters/regulations-gov.js.map +1 -0
- package/dist/ingest/framework.d.ts +84 -0
- package/dist/ingest/framework.d.ts.map +1 -0
- package/dist/ingest/framework.js +8 -0
- package/dist/ingest/framework.js.map +1 -0
- package/dist/tools/action-items.d.ts +23 -0
- package/dist/tools/action-items.d.ts.map +1 -0
- package/dist/tools/action-items.js +118 -0
- package/dist/tools/action-items.js.map +1 -0
- package/dist/tools/applicability.d.ts +26 -0
- package/dist/tools/applicability.d.ts.map +1 -0
- package/dist/tools/applicability.js +49 -0
- package/dist/tools/applicability.js.map +1 -0
- package/dist/tools/compare.d.ts +20 -0
- package/dist/tools/compare.d.ts.map +1 -0
- package/dist/tools/compare.js +35 -0
- package/dist/tools/compare.js.map +1 -0
- package/dist/tools/definitions.d.ts +22 -0
- package/dist/tools/definitions.d.ts.map +1 -0
- package/dist/tools/definitions.js +43 -0
- package/dist/tools/definitions.js.map +1 -0
- package/dist/tools/evidence.d.ts +23 -0
- package/dist/tools/evidence.d.ts.map +1 -0
- package/dist/tools/evidence.js +27 -0
- package/dist/tools/evidence.js.map +1 -0
- package/dist/tools/list.d.ts +25 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/list.js +66 -0
- package/dist/tools/list.js.map +1 -0
- package/dist/tools/map.d.ts +26 -0
- package/dist/tools/map.d.ts.map +1 -0
- package/dist/tools/map.js +58 -0
- package/dist/tools/map.js.map +1 -0
- package/dist/tools/registry.d.ts +19 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +260 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/search.d.ts +15 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +94 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/section.d.ts +19 -0
- package/dist/tools/section.d.ts.map +1 -0
- package/dist/tools/section.js +50 -0
- package/dist/tools/section.js.map +1 -0
- package/package.json +76 -0
- package/scripts/build-db.ts +268 -0
- package/scripts/ingest.ts +214 -0
- package/scripts/load-seed-data.ts +133 -0
- package/scripts/quality-test.ts +346 -0
- package/scripts/test-mcp-tools.ts +187 -0
- package/scripts/test-remaining-tools.ts +107 -0
- package/src/index.ts +55 -0
- package/src/ingest/adapters/california-leginfo.ts +322 -0
- package/src/ingest/adapters/ecfr.ts +403 -0
- package/src/ingest/adapters/regulations-gov.ts +112 -0
- package/src/ingest/framework.ts +92 -0
- package/src/tools/action-items.ts +164 -0
- package/src/tools/applicability.ts +91 -0
- package/src/tools/compare.ts +61 -0
- package/src/tools/definitions.ts +79 -0
- package/src/tools/evidence.ts +53 -0
- package/src/tools/list.ts +120 -0
- package/src/tools/map.ts +100 -0
- package/src/tools/registry.ts +275 -0
- package/src/tools/search.ts +132 -0
- package/src/tools/section.ts +85 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { searchRegulations } from './search.js';
|
|
3
|
+
import { getSection } from './section.js';
|
|
4
|
+
import { listRegulations } from './list.js';
|
|
5
|
+
import { compareRequirements } from './compare.js';
|
|
6
|
+
import { mapControls } from './map.js';
|
|
7
|
+
import { checkApplicability } from './applicability.js';
|
|
8
|
+
import { getDefinitions } from './definitions.js';
|
|
9
|
+
import { getEvidenceRequirements } from './evidence.js';
|
|
10
|
+
import { getComplianceActionItems } from './action-items.js';
|
|
11
|
+
/**
|
|
12
|
+
* Centralized registry of all MCP tools.
|
|
13
|
+
* Single source of truth for both stdio and HTTP servers.
|
|
14
|
+
*/
|
|
15
|
+
export const TOOLS = [
|
|
16
|
+
{
|
|
17
|
+
name: 'search_regulations',
|
|
18
|
+
description: 'Search across all US regulations using full-text search. Returns relevant sections with highlighted snippets. Token-efficient: returns 32-token snippets with >>> <<< markers around matched terms.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
query: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Search query (supports natural language and technical terms)',
|
|
25
|
+
},
|
|
26
|
+
regulations: {
|
|
27
|
+
type: 'array',
|
|
28
|
+
items: { type: 'string' },
|
|
29
|
+
description: 'Optional: Filter results to specific regulations (e.g., ["HIPAA", "CCPA"])',
|
|
30
|
+
},
|
|
31
|
+
limit: {
|
|
32
|
+
type: 'number',
|
|
33
|
+
description: 'Maximum number of results to return (default: 10, max: 1000)',
|
|
34
|
+
default: 10,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
required: ['query'],
|
|
38
|
+
},
|
|
39
|
+
handler: async (db, args) => {
|
|
40
|
+
return await searchRegulations(db, args);
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'get_section',
|
|
45
|
+
description: 'Retrieve the full text of a specific regulation section. Returns section content, metadata, and cross-references. Large sections are automatically truncated with a warning.',
|
|
46
|
+
inputSchema: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
regulation: {
|
|
50
|
+
type: 'string',
|
|
51
|
+
description: 'Regulation ID (e.g., "HIPAA", "CCPA")',
|
|
52
|
+
},
|
|
53
|
+
section: {
|
|
54
|
+
type: 'string',
|
|
55
|
+
description: 'Section number (e.g., "164.502", "1798.100")',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ['regulation', 'section'],
|
|
59
|
+
},
|
|
60
|
+
handler: async (db, args) => {
|
|
61
|
+
return await getSection(db, args);
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'list_regulations',
|
|
66
|
+
description: 'List all available regulations or get the structure of a specific regulation. Without parameters, returns all regulations with metadata. With a regulation ID, returns chapters and sections organized hierarchically.',
|
|
67
|
+
inputSchema: {
|
|
68
|
+
type: 'object',
|
|
69
|
+
properties: {
|
|
70
|
+
regulation: {
|
|
71
|
+
type: 'string',
|
|
72
|
+
description: 'Optional: Regulation ID to get detailed structure for (e.g., "HIPAA")',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
handler: async (db, args) => {
|
|
77
|
+
return await listRegulations(db, args);
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'compare_requirements',
|
|
82
|
+
description: 'Compare requirements across multiple regulations for a specific topic. Searches each regulation and returns the top matching sections with relevance scores.',
|
|
83
|
+
inputSchema: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
topic: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'Topic to compare (e.g., "breach notification", "access controls")',
|
|
89
|
+
},
|
|
90
|
+
regulations: {
|
|
91
|
+
type: 'array',
|
|
92
|
+
items: { type: 'string' },
|
|
93
|
+
description: 'List of regulations to compare (e.g., ["HIPAA", "CCPA"])',
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ['topic', 'regulations'],
|
|
97
|
+
},
|
|
98
|
+
handler: async (db, args) => {
|
|
99
|
+
return await compareRequirements(db, args);
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: 'map_controls',
|
|
104
|
+
description: 'Map NIST controls (800-53, CSF) to regulation sections. Shows which regulatory requirements satisfy specific control objectives. Can filter by control ID or regulation.',
|
|
105
|
+
inputSchema: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
properties: {
|
|
108
|
+
framework: {
|
|
109
|
+
type: 'string',
|
|
110
|
+
description: 'Control framework (e.g., "NIST_CSF", "NIST_800_53", "ISO27001")',
|
|
111
|
+
},
|
|
112
|
+
control: {
|
|
113
|
+
type: 'string',
|
|
114
|
+
description: 'Optional: Specific control ID (e.g., "AC-1", "PR.AC-1")',
|
|
115
|
+
},
|
|
116
|
+
regulation: {
|
|
117
|
+
type: 'string',
|
|
118
|
+
description: 'Optional: Filter to specific regulation (e.g., "HIPAA")',
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
required: ['framework'],
|
|
122
|
+
},
|
|
123
|
+
handler: async (db, args) => {
|
|
124
|
+
return await mapControls(db, args);
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'check_applicability',
|
|
129
|
+
description: 'Determine which regulations apply to a specific sector or subsector. Returns applicable regulations with confidence levels (definite, likely, possible).',
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
sector: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
description: 'Industry sector (e.g., "healthcare", "financial", "retail", "technology")',
|
|
136
|
+
},
|
|
137
|
+
subsector: {
|
|
138
|
+
type: 'string',
|
|
139
|
+
description: 'Optional: Specific subsector (e.g., "hospital", "bank", "e-commerce")',
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
required: ['sector'],
|
|
143
|
+
},
|
|
144
|
+
handler: async (db, args) => {
|
|
145
|
+
return await checkApplicability(db, args);
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: 'get_definitions',
|
|
150
|
+
description: 'Look up official term definitions across regulations. Uses partial matching to find terms (e.g., "health" matches "protected health information").',
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
term: {
|
|
155
|
+
type: 'string',
|
|
156
|
+
description: 'Term to look up (e.g., "protected health information", "personal data")',
|
|
157
|
+
},
|
|
158
|
+
regulation: {
|
|
159
|
+
type: 'string',
|
|
160
|
+
description: 'Optional: Filter to specific regulation (e.g., "HIPAA")',
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
required: ['term'],
|
|
164
|
+
},
|
|
165
|
+
handler: async (db, args) => {
|
|
166
|
+
return await getDefinitions(db, args);
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'get_evidence_requirements',
|
|
171
|
+
description: 'Get compliance evidence requirements for a specific section (e.g., audit logs, policies, procedures). MVP: Returns placeholder until evidence data is seeded.',
|
|
172
|
+
inputSchema: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
regulation: {
|
|
176
|
+
type: 'string',
|
|
177
|
+
description: 'Regulation ID (e.g., "HIPAA")',
|
|
178
|
+
},
|
|
179
|
+
section: {
|
|
180
|
+
type: 'string',
|
|
181
|
+
description: 'Section number (e.g., "164.312(b)")',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
required: ['regulation', 'section'],
|
|
185
|
+
},
|
|
186
|
+
handler: async (db, args) => {
|
|
187
|
+
return await getEvidenceRequirements(db, args);
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: 'get_compliance_action_items',
|
|
192
|
+
description: 'Generate structured compliance action items from regulation sections. Extracts priority (high/medium/low) based on regulatory language and identifies evidence needed.',
|
|
193
|
+
inputSchema: {
|
|
194
|
+
type: 'object',
|
|
195
|
+
properties: {
|
|
196
|
+
regulation: {
|
|
197
|
+
type: 'string',
|
|
198
|
+
description: 'Regulation ID (e.g., "HIPAA", "CCPA")',
|
|
199
|
+
},
|
|
200
|
+
sections: {
|
|
201
|
+
type: 'array',
|
|
202
|
+
items: { type: 'string' },
|
|
203
|
+
description: 'Section numbers to generate action items for (e.g., ["164.308(a)(1)(ii)(A)", "164.312(b)"])',
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
required: ['regulation', 'sections'],
|
|
207
|
+
},
|
|
208
|
+
handler: async (db, args) => {
|
|
209
|
+
return await getComplianceActionItems(db, args);
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
/**
|
|
214
|
+
* Register all tools with an MCP server instance.
|
|
215
|
+
* Use this for both stdio and HTTP servers to ensure parity.
|
|
216
|
+
*/
|
|
217
|
+
export function registerTools(server, db) {
|
|
218
|
+
// List available tools
|
|
219
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
220
|
+
tools: TOOLS.map(tool => ({
|
|
221
|
+
name: tool.name,
|
|
222
|
+
description: tool.description,
|
|
223
|
+
inputSchema: tool.inputSchema,
|
|
224
|
+
})),
|
|
225
|
+
}));
|
|
226
|
+
// Handle tool calls
|
|
227
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
228
|
+
const { name, arguments: args } = request.params;
|
|
229
|
+
const tool = TOOLS.find(t => t.name === name);
|
|
230
|
+
if (!tool) {
|
|
231
|
+
return {
|
|
232
|
+
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
233
|
+
isError: true,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
const result = await tool.handler(db, args || {});
|
|
238
|
+
return {
|
|
239
|
+
content: [
|
|
240
|
+
{
|
|
241
|
+
type: 'text',
|
|
242
|
+
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
catch (error) {
|
|
248
|
+
return {
|
|
249
|
+
content: [
|
|
250
|
+
{
|
|
251
|
+
type: 'text',
|
|
252
|
+
text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
253
|
+
},
|
|
254
|
+
],
|
|
255
|
+
isError: true,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/tools/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Database } from 'better-sqlite3';
|
|
2
|
+
export interface SearchInput {
|
|
3
|
+
query: string;
|
|
4
|
+
regulations?: string[];
|
|
5
|
+
limit?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SearchResult {
|
|
8
|
+
regulation: string;
|
|
9
|
+
section: string;
|
|
10
|
+
title: string;
|
|
11
|
+
snippet: string;
|
|
12
|
+
relevance: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function searchRegulations(db: Database, input: SearchInput): Promise<SearchResult[]>;
|
|
15
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAyCD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CAyEzB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Escape special FTS5 query characters and build optimal search query.
|
|
3
|
+
* Uses adaptive logic:
|
|
4
|
+
* - Short queries (1-3 words): AND logic with exact matching for precision
|
|
5
|
+
* - Long queries (4+ words): OR logic with prefix matching for recall
|
|
6
|
+
* This prevents empty results on complex queries while maintaining precision on simple ones.
|
|
7
|
+
*
|
|
8
|
+
* Handles hyphenated terms by converting them to spaces (e.g., "third-party" → "third party")
|
|
9
|
+
* to avoid FTS5 syntax errors where hyphens are interpreted as operators.
|
|
10
|
+
*/
|
|
11
|
+
function escapeFts5Query(query) {
|
|
12
|
+
// Common stopwords that add noise to searches
|
|
13
|
+
const stopwords = new Set(['a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by']);
|
|
14
|
+
// Normalize query: remove quotes, convert hyphens to spaces
|
|
15
|
+
// This allows "third-party" to become "third party" which FTS5 handles naturally
|
|
16
|
+
const words = query
|
|
17
|
+
.replace(/['"]/g, '') // Remove quotes
|
|
18
|
+
.replace(/-/g, ' ') // Convert hyphens to spaces (fixes "third-party" → "third party")
|
|
19
|
+
.split(/\s+/)
|
|
20
|
+
.filter(word => word.length > 2 && !stopwords.has(word.toLowerCase())); // Filter short words and stopwords
|
|
21
|
+
if (words.length === 0) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
if (words.length <= 2) {
|
|
25
|
+
// Short queries (1-2 words): Use AND logic with prefix matching for precision
|
|
26
|
+
// Example: "incident reporting" → incident* reporting*
|
|
27
|
+
// Prefix matching handles word variations (encrypt vs encryption)
|
|
28
|
+
return words.map(word => `${word}*`).join(' ');
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Long queries (3+ words): Use OR logic with prefix matching for better recall
|
|
32
|
+
// Example: "encryption transmission storage" → encryption* OR transmission* OR storage*
|
|
33
|
+
// BM25 will still rank documents with more matches higher
|
|
34
|
+
return words.map(word => `${word}*`).join(' OR ');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export async function searchRegulations(db, input) {
|
|
38
|
+
let { query, regulations, limit = 10 } = input;
|
|
39
|
+
// Validate and sanitize limit parameter
|
|
40
|
+
if (!Number.isFinite(limit) || limit < 0) {
|
|
41
|
+
limit = 10; // Default to safe value
|
|
42
|
+
}
|
|
43
|
+
// Cap at reasonable maximum
|
|
44
|
+
limit = Math.min(Math.floor(limit), 1000);
|
|
45
|
+
if (!query || query.trim().length === 0) {
|
|
46
|
+
throw new Error('Query cannot be empty. Please provide a search term.');
|
|
47
|
+
}
|
|
48
|
+
const escapedQuery = escapeFts5Query(query);
|
|
49
|
+
if (!escapedQuery) {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
const params = [escapedQuery];
|
|
53
|
+
// Build optional regulation filter
|
|
54
|
+
let regulationFilter = '';
|
|
55
|
+
if (regulations && regulations.length > 0) {
|
|
56
|
+
const placeholders = regulations.map(() => '?').join(', ');
|
|
57
|
+
regulationFilter = ` AND regulation IN (${placeholders})`;
|
|
58
|
+
params.push(...regulations);
|
|
59
|
+
}
|
|
60
|
+
// Search in sections
|
|
61
|
+
const sectionsQuery = `
|
|
62
|
+
SELECT
|
|
63
|
+
sections_fts.regulation,
|
|
64
|
+
sections_fts.section_number as section,
|
|
65
|
+
sections_fts.title,
|
|
66
|
+
snippet(sections_fts, 3, '>>>', '<<<', '...', 32) as snippet,
|
|
67
|
+
bm25(sections_fts) as relevance
|
|
68
|
+
FROM sections_fts
|
|
69
|
+
WHERE sections_fts MATCH ?
|
|
70
|
+
${regulationFilter}
|
|
71
|
+
ORDER BY bm25(sections_fts)
|
|
72
|
+
LIMIT ?
|
|
73
|
+
`;
|
|
74
|
+
try {
|
|
75
|
+
// Execute query
|
|
76
|
+
const sectionsParams = [...params, limit];
|
|
77
|
+
const sectionStmt = db.prepare(sectionsQuery);
|
|
78
|
+
const sectionRows = sectionStmt.all(...sectionsParams);
|
|
79
|
+
// BM25 returns negative scores; convert to positive for clarity
|
|
80
|
+
const results = sectionRows.map(row => ({
|
|
81
|
+
...row,
|
|
82
|
+
relevance: Math.abs(row.relevance),
|
|
83
|
+
}));
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
// If FTS5 query fails (e.g., syntax error), return empty results
|
|
88
|
+
if (error instanceof Error && error.message.includes('fts5')) {
|
|
89
|
+
return [];
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Database } from 'better-sqlite3';
|
|
2
|
+
export interface GetSectionInput {
|
|
3
|
+
regulation: string;
|
|
4
|
+
section: string;
|
|
5
|
+
}
|
|
6
|
+
export interface SectionData {
|
|
7
|
+
regulation: string;
|
|
8
|
+
section_number: string;
|
|
9
|
+
title: string | null;
|
|
10
|
+
text: string;
|
|
11
|
+
chapter: string | null;
|
|
12
|
+
parent_section: string | null;
|
|
13
|
+
cross_references: string[] | null;
|
|
14
|
+
truncated?: boolean;
|
|
15
|
+
original_length?: number;
|
|
16
|
+
token_estimate?: number;
|
|
17
|
+
}
|
|
18
|
+
export declare function getSection(db: Database, input: GetSectionInput): Promise<SectionData | null>;
|
|
19
|
+
//# sourceMappingURL=section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.d.ts","sourceRoot":"","sources":["../../src/tools/section.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CA6D7B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export async function getSection(db, input) {
|
|
2
|
+
const { regulation, section } = input;
|
|
3
|
+
const sql = `
|
|
4
|
+
SELECT
|
|
5
|
+
regulation,
|
|
6
|
+
section_number,
|
|
7
|
+
title,
|
|
8
|
+
text,
|
|
9
|
+
chapter,
|
|
10
|
+
parent_section,
|
|
11
|
+
cross_references
|
|
12
|
+
FROM sections
|
|
13
|
+
WHERE regulation = ? AND section_number = ?
|
|
14
|
+
`;
|
|
15
|
+
const row = db.prepare(sql).get(regulation, section);
|
|
16
|
+
if (!row) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
// Token management: Truncate very large sections to prevent context overflow
|
|
20
|
+
const MAX_CHARS = 50000; // ~12,500 tokens (safe for 200k context window)
|
|
21
|
+
const originalLength = row.text.length;
|
|
22
|
+
const tokenEstimate = Math.ceil(originalLength / 4); // ~4 chars per token
|
|
23
|
+
let text = row.text;
|
|
24
|
+
let truncated = false;
|
|
25
|
+
if (originalLength > MAX_CHARS) {
|
|
26
|
+
text = row.text.substring(0, MAX_CHARS) + '\n\n[... Section truncated due to length. Original: ' + originalLength + ' chars (~' + tokenEstimate + ' tokens). Use search_regulations to find specific content.]';
|
|
27
|
+
truncated = true;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
regulation: row.regulation,
|
|
31
|
+
section_number: row.section_number,
|
|
32
|
+
title: row.title,
|
|
33
|
+
text,
|
|
34
|
+
chapter: row.chapter,
|
|
35
|
+
parent_section: row.parent_section,
|
|
36
|
+
cross_references: row.cross_references ? (() => {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(row.cross_references);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
console.warn(`Invalid cross_references JSON for ${row.regulation} ${row.section_number}`);
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
})() : null,
|
|
45
|
+
truncated,
|
|
46
|
+
original_length: truncated ? originalLength : undefined,
|
|
47
|
+
token_estimate: truncated ? tokenEstimate : undefined,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=section.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"section.js","sourceRoot":"","sources":["../../src/tools/section.ts"],"names":[],"mappings":"AAoBA,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,EAAY,EACZ,KAAsB;IAEtB,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAEtC,MAAM,GAAG,GAAG;;;;;;;;;;;GAWX,CAAC;IAEF,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,OAAO,CAQtC,CAAC;IAEd,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAC7E,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,gDAAgD;IACzE,MAAM,cAAc,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;IACvC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,qBAAqB;IAC1E,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACpB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,IAAI,cAAc,GAAG,SAAS,EAAE,CAAC;QAC/B,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,GAAG,sDAAsD,GAAG,cAAc,GAAG,WAAW,GAAG,aAAa,GAAG,6DAA6D,CAAC;QAChN,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,IAAI;QACJ,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;YAC7C,IAAI,CAAC;gBACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;YAC1C,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,CAAC,IAAI,CAAC,qCAAqC,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;gBAC1F,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI;QACX,SAAS;QACT,eAAe,EAAE,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;QACvD,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;KACtD,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ansvar/us-regulations-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"mcpName": "us.ansvar/us-regulations-mcp",
|
|
5
|
+
"description": "MCP server for US cybersecurity and privacy regulations. Query HIPAA, CCPA, SOX, and more directly from Claude.",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"bin": {
|
|
9
|
+
"us-regulations-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"clean": "rm -rf dist && find . -name '*.tsbuildinfo' -not -path './node_modules/*' -delete",
|
|
14
|
+
"test": "vitest run",
|
|
15
|
+
"test:watch": "vitest",
|
|
16
|
+
"dev": "tsx src/index.ts",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"build:db": "tsx scripts/build-db.ts",
|
|
19
|
+
"ingest": "tsx scripts/ingest.ts",
|
|
20
|
+
"load-seed": "tsx scripts/load-seed-data.ts",
|
|
21
|
+
"test:mcp": "tsx scripts/test-mcp-tools.ts",
|
|
22
|
+
"lint": "eslint src --ext .ts",
|
|
23
|
+
"prepare": "test -f dist/index.js || npm run build",
|
|
24
|
+
"prepublishOnly": "npm run build"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"mcp",
|
|
28
|
+
"model-context-protocol",
|
|
29
|
+
"compliance",
|
|
30
|
+
"hipaa",
|
|
31
|
+
"ccpa",
|
|
32
|
+
"sox",
|
|
33
|
+
"us-regulations",
|
|
34
|
+
"cybersecurity",
|
|
35
|
+
"privacy",
|
|
36
|
+
"healthcare",
|
|
37
|
+
"financial",
|
|
38
|
+
"sarbanes-oxley",
|
|
39
|
+
"california-consumer-privacy-act",
|
|
40
|
+
"claude",
|
|
41
|
+
"llm"
|
|
42
|
+
],
|
|
43
|
+
"author": "Ansvar Systems <hello@ansvar.eu> (https://ansvar.eu)",
|
|
44
|
+
"license": "Apache-2.0",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "git+https://github.com/Ansvar-Systems/US_compliance_MCP.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/Ansvar-Systems/US_compliance_MCP/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://ansvar.eu",
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"dist",
|
|
58
|
+
"data",
|
|
59
|
+
"scripts",
|
|
60
|
+
"src"
|
|
61
|
+
],
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
64
|
+
"@types/cheerio": "^0.22.35",
|
|
65
|
+
"@types/node": "^25.0.10",
|
|
66
|
+
"tsx": "^4.21.0",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vitest": "^4.0.18"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
72
|
+
"better-sqlite3": "^12.6.2",
|
|
73
|
+
"cheerio": "^1.2.0",
|
|
74
|
+
"fast-xml-parser": "^5.3.3"
|
|
75
|
+
}
|
|
76
|
+
}
|