visus-mcp 0.6.2 → 0.9.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/.claude/settings.local.json +15 -1
- package/.env.status +7 -0
- package/CHANGELOG.md +110 -0
- package/CLAUDE.md +3 -0
- package/README.md +29 -19
- package/SECURITY.md +2 -0
- package/STATUS.md +320 -12
- package/dist/browser/playwright-renderer.d.ts.map +1 -1
- package/dist/browser/playwright-renderer.js +27 -5
- package/dist/browser/playwright-renderer.js.map +1 -1
- package/dist/content-handlers/index.d.ts +36 -0
- package/dist/content-handlers/index.d.ts.map +1 -0
- package/dist/content-handlers/index.js +59 -0
- package/dist/content-handlers/index.js.map +1 -0
- package/dist/content-handlers/json-handler.d.ts +28 -0
- package/dist/content-handlers/json-handler.d.ts.map +1 -0
- package/dist/content-handlers/json-handler.js +116 -0
- package/dist/content-handlers/json-handler.js.map +1 -0
- package/dist/content-handlers/pdf-handler.d.ts +29 -0
- package/dist/content-handlers/pdf-handler.d.ts.map +1 -0
- package/dist/content-handlers/pdf-handler.js +77 -0
- package/dist/content-handlers/pdf-handler.js.map +1 -0
- package/dist/content-handlers/svg-handler.d.ts +35 -0
- package/dist/content-handlers/svg-handler.d.ts.map +1 -0
- package/dist/content-handlers/svg-handler.js +206 -0
- package/dist/content-handlers/svg-handler.js.map +1 -0
- package/dist/content-handlers/types.d.ts +42 -0
- package/dist/content-handlers/types.d.ts.map +1 -0
- package/dist/content-handlers/types.js +7 -0
- package/dist/content-handlers/types.js.map +1 -0
- package/dist/sanitizer/framework-mapper.d.ts +4 -0
- package/dist/sanitizer/framework-mapper.d.ts.map +1 -1
- package/dist/sanitizer/framework-mapper.js +92 -0
- package/dist/sanitizer/framework-mapper.js.map +1 -1
- package/dist/sanitizer/threat-reporter.d.ts +5 -0
- package/dist/sanitizer/threat-reporter.d.ts.map +1 -1
- package/dist/sanitizer/threat-reporter.js +15 -6
- package/dist/sanitizer/threat-reporter.js.map +1 -1
- package/dist/tools/fetch-structured.d.ts.map +1 -1
- package/dist/tools/fetch-structured.js +4 -0
- package/dist/tools/fetch-structured.js.map +1 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +68 -4
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +4 -0
- package/dist/tools/read.js.map +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/server.json +25 -14
- package/src/browser/playwright-renderer.ts +29 -6
- package/src/content-handlers/index.ts +72 -0
- package/src/content-handlers/json-handler.ts +137 -0
- package/src/content-handlers/pdf-handler.ts +91 -0
- package/src/content-handlers/svg-handler.ts +243 -0
- package/src/content-handlers/types.ts +44 -0
- package/src/sanitizer/framework-mapper.ts +94 -0
- package/src/sanitizer/threat-reporter.ts +17 -6
- package/src/tools/fetch-structured.ts +5 -0
- package/src/tools/fetch.ts +76 -4
- package/src/tools/read.ts +5 -0
- package/src/types.ts +9 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
- package/.github/ISSUE_TEMPLATE/false_positive.md +0 -43
- package/.github/ISSUE_TEMPLATE/new_pattern.md +0 -49
- package/.github/ISSUE_TEMPLATE/security_report.md +0 -31
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -39
- package/.mcpregistry_github_token +0 -1
- package/.mcpregistry_registry_token +0 -1
- package/CONTRIBUTING.md +0 -329
- package/LINKEDIN-STRATEGY.md +0 -367
- package/ROADMAP.md +0 -221
- package/SECURITY-AUDIT-v1.md +0 -277
- package/SUBMISSION.md +0 -66
- package/TROUBLESHOOT-AUTH-20260322-2019.md +0 -291
- package/TROUBLESHOOT-BUILD-20260319-1450.md +0 -546
- package/TROUBLESHOOT-COGNITO-AUTH-20260324-2029.md +0 -415
- package/TROUBLESHOOT-COGNITO-JWT-20260324.md +0 -592
- package/TROUBLESHOOT-FETCH-20260320-1150.md +0 -168
- package/TROUBLESHOOT-JEST-20260323-1357.md +0 -139
- package/TROUBLESHOOT-LAMBDA-20260322-1945.md +0 -183
- package/TROUBLESHOOT-PLAYWRIGHT-20260321-1549.md +0 -217
- package/TROUBLESHOOT-SSL-20260320-1138.md +0 -171
- package/TROUBLESHOOT-STRUCTURED-20260320-1200.md +0 -246
- package/TROUBLESHOOT-TEST-20260320-0942.md +0 -281
- package/VISUS-CLAUDE-CODE-PROMPT.md +0 -324
- package/VISUS-PROJECT-PLAN.md +0 -205
- package/cdk.json +0 -73
- package/infrastructure/app.ts +0 -39
- package/infrastructure/stack.ts +0 -298
- package/jest.config.js +0 -33
- package/jest.setup.js +0 -9
- package/lambda-deploy/index.js +0 -81512
- package/lambda-deploy/index.js.map +0 -7
- package/lambda-package/browser/__mocks__/playwright-renderer.d.ts +0 -25
- package/lambda-package/browser/__mocks__/playwright-renderer.d.ts.map +0 -1
- package/lambda-package/browser/__mocks__/playwright-renderer.js +0 -119
- package/lambda-package/browser/__mocks__/playwright-renderer.js.map +0 -1
- package/lambda-package/browser/playwright-renderer.d.ts +0 -40
- package/lambda-package/browser/playwright-renderer.d.ts.map +0 -1
- package/lambda-package/browser/playwright-renderer.js +0 -214
- package/lambda-package/browser/playwright-renderer.js.map +0 -1
- package/lambda-package/browser/reader.d.ts +0 -31
- package/lambda-package/browser/reader.d.ts.map +0 -1
- package/lambda-package/browser/reader.js +0 -98
- package/lambda-package/browser/reader.js.map +0 -1
- package/lambda-package/index.d.ts +0 -18
- package/lambda-package/index.d.ts.map +0 -1
- package/lambda-package/index.js +0 -238
- package/lambda-package/index.js.map +0 -1
- package/lambda-package/lambda-handler.d.ts +0 -28
- package/lambda-package/lambda-handler.d.ts.map +0 -1
- package/lambda-package/lambda-handler.js +0 -257
- package/lambda-package/lambda-handler.js.map +0 -1
- package/lambda-package/package-lock.json +0 -7435
- package/lambda-package/package.json +0 -74
- package/lambda-package/runtime.d.ts +0 -50
- package/lambda-package/runtime.d.ts.map +0 -1
- package/lambda-package/runtime.js +0 -86
- package/lambda-package/runtime.js.map +0 -1
- package/lambda-package/sanitizer/elicit-runner.d.ts +0 -48
- package/lambda-package/sanitizer/elicit-runner.d.ts.map +0 -1
- package/lambda-package/sanitizer/elicit-runner.js +0 -100
- package/lambda-package/sanitizer/elicit-runner.js.map +0 -1
- package/lambda-package/sanitizer/framework-mapper.d.ts +0 -24
- package/lambda-package/sanitizer/framework-mapper.d.ts.map +0 -1
- package/lambda-package/sanitizer/framework-mapper.js +0 -342
- package/lambda-package/sanitizer/framework-mapper.js.map +0 -1
- package/lambda-package/sanitizer/hitl-gate.d.ts +0 -69
- package/lambda-package/sanitizer/hitl-gate.d.ts.map +0 -1
- package/lambda-package/sanitizer/hitl-gate.js +0 -101
- package/lambda-package/sanitizer/hitl-gate.js.map +0 -1
- package/lambda-package/sanitizer/index.d.ts +0 -63
- package/lambda-package/sanitizer/index.d.ts.map +0 -1
- package/lambda-package/sanitizer/index.js +0 -105
- package/lambda-package/sanitizer/index.js.map +0 -1
- package/lambda-package/sanitizer/injection-detector.d.ts +0 -34
- package/lambda-package/sanitizer/injection-detector.d.ts.map +0 -1
- package/lambda-package/sanitizer/injection-detector.js +0 -89
- package/lambda-package/sanitizer/injection-detector.js.map +0 -1
- package/lambda-package/sanitizer/patterns.d.ts +0 -30
- package/lambda-package/sanitizer/patterns.d.ts.map +0 -1
- package/lambda-package/sanitizer/patterns.js +0 -372
- package/lambda-package/sanitizer/patterns.js.map +0 -1
- package/lambda-package/sanitizer/pii-allowlist.d.ts +0 -49
- package/lambda-package/sanitizer/pii-allowlist.d.ts.map +0 -1
- package/lambda-package/sanitizer/pii-allowlist.js +0 -231
- package/lambda-package/sanitizer/pii-allowlist.js.map +0 -1
- package/lambda-package/sanitizer/pii-redactor.d.ts +0 -41
- package/lambda-package/sanitizer/pii-redactor.d.ts.map +0 -1
- package/lambda-package/sanitizer/pii-redactor.js +0 -213
- package/lambda-package/sanitizer/pii-redactor.js.map +0 -1
- package/lambda-package/sanitizer/severity-classifier.d.ts +0 -33
- package/lambda-package/sanitizer/severity-classifier.d.ts.map +0 -1
- package/lambda-package/sanitizer/severity-classifier.js +0 -113
- package/lambda-package/sanitizer/severity-classifier.js.map +0 -1
- package/lambda-package/sanitizer/threat-reporter.d.ts +0 -66
- package/lambda-package/sanitizer/threat-reporter.d.ts.map +0 -1
- package/lambda-package/sanitizer/threat-reporter.js +0 -163
- package/lambda-package/sanitizer/threat-reporter.js.map +0 -1
- package/lambda-package/tools/fetch-structured.d.ts +0 -51
- package/lambda-package/tools/fetch-structured.d.ts.map +0 -1
- package/lambda-package/tools/fetch-structured.js +0 -237
- package/lambda-package/tools/fetch-structured.js.map +0 -1
- package/lambda-package/tools/fetch.d.ts +0 -49
- package/lambda-package/tools/fetch.d.ts.map +0 -1
- package/lambda-package/tools/fetch.js +0 -131
- package/lambda-package/tools/fetch.js.map +0 -1
- package/lambda-package/tools/read.d.ts +0 -51
- package/lambda-package/tools/read.d.ts.map +0 -1
- package/lambda-package/tools/read.js +0 -127
- package/lambda-package/tools/read.js.map +0 -1
- package/lambda-package/tools/search.d.ts +0 -45
- package/lambda-package/tools/search.d.ts.map +0 -1
- package/lambda-package/tools/search.js +0 -220
- package/lambda-package/tools/search.js.map +0 -1
- package/lambda-package/types.d.ts +0 -167
- package/lambda-package/types.d.ts.map +0 -1
- package/lambda-package/types.js +0 -16
- package/lambda-package/types.js.map +0 -1
- package/lambda-package/utils/format-converter.d.ts +0 -39
- package/lambda-package/utils/format-converter.d.ts.map +0 -1
- package/lambda-package/utils/format-converter.js +0 -191
- package/lambda-package/utils/format-converter.js.map +0 -1
- package/lambda-package/utils/truncate.d.ts +0 -26
- package/lambda-package/utils/truncate.d.ts.map +0 -1
- package/lambda-package/utils/truncate.js +0 -54
- package/lambda-package/utils/truncate.js.map +0 -1
- package/lambda.zip +0 -0
- package/test-output.txt +0 -4
- package/tests/auth-smoke.test.ts +0 -480
- package/tests/elicit-runner.test.ts +0 -232
- package/tests/fetch-tool.test.ts +0 -922
- package/tests/hitl-gate.test.ts +0 -267
- package/tests/injection-corpus.ts +0 -338
- package/tests/pii-allowlist.test.ts +0 -282
- package/tests/reader.test.ts +0 -353
- package/tests/sanitizer.test.ts +0 -358
- package/tests/search.test.ts +0 -456
- package/tests/threat-reporter.test.ts +0 -334
- package/tsconfig.cdk.json +0 -35
package/dist/tools/fetch.js
CHANGED
|
@@ -9,6 +9,7 @@ import { renderPage } from '../browser/playwright-renderer.js';
|
|
|
9
9
|
import { sanitize } from '../sanitizer/index.js';
|
|
10
10
|
import { truncateContent } from '../utils/truncate.js';
|
|
11
11
|
import { detectFormat, convertJson, convertXml, convertRss } from '../utils/format-converter.js';
|
|
12
|
+
import { routeContentHandler, normalizeMimeType } from '../content-handlers/index.js';
|
|
12
13
|
import { Err } from '../types.js';
|
|
13
14
|
/**
|
|
14
15
|
* visus_fetch tool implementation
|
|
@@ -32,9 +33,72 @@ export async function visusFetch(input) {
|
|
|
32
33
|
return Err(renderResult.error);
|
|
33
34
|
}
|
|
34
35
|
const { html, title, contentType } = renderResult.value;
|
|
36
|
+
// rawContent can be string (HTML/JSON/SVG) or Buffer (PDF, binary)
|
|
35
37
|
const rawContent = html || '';
|
|
36
|
-
// Step 2: Detect
|
|
38
|
+
// Step 2: Detect content type and route to specialized handlers if applicable
|
|
37
39
|
const detectedContentType = contentType || 'text/html';
|
|
40
|
+
const normalizedMime = normalizeMimeType(detectedContentType);
|
|
41
|
+
// Check if content requires specialized handler (PDF, JSON, SVG)
|
|
42
|
+
if (normalizedMime === 'application/pdf' ||
|
|
43
|
+
normalizedMime === 'application/json' ||
|
|
44
|
+
normalizedMime === 'text/json' ||
|
|
45
|
+
normalizedMime === 'image/svg+xml') {
|
|
46
|
+
// Route to specialized content handler
|
|
47
|
+
// Note: rawContent may be Buffer for PDFs, string for JSON/SVG
|
|
48
|
+
const handlerResult = await routeContentHandler(rawContent, detectedContentType);
|
|
49
|
+
// Handle unsupported or error cases
|
|
50
|
+
if (handlerResult.status === 'rejected' || handlerResult.status === 'error') {
|
|
51
|
+
return Err(new Error(handlerResult.message));
|
|
52
|
+
}
|
|
53
|
+
// Type guard: ensure we have a success result
|
|
54
|
+
if (handlerResult.status !== 'sanitized') {
|
|
55
|
+
return Err(new Error('Unexpected handler result status'));
|
|
56
|
+
}
|
|
57
|
+
// Handler success - use the already-sanitized content
|
|
58
|
+
const sanitizedContent = handlerResult.sanitized_content;
|
|
59
|
+
const sanitization = handlerResult.sanitization;
|
|
60
|
+
const truncationResult = truncateContent(sanitizedContent);
|
|
61
|
+
// Determine format_detected based on MIME type
|
|
62
|
+
let formatDetected = 'html';
|
|
63
|
+
if (normalizedMime === 'application/json' || normalizedMime === 'text/json') {
|
|
64
|
+
formatDetected = 'json';
|
|
65
|
+
}
|
|
66
|
+
else if (normalizedMime === 'image/svg+xml') {
|
|
67
|
+
formatDetected = 'xml'; // SVG is XML-based
|
|
68
|
+
}
|
|
69
|
+
else if (normalizedMime === 'application/pdf') {
|
|
70
|
+
// PDF doesn't have a format_detected value in the current schema
|
|
71
|
+
// Leaving as 'html' for now
|
|
72
|
+
}
|
|
73
|
+
const output = {
|
|
74
|
+
url,
|
|
75
|
+
content: truncationResult.content,
|
|
76
|
+
sanitization: {
|
|
77
|
+
patterns_detected: sanitization.patterns_detected,
|
|
78
|
+
pii_types_redacted: sanitization.pii_types_redacted,
|
|
79
|
+
pii_allowlisted: sanitization.pii_allowlisted,
|
|
80
|
+
content_modified: sanitization.sanitized_fields > 0
|
|
81
|
+
},
|
|
82
|
+
metadata: {
|
|
83
|
+
title: title || 'Untitled',
|
|
84
|
+
fetched_at: new Date().toISOString(),
|
|
85
|
+
content_length_original: rawContent.length,
|
|
86
|
+
content_length_sanitized: sanitizedContent.length,
|
|
87
|
+
format_detected: formatDetected,
|
|
88
|
+
content_type: detectedContentType,
|
|
89
|
+
...(truncationResult.truncated && {
|
|
90
|
+
truncated: true,
|
|
91
|
+
truncated_at_chars: truncationResult.truncated_at_chars
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
return { ok: true, value: output };
|
|
96
|
+
}
|
|
97
|
+
// Step 3: For HTML/XML/RSS - use existing format conversion flow
|
|
98
|
+
// Type guard: rawContent should be string for non-binary types
|
|
99
|
+
if (Buffer.isBuffer(rawContent)) {
|
|
100
|
+
return Err(new Error('Unexpected binary content in HTML/XML/RSS path'));
|
|
101
|
+
}
|
|
38
102
|
const formatType = detectFormat(detectedContentType);
|
|
39
103
|
let processedContent = rawContent;
|
|
40
104
|
// Apply format-specific conversion (skip Readability for non-HTML)
|
|
@@ -48,13 +112,13 @@ export async function visusFetch(input) {
|
|
|
48
112
|
processedContent = convertRss(rawContent);
|
|
49
113
|
}
|
|
50
114
|
// For 'html' format, processedContent remains as rawContent
|
|
51
|
-
// Step
|
|
115
|
+
// Step 4: CRITICAL - Sanitize content (injection detection + PII redaction with allowlisting)
|
|
52
116
|
// This step CANNOT be skipped or bypassed
|
|
53
117
|
const sanitizationResult = sanitize(processedContent, url);
|
|
54
|
-
// Step
|
|
118
|
+
// Step 5: Apply token ceiling truncation (AFTER sanitization)
|
|
55
119
|
// Anthropic MCP Directory enforces 25,000 token response limit
|
|
56
120
|
const truncationResult = truncateContent(sanitizationResult.content);
|
|
57
|
-
// Step
|
|
121
|
+
// Step 6: Build output
|
|
58
122
|
const output = {
|
|
59
123
|
url,
|
|
60
124
|
content: truncationResult.content,
|
package/dist/tools/fetch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/tools/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/tools/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AACjG,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,KAAsB;IACrD,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC;IAE/D,kBAAkB;IAClB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,CAAC;QACH,2CAA2C;QAC3C,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE;YACzC,UAAU;YACV,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU;SAChD,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC;QACxD,mEAAmE;QACnE,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;QAE9B,8EAA8E;QAC9E,MAAM,mBAAmB,GAAG,WAAW,IAAI,WAAW,CAAC;QACvD,MAAM,cAAc,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;QAE9D,iEAAiE;QACjE,IAAI,cAAc,KAAK,iBAAiB;YACpC,cAAc,KAAK,kBAAkB;YACrC,cAAc,KAAK,WAAW;YAC9B,cAAc,KAAK,eAAe,EAAE,CAAC;YAEvC,uCAAuC;YACvC,+DAA+D;YAC/D,MAAM,aAAa,GAAG,MAAM,mBAAmB,CAAC,UAAU,EAAE,mBAAmB,CAAC,CAAC;YAEjF,oCAAoC;YACpC,IAAI,aAAa,CAAC,MAAM,KAAK,UAAU,IAAI,aAAa,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC5E,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;YAC/C,CAAC;YAED,8CAA8C;YAC9C,IAAI,aAAa,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBACzC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC,CAAC;YAC5D,CAAC;YAED,sDAAsD;YACtD,MAAM,gBAAgB,GAAG,aAAa,CAAC,iBAAiB,CAAC;YACzD,MAAM,YAAY,GAAG,aAAa,CAAC,YAAY,CAAC;YAChD,MAAM,gBAAgB,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;YAE3D,+CAA+C;YAC/C,IAAI,cAAc,GAAoC,MAAM,CAAC;YAC7D,IAAI,cAAc,KAAK,kBAAkB,IAAI,cAAc,KAAK,WAAW,EAAE,CAAC;gBAC5E,cAAc,GAAG,MAAM,CAAC;YAC1B,CAAC;iBAAM,IAAI,cAAc,KAAK,eAAe,EAAE,CAAC;gBAC9C,cAAc,GAAG,KAAK,CAAC,CAAC,mBAAmB;YAC7C,CAAC;iBAAM,IAAI,cAAc,KAAK,iBAAiB,EAAE,CAAC;gBAChD,iEAAiE;gBACjE,4BAA4B;YAC9B,CAAC;YAED,MAAM,MAAM,GAAqB;gBAC/B,GAAG;gBACH,OAAO,EAAE,gBAAgB,CAAC,OAAO;gBACjC,YAAY,EAAE;oBACZ,iBAAiB,EAAE,YAAY,CAAC,iBAAiB;oBACjD,kBAAkB,EAAE,YAAY,CAAC,kBAAkB;oBACnD,eAAe,EAAE,YAAY,CAAC,eAAe;oBAC7C,gBAAgB,EAAE,YAAY,CAAC,gBAAgB,GAAG,CAAC;iBACpD;gBACD,QAAQ,EAAE;oBACR,KAAK,EAAE,KAAK,IAAI,UAAU;oBAC1B,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACpC,uBAAuB,EAAE,UAAU,CAAC,MAAM;oBAC1C,wBAAwB,EAAE,gBAAgB,CAAC,MAAM;oBACjD,eAAe,EAAE,cAAc;oBAC/B,YAAY,EAAE,mBAAmB;oBACjC,GAAG,CAAC,gBAAgB,CAAC,SAAS,IAAI;wBAChC,SAAS,EAAE,IAAI;wBACf,kBAAkB,EAAE,gBAAgB,CAAC,kBAAkB;qBACxD,CAAC;iBACH;aACF,CAAC;YAEF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QACrC,CAAC;QAED,iEAAiE;QACjE,+DAA+D;QAC/D,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,UAAU,GAAG,YAAY,CAAC,mBAAmB,CAAC,CAAC;QAErD,IAAI,gBAAgB,GAAG,UAAU,CAAC;QAElC,mEAAmE;QACnE,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,gBAAgB,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;QAC7C,CAAC;aAAM,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YAChC,gBAAgB,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YAChC,gBAAgB,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QAC5C,CAAC;QACD,4DAA4D;QAE5D,8FAA8F;QAC9F,0CAA0C;QAC1C,MAAM,kBAAkB,GAAG,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAE3D,8DAA8D;QAC9D,+DAA+D;QAC/D,MAAM,gBAAgB,GAAG,eAAe,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAErE,uBAAuB;QACvB,MAAM,MAAM,GAAqB;YAC/B,GAAG;YACH,OAAO,EAAE,gBAAgB,CAAC,OAAO;YACjC,YAAY,EAAE;gBACZ,iBAAiB,EAAE,kBAAkB,CAAC,YAAY,CAAC,iBAAiB;gBACpE,kBAAkB,EAAE,kBAAkB,CAAC,YAAY,CAAC,kBAAkB;gBACtE,eAAe,EAAE,kBAAkB,CAAC,YAAY,CAAC,eAAe;gBAChE,gBAAgB,EAAE,kBAAkB,CAAC,YAAY,CAAC,gBAAgB;aACnE;YACD,QAAQ,EAAE;gBACR,KAAK,EAAE,KAAK,IAAI,UAAU;gBAC1B,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACpC,uBAAuB,EAAE,kBAAkB,CAAC,QAAQ,CAAC,eAAe;gBACpE,wBAAwB,EAAE,kBAAkB,CAAC,QAAQ,CAAC,gBAAgB;gBACtE,eAAe,EAAE,UAAU;gBAC3B,YAAY,EAAE,mBAAmB;gBACjC,GAAG,CAAC,gBAAgB,CAAC,SAAS,IAAI;oBAChC,SAAS,EAAE,IAAI;oBACf,kBAAkB,EAAE,gBAAgB,CAAC,kBAAkB;iBACxD,CAAC;aACH;YACD,+CAA+C;YAC/C,GAAG,CAAC,kBAAkB,CAAC,aAAa,IAAI,EAAE,aAAa,EAAE,kBAAkB,CAAC,aAAa,EAAE,CAAC;SAC7F,CAAC;QAEF,6CAA6C;QAC7C,IAAI,kBAAkB,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YACrD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC3B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,KAAK,EAAE,0BAA0B;gBACjC,GAAG;gBACH,QAAQ,EAAE,kBAAkB,CAAC,YAAY,CAAC,iBAAiB;gBAC3D,cAAc,EAAE,kBAAkB,CAAC,QAAQ,CAAC,cAAc;aAC3D,CAAC,CAAC,CAAC;QACN,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAErC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,IAAI,EAAE,aAAa;IACnB,KAAK,EAAE,4BAA4B;IACnC,WAAW,EAAE,sSAAsS;IACnT,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,EAAE;gBACH,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;YACD,MAAM,EAAE;gBACN,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC;gBAC1B,WAAW,EAAE,iDAAiD;gBAC9D,OAAO,EAAE,UAAU;aACpB;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;gBAC/D,OAAO,EAAE,KAAK;aACf;SACF;QACD,QAAQ,EAAE,CAAC,KAAK,CAAC;KAClB;IACD,YAAY,EAAE,IAAI;IAClB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,IAAI;CACpB,CAAC"}
|
package/dist/tools/read.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG3E;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAG3E;;;;;GAKG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC,CA0F9F;AAED;;GAEG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;CAuBnC,CAAC"}
|
package/dist/tools/read.js
CHANGED
|
@@ -40,6 +40,10 @@ export async function visusRead(input) {
|
|
|
40
40
|
return Err(renderResult.error);
|
|
41
41
|
}
|
|
42
42
|
const { html, title: pageTitle } = renderResult.value;
|
|
43
|
+
// Type guard: visus_read only works with HTML (string), not binary content
|
|
44
|
+
if (Buffer.isBuffer(html)) {
|
|
45
|
+
return Err(new Error('visus_read does not support binary content types (PDFs, images). Use visus_fetch instead.'));
|
|
46
|
+
}
|
|
43
47
|
// Step 2: Extract article content using Readability
|
|
44
48
|
const readerResult = extractArticle(html, url);
|
|
45
49
|
if (!readerResult.ok) {
|
package/dist/tools/read.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAElC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAqB;IACnD,MAAM,EAAE,GAAG,EAAE,UAAU,GAAG,KAAK,EAAE,GAAG,KAAK,CAAC;IAE1C,kBAAkB;IAClB,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,GAAG,CAAC,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,CAAC;QACH,2CAA2C;QAC3C,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,GAAG,EAAE;YACzC,UAAU;YACV,MAAM,EAAE,MAAM;SACf,CAAC,CAAC;QAEH,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC;QAEtD,oDAAoD;QACpD,MAAM,YAAY,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAE/C,IAAI,CAAC,YAAY,CAAC,EAAE,EAAE,CAAC;YACrB,OAAO,GAAG,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QAED,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC;QAEnC,4EAA4E;QAC5E,kDAAkD;QAClD,0CAA0C;QAC1C,MAAM,kBAAkB,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAE1D,8DAA8D;QAC9D,+DAA+D;QAC/D,MAAM,gBAAgB,GAAG,eAAe,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAErE,uBAAuB;QACvB,MAAM,MAAM,GAAoB;YAC9B,GAAG;YACH,OAAO,EAAE,gBAAgB,CAAC,OAAO;YACjC,QAAQ,EAAE;gBACR,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,SAAS,IAAI,UAAU;gBAC/C,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,SAAS,EAAE,OAAO,CAAC,aAAa;gBAChC,UAAU,EAAE,OAAO,CAAC,SAAS;gBAC7B,qBAAqB,EAAE,OAAO,CAAC,mBAAmB;gBAClD,SAAS,EAAE,IAAI;gBACf,kBAAkB,EAAE,kBAAkB,CAAC,YAAY,CAAC,iBAAiB,CAAC,MAAM;gBAC5E,YAAY,EAAE,kBAAkB,CAAC,YAAY,CAAC,kBAAkB,CAAC,MAAM;gBACvE,SAAS,EAAE,gBAAgB,CAAC,SAAS;gBACrC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACrC;YACD,+CAA+C;YAC/C,GAAG,CAAC,kBAAkB,CAAC,aAAa,IAAI,EAAE,aAAa,EAAE,kBAAkB,CAAC,aAAa,EAAE,CAAC;SAC7F,CAAC;QAEF,6CAA6C;QAC7C,IAAI,kBAAkB,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC;YACrD,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC3B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,KAAK,EAAE,iCAAiC;gBACxC,GAAG;gBACH,QAAQ,EAAE,kBAAkB,CAAC,YAAY,CAAC,iBAAiB;gBAC3D,cAAc,EAAE,kBAAkB,CAAC,QAAQ,CAAC,cAAc;aAC3D,CAAC,CAAC,CAAC;QACN,CAAC;QAED,yDAAyD;QACzD,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC;gBAC3B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,KAAK,EAAE,sBAAsB;gBAC7B,GAAG;gBACH,MAAM,EAAE,iDAAiD;aAC1D,CAAC,CAAC,CAAC;QACN,CAAC;QAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAErC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,GAAG,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG;IACrC,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,yCAAyC;IAChD,WAAW,EAAE,yRAAyR;IACtS,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,EAAE;gBACH,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,gDAAgD;aAC9D;YACD,UAAU,EAAE;gBACV,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kDAAkD;gBAC/D,OAAO,EAAE,KAAK;aACf;SACF;QACD,QAAQ,EAAE,CAAC,KAAK,CAAC;KAClB;IACD,YAAY,EAAE,IAAI;IAClB,eAAe,EAAE,KAAK;IACtB,cAAc,EAAE,IAAI;IACpB,aAAa,EAAE,IAAI;CACpB,CAAC"}
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -128,9 +128,17 @@ export interface VisusSearchOutput {
|
|
|
128
128
|
}
|
|
129
129
|
/**
|
|
130
130
|
* Result from browser rendering
|
|
131
|
+
*
|
|
132
|
+
* @property html - Response content as string (for text) or Buffer (for binary like PDFs)
|
|
133
|
+
* Use Buffer for application/pdf, image/*, and other binary types
|
|
134
|
+
* @property title - Page title extracted from response
|
|
135
|
+
* @property url - Final URL after redirects
|
|
136
|
+
* @property contentType - Content-Type header value (e.g., "application/pdf", "text/html")
|
|
137
|
+
* @property text - Optional text content (when available)
|
|
138
|
+
* @property error - Error message if rendering failed
|
|
131
139
|
*/
|
|
132
140
|
export interface BrowserRenderResult {
|
|
133
|
-
html: string;
|
|
141
|
+
html: string | Buffer;
|
|
134
142
|
title: string;
|
|
135
143
|
url: string;
|
|
136
144
|
contentType?: string;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqKH;;GAEG;AACH,MAAM,UAAU,EAAE,CAAI,KAAQ;IAC5B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAC7B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,GAAG,CAAI,KAAQ;IAC7B,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC9B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "visus-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"mcpName": "io.github.visus-mcp/visus-mcp",
|
|
5
5
|
"description": "Security-first MCP server. Sanitizes web content before it reaches your LLM — strips prompt injection, redacts PII, and reduces token consumption by up to 70%.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"cheerio": "^1.2.0",
|
|
49
49
|
"fast-xml-parser": "^5.5.8",
|
|
50
50
|
"jsdom": "^29.0.1",
|
|
51
|
+
"pdf-parse": "^2.4.5",
|
|
51
52
|
"playwright": "^1.58.2",
|
|
52
53
|
"undici": "^7.24.5"
|
|
53
54
|
},
|
package/server.json
CHANGED
|
@@ -1,43 +1,54 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-
|
|
2
|
+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-07-09/server.schema.json",
|
|
3
3
|
"name": "io.github.visus-mcp/visus-mcp",
|
|
4
|
-
"description": "Security-first web access
|
|
4
|
+
"description": "Security-first web access. Sanitizes pages, blocks injection, redacts PII. Now with PDF/JSON/SVG.",
|
|
5
|
+
"status": "active",
|
|
6
|
+
"website_url": "https://github.com/visus-mcp/visus-mcp",
|
|
5
7
|
"repository": {
|
|
6
8
|
"url": "https://github.com/visus-mcp/visus-mcp",
|
|
7
9
|
"source": "github"
|
|
8
10
|
},
|
|
9
|
-
"version": "0.
|
|
11
|
+
"version": "0.9.0",
|
|
10
12
|
"packages": [
|
|
11
13
|
{
|
|
12
14
|
"registryType": "npm",
|
|
15
|
+
"registryBaseUrl": "https://registry.npmjs.org",
|
|
13
16
|
"identifier": "visus-mcp",
|
|
14
|
-
"version": "0.
|
|
17
|
+
"version": "0.9.0",
|
|
18
|
+
"runtimeHint": "npx",
|
|
15
19
|
"transport": {
|
|
16
20
|
"type": "stdio"
|
|
17
21
|
},
|
|
18
22
|
"environmentVariables": [
|
|
19
23
|
{
|
|
24
|
+
"name": "VISUS_TIMEOUT_MS",
|
|
20
25
|
"description": "Optional timeout in milliseconds for page fetching (default: 10000)",
|
|
21
|
-
"isRequired": false,
|
|
22
26
|
"format": "number",
|
|
23
|
-
"
|
|
24
|
-
"name": "VISUS_TIMEOUT_MS"
|
|
27
|
+
"isRequired": false
|
|
25
28
|
},
|
|
26
29
|
{
|
|
30
|
+
"name": "VISUS_MAX_CONTENT_KB",
|
|
27
31
|
"description": "Optional maximum content size in KB before truncation (default: 512)",
|
|
28
|
-
"isRequired": false,
|
|
29
32
|
"format": "number",
|
|
30
|
-
"
|
|
31
|
-
"name": "VISUS_MAX_CONTENT_KB"
|
|
33
|
+
"isRequired": false
|
|
32
34
|
},
|
|
33
35
|
{
|
|
36
|
+
"name": "VISUS_RENDERER_URL",
|
|
34
37
|
"description": "Optional URL for managed Lambda renderer (Phase 2 - not yet available)",
|
|
35
|
-
"isRequired": false,
|
|
36
38
|
"format": "string",
|
|
37
|
-
"
|
|
38
|
-
"name": "VISUS_RENDERER_URL"
|
|
39
|
+
"isRequired": false
|
|
39
40
|
}
|
|
40
41
|
]
|
|
41
42
|
}
|
|
42
|
-
]
|
|
43
|
+
],
|
|
44
|
+
"_meta": {
|
|
45
|
+
"io.modelcontextprotocol.registry/publisher-provided": {
|
|
46
|
+
"tool": "mcp-publisher",
|
|
47
|
+
"version": "0.9.0",
|
|
48
|
+
"build_info": {
|
|
49
|
+
"timestamp": "2026-03-26T00:00:00Z",
|
|
50
|
+
"release": "v0.9.0 — NIST AI RMF & CSF 2.0 framework mappings, 6-framework threat alignment, expanded compliance reporting"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
43
54
|
}
|
|
@@ -127,6 +127,8 @@ async function renderWithLambda(
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// Success response
|
|
130
|
+
// TODO: Lambda renderer needs PDF support - should return binary content as base64
|
|
131
|
+
// for application/pdf responses instead of always converting to HTML string
|
|
130
132
|
return Ok({
|
|
131
133
|
html: body.html,
|
|
132
134
|
title: body.title,
|
|
@@ -176,17 +178,38 @@ async function renderWithFetch(
|
|
|
176
178
|
return Err(new Error(`HTTP ${response.status}: ${response.statusText}`));
|
|
177
179
|
}
|
|
178
180
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
// Capture Content-Type header
|
|
181
|
+
// Capture Content-Type header before reading body
|
|
182
182
|
const contentTypeHeader = response.headers.get('content-type');
|
|
183
183
|
const contentType = contentTypeHeader
|
|
184
184
|
? contentTypeHeader.split(';')[0].trim() // Remove charset and other params
|
|
185
185
|
: 'text/html'; // Default to HTML if missing
|
|
186
186
|
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
const
|
|
187
|
+
// Read response body - use arrayBuffer() for binary types, text() for text types
|
|
188
|
+
// CRITICAL: pdf-parse requires original binary bytes, not UTF-8 string conversion
|
|
189
|
+
const isBinary = contentType === 'application/pdf' ||
|
|
190
|
+
contentType.startsWith('image/') ||
|
|
191
|
+
contentType.startsWith('application/octet-stream');
|
|
192
|
+
|
|
193
|
+
let html: string | Buffer;
|
|
194
|
+
let title = '';
|
|
195
|
+
|
|
196
|
+
if (isBinary) {
|
|
197
|
+
// Binary content (PDF, images, etc.) - preserve byte integrity
|
|
198
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
199
|
+
html = Buffer.from(arrayBuffer);
|
|
200
|
+
// Title extraction not meaningful for binary content
|
|
201
|
+
title = '';
|
|
202
|
+
} else {
|
|
203
|
+
// Text content (HTML, JSON, etc.) - read as UTF-8 string
|
|
204
|
+
const textContent = await response.text();
|
|
205
|
+
html = textContent;
|
|
206
|
+
|
|
207
|
+
// Extract title using regex (HTML only)
|
|
208
|
+
if (contentType.includes('html')) {
|
|
209
|
+
const titleMatch = textContent.match(/<title[^>]*>(.*?)<\/title>/i);
|
|
210
|
+
title = titleMatch ? titleMatch[1].trim() : '';
|
|
211
|
+
}
|
|
212
|
+
}
|
|
190
213
|
|
|
191
214
|
return Ok({
|
|
192
215
|
html,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content Handlers Module
|
|
3
|
+
*
|
|
4
|
+
* Central routing for content-type specific sanitization handlers.
|
|
5
|
+
* Detects MIME type from Content-Type header and routes to appropriate handler.
|
|
6
|
+
*
|
|
7
|
+
* Supported content types:
|
|
8
|
+
* - application/pdf -> PDF handler
|
|
9
|
+
* - application/json -> JSON handler
|
|
10
|
+
* - image/svg+xml -> SVG handler
|
|
11
|
+
*
|
|
12
|
+
* Unsupported types return structured rejection (no throw).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { handlePdf } from './pdf-handler.js';
|
|
16
|
+
import { handleJson } from './json-handler.js';
|
|
17
|
+
import { handleSvg } from './svg-handler.js';
|
|
18
|
+
import type { HandlerResult } from './types.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Normalize Content-Type header to base MIME type
|
|
22
|
+
*
|
|
23
|
+
* Examples:
|
|
24
|
+
* - "application/pdf; charset=utf-8" -> "application/pdf"
|
|
25
|
+
* - "application/json" -> "application/json"
|
|
26
|
+
* - "IMAGE/SVG+XML" -> "image/svg+xml"
|
|
27
|
+
*
|
|
28
|
+
* @param contentType - Raw Content-Type header value
|
|
29
|
+
* @returns Normalized MIME type (lowercase, parameters stripped)
|
|
30
|
+
*/
|
|
31
|
+
export function normalizeMimeType(contentType: string): string {
|
|
32
|
+
return contentType.toLowerCase().split(';')[0].trim();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Route content to appropriate handler based on MIME type
|
|
37
|
+
*
|
|
38
|
+
* @param content - Raw content (string or Buffer)
|
|
39
|
+
* @param contentType - Content-Type header value
|
|
40
|
+
* @returns Handler result (success or error/rejected)
|
|
41
|
+
*/
|
|
42
|
+
export async function routeContentHandler(
|
|
43
|
+
content: string | Buffer,
|
|
44
|
+
contentType: string
|
|
45
|
+
): Promise<HandlerResult> {
|
|
46
|
+
const mimeType = normalizeMimeType(contentType);
|
|
47
|
+
|
|
48
|
+
// Route to appropriate handler
|
|
49
|
+
switch (mimeType) {
|
|
50
|
+
case 'application/pdf':
|
|
51
|
+
return handlePdf(content, mimeType);
|
|
52
|
+
|
|
53
|
+
case 'application/json':
|
|
54
|
+
case 'text/json':
|
|
55
|
+
return handleJson(content, mimeType);
|
|
56
|
+
|
|
57
|
+
case 'image/svg+xml':
|
|
58
|
+
return handleSvg(content, mimeType);
|
|
59
|
+
|
|
60
|
+
default:
|
|
61
|
+
// Unsupported content type - return structured rejection
|
|
62
|
+
return {
|
|
63
|
+
status: 'rejected',
|
|
64
|
+
reason: 'UNSUPPORTED_CONTENT_TYPE',
|
|
65
|
+
mime: mimeType,
|
|
66
|
+
message: `Content type ${mimeType} is not supported by Visus-MCP.`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Re-export types
|
|
72
|
+
export type { HandlerResult, HandlerSuccessResult, HandlerErrorResult } from './types.js';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Content Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles application/json content type. Recursively traverses all nodes in the JSON
|
|
5
|
+
* object tree and applies the full injection pattern registry to every string value.
|
|
6
|
+
*
|
|
7
|
+
* What it handles:
|
|
8
|
+
* - All string values in the JSON tree (any depth)
|
|
9
|
+
* - Arrays, nested objects, and mixed-type arrays
|
|
10
|
+
* - Falls back to plain text pipeline if JSON.parse fails
|
|
11
|
+
*
|
|
12
|
+
* What it strips:
|
|
13
|
+
* - Nothing (preserves original structure)
|
|
14
|
+
*
|
|
15
|
+
* What it passes through:
|
|
16
|
+
* - Sanitized JSON with original structure preserved
|
|
17
|
+
* - All non-string values pass through unchanged
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { sanitize } from '../sanitizer/index.js';
|
|
21
|
+
import type { HandlerResult } from './types.js';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Handle JSON content
|
|
25
|
+
*
|
|
26
|
+
* @param content - Raw JSON string
|
|
27
|
+
* @param mimeType - Original MIME type
|
|
28
|
+
* @returns Sanitized handler result
|
|
29
|
+
*/
|
|
30
|
+
export function handleJson(
|
|
31
|
+
content: string | Buffer,
|
|
32
|
+
mimeType: string
|
|
33
|
+
): HandlerResult {
|
|
34
|
+
const startTime = Date.now();
|
|
35
|
+
|
|
36
|
+
// Convert Buffer to string if needed
|
|
37
|
+
const jsonString = Buffer.isBuffer(content) ? content.toString('utf-8') : content;
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Parse JSON
|
|
41
|
+
const parsed = JSON.parse(jsonString);
|
|
42
|
+
|
|
43
|
+
// Track sanitization metadata across all fields
|
|
44
|
+
let sanitizedFieldCount = 0;
|
|
45
|
+
const allPatternsDetected = new Set<string>();
|
|
46
|
+
const allPiiTypesRedacted = new Set<string>();
|
|
47
|
+
const allPiiAllowlisted: Array<{ type: string; value: string; reason: string }> = [];
|
|
48
|
+
|
|
49
|
+
// Recursively sanitize all string values
|
|
50
|
+
const sanitized = recursiveSanitize(parsed, (text: string) => {
|
|
51
|
+
const result = sanitize(text);
|
|
52
|
+
if (result.sanitization.content_modified) {
|
|
53
|
+
sanitizedFieldCount++;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Aggregate metadata
|
|
57
|
+
result.sanitization.patterns_detected.forEach(p => allPatternsDetected.add(p));
|
|
58
|
+
result.sanitization.pii_types_redacted.forEach(p => allPiiTypesRedacted.add(p));
|
|
59
|
+
allPiiAllowlisted.push(...result.sanitization.pii_allowlisted);
|
|
60
|
+
|
|
61
|
+
return result.content;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Re-stringify with 2-space indent
|
|
65
|
+
const sanitizedJson = JSON.stringify(sanitized, null, 2);
|
|
66
|
+
|
|
67
|
+
const processingTime = Date.now() - startTime;
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
status: 'sanitized',
|
|
71
|
+
content_type: mimeType,
|
|
72
|
+
sanitized_content: sanitizedJson,
|
|
73
|
+
sanitization: {
|
|
74
|
+
patterns_detected: Array.from(allPatternsDetected),
|
|
75
|
+
pii_types_redacted: Array.from(allPiiTypesRedacted),
|
|
76
|
+
pii_allowlisted: allPiiAllowlisted,
|
|
77
|
+
sanitized_fields: sanitizedFieldCount
|
|
78
|
+
},
|
|
79
|
+
processing_time_ms: processingTime
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
} catch (error) {
|
|
83
|
+
// JSON.parse failed - fall back to plain text sanitization
|
|
84
|
+
const sanitizationResult = sanitize(jsonString);
|
|
85
|
+
|
|
86
|
+
const processingTime = Date.now() - startTime;
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
status: 'sanitized',
|
|
90
|
+
content_type: mimeType,
|
|
91
|
+
sanitized_content: sanitizationResult.content,
|
|
92
|
+
sanitization: {
|
|
93
|
+
patterns_detected: sanitizationResult.sanitization.patterns_detected,
|
|
94
|
+
pii_types_redacted: sanitizationResult.sanitization.pii_types_redacted,
|
|
95
|
+
pii_allowlisted: sanitizationResult.sanitization.pii_allowlisted,
|
|
96
|
+
sanitized_fields: sanitizationResult.sanitization.patterns_detected.length
|
|
97
|
+
},
|
|
98
|
+
processing_time_ms: processingTime
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Recursively traverse JSON tree and sanitize all string values
|
|
105
|
+
*
|
|
106
|
+
* @param obj - JSON object/array/primitive
|
|
107
|
+
* @param sanitizeFn - Function to sanitize string values
|
|
108
|
+
* @returns Sanitized object with same structure
|
|
109
|
+
*/
|
|
110
|
+
function recursiveSanitize(obj: any, sanitizeFn: (text: string) => string): any {
|
|
111
|
+
// Handle null
|
|
112
|
+
if (obj === null) {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Handle string - sanitize it
|
|
117
|
+
if (typeof obj === 'string') {
|
|
118
|
+
return sanitizeFn(obj);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Handle array - recursively sanitize each element
|
|
122
|
+
if (Array.isArray(obj)) {
|
|
123
|
+
return obj.map((item) => recursiveSanitize(item, sanitizeFn));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Handle object - recursively sanitize each value
|
|
127
|
+
if (typeof obj === 'object') {
|
|
128
|
+
const sanitizedObj: Record<string, any> = {};
|
|
129
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
130
|
+
sanitizedObj[key] = recursiveSanitize(value, sanitizeFn);
|
|
131
|
+
}
|
|
132
|
+
return sanitizedObj;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Handle primitives (number, boolean, undefined) - pass through
|
|
136
|
+
return obj;
|
|
137
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF Content Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles application/pdf content type. Extracts text and metadata from PDF files,
|
|
5
|
+
* passes all text through the injection pattern registry, and returns sanitized plain text.
|
|
6
|
+
*
|
|
7
|
+
* What it handles:
|
|
8
|
+
* - PDF body text (full document)
|
|
9
|
+
* - PDF metadata: title, author, subject, keywords, creator, producer
|
|
10
|
+
* - Annotation text
|
|
11
|
+
* - Form field values
|
|
12
|
+
*
|
|
13
|
+
* What it strips:
|
|
14
|
+
* - Embedded binary objects (fonts, images, attachments)
|
|
15
|
+
* - Returns only extracted text, not original binary
|
|
16
|
+
*
|
|
17
|
+
* What it passes through:
|
|
18
|
+
* - All extracted text after injection pattern sanitization
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { PDFParse } from 'pdf-parse';
|
|
22
|
+
import { sanitize } from '../sanitizer/index.js';
|
|
23
|
+
import type { HandlerResult } from './types.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Handle PDF content
|
|
27
|
+
*
|
|
28
|
+
* @param content - Raw PDF binary data as Buffer or string
|
|
29
|
+
* @param mimeType - Original MIME type
|
|
30
|
+
* @returns Sanitized handler result
|
|
31
|
+
*/
|
|
32
|
+
export async function handlePdf(
|
|
33
|
+
content: string | Buffer,
|
|
34
|
+
mimeType: string
|
|
35
|
+
): Promise<HandlerResult> {
|
|
36
|
+
const startTime = Date.now();
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Ensure we have a Buffer
|
|
40
|
+
const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content);
|
|
41
|
+
|
|
42
|
+
// Parse PDF using pdf-parse v2 API
|
|
43
|
+
const parser = new PDFParse({ data: buffer });
|
|
44
|
+
|
|
45
|
+
// Get text and metadata separately
|
|
46
|
+
const textResult = await parser.getText();
|
|
47
|
+
const infoResult = await parser.getInfo();
|
|
48
|
+
|
|
49
|
+
// Extract text and metadata
|
|
50
|
+
const bodyText = textResult.text || '';
|
|
51
|
+
const metadata = infoResult.info || {};
|
|
52
|
+
|
|
53
|
+
// Build combined text from body + metadata
|
|
54
|
+
let combinedText = bodyText;
|
|
55
|
+
|
|
56
|
+
// Append metadata fields
|
|
57
|
+
const metadataFields = ['Title', 'Author', 'Subject', 'Keywords', 'Creator', 'Producer'];
|
|
58
|
+
for (const field of metadataFields) {
|
|
59
|
+
const value = metadata[field];
|
|
60
|
+
if (value && typeof value === 'string') {
|
|
61
|
+
combinedText += `\n\n${field}: ${value}`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Pass through injection detection pipeline
|
|
66
|
+
const sanitizationResult = sanitize(combinedText);
|
|
67
|
+
|
|
68
|
+
const processingTime = Date.now() - startTime;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
status: 'sanitized',
|
|
72
|
+
content_type: mimeType,
|
|
73
|
+
sanitized_content: sanitizationResult.content,
|
|
74
|
+
sanitization: {
|
|
75
|
+
patterns_detected: sanitizationResult.sanitization.patterns_detected,
|
|
76
|
+
pii_types_redacted: sanitizationResult.sanitization.pii_types_redacted,
|
|
77
|
+
pii_allowlisted: sanitizationResult.sanitization.pii_allowlisted,
|
|
78
|
+
sanitized_fields: sanitizationResult.sanitization.patterns_detected.length
|
|
79
|
+
},
|
|
80
|
+
processing_time_ms: processingTime
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
status: 'error',
|
|
86
|
+
reason: 'PDF_PARSE_FAILED',
|
|
87
|
+
mime: mimeType,
|
|
88
|
+
message: error instanceof Error ? error.message : String(error)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|