@browserless.io/mcp 1.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 (81) hide show
  1. package/LICENSE +557 -0
  2. package/README.md +280 -0
  3. package/bin/cli.js +2 -0
  4. package/build/src/@types/types.d.ts +538 -0
  5. package/build/src/config.d.ts +3 -0
  6. package/build/src/config.js +42 -0
  7. package/build/src/index.d.ts +4 -0
  8. package/build/src/index.js +153 -0
  9. package/build/src/lib/account-resolver.d.ts +17 -0
  10. package/build/src/lib/account-resolver.js +78 -0
  11. package/build/src/lib/agent-client.d.ts +58 -0
  12. package/build/src/lib/agent-client.js +530 -0
  13. package/build/src/lib/agent-format.d.ts +35 -0
  14. package/build/src/lib/agent-format.js +155 -0
  15. package/build/src/lib/amplitude.d.ts +11 -0
  16. package/build/src/lib/amplitude.js +65 -0
  17. package/build/src/lib/analytics.d.ts +18 -0
  18. package/build/src/lib/analytics.js +79 -0
  19. package/build/src/lib/api-client.d.ts +17 -0
  20. package/build/src/lib/api-client.js +357 -0
  21. package/build/src/lib/bounded-event-store.d.ts +22 -0
  22. package/build/src/lib/bounded-event-store.js +69 -0
  23. package/build/src/lib/cache.d.ts +12 -0
  24. package/build/src/lib/cache.js +49 -0
  25. package/build/src/lib/define-tool.d.ts +71 -0
  26. package/build/src/lib/define-tool.js +71 -0
  27. package/build/src/lib/error-classifier.d.ts +4 -0
  28. package/build/src/lib/error-classifier.js +125 -0
  29. package/build/src/lib/redis-oauth-proxy.d.ts +13 -0
  30. package/build/src/lib/redis-oauth-proxy.js +214 -0
  31. package/build/src/lib/retry.d.ts +2 -0
  32. package/build/src/lib/retry.js +19 -0
  33. package/build/src/lib/schema-fields.d.ts +10 -0
  34. package/build/src/lib/schema-fields.js +27 -0
  35. package/build/src/lib/supabase-token-patch.d.ts +6 -0
  36. package/build/src/lib/supabase-token-patch.js +33 -0
  37. package/build/src/lib/utils.d.ts +27 -0
  38. package/build/src/lib/utils.js +67 -0
  39. package/build/src/prompts/extract-content.d.ts +2 -0
  40. package/build/src/prompts/extract-content.js +33 -0
  41. package/build/src/prompts/scrape-url.d.ts +2 -0
  42. package/build/src/prompts/scrape-url.js +36 -0
  43. package/build/src/resources/api-docs.d.ts +3 -0
  44. package/build/src/resources/api-docs.js +54 -0
  45. package/build/src/resources/status.d.ts +3 -0
  46. package/build/src/resources/status.js +30 -0
  47. package/build/src/skills/autonomous-login.md +95 -0
  48. package/build/src/skills/captchas.md +48 -0
  49. package/build/src/skills/cookie-consent.md +50 -0
  50. package/build/src/skills/dynamic-content.md +72 -0
  51. package/build/src/skills/index.d.ts +9 -0
  52. package/build/src/skills/index.js +221 -0
  53. package/build/src/skills/modals.md +56 -0
  54. package/build/src/skills/screenshots.md +53 -0
  55. package/build/src/skills/shadow-dom.md +64 -0
  56. package/build/src/skills/snapshot-misses.md +67 -0
  57. package/build/src/skills/system-prompt.d.ts +2 -0
  58. package/build/src/skills/system-prompt.js +128 -0
  59. package/build/src/skills/tabs.md +77 -0
  60. package/build/src/tools/agent.d.ts +15 -0
  61. package/build/src/tools/agent.js +299 -0
  62. package/build/src/tools/crawl.d.ts +75 -0
  63. package/build/src/tools/crawl.js +426 -0
  64. package/build/src/tools/download.d.ts +11 -0
  65. package/build/src/tools/download.js +92 -0
  66. package/build/src/tools/export.d.ts +28 -0
  67. package/build/src/tools/export.js +129 -0
  68. package/build/src/tools/function.d.ts +24 -0
  69. package/build/src/tools/function.js +144 -0
  70. package/build/src/tools/map.d.ts +23 -0
  71. package/build/src/tools/map.js +129 -0
  72. package/build/src/tools/performance.d.ts +25 -0
  73. package/build/src/tools/performance.js +103 -0
  74. package/build/src/tools/schemas.d.ts +466 -0
  75. package/build/src/tools/schemas.js +487 -0
  76. package/build/src/tools/search.d.ts +67 -0
  77. package/build/src/tools/search.js +184 -0
  78. package/build/src/tools/smartscraper.d.ts +42 -0
  79. package/build/src/tools/smartscraper.js +136 -0
  80. package/package.json +111 -0
  81. package/patches/mcp-proxy+6.4.0.patch +31 -0
