visus-mcp 0.3.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.
Files changed (96) hide show
  1. package/.claude/settings.local.json +22 -0
  2. package/LINKEDIN-STRATEGY.md +367 -0
  3. package/README.md +491 -16
  4. package/ROADMAP.md +167 -30
  5. package/SECURITY-AUDIT-v1.md +277 -0
  6. package/STATUS.md +801 -42
  7. package/TROUBLESHOOT-AUTH-20260322-2019.md +291 -0
  8. package/TROUBLESHOOT-JEST-20260323-1357.md +139 -0
  9. package/TROUBLESHOOT-LAMBDA-20260322-1945.md +183 -0
  10. package/VISUS-CLAUDE-CODE-PROMPT.md +1 -1
  11. package/VISUS-PROJECT-PLAN.md +7 -0
  12. package/dist/browser/playwright-renderer.d.ts.map +1 -1
  13. package/dist/browser/playwright-renderer.js +7 -0
  14. package/dist/browser/playwright-renderer.js.map +1 -1
  15. package/dist/browser/reader.d.ts +31 -0
  16. package/dist/browser/reader.d.ts.map +1 -0
  17. package/dist/browser/reader.js +98 -0
  18. package/dist/browser/reader.js.map +1 -0
  19. package/dist/index.d.ts +1 -1
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +37 -5
  22. package/dist/index.js.map +1 -1
  23. package/dist/lambda-handler.d.ts +0 -6
  24. package/dist/lambda-handler.d.ts.map +1 -1
  25. package/dist/lambda-handler.js +97 -25
  26. package/dist/lambda-handler.js.map +1 -1
  27. package/dist/sanitizer/framework-mapper.d.ts +22 -0
  28. package/dist/sanitizer/framework-mapper.d.ts.map +1 -0
  29. package/dist/sanitizer/framework-mapper.js +296 -0
  30. package/dist/sanitizer/framework-mapper.js.map +1 -0
  31. package/dist/sanitizer/index.d.ts +2 -0
  32. package/dist/sanitizer/index.d.ts.map +1 -1
  33. package/dist/sanitizer/index.js +14 -1
  34. package/dist/sanitizer/index.js.map +1 -1
  35. package/dist/sanitizer/patterns.js +1 -1
  36. package/dist/sanitizer/patterns.js.map +1 -1
  37. package/dist/sanitizer/severity-classifier.d.ts +33 -0
  38. package/dist/sanitizer/severity-classifier.d.ts.map +1 -0
  39. package/dist/sanitizer/severity-classifier.js +113 -0
  40. package/dist/sanitizer/severity-classifier.js.map +1 -0
  41. package/dist/sanitizer/threat-reporter.d.ts +65 -0
  42. package/dist/sanitizer/threat-reporter.d.ts.map +1 -0
  43. package/dist/sanitizer/threat-reporter.js +160 -0
  44. package/dist/sanitizer/threat-reporter.js.map +1 -0
  45. package/dist/tools/fetch-structured.d.ts +5 -0
  46. package/dist/tools/fetch-structured.d.ts.map +1 -1
  47. package/dist/tools/fetch-structured.js +54 -6
  48. package/dist/tools/fetch-structured.js.map +1 -1
  49. package/dist/tools/fetch.d.ts +5 -0
  50. package/dist/tools/fetch.d.ts.map +1 -1
  51. package/dist/tools/fetch.js +42 -9
  52. package/dist/tools/fetch.js.map +1 -1
  53. package/dist/tools/read.d.ts +51 -0
  54. package/dist/tools/read.d.ts.map +1 -0
  55. package/dist/tools/read.js +127 -0
  56. package/dist/tools/read.js.map +1 -0
  57. package/dist/tools/search.d.ts +45 -0
  58. package/dist/tools/search.d.ts.map +1 -0
  59. package/dist/tools/search.js +220 -0
  60. package/dist/tools/search.js.map +1 -0
  61. package/dist/types.d.ts +64 -0
  62. package/dist/types.d.ts.map +1 -1
  63. package/dist/types.js.map +1 -1
  64. package/dist/utils/format-converter.d.ts +39 -0
  65. package/dist/utils/format-converter.d.ts.map +1 -0
  66. package/dist/utils/format-converter.js +191 -0
  67. package/dist/utils/format-converter.js.map +1 -0
  68. package/dist/utils/truncate.d.ts +26 -0
  69. package/dist/utils/truncate.d.ts.map +1 -0
  70. package/dist/utils/truncate.js +54 -0
  71. package/dist/utils/truncate.js.map +1 -0
  72. package/infrastructure/stack.ts +55 -6
  73. package/jest.config.js +3 -0
  74. package/package.json +9 -2
  75. package/src/browser/playwright-renderer.ts +8 -0
  76. package/src/browser/reader.ts +129 -0
  77. package/src/index.ts +49 -5
  78. package/src/lambda-handler.ts +131 -26
  79. package/src/sanitizer/framework-mapper.ts +347 -0
  80. package/src/sanitizer/index.ts +18 -1
  81. package/src/sanitizer/patterns.ts +1 -1
  82. package/src/sanitizer/severity-classifier.ts +132 -0
  83. package/src/sanitizer/threat-reporter.ts +261 -0
  84. package/src/tools/fetch-structured.ts +58 -6
  85. package/src/tools/fetch.ts +44 -9
  86. package/src/tools/read.ts +143 -0
  87. package/src/tools/search.ts +263 -0
  88. package/src/types.ts +69 -0
  89. package/src/utils/format-converter.ts +236 -0
  90. package/src/utils/truncate.ts +64 -0
  91. package/tests/auth-smoke.test.ts +480 -0
  92. package/tests/fetch-tool.test.ts +595 -2
  93. package/tests/reader.test.ts +353 -0
  94. package/tests/sanitizer.test.ts +52 -0
  95. package/tests/search.test.ts +456 -0
  96. package/tests/threat-reporter.test.ts +266 -0
