@youdotcom-oss/mcp 1.5.0 → 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.
- package/bin/stdio.js +394 -415
- package/package.json +12 -23
- package/src/contents/contents.schemas.ts +3 -48
- package/src/contents/contents.utils.ts +33 -133
- package/src/contents/register-contents-tool.ts +24 -24
- package/src/contents/tests/contents.utils.spec.ts +58 -134
- package/src/express/express.schema.ts +23 -0
- package/src/express/express.utils.ts +10 -121
- package/src/express/register-express-tool.ts +19 -19
- package/src/express/tests/express.utils.spec.ts +80 -159
- package/src/get-mcp-server.ts +3 -3
- package/src/http.ts +38 -38
- package/src/search/register-search-tool.ts +23 -23
- package/src/search/search.schema.ts +38 -0
- package/src/search/search.utils.ts +18 -96
- package/src/search/tests/search.utils.spec.ts +61 -194
- package/src/shared/format-search-results-text.ts +16 -16
- package/src/shared/get-logger.ts +4 -4
- package/src/shared/tests/shared.utils.spec.ts +56 -56
- package/src/shared/use-client-version.ts +8 -8
- package/src/stdio.ts +16 -16
- package/src/tests/http.spec.ts +105 -105
- package/src/tests/tool.spec.ts +212 -212
- package/dist/contents/contents.schemas.d.ts +0 -55
- package/dist/contents/contents.utils.d.ts +0 -28
- package/dist/contents/register-contents-tool.d.ts +0 -10
- package/dist/express/express.schemas.d.ts +0 -56
- package/dist/express/express.utils.d.ts +0 -45
- package/dist/express/register-express-tool.d.ts +0 -6
- package/dist/get-mcp-server.d.ts +0 -2
- package/dist/http.d.ts +0 -3
- package/dist/main.d.ts +0 -9
- package/dist/search/register-search-tool.d.ts +0 -6
- package/dist/search/search.schemas.d.ts +0 -133
- package/dist/search/search.utils.d.ts +0 -98
- package/dist/shared/api-constants.d.ts +0 -9
- package/dist/shared/check-response-for-errors.d.ts +0 -6
- package/dist/shared/format-search-results-text.d.ts +0 -19
- package/dist/shared/generate-error-report-link.d.ts +0 -9
- package/dist/shared/get-logger.d.ts +0 -7
- package/dist/shared/use-client-version.d.ts +0 -6
- package/dist/stdio.d.ts +0 -2
- package/src/express/express.schemas.ts +0 -99
- package/src/main.ts +0 -9
- package/src/search/search.schemas.ts +0 -147
- package/src/shared/api-constants.ts +0 -10
- package/src/shared/check-response-for-errors.ts +0 -13
- package/src/shared/generate-error-report-link.ts +0 -37
- package/src/tests/exports.spec.ts +0 -24
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { describe, expect, test } from 'bun:test'
|
|
2
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
3
|
-
import { useGetClientVersion } from '../use-client-version.ts'
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
3
|
+
import { useGetClientVersion } from '../use-client-version.ts'
|
|
4
4
|
|
|
5
5
|
describe('useGetClientVersion', () => {
|
|
6
6
|
test('returns formatted string with all fields present', () => {
|
|
@@ -13,15 +13,15 @@ describe('useGetClientVersion', () => {
|
|
|
13
13
|
websiteUrl: 'https://example.com',
|
|
14
14
|
}),
|
|
15
15
|
},
|
|
16
|
-
} as unknown as McpServer
|
|
16
|
+
} as unknown as McpServer
|
|
17
17
|
|
|
18
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
19
|
-
const result = getUserAgent()
|
|
18
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
19
|
+
const result = getUserAgent()
|
|
20
20
|
|
|
21
21
|
expect(result).toMatch(
|
|
22
22
|
/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0; Test Client; https:\/\/example\.com\)$/,
|
|
23
|
-
)
|
|
24
|
-
})
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
25
|
|
|
26
26
|
test('returns formatted string with name and version only', () => {
|
|
27
27
|
const mockMcp = {
|
|
@@ -31,39 +31,39 @@ describe('useGetClientVersion', () => {
|
|
|
31
31
|
version: '1.0.0',
|
|
32
32
|
}),
|
|
33
33
|
},
|
|
34
|
-
} as unknown as McpServer
|
|
34
|
+
} as unknown as McpServer
|
|
35
35
|
|
|
36
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
37
|
-
const result = getUserAgent()
|
|
36
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
37
|
+
const result = getUserAgent()
|
|
38
38
|
|
|
39
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0\)$/)
|
|
40
|
-
})
|
|
39
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0\)$/)
|
|
40
|
+
})
|
|
41
41
|
|
|
42
42
|
test('returns UNKNOWN when no client version available', () => {
|
|
43
43
|
const mockMcp = {
|
|
44
44
|
server: {
|
|
45
45
|
getClientVersion: () => null,
|
|
46
46
|
},
|
|
47
|
-
} as unknown as McpServer
|
|
47
|
+
} as unknown as McpServer
|
|
48
48
|
|
|
49
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
50
|
-
const result = getUserAgent()
|
|
49
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
50
|
+
const result = getUserAgent()
|
|
51
51
|
|
|
52
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; UNKNOWN\)$/)
|
|
53
|
-
})
|
|
52
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; UNKNOWN\)$/)
|
|
53
|
+
})
|
|
54
54
|
|
|
55
55
|
test('returns UNKNOWN when getClientVersion returns undefined', () => {
|
|
56
56
|
const mockMcp = {
|
|
57
57
|
server: {
|
|
58
58
|
getClientVersion: () => undefined,
|
|
59
59
|
},
|
|
60
|
-
} as unknown as McpServer
|
|
60
|
+
} as unknown as McpServer
|
|
61
61
|
|
|
62
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
63
|
-
const result = getUserAgent()
|
|
62
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
63
|
+
const result = getUserAgent()
|
|
64
64
|
|
|
65
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; UNKNOWN\)$/)
|
|
66
|
-
})
|
|
65
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; UNKNOWN\)$/)
|
|
66
|
+
})
|
|
67
67
|
|
|
68
68
|
test('filters out empty strings from fields', () => {
|
|
69
69
|
const mockMcp = {
|
|
@@ -75,14 +75,14 @@ describe('useGetClientVersion', () => {
|
|
|
75
75
|
websiteUrl: 'https://example.com',
|
|
76
76
|
}),
|
|
77
77
|
},
|
|
78
|
-
} as unknown as McpServer
|
|
78
|
+
} as unknown as McpServer
|
|
79
79
|
|
|
80
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
81
|
-
const result = getUserAgent()
|
|
80
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
81
|
+
const result = getUserAgent()
|
|
82
82
|
|
|
83
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0; https:\/\/example\.com\)$/)
|
|
84
|
-
expect(result).not.toContain(';;')
|
|
85
|
-
})
|
|
83
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0; https:\/\/example\.com\)$/)
|
|
84
|
+
expect(result).not.toContain(';;') // No double semicolons
|
|
85
|
+
})
|
|
86
86
|
|
|
87
87
|
test('filters out null values from fields', () => {
|
|
88
88
|
const mockMcp = {
|
|
@@ -94,13 +94,13 @@ describe('useGetClientVersion', () => {
|
|
|
94
94
|
websiteUrl: 'https://example.com',
|
|
95
95
|
}),
|
|
96
96
|
},
|
|
97
|
-
} as unknown as McpServer
|
|
97
|
+
} as unknown as McpServer
|
|
98
98
|
|
|
99
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
100
|
-
const result = getUserAgent()
|
|
99
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
100
|
+
const result = getUserAgent()
|
|
101
101
|
|
|
102
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0; https:\/\/example\.com\)$/)
|
|
103
|
-
})
|
|
102
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0; https:\/\/example\.com\)$/)
|
|
103
|
+
})
|
|
104
104
|
|
|
105
105
|
test('handles partial fields - name, version, and title only', () => {
|
|
106
106
|
const mockMcp = {
|
|
@@ -111,13 +111,13 @@ describe('useGetClientVersion', () => {
|
|
|
111
111
|
title: 'Claude Desktop App',
|
|
112
112
|
}),
|
|
113
113
|
},
|
|
114
|
-
} as unknown as McpServer
|
|
114
|
+
} as unknown as McpServer
|
|
115
115
|
|
|
116
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
117
|
-
const result = getUserAgent()
|
|
116
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
117
|
+
const result = getUserAgent()
|
|
118
118
|
|
|
119
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; Claude Desktop; 0\.7\.6; Claude Desktop App\)$/)
|
|
120
|
-
})
|
|
119
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; Claude Desktop; 0\.7\.6; Claude Desktop App\)$/)
|
|
120
|
+
})
|
|
121
121
|
|
|
122
122
|
test('handles Claude Desktop client info format', () => {
|
|
123
123
|
const mockMcp = {
|
|
@@ -127,13 +127,13 @@ describe('useGetClientVersion', () => {
|
|
|
127
127
|
version: '0.7.6',
|
|
128
128
|
}),
|
|
129
129
|
},
|
|
130
|
-
} as unknown as McpServer
|
|
130
|
+
} as unknown as McpServer
|
|
131
131
|
|
|
132
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
133
|
-
const result = getUserAgent()
|
|
132
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
133
|
+
const result = getUserAgent()
|
|
134
134
|
|
|
135
|
-
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; Claude Desktop; 0\.7\.6\)$/)
|
|
136
|
-
})
|
|
135
|
+
expect(result).toMatch(/^MCP\/[\d.]+(-[\w.]+)? \(You\.com; Claude Desktop; 0\.7\.6\)$/)
|
|
136
|
+
})
|
|
137
137
|
|
|
138
138
|
test('returns a function that can be called multiple times', () => {
|
|
139
139
|
const mockMcp = {
|
|
@@ -143,18 +143,18 @@ describe('useGetClientVersion', () => {
|
|
|
143
143
|
version: '1.0.0',
|
|
144
144
|
}),
|
|
145
145
|
},
|
|
146
|
-
} as unknown as McpServer
|
|
146
|
+
} as unknown as McpServer
|
|
147
147
|
|
|
148
|
-
const getUserAgent = useGetClientVersion(mockMcp)
|
|
148
|
+
const getUserAgent = useGetClientVersion(mockMcp)
|
|
149
149
|
|
|
150
150
|
// Call multiple times to ensure consistent results
|
|
151
|
-
const result1 = getUserAgent()
|
|
152
|
-
const result2 = getUserAgent()
|
|
153
|
-
const result3 = getUserAgent()
|
|
154
|
-
|
|
155
|
-
const pattern = /^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0\)
|
|
156
|
-
expect(result1).toMatch(pattern)
|
|
157
|
-
expect(result2).toMatch(pattern)
|
|
158
|
-
expect(result3).toMatch(pattern)
|
|
159
|
-
})
|
|
160
|
-
})
|
|
151
|
+
const result1 = getUserAgent()
|
|
152
|
+
const result2 = getUserAgent()
|
|
153
|
+
const result3 = getUserAgent()
|
|
154
|
+
|
|
155
|
+
const pattern = /^MCP\/[\d.]+(-[\w.]+)? \(You\.com; test-client; 1\.0\.0\)$/
|
|
156
|
+
expect(result1).toMatch(pattern)
|
|
157
|
+
expect(result2).toMatch(pattern)
|
|
158
|
+
expect(result3).toMatch(pattern)
|
|
159
|
+
})
|
|
160
|
+
})
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
-
import packageJson from '../../package.json' with { type: 'json' }
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
2
|
+
import packageJson from '../../package.json' with { type: 'json' }
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Creates User-Agent string for API requests
|
|
6
6
|
* Used by search and express agent API calls
|
|
7
7
|
*/
|
|
8
|
-
const setUserAgent = (client: string) => `MCP/${packageJson.version} (You.com; ${client})
|
|
8
|
+
const setUserAgent = (client: string) => `MCP/${packageJson.version} (You.com; ${client})`
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Get's function that returns a formatted client version information into a string
|
|
12
12
|
* Used by stdio.ts and http.ts for logging/debugging
|
|
13
13
|
*/
|
|
14
14
|
export const useGetClientVersion = (mcp: McpServer) => () => {
|
|
15
|
-
const clientVersion = mcp.server.getClientVersion()
|
|
15
|
+
const clientVersion = mcp.server.getClientVersion()
|
|
16
16
|
if (clientVersion) {
|
|
17
|
-
const { name, version, title, websiteUrl } = clientVersion
|
|
18
|
-
return setUserAgent([name, version, title, websiteUrl].filter(Boolean).join('; '))
|
|
17
|
+
const { name, version, title, websiteUrl } = clientVersion
|
|
18
|
+
return setUserAgent([name, version, title, websiteUrl].filter(Boolean).join('; '))
|
|
19
19
|
}
|
|
20
|
-
return setUserAgent('UNKNOWN')
|
|
21
|
-
}
|
|
20
|
+
return setUserAgent('UNKNOWN')
|
|
21
|
+
}
|
package/src/stdio.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
-
import { registerContentsTool } from './contents/register-contents-tool.ts'
|
|
4
|
-
import { registerExpressTool } from './express/register-express-tool.ts'
|
|
5
|
-
import { getMCpServer } from './get-mcp-server.ts'
|
|
6
|
-
import { registerSearchTool } from './search/register-search-tool.ts'
|
|
7
|
-
import { useGetClientVersion } from './shared/use-client-version.ts'
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
3
|
+
import { registerContentsTool } from './contents/register-contents-tool.ts'
|
|
4
|
+
import { registerExpressTool } from './express/register-express-tool.ts'
|
|
5
|
+
import { getMCpServer } from './get-mcp-server.ts'
|
|
6
|
+
import { registerSearchTool } from './search/register-search-tool.ts'
|
|
7
|
+
import { useGetClientVersion } from './shared/use-client-version.ts'
|
|
8
8
|
|
|
9
|
-
const YDC_API_KEY = process.env.YDC_API_KEY
|
|
9
|
+
const YDC_API_KEY = process.env.YDC_API_KEY
|
|
10
10
|
|
|
11
11
|
try {
|
|
12
|
-
const mcp = getMCpServer()
|
|
13
|
-
const getUserAgent = useGetClientVersion(mcp)
|
|
12
|
+
const mcp = getMCpServer()
|
|
13
|
+
const getUserAgent = useGetClientVersion(mcp)
|
|
14
14
|
|
|
15
|
-
registerSearchTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
16
|
-
registerExpressTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
17
|
-
registerContentsTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
15
|
+
registerSearchTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
16
|
+
registerExpressTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
17
|
+
registerContentsTool({ mcp, YDC_API_KEY, getUserAgent })
|
|
18
18
|
|
|
19
|
-
const transport = new StdioServerTransport()
|
|
20
|
-
await mcp.connect(transport)
|
|
19
|
+
const transport = new StdioServerTransport()
|
|
20
|
+
await mcp.connect(transport)
|
|
21
21
|
} catch (error) {
|
|
22
|
-
process.stderr.write(`Failed to start server: ${error}\n`)
|
|
23
|
-
process.exit(1)
|
|
22
|
+
process.stderr.write(`Failed to start server: ${error}\n`)
|
|
23
|
+
process.exit(1)
|
|
24
24
|
}
|
package/src/tests/http.spec.ts
CHANGED
|
@@ -1,56 +1,56 @@
|
|
|
1
|
-
import { afterAll, beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
|
|
2
|
-
import httpApp from '../http.ts'
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, setDefaultTimeout, test } from 'bun:test'
|
|
2
|
+
import httpApp from '../http.ts'
|
|
3
3
|
|
|
4
4
|
// Increase default timeout for hooks to prevent intermittent failures
|
|
5
|
-
setDefaultTimeout(15_000)
|
|
5
|
+
setDefaultTimeout(15_000)
|
|
6
6
|
|
|
7
|
-
let server: ReturnType<typeof Bun.serve
|
|
8
|
-
let baseUrl: string
|
|
9
|
-
const testApiKey = process.env.YDC_API_KEY
|
|
7
|
+
let server: ReturnType<typeof Bun.serve>
|
|
8
|
+
let baseUrl: string
|
|
9
|
+
const testApiKey = process.env.YDC_API_KEY
|
|
10
10
|
|
|
11
11
|
beforeAll(async () => {
|
|
12
12
|
// Start HTTP server on random port
|
|
13
|
-
const port = Math.floor(Math.random() * 10000) + 20000
|
|
14
|
-
baseUrl = `http://localhost:${port}
|
|
13
|
+
const port = Math.floor(Math.random() * 10000) + 20000
|
|
14
|
+
baseUrl = `http://localhost:${port}`
|
|
15
15
|
|
|
16
16
|
// Start actual HTTP server using Bun
|
|
17
17
|
server = Bun.serve({
|
|
18
18
|
port,
|
|
19
19
|
fetch: httpApp.fetch.bind(httpApp),
|
|
20
|
-
})
|
|
20
|
+
})
|
|
21
21
|
|
|
22
22
|
// Wait a bit for server to start
|
|
23
|
-
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
24
|
-
})
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 500))
|
|
24
|
+
})
|
|
25
25
|
|
|
26
26
|
afterAll(async () => {
|
|
27
27
|
if (server) {
|
|
28
|
-
server.stop()
|
|
28
|
+
server.stop()
|
|
29
29
|
// Wait a bit for server to fully stop
|
|
30
|
-
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
30
|
+
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
31
31
|
}
|
|
32
|
-
})
|
|
32
|
+
})
|
|
33
33
|
|
|
34
34
|
describe('HTTP Server Endpoints', () => {
|
|
35
35
|
test('health endpoint returns service status', async () => {
|
|
36
|
-
const response = await fetch(`${baseUrl}/mcp-health`)
|
|
36
|
+
const response = await fetch(`${baseUrl}/mcp-health`)
|
|
37
37
|
|
|
38
|
-
expect(response.status).toBe(200)
|
|
39
|
-
expect(response.headers.get('content-type')).toContain('application/json')
|
|
38
|
+
expect(response.status).toBe(200)
|
|
39
|
+
expect(response.headers.get('content-type')).toContain('application/json')
|
|
40
40
|
|
|
41
41
|
const data = (await response.json()) as {
|
|
42
|
-
status: string
|
|
43
|
-
timestamp: string
|
|
44
|
-
version: string
|
|
45
|
-
service: string
|
|
46
|
-
}
|
|
47
|
-
expect(data).toHaveProperty('status', 'healthy')
|
|
48
|
-
expect(data).toHaveProperty('timestamp')
|
|
49
|
-
expect(data).toHaveProperty('version')
|
|
50
|
-
expect(data).toHaveProperty('service', 'youdotcom-mcp-server')
|
|
51
|
-
expect(typeof data.timestamp).toBe('string')
|
|
52
|
-
expect(typeof data.version).toBe('string')
|
|
53
|
-
})
|
|
42
|
+
status: string
|
|
43
|
+
timestamp: string
|
|
44
|
+
version: string
|
|
45
|
+
service: string
|
|
46
|
+
}
|
|
47
|
+
expect(data).toHaveProperty('status', 'healthy')
|
|
48
|
+
expect(data).toHaveProperty('timestamp')
|
|
49
|
+
expect(data).toHaveProperty('version')
|
|
50
|
+
expect(data).toHaveProperty('service', 'youdotcom-mcp-server')
|
|
51
|
+
expect(typeof data.timestamp).toBe('string')
|
|
52
|
+
expect(typeof data.version).toBe('string')
|
|
53
|
+
})
|
|
54
54
|
|
|
55
55
|
test('mcp endpoint requires authorization header', async () => {
|
|
56
56
|
const response = await fetch(`${baseUrl}/mcp`, {
|
|
@@ -59,14 +59,14 @@ describe('HTTP Server Endpoints', () => {
|
|
|
59
59
|
'Content-Type': 'application/json',
|
|
60
60
|
},
|
|
61
61
|
body: JSON.stringify({}),
|
|
62
|
-
})
|
|
62
|
+
})
|
|
63
63
|
|
|
64
|
-
expect(response.status).toBe(401)
|
|
65
|
-
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
64
|
+
expect(response.status).toBe(401)
|
|
65
|
+
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
66
66
|
|
|
67
|
-
const text = await response.text()
|
|
68
|
-
expect(text).toBe('Unauthorized: Authorization header required')
|
|
69
|
-
})
|
|
67
|
+
const text = await response.text()
|
|
68
|
+
expect(text).toBe('Unauthorized: Authorization header required')
|
|
69
|
+
})
|
|
70
70
|
|
|
71
71
|
test('mcp endpoint requires Bearer token format', async () => {
|
|
72
72
|
const response = await fetch(`${baseUrl}/mcp`, {
|
|
@@ -76,14 +76,14 @@ describe('HTTP Server Endpoints', () => {
|
|
|
76
76
|
Authorization: 'InvalidFormat token123',
|
|
77
77
|
},
|
|
78
78
|
body: JSON.stringify({}),
|
|
79
|
-
})
|
|
79
|
+
})
|
|
80
80
|
|
|
81
|
-
expect(response.status).toBe(401)
|
|
82
|
-
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
81
|
+
expect(response.status).toBe(401)
|
|
82
|
+
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
83
83
|
|
|
84
|
-
const text = await response.text()
|
|
85
|
-
expect(text).toBe('Unauthorized: Bearer token required')
|
|
86
|
-
})
|
|
84
|
+
const text = await response.text()
|
|
85
|
+
expect(text).toBe('Unauthorized: Bearer token required')
|
|
86
|
+
})
|
|
87
87
|
|
|
88
88
|
test('mcp endpoint accepts valid Bearer token', async () => {
|
|
89
89
|
const response = await fetch(`${baseUrl}/mcp`, {
|
|
@@ -106,19 +106,19 @@ describe('HTTP Server Endpoints', () => {
|
|
|
106
106
|
},
|
|
107
107
|
},
|
|
108
108
|
}),
|
|
109
|
-
})
|
|
109
|
+
})
|
|
110
110
|
|
|
111
|
-
expect(response.status).toBe(200)
|
|
112
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
111
|
+
expect(response.status).toBe(200)
|
|
112
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
113
113
|
|
|
114
114
|
// StreamableHTTPTransport uses SSE format, so response will be streaming
|
|
115
|
-
const text = await response.text()
|
|
116
|
-
expect(text).toContain('data:')
|
|
117
|
-
expect(text).toContain('jsonrpc')
|
|
118
|
-
expect(text).toContain('result')
|
|
119
|
-
expect(text).toContain('protocolVersion')
|
|
120
|
-
expect(text).toContain('capabilities')
|
|
121
|
-
})
|
|
115
|
+
const text = await response.text()
|
|
116
|
+
expect(text).toContain('data:')
|
|
117
|
+
expect(text).toContain('jsonrpc')
|
|
118
|
+
expect(text).toContain('result')
|
|
119
|
+
expect(text).toContain('protocolVersion')
|
|
120
|
+
expect(text).toContain('capabilities')
|
|
121
|
+
})
|
|
122
122
|
|
|
123
123
|
test('mcp endpoint with trailing slash works identically', async () => {
|
|
124
124
|
const response = await fetch(`${baseUrl}/mcp/`, {
|
|
@@ -141,18 +141,18 @@ describe('HTTP Server Endpoints', () => {
|
|
|
141
141
|
},
|
|
142
142
|
},
|
|
143
143
|
}),
|
|
144
|
-
})
|
|
144
|
+
})
|
|
145
145
|
|
|
146
|
-
expect(response.status).toBe(200)
|
|
147
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
146
|
+
expect(response.status).toBe(200)
|
|
147
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
148
148
|
|
|
149
|
-
const text = await response.text()
|
|
150
|
-
expect(text).toContain('data:')
|
|
151
|
-
expect(text).toContain('jsonrpc')
|
|
152
|
-
expect(text).toContain('result')
|
|
153
|
-
expect(text).toContain('protocolVersion')
|
|
154
|
-
expect(text).toContain('capabilities')
|
|
155
|
-
})
|
|
149
|
+
const text = await response.text()
|
|
150
|
+
expect(text).toContain('data:')
|
|
151
|
+
expect(text).toContain('jsonrpc')
|
|
152
|
+
expect(text).toContain('result')
|
|
153
|
+
expect(text).toContain('protocolVersion')
|
|
154
|
+
expect(text).toContain('capabilities')
|
|
155
|
+
})
|
|
156
156
|
|
|
157
157
|
test('mcp endpoint with trailing slash requires authorization', async () => {
|
|
158
158
|
const response = await fetch(`${baseUrl}/mcp/`, {
|
|
@@ -161,15 +161,15 @@ describe('HTTP Server Endpoints', () => {
|
|
|
161
161
|
'Content-Type': 'application/json',
|
|
162
162
|
},
|
|
163
163
|
body: JSON.stringify({}),
|
|
164
|
-
})
|
|
164
|
+
})
|
|
165
165
|
|
|
166
|
-
expect(response.status).toBe(401)
|
|
167
|
-
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
166
|
+
expect(response.status).toBe(401)
|
|
167
|
+
expect(response.headers.get('content-type')).toContain('text/plain')
|
|
168
168
|
|
|
169
|
-
const text = await response.text()
|
|
170
|
-
expect(text).toBe('Unauthorized: Authorization header required')
|
|
171
|
-
})
|
|
172
|
-
})
|
|
169
|
+
const text = await response.text()
|
|
170
|
+
expect(text).toBe('Unauthorized: Authorization header required')
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
173
|
|
|
174
174
|
describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
175
175
|
test('mcp endpoint responds to valid Bearer token', async () => {
|
|
@@ -186,15 +186,15 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
186
186
|
method: 'ping',
|
|
187
187
|
id: 1,
|
|
188
188
|
}),
|
|
189
|
-
})
|
|
189
|
+
})
|
|
190
190
|
|
|
191
191
|
// Should get a response (not 401/403), even if the method isn't supported
|
|
192
|
-
expect(response.status).not.toBe(401)
|
|
193
|
-
expect(response.status).not.toBe(403)
|
|
192
|
+
expect(response.status).not.toBe(401)
|
|
193
|
+
expect(response.status).not.toBe(403)
|
|
194
194
|
|
|
195
195
|
// Should be SSE response for StreamableHTTPTransport
|
|
196
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
197
|
-
})
|
|
196
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
197
|
+
})
|
|
198
198
|
|
|
199
199
|
test('mcp endpoint processes JSON-RPC requests', async () => {
|
|
200
200
|
// Test basic JSON-RPC structure handling
|
|
@@ -210,17 +210,17 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
210
210
|
method: 'unknown-method',
|
|
211
211
|
id: 123,
|
|
212
212
|
}),
|
|
213
|
-
})
|
|
213
|
+
})
|
|
214
214
|
|
|
215
|
-
expect(response.status).toBe(200)
|
|
216
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
215
|
+
expect(response.status).toBe(200)
|
|
216
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
217
217
|
|
|
218
218
|
// StreamableHTTPTransport uses SSE format
|
|
219
|
-
const text = await response.text()
|
|
220
|
-
expect(text).toContain('data:')
|
|
221
|
-
expect(text).toContain('jsonrpc')
|
|
222
|
-
expect(text).toContain('123')
|
|
223
|
-
})
|
|
219
|
+
const text = await response.text()
|
|
220
|
+
expect(text).toContain('data:')
|
|
221
|
+
expect(text).toContain('jsonrpc')
|
|
222
|
+
expect(text).toContain('123')
|
|
223
|
+
})
|
|
224
224
|
|
|
225
225
|
test('mcp endpoint extracts Bearer token correctly', async () => {
|
|
226
226
|
// Test that different tokens are processed
|
|
@@ -236,7 +236,7 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
236
236
|
method: 'test',
|
|
237
237
|
id: 1,
|
|
238
238
|
}),
|
|
239
|
-
})
|
|
239
|
+
})
|
|
240
240
|
|
|
241
241
|
const response2 = await fetch(`${baseUrl}/mcp`, {
|
|
242
242
|
method: 'POST',
|
|
@@ -250,12 +250,12 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
250
250
|
method: 'test',
|
|
251
251
|
id: 2,
|
|
252
252
|
}),
|
|
253
|
-
})
|
|
253
|
+
})
|
|
254
254
|
|
|
255
255
|
// Both should be processed (not authentication errors)
|
|
256
|
-
expect(response1.status).not.toBe(401)
|
|
257
|
-
expect(response2.status).not.toBe(401)
|
|
258
|
-
})
|
|
256
|
+
expect(response1.status).not.toBe(401)
|
|
257
|
+
expect(response2.status).not.toBe(401)
|
|
258
|
+
})
|
|
259
259
|
|
|
260
260
|
test('mcp endpoint uses StreamableHTTPTransport', async () => {
|
|
261
261
|
// Test that the transport is properly handling requests
|
|
@@ -271,17 +271,17 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
271
271
|
method: 'test',
|
|
272
272
|
id: 42,
|
|
273
273
|
}),
|
|
274
|
-
})
|
|
274
|
+
})
|
|
275
275
|
|
|
276
|
-
expect(response.status).toBe(200)
|
|
277
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
276
|
+
expect(response.status).toBe(200)
|
|
277
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
278
278
|
|
|
279
279
|
// StreamableHTTPTransport uses SSE format
|
|
280
|
-
const text = await response.text()
|
|
281
|
-
expect(text).toContain('data:')
|
|
282
|
-
expect(text).toContain('jsonrpc')
|
|
283
|
-
expect(text).toContain('42')
|
|
284
|
-
})
|
|
280
|
+
const text = await response.text()
|
|
281
|
+
expect(text).toContain('data:')
|
|
282
|
+
expect(text).toContain('jsonrpc')
|
|
283
|
+
expect(text).toContain('42')
|
|
284
|
+
})
|
|
285
285
|
|
|
286
286
|
test(
|
|
287
287
|
'mcp server handles search tool request for latest tech news',
|
|
@@ -305,18 +305,18 @@ describe('HTTP MCP Endpoint Basic Functionality', () => {
|
|
|
305
305
|
},
|
|
306
306
|
},
|
|
307
307
|
}),
|
|
308
|
-
})
|
|
308
|
+
})
|
|
309
309
|
|
|
310
|
-
expect(response.status).toBe(200)
|
|
311
|
-
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
310
|
+
expect(response.status).toBe(200)
|
|
311
|
+
expect(response.headers.get('content-type')).toContain('text/event-stream')
|
|
312
312
|
|
|
313
|
-
const text = await response.text()
|
|
314
|
-
expect(text).toContain('data:')
|
|
315
|
-
expect(text).toContain('jsonrpc')
|
|
316
|
-
expect(text).toContain('result')
|
|
317
|
-
expect(text).toContain('latest tech news')
|
|
318
|
-
expect(text).toContain('Search Results for')
|
|
313
|
+
const text = await response.text()
|
|
314
|
+
expect(text).toContain('data:')
|
|
315
|
+
expect(text).toContain('jsonrpc')
|
|
316
|
+
expect(text).toContain('result')
|
|
317
|
+
expect(text).toContain('latest tech news')
|
|
318
|
+
expect(text).toContain('Search Results for')
|
|
319
319
|
},
|
|
320
320
|
{ retry: 2 },
|
|
321
|
-
)
|
|
322
|
-
})
|
|
321
|
+
)
|
|
322
|
+
})
|