@@ -0,0 +1,184 @@
1
+ import { UserError } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { defineTool } from '../lib/define-tool.js';
4
+ export const SearchSourceSchema = z.enum(['web', 'news', 'images']);
5
+ export const SearchCategorySchema = z.enum(['github', 'research', 'pdf']);
6
+ export const TimeBasedOptionsSchema = z.enum(['day', 'week', 'month', 'year']);
7
+ export const SearchScrapeOptionsSchema = z.object({
8
+ formats: z
9
+ .array(z.enum(['markdown', 'html', 'links', 'screenshot']))
10
+ .optional()
11
+ .describe('Output formats for scraped content'),
12
+ onlyMainContent: z
13
+ .boolean()
14
+ .optional()
15
+ .describe('Extract only the main content using Readability'),
16
+ includeTags: z
17
+ .array(z.string())
18
+ .optional()
19
+ .describe('Only include content from these HTML tags'),
20
+ excludeTags: z
21
+ .array(z.string())
22
+ .optional()
23
+ .describe('Exclude content from these HTML tags'),
24
+ });
25
+ export const SearchParamsSchema = z.object({
26
+ query: z.string().min(1).describe('The search query string'),
27
+ limit: z
28
+ .number()
29
+ .int()
30
+ .positive()
31
+ .max(100)
32
+ .optional()
33
+ .default(10)
34
+ .describe('Maximum number of results to return (default: 10, max: 100)'),
35
+ lang: z
36
+ .string()
37
+ .optional()
38
+ .default('en')
39
+ .describe('Language code for search results (default: "en")'),
40
+ country: z
41
+ .string()
42
+ .optional()
43
+ .describe('Country code for geo-targeted results'),
44
+ location: z
45
+ .string()
46
+ .optional()
47
+ .describe('Location string for geo-targeted results'),
48
+ tbs: TimeBasedOptionsSchema.optional().describe('Time-based filter: "day", "week", "month", "year"'),
49
+ sources: z
50
+ .array(SearchSourceSchema)
51
+ .optional()
52
+ .default(['web'])
53
+ .describe('Search sources: "web", "news", "images" (default: ["web"])'),
54
+ categories: z
55
+ .array(SearchCategorySchema)
56
+ .optional()
57
+ .describe('Filter by categories: "github", "research", "pdf"'),
58
+ scrapeOptions: SearchScrapeOptionsSchema.optional().describe('Options for scraping each search result'),
59
+ timeout: z
60
+ .number()
61
+ .int()
62
+ .positive()
63
+ .optional()
64
+ .describe('Request timeout in milliseconds'),
65
+ });
66
+ export function registerSearchTool(server, config, analytics) {
67
+ defineTool(server, config, analytics, {
68
+ name: 'browserless_search',
69
+ description: 'Search the web using Browserless and optionally scrape each result. ' +
70
+ 'Performs web searches via SearXNG and can return results from web, news, or images. ' +
71
+ 'Optionally scrape each result URL to get markdown, HTML, links, or screenshots. ' +
72
+ 'Useful for research, gathering information, and finding relevant web pages.',
73
+ parameters: SearchParamsSchema,
74
+ annotations: {
75
+ title: 'Browserless Search',
76
+ readOnlyHint: true,
77
+ destructiveHint: false,
78
+ openWorldHint: true,
79
+ },
80
+ run: async ({ client, params, log }) => {
81
+ const response = await client.search({
82
+ query: params.query,
83
+ limit: params.limit,
84
+ lang: params.lang,
85
+ country: params.country,
86
+ location: params.location,
87
+ tbs: params.tbs,
88
+ sources: params.sources,
89
+ categories: params.categories,
90
+ scrapeOptions: params.scrapeOptions,
91
+ timeout: params.timeout,
92
+ });
93
+ log.debug(`Search response: success=${response.success}, totalResults=${response.totalResults}`);
94
+ return response;
95
+ },
96
+ analyticsProps: (params, result) => ({
97
+ query: params.query,
98
+ limit: params.limit ?? 10,
99
+ sources: (params.sources ?? ['web']).join(','),
100
+ success: result.success,
101
+ total_results: result.totalResults,
102
+ }),
103
+ format: (response, params) => {
104
+ if (!response.success) {
105
+ throw new UserError(`Search failed: ${response.error ?? 'Unknown error'}`);
106
+ }
107
+ const blocks = [];
108
+ if (response.data.web && response.data.web.length > 0) {
109
+ const webResults = response.data.web
110
+ .map((result, index) => {
111
+ let text = `### ${index + 1}. ${result.title}\n`;
112
+ text += `**URL:** ${result.url}\n`;
113
+ if (result.description)
114
+ text += `**Description:** ${result.description}\n`;
115
+ if (result.markdown) {
116
+ const truncated = result.markdown.length > 1000
117
+ ? `${result.markdown.slice(0, 1000)}...`
118
+ : result.markdown;
119
+ text += `\n**Content:**\n${truncated}\n`;
120
+ }
121
+ return text;
122
+ })
123
+ .join('\n---\n');
124
+ blocks.push({
125
+ type: 'text',
126
+ text: `## Web Results (${response.data.web.length})\n\n${webResults}`,
127
+ });
128
+ }
129
+ if (response.data.news && response.data.news.length > 0) {
130
+ const newsResults = response.data.news
131
+ .map((result, index) => {
132
+ let text = `### ${index + 1}. ${result.title}\n`;
133
+ text += `**URL:** ${result.url}\n`;
134
+ if (result.date)
135
+ text += `**Date:** ${result.date}\n`;
136
+ if (result.description)
137
+ text += `**Description:** ${result.description}\n`;
138
+ return text;
139
+ })
140
+ .join('\n---\n');
141
+ blocks.push({
142
+ type: 'text',
143
+ text: `## News Results (${response.data.news.length})\n\n${newsResults}`,
144
+ });
145
+ }
146
+ if (response.data.images && response.data.images.length > 0) {
147
+ const imageResults = response.data.images
148
+ .map((result, index) => {
149
+ let text = `### ${index + 1}. ${result.title ?? 'Image'}\n`;
150
+ if (result.imageUrl)
151
+ text += `**Image URL:** ${result.imageUrl}\n`;
152
+ if (result.url)
153
+ text += `**Source:** ${result.url}\n`;
154
+ if (result.imageWidth && result.imageHeight) {
155
+ text += `**Size:** ${result.imageWidth}x${result.imageHeight}\n`;
156
+ }
157
+ return text;
158
+ })
159
+ .join('\n---\n');
160
+ blocks.push({
161
+ type: 'text',
162
+ text: `## Image Results (${response.data.images.length})\n\n${imageResults}`,
163
+ });
164
+ }
165
+ if (blocks.length === 0) {
166
+ blocks.push({
167
+ type: 'text',
168
+ text: `No results found for query: "${params.query}"`,
169
+ });
170
+ }
171
+ blocks.push({
172
+ type: 'text',
173
+ text: [
174
+ '---',
175
+ `Query: ${params.query}`,
176
+ `Total Results: ${response.totalResults}`,
177
+ `Sources: ${(params.sources ?? ['web']).join(', ')}`,
178
+ '---',
179
+ ].join('\n'),
180
+ });
181
+ return blocks;
182
+ },
183
+ });
184
+ }
@@ -0,0 +1,42 @@
1
+ import { FastMCP } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { AnalyticsHelper } from '../lib/analytics.js';
4
+ import type { McpConfig } from '../@types/types.js';
5
+ /**
6
+ * Output formats that can be requested.
7
+ * Mirrors the Firecrawl "formats" convention used by the Browserless API.
8
+ */
9
+ export declare const ScrapeFormatSchema: z.ZodEnum<{
10
+ screenshot: "screenshot";
11
+ pdf: "pdf";
12
+ markdown: "markdown";
13
+ links: "links";
14
+ html: "html";
15
+ }>;
16
+ export declare const SmartScraperParamsSchema: z.ZodObject<{
17
+ url: z.ZodURL;
18
+ formats: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEnum<{
19
+ screenshot: "screenshot";
20
+ pdf: "pdf";
21
+ markdown: "markdown";
22
+ links: "links";
23
+ html: "html";
24
+ }>>>>;
25
+ timeout: z.ZodOptional<z.ZodNumber>;
26
+ profile: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$strip>;
28
+ export declare const SmartScraperResponseSchema: z.ZodObject<{
29
+ ok: z.ZodBoolean;
30
+ statusCode: z.ZodNullable<z.ZodNumber>;
31
+ content: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodNull]>;
32
+ contentType: z.ZodNullable<z.ZodString>;
33
+ headers: z.ZodRecord<z.ZodString, z.ZodString>;
34
+ strategy: z.ZodString;
35
+ attempted: z.ZodArray<z.ZodString>;
36
+ message: z.ZodNullable<z.ZodString>;
37
+ screenshot: z.ZodNullable<z.ZodString>;
38
+ pdf: z.ZodNullable<z.ZodString>;
39
+ markdown: z.ZodNullable<z.ZodString>;
40
+ links: z.ZodNullable<z.ZodArray<z.ZodString>>;
41
+ }, z.core.$strip>;
42
+ export declare function registerSmartScraperTool(server: FastMCP, config: McpConfig, analytics?: AnalyticsHelper): void;
@@ -0,0 +1,136 @@
1
+ import { UserError } from 'fastmcp';
2
+ import { z } from 'zod';
3
+ import { defineTool, validateHttpUrl } from '../lib/define-tool.js';
4
+ import { profileField } from './schemas.js';
5
+ import { ResponseCache } from '../lib/cache.js';
6
+ /**
7
+ * Output formats that can be requested.
8
+ * Mirrors the Firecrawl "formats" convention used by the Browserless API.
9
+ */
10
+ export const ScrapeFormatSchema = z.enum([
11
+ 'markdown',
12
+ 'html',
13
+ 'screenshot',
14
+ 'pdf',
15
+ 'links',
16
+ ]);
17
+ export const SmartScraperParamsSchema = z.object({
18
+ url: z.url().describe('The URL to scrape (must be http or https)'),
19
+ formats: z
20
+ .array(ScrapeFormatSchema)
21
+ .optional()
22
+ .default(['markdown'])
23
+ .describe('Output formats to include: "markdown", "html", "screenshot", "pdf", "links". Defaults to ["markdown"].'),
24
+ timeout: z
25
+ .number()
26
+ .int()
27
+ .positive()
28
+ .optional()
29
+ .describe('Request timeout in milliseconds'),
30
+ profile: profileField('before scraping'),
31
+ });
32
+ export const SmartScraperResponseSchema = z.object({
33
+ ok: z.boolean(),
34
+ statusCode: z.number().nullable(),
35
+ content: z.union([z.string(), z.record(z.string(), z.unknown()), z.null()]),
36
+ contentType: z.string().nullable(),
37
+ headers: z.record(z.string(), z.string()),
38
+ strategy: z.string(),
39
+ attempted: z.array(z.string()),
40
+ message: z.string().nullable(),
41
+ screenshot: z.string().nullable(),
42
+ pdf: z.string().nullable(),
43
+ markdown: z.string().nullable(),
44
+ links: z.array(z.string()).nullable(),
45
+ });
46
+ export function registerSmartScraperTool(server, config, analytics) {
47
+ const cache = new ResponseCache(config.cacheTtlMs);
48
+ defineTool(server, config, analytics, {
49
+ name: 'browserless_smartscraper',
50
+ description: 'Scrape any webpage using the Browserless smart scraper. ' +
51
+ 'Returns page content in requested formats (markdown, html, screenshot, pdf, links). ' +
52
+ 'Handles JavaScript-heavy pages, anti-bot measures, and multiple scraping strategies automatically.',
53
+ parameters: SmartScraperParamsSchema,
54
+ annotations: {
55
+ title: 'Browserless Smart Scraper',
56
+ readOnlyHint: true,
57
+ destructiveHint: false,
58
+ openWorldHint: true,
59
+ },
60
+ validateUrl: (p) => validateHttpUrl(p.url),
61
+ profileNotFoundMessage: (profile) => `Profile "${profile}" was not found for the configured API ` +
62
+ `token. Create the profile with Browserless.saveProfile in a ` +
63
+ `live session first, or omit the profile parameter to scrape ` +
64
+ `anonymously.`,
65
+ cache,
66
+ run: async ({ client, params }) => client.smartScrape({
67
+ url: params.url,
68
+ formats: params.formats,
69
+ timeout: params.timeout,
70
+ profile: params.profile,
71
+ }),
72
+ analyticsProps: (params, result) => ({
73
+ url: params.url,
74
+ formats: (params.formats ?? ['markdown']).join(','),
75
+ timeout: params.timeout ?? config.requestTimeout,
76
+ cache_hit: result.cacheHit,
77
+ ok: result.ok,
78
+ status_code: result.statusCode,
79
+ strategy: result.strategy,
80
+ profile_used: !!params.profile,
81
+ }),
82
+ format: (response) => {
83
+ if (!response.ok) {
84
+ throw new UserError(`Scraping failed: ${response.message ?? 'Unknown error'} ` +
85
+ `(status: ${response.statusCode}, strategies attempted: ${response.attempted.join(', ')})`);
86
+ }
87
+ const blocks = [];
88
+ // Primary text content: prefer markdown > string content > object content > diagnostic
89
+ let textContent;
90
+ if (response.markdown) {
91
+ textContent = response.markdown;
92
+ }
93
+ else if (typeof response.content === 'string' && response.content) {
94
+ textContent = response.content;
95
+ }
96
+ else if (response.content && typeof response.content === 'object') {
97
+ textContent = JSON.stringify(response.content, null, 2);
98
+ }
99
+ else {
100
+ textContent = `[No page content returned by the API. Strategy: ${response.strategy}, Status: ${response.statusCode}]`;
101
+ }
102
+ blocks.push({ type: 'text', text: textContent });
103
+ blocks.push({
104
+ type: 'text',
105
+ text: [
106
+ '---',
107
+ `Strategy: ${response.strategy}`,
108
+ `Status: ${response.statusCode}`,
109
+ `Content-Type: ${response.contentType}`,
110
+ `Strategies Attempted: ${response.attempted.join(', ')}`,
111
+ '---',
112
+ ].join('\n'),
113
+ });
114
+ if (response.screenshot) {
115
+ blocks.push({
116
+ type: 'image',
117
+ data: response.screenshot,
118
+ mimeType: 'image/png',
119
+ });
120
+ }
121
+ if (response.pdf) {
122
+ blocks.push({
123
+ type: 'text',
124
+ text: `[PDF Document - base64 encoded, ${response.pdf.length} characters]\n${response.pdf}`,
125
+ });
126
+ }
127
+ if (response.links && response.links.length > 0) {
128
+ blocks.push({
129
+ type: 'text',
130
+ text: `## Links (${response.links.length})\n${response.links.join('\n')}`,
131
+ });
132
+ }
133
+ return blocks;
134
+ },
135
+ });
136
+ }
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@browserless.io/mcp",
3
+ "version": "1.6.0",
4
+ "description": "MCP (Model Context Protocol) server for the Browserless.io browser automation platform",
5
+ "author": "browserless.io",
6
+ "license": "SSPL-1.0",
7
+ "type": "module",
8
+ "main": "./build/src/index.js",
9
+ "types": "./build/src/index.d.ts",
10
+ "bin": {
11
+ "browserless-mcp": "./bin/cli.js"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./build/src/index.d.ts",
16
+ "import": "./build/src/index.js"
17
+ },
18
+ "./skills": {
19
+ "types": "./build/src/skills/index.d.ts",
20
+ "import": "./build/src/skills/index.js"
21
+ },
22
+ "./agent-client": {
23
+ "types": "./build/src/lib/agent-client.d.ts",
24
+ "import": "./build/src/lib/agent-client.js"
25
+ },
26
+ "./schemas": {
27
+ "types": "./build/src/tools/schemas.d.ts",
28
+ "import": "./build/src/tools/schemas.js"
29
+ },
30
+ "./prompt": {
31
+ "types": "./build/src/skills/system-prompt.d.ts",
32
+ "import": "./build/src/skills/system-prompt.js"
33
+ },
34
+ "./errors": {
35
+ "types": "./build/src/lib/error-classifier.d.ts",
36
+ "import": "./build/src/lib/error-classifier.js"
37
+ },
38
+ "./format": {
39
+ "types": "./build/src/lib/agent-format.d.ts",
40
+ "import": "./build/src/lib/agent-format.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "build/src/**/*.js",
45
+ "build/src/**/*.d.ts",
46
+ "build/src/**/*.md",
47
+ "bin/*",
48
+ "patches/**",
49
+ "README.md"
50
+ ],
51
+ "scripts": {
52
+ "build": "tsc && npm run build:skills && npm run build:types",
53
+ "build:skills": "mkdir -p build/src/skills && cp src/skills/*.md build/src/skills/",
54
+ "build:types": "mkdir -p build/src/@types && cp src/@types/*.d.ts build/src/@types/",
55
+ "clean": "rm -rf build",
56
+ "dev": "BROWSERLESS_TOKEN=${BROWSERLESS_TOKEN:-test} node --watch build/src/index.js",
57
+ "lint": "eslint src test",
58
+ "test": "npm run build && mocha",
59
+ "coverage": "npm run build && c8 --reporter=text --reporter=html --reporter=lcov --check-coverage --lines 80 --branches 70 --functions 80 mocha",
60
+ "start": "node build/src/index.js",
61
+ "start:http": "TRANSPORT=httpStream PORT=8080 node build/src/index.js",
62
+ "postinstall": "patch-package",
63
+ "prepack": "npm run build",
64
+ "prepare": "husky",
65
+ "prettier": "prettier '{src,test,bin,.github}/**/*.{js,ts,json,yml,yaml,md}' --log-level error --write"
66
+ },
67
+ "prettier": {
68
+ "semi": true,
69
+ "trailingComma": "all",
70
+ "singleQuote": true,
71
+ "printWidth": 80
72
+ },
73
+ "dependencies": {
74
+ "@aws-sdk/client-sqs": "^3.1053.0",
75
+ "fastmcp": "^4.0.0",
76
+ "ioredis": "^5.10.1",
77
+ "patch-package": "^8.0.1",
78
+ "ws": "^8.21.0",
79
+ "zod": "^4.4.3"
80
+ },
81
+ "devDependencies": {
82
+ "@eslint/js": "^10.0.1",
83
+ "@types/chai": "^5.2.2",
84
+ "@types/ioredis-mock": "^8.2.7",
85
+ "@types/mocha": "^10.0.10",
86
+ "@types/node": "^25.9.1",
87
+ "@types/sinon": "^21.0.1",
88
+ "@types/ws": "^8.18.1",
89
+ "c8": "^11.0.0",
90
+ "chai": "^6.2.2",
91
+ "eslint": "^10.4.0",
92
+ "globals": "^17.6.0",
93
+ "husky": "^9.1.7",
94
+ "ioredis-mock": "^8.13.1",
95
+ "mocha": "^11.7.6",
96
+ "prettier": "^3.8.3",
97
+ "sinon": "^22.0.0",
98
+ "typescript": "^6.0.3",
99
+ "typescript-eslint": "^8.60.0"
100
+ },
101
+ "engines": {
102
+ "node": ">=24"
103
+ },
104
+ "overrides": {
105
+ "mocha": {
106
+ "diff": "^8.0.3",
107
+ "minimatch": "^10.2.1",
108
+ "glob": "^11.0.0"
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,31 @@
1
+ diff --git a/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs b/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
2
+ index bfd727e..3350845 100644
3
+ --- a/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
4
+ +++ b/node_modules/mcp-proxy/dist/stdio-CyOk7u4Q.mjs
5
+ @@ -15479,6 +15479,10 @@ const handleStreamRequest = async ({ activeTransports, authenticate, authMiddlew
6
+ const sessionId = req.headers["mcp-session-id"];
7
+ const activeTransport = sessionId ? activeTransports[sessionId] : void 0;
8
+ if (!sessionId) {
9
+ + if (stateless) {
10
+ + res.writeHead(405, { Allow: "POST" }).end("Method Not Allowed");
11
+ + return true;
12
+ + }
13
+ res.writeHead(400).end("No sessionId");
14
+ return true;
15
+ }
16
+ diff --git a/node_modules/mcp-proxy/src/startHTTPServer.ts b/node_modules/mcp-proxy/src/startHTTPServer.ts
17
+ index 80e8cb7..ac051a1 100644
18
+ --- a/node_modules/mcp-proxy/src/startHTTPServer.ts
19
+ +++ b/node_modules/mcp-proxy/src/startHTTPServer.ts
20
+ @@ -686,6 +686,11 @@ const handleStreamRequest = async <T extends ServerLike>({
21
+ | undefined = sessionId ? activeTransports[sessionId] : undefined;
22
+
23
+ if (!sessionId) {
24
+ + if (stateless) {
25
+ + res.writeHead(405, { Allow: "POST" }).end("Method Not Allowed");
26
+ +
27
+ + return true;
28
+ + }
29
+ res.writeHead(400).end("No sessionId");
30
+
31
+ return true;