visus-mcp 0.2.0 → 0.6.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 +22 -0
- package/LINKEDIN-STRATEGY.md +367 -0
- package/README.md +491 -16
- package/ROADMAP.md +214 -34
- package/SECURITY-AUDIT-v1.md +277 -0
- package/STATUS.md +801 -42
- package/TROUBLESHOOT-AUTH-20260322-2019.md +291 -0
- package/TROUBLESHOOT-JEST-20260323-1357.md +139 -0
- package/TROUBLESHOOT-LAMBDA-20260322-1945.md +183 -0
- package/VISUS-CLAUDE-CODE-PROMPT.md +1 -1
- package/VISUS-PROJECT-PLAN.md +7 -0
- package/dist/browser/playwright-renderer.d.ts.map +1 -1
- package/dist/browser/playwright-renderer.js +7 -0
- package/dist/browser/playwright-renderer.js.map +1 -1
- package/dist/browser/reader.d.ts +31 -0
- package/dist/browser/reader.d.ts.map +1 -0
- package/dist/browser/reader.js +98 -0
- package/dist/browser/reader.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +37 -5
- package/dist/index.js.map +1 -1
- package/dist/lambda-handler.d.ts +0 -6
- package/dist/lambda-handler.d.ts.map +1 -1
- package/dist/lambda-handler.js +97 -25
- package/dist/lambda-handler.js.map +1 -1
- package/dist/sanitizer/framework-mapper.d.ts +22 -0
- package/dist/sanitizer/framework-mapper.d.ts.map +1 -0
- package/dist/sanitizer/framework-mapper.js +296 -0
- package/dist/sanitizer/framework-mapper.js.map +1 -0
- package/dist/sanitizer/index.d.ts +10 -2
- package/dist/sanitizer/index.d.ts.map +1 -1
- package/dist/sanitizer/index.js +22 -6
- package/dist/sanitizer/index.js.map +1 -1
- package/dist/sanitizer/patterns.js +1 -1
- package/dist/sanitizer/patterns.js.map +1 -1
- package/dist/sanitizer/pii-allowlist.d.ts +49 -0
- package/dist/sanitizer/pii-allowlist.d.ts.map +1 -0
- package/dist/sanitizer/pii-allowlist.js +231 -0
- package/dist/sanitizer/pii-allowlist.js.map +1 -0
- package/dist/sanitizer/pii-redactor.d.ts +13 -1
- package/dist/sanitizer/pii-redactor.d.ts.map +1 -1
- package/dist/sanitizer/pii-redactor.js +26 -2
- package/dist/sanitizer/pii-redactor.js.map +1 -1
- package/dist/sanitizer/severity-classifier.d.ts +33 -0
- package/dist/sanitizer/severity-classifier.d.ts.map +1 -0
- package/dist/sanitizer/severity-classifier.js +113 -0
- package/dist/sanitizer/severity-classifier.js.map +1 -0
- package/dist/sanitizer/threat-reporter.d.ts +65 -0
- package/dist/sanitizer/threat-reporter.d.ts.map +1 -0
- package/dist/sanitizer/threat-reporter.js +160 -0
- package/dist/sanitizer/threat-reporter.js.map +1 -0
- package/dist/tools/fetch-structured.d.ts +5 -0
- package/dist/tools/fetch-structured.d.ts.map +1 -1
- package/dist/tools/fetch-structured.js +59 -8
- package/dist/tools/fetch-structured.js.map +1 -1
- package/dist/tools/fetch.d.ts +5 -0
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +43 -9
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/read.d.ts +51 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +127 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/search.d.ts +45 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +220 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/format-converter.d.ts +39 -0
- package/dist/utils/format-converter.d.ts.map +1 -0
- package/dist/utils/format-converter.js +191 -0
- package/dist/utils/format-converter.js.map +1 -0
- package/dist/utils/truncate.d.ts +26 -0
- package/dist/utils/truncate.d.ts.map +1 -0
- package/dist/utils/truncate.js +54 -0
- package/dist/utils/truncate.js.map +1 -0
- package/infrastructure/stack.ts +55 -6
- package/jest.config.js +3 -0
- package/package.json +9 -2
- package/src/browser/playwright-renderer.ts +8 -0
- package/src/browser/reader.ts +129 -0
- package/src/index.ts +49 -5
- package/src/lambda-handler.ts +131 -26
- package/src/sanitizer/framework-mapper.ts +347 -0
- package/src/sanitizer/index.ts +28 -6
- package/src/sanitizer/patterns.ts +1 -1
- package/src/sanitizer/pii-allowlist.ts +273 -0
- package/src/sanitizer/pii-redactor.ts +43 -2
- package/src/sanitizer/severity-classifier.ts +132 -0
- package/src/sanitizer/threat-reporter.ts +261 -0
- package/src/tools/fetch-structured.ts +63 -8
- package/src/tools/fetch.ts +45 -9
- package/src/tools/read.ts +143 -0
- package/src/tools/search.ts +263 -0
- package/src/types.ts +71 -0
- package/src/utils/format-converter.ts +236 -0
- package/src/utils/truncate.ts +64 -0
- package/tests/auth-smoke.test.ts +480 -0
- package/tests/fetch-tool.test.ts +595 -2
- package/tests/pii-allowlist.test.ts +282 -0
- package/tests/reader.test.ts +353 -0
- package/tests/sanitizer.test.ts +52 -0
- package/tests/search.test.ts +456 -0
- package/tests/threat-reporter.test.ts +266 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* visus_read MCP Tool
|
|
3
|
+
*
|
|
4
|
+
* Extracts clean article content from a web page using Mozilla Readability,
|
|
5
|
+
* stripping navigation, ads, and boilerplate. Full prompt injection sanitization
|
|
6
|
+
* and PII redaction applied before content reaches the LLM.
|
|
7
|
+
*
|
|
8
|
+
* CRITICAL: ALL content MUST pass through the sanitizer. This cannot be bypassed.
|
|
9
|
+
*
|
|
10
|
+
* Pipeline order:
|
|
11
|
+
* 1. Playwright renders page (full JS execution)
|
|
12
|
+
* 2. Reader extracts main content (reduces input size)
|
|
13
|
+
* 3. Sanitizer runs on clean text
|
|
14
|
+
* 4. Token ceiling applied (24,000 token cap)
|
|
15
|
+
*/
|
|
16
|
+
import { renderPage } from '../browser/playwright-renderer.js';
|
|
17
|
+
import { extractArticle } from '../browser/reader.js';
|
|
18
|
+
import { sanitize } from '../sanitizer/index.js';
|
|
19
|
+
import { truncateContent } from '../utils/truncate.js';
|
|
20
|
+
import { Err } from '../types.js';
|
|
21
|
+
/**
|
|
22
|
+
* visus_read tool implementation
|
|
23
|
+
*
|
|
24
|
+
* @param input Tool input parameters
|
|
25
|
+
* @returns Sanitized article content with metadata
|
|
26
|
+
*/
|
|
27
|
+
export async function visusRead(input) {
|
|
28
|
+
const { url, timeout_ms = 10000 } = input;
|
|
29
|
+
// Validate inputs
|
|
30
|
+
if (!url || typeof url !== 'string') {
|
|
31
|
+
return Err(new Error('Invalid input: url must be a non-empty string'));
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
// Step 1: Render the page using Playwright
|
|
35
|
+
const renderResult = await renderPage(url, {
|
|
36
|
+
timeout_ms,
|
|
37
|
+
format: 'html'
|
|
38
|
+
});
|
|
39
|
+
if (!renderResult.ok) {
|
|
40
|
+
return Err(renderResult.error);
|
|
41
|
+
}
|
|
42
|
+
const { html, title: pageTitle } = renderResult.value;
|
|
43
|
+
// Step 2: Extract article content using Readability
|
|
44
|
+
const readerResult = extractArticle(html, url);
|
|
45
|
+
if (!readerResult.ok) {
|
|
46
|
+
return Err(readerResult.error);
|
|
47
|
+
}
|
|
48
|
+
const article = readerResult.value;
|
|
49
|
+
// Step 3: CRITICAL - Sanitize content (injection detection + PII redaction)
|
|
50
|
+
// Sanitization runs AFTER Readability, not before
|
|
51
|
+
// This step CANNOT be skipped or bypassed
|
|
52
|
+
const sanitizationResult = sanitize(article.content, url);
|
|
53
|
+
// Step 4: Apply token ceiling truncation (AFTER sanitization)
|
|
54
|
+
// Anthropic MCP Directory enforces 25,000 token response limit
|
|
55
|
+
const truncationResult = truncateContent(sanitizationResult.content);
|
|
56
|
+
// Step 5: Build output
|
|
57
|
+
const output = {
|
|
58
|
+
url,
|
|
59
|
+
content: truncationResult.content,
|
|
60
|
+
metadata: {
|
|
61
|
+
title: article.title || pageTitle || 'Untitled',
|
|
62
|
+
author: article.byline,
|
|
63
|
+
published: article.publishedTime,
|
|
64
|
+
word_count: article.wordCount,
|
|
65
|
+
reader_mode_available: article.readerModeAvailable,
|
|
66
|
+
sanitized: true,
|
|
67
|
+
injections_removed: sanitizationResult.sanitization.patterns_detected.length,
|
|
68
|
+
pii_redacted: sanitizationResult.sanitization.pii_types_redacted.length,
|
|
69
|
+
truncated: truncationResult.truncated,
|
|
70
|
+
fetched_at: new Date().toISOString()
|
|
71
|
+
},
|
|
72
|
+
// Include threat_report only if findings exist
|
|
73
|
+
...(sanitizationResult.threat_report && { threat_report: sanitizationResult.threat_report })
|
|
74
|
+
};
|
|
75
|
+
// Log to stderr if critical threats detected
|
|
76
|
+
if (sanitizationResult.metadata.has_critical_threats) {
|
|
77
|
+
console.error(JSON.stringify({
|
|
78
|
+
timestamp: new Date().toISOString(),
|
|
79
|
+
event: 'reader_critical_threat_detected',
|
|
80
|
+
url,
|
|
81
|
+
patterns: sanitizationResult.sanitization.patterns_detected,
|
|
82
|
+
severity_score: sanitizationResult.metadata.severity_score
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
// Log to stderr if reader mode failed (non-article page)
|
|
86
|
+
if (!article.readerModeAvailable) {
|
|
87
|
+
console.error(JSON.stringify({
|
|
88
|
+
timestamp: new Date().toISOString(),
|
|
89
|
+
event: 'reader_mode_fallback',
|
|
90
|
+
url,
|
|
91
|
+
reason: 'Readability could not extract article structure'
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
return { ok: true, value: output };
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* MCP tool definition for registration
|
|
102
|
+
*/
|
|
103
|
+
export const visusReadToolDefinition = {
|
|
104
|
+
name: 'visus_read',
|
|
105
|
+
title: 'Read Web Page (Reader Mode + Sanitized)',
|
|
106
|
+
description: 'Extracts clean article content from a web page using Mozilla Readability, stripping navigation, ads, and boilerplate. Full prompt injection sanitization and PII redaction applied before content reaches the LLM. Optimized for context-efficient, safe web reading in Claude Desktop.',
|
|
107
|
+
inputSchema: {
|
|
108
|
+
type: 'object',
|
|
109
|
+
properties: {
|
|
110
|
+
url: {
|
|
111
|
+
type: 'string',
|
|
112
|
+
description: 'The URL to fetch (must be http:// or https://)'
|
|
113
|
+
},
|
|
114
|
+
timeout_ms: {
|
|
115
|
+
type: 'number',
|
|
116
|
+
description: 'Request timeout in milliseconds (default: 10000)',
|
|
117
|
+
default: 10000
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
required: ['url']
|
|
121
|
+
},
|
|
122
|
+
readOnlyHint: true,
|
|
123
|
+
destructiveHint: false,
|
|
124
|
+
idempotentHint: true,
|
|
125
|
+
openWorldHint: true
|
|
126
|
+
};
|
|
127
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visus Search Tool - Safe Web Search
|
|
3
|
+
*
|
|
4
|
+
* Queries DuckDuckGo's Instant Answer API and sanitizes all results
|
|
5
|
+
* before returning them to the LLM.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY: Every search result snippet and title passes through the
|
|
8
|
+
* sanitization pipeline. This prevents prompt injection via search results.
|
|
9
|
+
*/
|
|
10
|
+
import type { VisusSearchInput, VisusSearchOutput, Result } from '../types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Search the web via DuckDuckGo and return sanitized results
|
|
13
|
+
*
|
|
14
|
+
* @param input Search query and options
|
|
15
|
+
* @returns Sanitized search results with injection detection metadata
|
|
16
|
+
*/
|
|
17
|
+
export declare function visusSearch(input: VisusSearchInput): Promise<Result<VisusSearchOutput, Error>>;
|
|
18
|
+
/**
|
|
19
|
+
* Tool definition for MCP registration
|
|
20
|
+
*/
|
|
21
|
+
export declare const visusSearchToolDefinition: {
|
|
22
|
+
name: string;
|
|
23
|
+
title: string;
|
|
24
|
+
description: string;
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: string;
|
|
27
|
+
properties: {
|
|
28
|
+
query: {
|
|
29
|
+
type: string;
|
|
30
|
+
description: string;
|
|
31
|
+
};
|
|
32
|
+
max_results: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
default: number;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
required: string[];
|
|
39
|
+
};
|
|
40
|
+
readOnlyHint: boolean;
|
|
41
|
+
destructiveHint: boolean;
|
|
42
|
+
idempotentHint: boolean;
|
|
43
|
+
openWorldHint: boolean;
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiB/E;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC,CAkLpG;AAuBD;;GAEG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;CAuBrC,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visus Search Tool - Safe Web Search
|
|
3
|
+
*
|
|
4
|
+
* Queries DuckDuckGo's Instant Answer API and sanitizes all results
|
|
5
|
+
* before returning them to the LLM.
|
|
6
|
+
*
|
|
7
|
+
* SECURITY: Every search result snippet and title passes through the
|
|
8
|
+
* sanitization pipeline. This prevents prompt injection via search results.
|
|
9
|
+
*/
|
|
10
|
+
import { sanitize } from '../sanitizer/index.js';
|
|
11
|
+
import { generateThreatReport } from '../sanitizer/threat-reporter.js';
|
|
12
|
+
import { Ok, Err } from '../types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Search the web via DuckDuckGo and return sanitized results
|
|
15
|
+
*
|
|
16
|
+
* @param input Search query and options
|
|
17
|
+
* @returns Sanitized search results with injection detection metadata
|
|
18
|
+
*/
|
|
19
|
+
export async function visusSearch(input) {
|
|
20
|
+
// Validate input
|
|
21
|
+
if (!input.query || typeof input.query !== 'string' || input.query.trim().length === 0) {
|
|
22
|
+
return Err(new Error('query must be a non-empty string'));
|
|
23
|
+
}
|
|
24
|
+
// Enforce max_results cap
|
|
25
|
+
const maxResults = Math.min(input.max_results ?? 5, 10);
|
|
26
|
+
try {
|
|
27
|
+
// Call DuckDuckGo Instant Answer API
|
|
28
|
+
const query = encodeURIComponent(input.query.trim());
|
|
29
|
+
const apiUrl = `https://api.duckduckgo.com/?q=${query}&format=json&no_redirect=1&no_html=1`;
|
|
30
|
+
const controller = new AbortController();
|
|
31
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
32
|
+
let response;
|
|
33
|
+
try {
|
|
34
|
+
response = await fetch(apiUrl, {
|
|
35
|
+
signal: controller.signal,
|
|
36
|
+
headers: {
|
|
37
|
+
'User-Agent': 'visus-mcp/0.3.0 (https://github.com/lateos/visus-mcp)'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
}
|
|
44
|
+
if (!response.ok) {
|
|
45
|
+
return Ok({
|
|
46
|
+
query: input.query,
|
|
47
|
+
result_count: 0,
|
|
48
|
+
sanitized: true,
|
|
49
|
+
results: [],
|
|
50
|
+
total_injections_removed: 0,
|
|
51
|
+
message: `Search unavailable (HTTP ${response.status})`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
const data = await response.json();
|
|
55
|
+
// Extract results from DuckDuckGo response
|
|
56
|
+
const rawResults = [];
|
|
57
|
+
// Add AbstractText as first result if present
|
|
58
|
+
if (data.AbstractText && data.AbstractURL) {
|
|
59
|
+
rawResults.push({
|
|
60
|
+
title: extractTitle(data.AbstractText),
|
|
61
|
+
url: data.AbstractURL,
|
|
62
|
+
snippet: data.AbstractText
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Extract from RelatedTopics
|
|
66
|
+
if (data.RelatedTopics) {
|
|
67
|
+
for (const topic of data.RelatedTopics) {
|
|
68
|
+
// Handle both direct topics and nested topic groups
|
|
69
|
+
if ('Topics' in topic && Array.isArray(topic.Topics)) {
|
|
70
|
+
// Nested topics group
|
|
71
|
+
for (const nestedTopic of topic.Topics) {
|
|
72
|
+
if (nestedTopic.Text && nestedTopic.FirstURL) {
|
|
73
|
+
rawResults.push({
|
|
74
|
+
title: extractTitle(nestedTopic.Text),
|
|
75
|
+
url: nestedTopic.FirstURL,
|
|
76
|
+
snippet: nestedTopic.Text
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if ('Text' in topic && topic.Text && topic.FirstURL) {
|
|
82
|
+
// Direct topic
|
|
83
|
+
rawResults.push({
|
|
84
|
+
title: extractTitle(topic.Text),
|
|
85
|
+
url: topic.FirstURL,
|
|
86
|
+
snippet: topic.Text
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Stop if we've collected enough results
|
|
90
|
+
if (rawResults.length >= maxResults) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Filter out results with empty URLs and limit to max_results
|
|
96
|
+
const validResults = rawResults
|
|
97
|
+
.filter(r => r.url && r.url.trim().length > 0)
|
|
98
|
+
.slice(0, maxResults);
|
|
99
|
+
// If no results found, return empty array with message
|
|
100
|
+
if (validResults.length === 0) {
|
|
101
|
+
return Ok({
|
|
102
|
+
query: input.query,
|
|
103
|
+
result_count: 0,
|
|
104
|
+
sanitized: true,
|
|
105
|
+
results: [],
|
|
106
|
+
total_injections_removed: 0,
|
|
107
|
+
message: 'No results found'
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// Sanitize each result independently
|
|
111
|
+
const sanitizedResults = [];
|
|
112
|
+
const allPatternsDetected = new Set();
|
|
113
|
+
let totalInjectionsRemoved = 0;
|
|
114
|
+
let totalPIIRedacted = 0;
|
|
115
|
+
for (const result of validResults) {
|
|
116
|
+
// Sanitize title
|
|
117
|
+
const titleSanitization = sanitize(result.title);
|
|
118
|
+
// Sanitize snippet
|
|
119
|
+
const snippetSanitization = sanitize(result.snippet);
|
|
120
|
+
const injectionsRemoved = titleSanitization.sanitization.patterns_detected.length +
|
|
121
|
+
snippetSanitization.sanitization.patterns_detected.length;
|
|
122
|
+
const piiRedacted = titleSanitization.sanitization.pii_types_redacted.length +
|
|
123
|
+
snippetSanitization.sanitization.pii_types_redacted.length;
|
|
124
|
+
totalInjectionsRemoved += injectionsRemoved;
|
|
125
|
+
totalPIIRedacted += piiRedacted;
|
|
126
|
+
// Collect all patterns detected across all results
|
|
127
|
+
titleSanitization.sanitization.patterns_detected.forEach(p => allPatternsDetected.add(p));
|
|
128
|
+
snippetSanitization.sanitization.patterns_detected.forEach(p => allPatternsDetected.add(p));
|
|
129
|
+
sanitizedResults.push({
|
|
130
|
+
title: titleSanitization.content,
|
|
131
|
+
url: result.url,
|
|
132
|
+
snippet: snippetSanitization.content,
|
|
133
|
+
injections_removed: injectionsRemoved,
|
|
134
|
+
pii_redacted: piiRedacted
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Generate aggregated threat report for all search results
|
|
138
|
+
const threatReport = generateThreatReport({
|
|
139
|
+
patterns_detected: Array.from(allPatternsDetected),
|
|
140
|
+
pii_redacted: totalPIIRedacted,
|
|
141
|
+
source_url: `DuckDuckGo Search: ${input.query}`
|
|
142
|
+
});
|
|
143
|
+
return Ok({
|
|
144
|
+
query: input.query,
|
|
145
|
+
result_count: sanitizedResults.length,
|
|
146
|
+
sanitized: true,
|
|
147
|
+
results: sanitizedResults,
|
|
148
|
+
total_injections_removed: totalInjectionsRemoved,
|
|
149
|
+
// Include threat_report only if findings exist
|
|
150
|
+
...(threatReport && { threat_report: threatReport })
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
// Handle timeout or network errors
|
|
155
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
156
|
+
return Ok({
|
|
157
|
+
query: input.query,
|
|
158
|
+
result_count: 0,
|
|
159
|
+
sanitized: true,
|
|
160
|
+
results: [],
|
|
161
|
+
total_injections_removed: 0,
|
|
162
|
+
message: 'Search unavailable (timeout)'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return Ok({
|
|
166
|
+
query: input.query,
|
|
167
|
+
result_count: 0,
|
|
168
|
+
sanitized: true,
|
|
169
|
+
results: [],
|
|
170
|
+
total_injections_removed: 0,
|
|
171
|
+
message: `Search unavailable: ${error instanceof Error ? error.message : String(error)}`
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Extract title from text (first sentence or up to 80 chars)
|
|
177
|
+
*/
|
|
178
|
+
function extractTitle(text) {
|
|
179
|
+
// Try to find first sentence
|
|
180
|
+
const firstSentenceMatch = text.match(/^[^.!?]+[.!?]/);
|
|
181
|
+
if (firstSentenceMatch) {
|
|
182
|
+
const sentence = firstSentenceMatch[0].trim();
|
|
183
|
+
if (sentence.length <= 80) {
|
|
184
|
+
return sentence;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Fallback to first 80 chars
|
|
188
|
+
if (text.length <= 80) {
|
|
189
|
+
return text.trim();
|
|
190
|
+
}
|
|
191
|
+
return text.substring(0, 77).trim() + '...';
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Tool definition for MCP registration
|
|
195
|
+
*/
|
|
196
|
+
export const visusSearchToolDefinition = {
|
|
197
|
+
name: 'visus_search',
|
|
198
|
+
title: 'Search the Web (Sanitized)',
|
|
199
|
+
description: 'Searches the web via DuckDuckGo and returns sanitized results with prompt injection and PII removed before reaching the LLM. Use before visus_fetch or visus_read to safely discover and then read pages.',
|
|
200
|
+
inputSchema: {
|
|
201
|
+
type: 'object',
|
|
202
|
+
properties: {
|
|
203
|
+
query: {
|
|
204
|
+
type: 'string',
|
|
205
|
+
description: 'Search query'
|
|
206
|
+
},
|
|
207
|
+
max_results: {
|
|
208
|
+
type: 'number',
|
|
209
|
+
description: 'Maximum number of results to return (default: 5, max: 10)',
|
|
210
|
+
default: 5
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
required: ['query']
|
|
214
|
+
},
|
|
215
|
+
readOnlyHint: true,
|
|
216
|
+
destructiveHint: false,
|
|
217
|
+
idempotentHint: true,
|
|
218
|
+
openWorldHint: true
|
|
219
|
+
};
|
|
220
|
+
//# 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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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared TypeScript interfaces for Visus MCP tool
|
|
3
3
|
*/
|
|
4
|
+
import type { ThreatReport } from './sanitizer/threat-reporter.js';
|
|
4
5
|
/**
|
|
5
6
|
* Input options for visus_fetch tool
|
|
6
7
|
*/
|
|
@@ -18,6 +19,11 @@ export interface VisusFetchOutput {
|
|
|
18
19
|
sanitization: {
|
|
19
20
|
patterns_detected: string[];
|
|
20
21
|
pii_types_redacted: string[];
|
|
22
|
+
pii_allowlisted: Array<{
|
|
23
|
+
type: string;
|
|
24
|
+
value: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
}>;
|
|
21
27
|
content_modified: boolean;
|
|
22
28
|
};
|
|
23
29
|
metadata: {
|
|
@@ -25,7 +31,12 @@ export interface VisusFetchOutput {
|
|
|
25
31
|
fetched_at: string;
|
|
26
32
|
content_length_original: number;
|
|
27
33
|
content_length_sanitized: number;
|
|
34
|
+
format_detected?: 'html' | 'json' | 'xml' | 'rss';
|
|
35
|
+
content_type?: string;
|
|
36
|
+
truncated?: boolean;
|
|
37
|
+
truncated_at_chars?: number;
|
|
28
38
|
};
|
|
39
|
+
threat_report?: ThreatReport;
|
|
29
40
|
}
|
|
30
41
|
/**
|
|
31
42
|
* Input for visus_fetch_structured tool
|
|
@@ -35,6 +46,13 @@ export interface VisusFetchStructuredInput {
|
|
|
35
46
|
schema: Record<string, string>;
|
|
36
47
|
timeout_ms?: number;
|
|
37
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Input for visus_read tool
|
|
51
|
+
*/
|
|
52
|
+
export interface VisusReadInput {
|
|
53
|
+
url: string;
|
|
54
|
+
timeout_ms?: number;
|
|
55
|
+
}
|
|
38
56
|
/**
|
|
39
57
|
* Output from visus_fetch_structured tool
|
|
40
58
|
*/
|
|
@@ -44,6 +62,11 @@ export interface VisusFetchStructuredOutput {
|
|
|
44
62
|
sanitization: {
|
|
45
63
|
patterns_detected: string[];
|
|
46
64
|
pii_types_redacted: string[];
|
|
65
|
+
pii_allowlisted: Array<{
|
|
66
|
+
type: string;
|
|
67
|
+
value: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
}>;
|
|
47
70
|
content_modified: boolean;
|
|
48
71
|
};
|
|
49
72
|
metadata: {
|
|
@@ -51,7 +74,57 @@ export interface VisusFetchStructuredOutput {
|
|
|
51
74
|
fetched_at: string;
|
|
52
75
|
content_length_original: number;
|
|
53
76
|
content_length_sanitized: number;
|
|
77
|
+
format_detected?: 'html' | 'json' | 'xml' | 'rss';
|
|
78
|
+
content_type?: string;
|
|
79
|
+
truncated?: boolean;
|
|
80
|
+
truncated_at_chars?: number;
|
|
81
|
+
};
|
|
82
|
+
threat_report?: ThreatReport;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Output from visus_read tool
|
|
86
|
+
*/
|
|
87
|
+
export interface VisusReadOutput {
|
|
88
|
+
url: string;
|
|
89
|
+
content: string;
|
|
90
|
+
metadata: {
|
|
91
|
+
title: string;
|
|
92
|
+
author: string | null;
|
|
93
|
+
published: string | null;
|
|
94
|
+
word_count: number;
|
|
95
|
+
reader_mode_available: boolean;
|
|
96
|
+
sanitized: true;
|
|
97
|
+
injections_removed: number;
|
|
98
|
+
pii_redacted: number;
|
|
99
|
+
truncated: boolean;
|
|
100
|
+
fetched_at?: string;
|
|
54
101
|
};
|
|
102
|
+
threat_report?: ThreatReport;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Input for visus_search tool
|
|
106
|
+
*/
|
|
107
|
+
export interface VisusSearchInput {
|
|
108
|
+
query: string;
|
|
109
|
+
max_results?: number;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Output from visus_search tool
|
|
113
|
+
*/
|
|
114
|
+
export interface VisusSearchOutput {
|
|
115
|
+
query: string;
|
|
116
|
+
result_count: number;
|
|
117
|
+
sanitized: true;
|
|
118
|
+
results: Array<{
|
|
119
|
+
title: string;
|
|
120
|
+
url: string;
|
|
121
|
+
snippet: string;
|
|
122
|
+
injections_removed: number;
|
|
123
|
+
pii_redacted: number;
|
|
124
|
+
}>;
|
|
125
|
+
total_injections_removed: number;
|
|
126
|
+
message?: string;
|
|
127
|
+
threat_report?: ThreatReport;
|
|
55
128
|
}
|
|
56
129
|
/**
|
|
57
130
|
* Result from browser rendering
|
|
@@ -60,6 +133,7 @@ export interface BrowserRenderResult {
|
|
|
60
133
|
html: string;
|
|
61
134
|
title: string;
|
|
62
135
|
url: string;
|
|
136
|
+
contentType?: string;
|
|
63
137
|
text?: string;
|
|
64
138
|
error?: string;
|
|
65
139
|
}
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE;QACZ,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,kBAAkB,EAAE,MAAM,EAAE,CAAC;QAC7B,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;IACF,QAAQ,EAAE;QACR,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,uBAAuB,EAAE,MAAM,CAAC;QAChC,wBAAwB,EAAE,MAAM,CAAC;
|
|
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;AA6JH;;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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format Converter - Content-Type based format detection and conversion
|
|
3
|
+
*
|
|
4
|
+
* Handles format-appropriate conversion based on detected Content-Type.
|
|
5
|
+
* Supports HTML, JSON, XML, and RSS/Atom feeds.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Detected format type
|
|
9
|
+
*/
|
|
10
|
+
export type FormatType = 'html' | 'json' | 'xml' | 'rss';
|
|
11
|
+
/**
|
|
12
|
+
* Detect format from Content-Type header
|
|
13
|
+
*
|
|
14
|
+
* @param contentType - Content-Type header value (e.g., "application/json", "text/html; charset=utf-8")
|
|
15
|
+
* @returns Detected format type
|
|
16
|
+
*/
|
|
17
|
+
export declare function detectFormat(contentType: string): FormatType;
|
|
18
|
+
/**
|
|
19
|
+
* Convert JSON content to formatted string
|
|
20
|
+
*
|
|
21
|
+
* @param raw - Raw JSON string
|
|
22
|
+
* @returns Formatted JSON string with prefix, or raw string if parse fails
|
|
23
|
+
*/
|
|
24
|
+
export declare function convertJson(raw: string): string;
|
|
25
|
+
/**
|
|
26
|
+
* Convert XML content to clean text representation
|
|
27
|
+
*
|
|
28
|
+
* @param raw - Raw XML string
|
|
29
|
+
* @returns Formatted XML representation with prefix, or tag-stripped fallback if parse fails
|
|
30
|
+
*/
|
|
31
|
+
export declare function convertXml(raw: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Convert RSS/Atom feed content to clean Markdown
|
|
34
|
+
*
|
|
35
|
+
* @param raw - Raw RSS/Atom XML string
|
|
36
|
+
* @returns Formatted Markdown representation, or falls back to convertXml if parse fails
|
|
37
|
+
*/
|
|
38
|
+
export declare function convertRss(raw: string): string;
|
|
39
|
+
//# sourceMappingURL=format-converter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-converter.d.ts","sourceRoot":"","sources":["../../src/utils/format-converter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,CAAC;AAEzD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CA4B5D;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoB9C;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiC9C"}
|