@@ -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
  */
@@ -30,7 +31,12 @@ export interface VisusFetchOutput {
30
31
  fetched_at: string;
31
32
  content_length_original: number;
32
33
  content_length_sanitized: number;
34
+ format_detected?: 'html' | 'json' | 'xml' | 'rss';
35
+ content_type?: string;
36
+ truncated?: boolean;
37
+ truncated_at_chars?: number;
33
38
  };
39
+ threat_report?: ThreatReport;
34
40
  }
35
41
  /**
36
42
  * Input for visus_fetch_structured tool
@@ -40,6 +46,13 @@ export interface VisusFetchStructuredInput {
40
46
  schema: Record<string, string>;
41
47
  timeout_ms?: number;
42
48
  }
49
+ /**
50
+ * Input for visus_read tool
51
+ */
52
+ export interface VisusReadInput {
53
+ url: string;
54
+ timeout_ms?: number;
55
+ }
43
56
  /**
44
57
  * Output from visus_fetch_structured tool
45
58
  */
@@ -61,7 +74,57 @@ export interface VisusFetchStructuredOutput {
61
74
  fetched_at: string;
62
75
  content_length_original: number;
63
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;
64
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;
65
128
  }
66
129
  /**
67
130
  * Result from browser rendering
@@ -70,6 +133,7 @@ export interface BrowserRenderResult {
70
133
  html: string;
71
134
  title: string;
72
135
  url: string;
136
+ contentType?: string;
73
137
  text?: string;
74
138
  error?: string;
75
139
  }
@@ -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,eAAe,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACxE,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;KAClC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IACpC,YAAY,EAAE;QACZ,iBAAiB,EAAE,MAAM,EAAE,CAAC;QAC5B,kBAAkB,EAAE,MAAM,EAAE,CAAC;QAC7B,eAAe,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACxE,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;KAClC,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,KAAK,IAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAE5B;;GAEG;AACH,wBAAgB,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,CAEhD;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAEjD"}
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;AAwFH;;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"}
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"}
@@ -0,0 +1,191 @@
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
+ import { XMLParser } from 'fast-xml-parser';
8
+ /**
9
+ * Detect format from Content-Type header
10
+ *
11
+ * @param contentType - Content-Type header value (e.g., "application/json", "text/html; charset=utf-8")
12
+ * @returns Detected format type
13
+ */
14
+ export function detectFormat(contentType) {
15
+ // Normalize: lowercase and extract MIME type (before semicolon)
16
+ const mimeType = contentType.toLowerCase().split(';')[0].trim();
17
+ // HTML formats
18
+ if (mimeType === 'text/html' || mimeType === 'application/xhtml+xml') {
19
+ return 'html';
20
+ }
21
+ // JSON formats
22
+ if (mimeType === 'application/json' || mimeType === 'text/json') {
23
+ return 'json';
24
+ }
25
+ // RSS/Atom feed formats
26
+ if (mimeType === 'application/rss+xml' ||
27
+ mimeType === 'application/atom+xml' ||
28
+ mimeType === 'application/feed+json') {
29
+ return 'rss';
30
+ }
31
+ // XML formats (must come after RSS check)
32
+ if (mimeType === 'application/xml' || mimeType === 'text/xml') {
33
+ return 'xml';
34
+ }
35
+ // Default to HTML for unknown types
36
+ return 'html';
37
+ }
38
+ /**
39
+ * Convert JSON content to formatted string
40
+ *
41
+ * @param raw - Raw JSON string
42
+ * @returns Formatted JSON string with prefix, or raw string if parse fails
43
+ */
44
+ export function convertJson(raw) {
45
+ try {
46
+ // Parse and re-stringify with 2-space indent for readability
47
+ const parsed = JSON.parse(raw);
48
+ const formatted = JSON.stringify(parsed, null, 2);
49
+ return `JSON Response:\n\n${formatted}`;
50
+ }
51
+ catch (error) {
52
+ // Parse failed, return raw string unchanged
53
+ return raw;
54
+ }
55
+ }
56
+ /**
57
+ * Convert XML content to clean text representation
58
+ *
59
+ * @param raw - Raw XML string
60
+ * @returns Formatted XML representation with prefix, or tag-stripped fallback if parse fails
61
+ */
62
+ export function convertXml(raw) {
63
+ try {
64
+ const parser = new XMLParser({
65
+ ignoreAttributes: false,
66
+ attributeNamePrefix: '@_',
67
+ textNodeName: '#text',
68
+ ignoreDeclaration: true,
69
+ ignorePiTags: true,
70
+ removeNSPrefix: true,
71
+ });
72
+ const parsed = parser.parse(raw);
73
+ const formatted = JSON.stringify(parsed, null, 2);
74
+ return `XML Response:\n\n${formatted}`;
75
+ }
76
+ catch (error) {
77
+ // Parse failed, strip XML tags using regex and return
78
+ const stripped = raw.replace(/<[^>]+>/g, '').trim();
79
+ return `XML Response:\n\n${stripped}`;
80
+ }
81
+ }
82
+ /**
83
+ * Convert RSS/Atom feed content to clean Markdown
84
+ *
85
+ * @param raw - Raw RSS/Atom XML string
86
+ * @returns Formatted Markdown representation, or falls back to convertXml if parse fails
87
+ */
88
+ export function convertRss(raw) {
89
+ try {
90
+ const parser = new XMLParser({
91
+ ignoreAttributes: false,
92
+ attributeNamePrefix: '@_',
93
+ textNodeName: '#text',
94
+ removeNSPrefix: true,
95
+ });
96
+ const parsed = parser.parse(raw);
97
+ // Handle RSS 2.0 format
98
+ if (parsed.rss && parsed.rss.channel) {
99
+ return formatRss2(parsed.rss.channel);
100
+ }
101
+ // Handle Atom format
102
+ if (parsed.feed) {
103
+ return formatAtom(parsed.feed);
104
+ }
105
+ // Handle RSS 1.0 (RDF) format
106
+ if (parsed.rdf && parsed.rdf.channel) {
107
+ return formatRss2(parsed.rdf.channel);
108
+ }
109
+ // Unknown feed format, fall back to XML
110
+ return convertXml(raw);
111
+ }
112
+ catch (error) {
113
+ // Parse failed, fall back to XML converter
114
+ return convertXml(raw);
115
+ }
116
+ }
117
+ /**
118
+ * Format RSS 2.0 feed data as Markdown
119
+ */
120
+ function formatRss2(channel) {
121
+ const title = channel.title || 'Untitled Feed';
122
+ const description = channel.description || '';
123
+ const items = Array.isArray(channel.item) ? channel.item : (channel.item ? [channel.item] : []);
124
+ let markdown = `RSS Feed:\n\n# ${title}\n`;
125
+ if (description) {
126
+ markdown += `${description}\n`;
127
+ }
128
+ markdown += '\n## Items\n\n';
129
+ // Extract up to 10 items
130
+ const itemsToShow = items.slice(0, 10);
131
+ for (const item of itemsToShow) {
132
+ const itemTitle = item.title || 'Untitled';
133
+ const itemLink = item.link || '';
134
+ const itemDescription = item.description || '';
135
+ const itemPubDate = item.pubDate || '';
136
+ // Truncate description to 200 chars
137
+ const truncatedDesc = itemDescription.length > 200
138
+ ? itemDescription.substring(0, 200) + '...'
139
+ : itemDescription;
140
+ markdown += `### ${itemTitle}\n\n`;
141
+ if (truncatedDesc) {
142
+ markdown += `${truncatedDesc}\n\n`;
143
+ }
144
+ if (itemLink) {
145
+ markdown += `Link: ${itemLink}\n`;
146
+ }
147
+ if (itemPubDate) {
148
+ markdown += `Published: ${itemPubDate}\n`;
149
+ }
150
+ markdown += '\n---\n\n';
151
+ }
152
+ return markdown;
153
+ }
154
+ /**
155
+ * Format Atom feed data as Markdown
156
+ */
157
+ function formatAtom(feed) {
158
+ const title = feed.title || 'Untitled Feed';
159
+ const subtitle = feed.subtitle || '';
160
+ const entries = Array.isArray(feed.entry) ? feed.entry : (feed.entry ? [feed.entry] : []);
161
+ let markdown = `RSS Feed:\n\n# ${title}\n`;
162
+ if (subtitle) {
163
+ markdown += `${subtitle}\n`;
164
+ }
165
+ markdown += '\n## Items\n\n';
166
+ // Extract up to 10 entries
167
+ const entriesToShow = entries.slice(0, 10);
168
+ for (const entry of entriesToShow) {
169
+ const entryTitle = entry.title || 'Untitled';
170
+ const entryLink = entry.link ? (entry.link['@_href'] || entry.link) : '';
171
+ const entrySummary = entry.summary || entry.content || '';
172
+ const entryPublished = entry.published || entry.updated || '';
173
+ // Truncate summary to 200 chars
174
+ const truncatedSummary = entrySummary.length > 200
175
+ ? entrySummary.substring(0, 200) + '...'
176
+ : entrySummary;
177
+ markdown += `### ${entryTitle}\n\n`;
178
+ if (truncatedSummary) {
179
+ markdown += `${truncatedSummary}\n\n`;
180
+ }
181
+ if (entryLink) {
182
+ markdown += `Link: ${entryLink}\n`;
183
+ }
184
+ if (entryPublished) {
185
+ markdown += `Published: ${entryPublished}\n`;
186
+ }
187
+ markdown += '\n---\n\n';
188
+ }
189
+ return markdown;
190
+ }
191
+ //# sourceMappingURL=format-converter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-converter.js","sourceRoot":"","sources":["../../src/utils/format-converter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Token-aware content truncation utility
3
+ *
4
+ * Anthropic MCP Directory enforces a 25,000 token response limit.
5
+ * This utility provides safe truncation with token estimation.
6
+ */
7
+ /**
8
+ * Truncate content if it exceeds the token ceiling
9
+ *
10
+ * @param content Content to potentially truncate
11
+ * @returns Truncated content and metadata
12
+ */
13
+ export declare function truncateContent(content: string): {
14
+ content: string;
15
+ truncated: boolean;
16
+ truncated_at_chars?: number;
17
+ };
18
+ /**
19
+ * Estimate token count for a given string
20
+ * Uses conservative 4 chars per token approximation
21
+ *
22
+ * @param text Text to estimate
23
+ * @returns Estimated token count
24
+ */
25
+ export declare function estimateTokens(text: string): number;
26
+ //# sourceMappingURL=truncate.d.ts.map