@youdotcom-oss/mcp 1.3.5-next.5 → 1.3.6
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/README.md +1 -1
- package/bin/stdio.js +17 -39
- package/package.json +17 -39
- package/src/contents/register-contents-tool.ts +90 -0
- package/src/contents/tests/contents.utils.spec.ts +188 -0
- package/src/express/register-express-tool.ts +67 -0
- package/src/express/tests/express.utils.spec.ts +244 -0
- package/src/get-mcp-server.ts +17 -0
- package/src/http.ts +72 -0
- package/src/search/register-search-tool.ts +87 -0
- package/src/search/tests/search.utils.spec.ts +217 -0
- package/src/shared/generate-error-report-link.ts +37 -0
- package/src/shared/get-logger.ts +10 -0
- package/src/shared/tests/shared.utils.spec.ts +160 -0
- package/src/shared/use-client-version.ts +21 -0
- package/src/stdio.ts +24 -0
- package/src/tests/exports.spec.ts +24 -0
- package/src/tests/http.spec.ts +318 -0
- package/src/tests/tool.spec.ts +496 -0
- package/src/utils.ts +8 -0
- package/AGENTS.md +0 -744
- package/CONTRIBUTING.md +0 -246
- package/docs/API.md +0 -319
package/README.md
CHANGED
|
@@ -288,7 +288,7 @@ Interested in contributing to the You.com MCP Server? We'd love your help!
|
|
|
288
288
|
Need technical details? Check [AGENTS.md](./AGENTS.md) for complete development setup, architecture overview, code patterns, and testing guidelines.
|
|
289
289
|
|
|
290
290
|
1. Fork the repository
|
|
291
|
-
2. Create a feature branch following naming conventions in [CONTRIBUTING.md](
|
|
291
|
+
2. Create a feature branch following naming conventions in [CONTRIBUTING.md](../../CONTRIBUTING.md)
|
|
292
292
|
3. Follow the code style guidelines and use conventional commits
|
|
293
293
|
4. Write tests for your changes (maintain >80% coverage)
|
|
294
294
|
5. Run quality checks: `bun run check && bun test`
|
package/bin/stdio.js
CHANGED
|
@@ -12155,7 +12155,7 @@ class StdioServerTransport {
|
|
|
12155
12155
|
// package.json
|
|
12156
12156
|
var package_default = {
|
|
12157
12157
|
name: "@youdotcom-oss/mcp",
|
|
12158
|
-
version: "1.3.
|
|
12158
|
+
version: "1.3.6",
|
|
12159
12159
|
description: "You.com API Model Context Protocol Server",
|
|
12160
12160
|
license: "MIT",
|
|
12161
12161
|
engines: {
|
|
@@ -12164,12 +12164,13 @@ var package_default = {
|
|
|
12164
12164
|
},
|
|
12165
12165
|
repository: {
|
|
12166
12166
|
type: "git",
|
|
12167
|
-
url: "git+https://github.com/youdotcom-oss/
|
|
12167
|
+
url: "git+https://github.com/youdotcom-oss/dx-toolkit.git",
|
|
12168
|
+
directory: "packages/mcp"
|
|
12168
12169
|
},
|
|
12169
12170
|
bugs: {
|
|
12170
|
-
url: "https://github.com/youdotcom-oss/
|
|
12171
|
+
url: "https://github.com/youdotcom-oss/dx-toolkit/issues"
|
|
12171
12172
|
},
|
|
12172
|
-
homepage: "https://github.com/youdotcom-oss/
|
|
12173
|
+
homepage: "https://github.com/youdotcom-oss/dx-toolkit/tree/main/packages/mcp#readme",
|
|
12173
12174
|
author: "You.com (https://you.com)",
|
|
12174
12175
|
keywords: [
|
|
12175
12176
|
"mcp",
|
|
@@ -12178,19 +12179,17 @@ var package_default = {
|
|
|
12178
12179
|
],
|
|
12179
12180
|
bin: "bin/stdio.js",
|
|
12180
12181
|
type: "module",
|
|
12181
|
-
main: "./src/
|
|
12182
|
+
main: "./src/utils.ts",
|
|
12182
12183
|
exports: {
|
|
12183
|
-
".": "./src/
|
|
12184
|
+
".": "./src/utils.ts",
|
|
12185
|
+
"./http": "./src/http.ts",
|
|
12186
|
+
"./stdio": "./src/stdio.ts"
|
|
12184
12187
|
},
|
|
12185
12188
|
files: [
|
|
12186
12189
|
"bin/stdio.js",
|
|
12187
|
-
"src
|
|
12188
|
-
"src
|
|
12189
|
-
"src
|
|
12190
|
-
"src/shared/format-search-results-text.ts",
|
|
12191
|
-
"AGENTS.md",
|
|
12192
|
-
"CONTRIBUTING.md",
|
|
12193
|
-
"docs/API.md"
|
|
12190
|
+
"./src/**",
|
|
12191
|
+
"!./src/**/tests/*",
|
|
12192
|
+
"!./src/**/*.spec.@(tsx|ts)"
|
|
12194
12193
|
],
|
|
12195
12194
|
publishConfig: {
|
|
12196
12195
|
access: "public"
|
|
@@ -12209,41 +12208,20 @@ var package_default = {
|
|
|
12209
12208
|
inspect: "bash -c 'source .env 2>/dev/null || true; bunx @modelcontextprotocol/inspector -e YDC_API_KEY=$YDC_API_KEY bun dev'",
|
|
12210
12209
|
lint: "biome lint",
|
|
12211
12210
|
"lint:fix": "biome lint --write",
|
|
12212
|
-
prepare: "git config core.hooksPath .hooks",
|
|
12213
12211
|
start: "bun run bin/http",
|
|
12214
12212
|
test: "bun test",
|
|
12215
12213
|
"test:coverage": "bun test --coverage",
|
|
12216
12214
|
"test:coverage:watch": "bun test --coverage --watch",
|
|
12217
12215
|
"test:watch": "bun test --watch"
|
|
12218
12216
|
},
|
|
12219
|
-
"lint-staged": {
|
|
12220
|
-
"*.{ts,tsx}": [
|
|
12221
|
-
"biome check --write --no-errors-on-unmatched"
|
|
12222
|
-
],
|
|
12223
|
-
"*.{json,md}": [
|
|
12224
|
-
"biome format --write --no-errors-on-unmatched"
|
|
12225
|
-
],
|
|
12226
|
-
"package.json": [
|
|
12227
|
-
"format-package --write"
|
|
12228
|
-
]
|
|
12229
|
-
},
|
|
12230
12217
|
dependencies: {
|
|
12231
|
-
zod: "^4.1.13"
|
|
12218
|
+
zod: "^4.1.13",
|
|
12219
|
+
"@hono/mcp": "^0.2.0",
|
|
12220
|
+
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
12221
|
+
hono: "^4.10.7"
|
|
12232
12222
|
},
|
|
12233
12223
|
devDependencies: {
|
|
12234
|
-
"@
|
|
12235
|
-
"@commitlint/cli": "20.1.0",
|
|
12236
|
-
"@commitlint/config-conventional": "20.0.0",
|
|
12237
|
-
"@eslint/js": "9.39.1",
|
|
12238
|
-
"@modelcontextprotocol/inspector": "0.17.5",
|
|
12239
|
-
"@types/bun": "latest",
|
|
12240
|
-
typescript: "5.9.3",
|
|
12241
|
-
"lint-staged": "16.2.7",
|
|
12242
|
-
"format-package": "7.0.0",
|
|
12243
|
-
"@hono/bun-compress": "0.1.0",
|
|
12244
|
-
"@hono/mcp": "0.2.0",
|
|
12245
|
-
"@modelcontextprotocol/sdk": "1.24.3",
|
|
12246
|
-
hono: "4.10.7"
|
|
12224
|
+
"@modelcontextprotocol/inspector": "0.17.5"
|
|
12247
12225
|
}
|
|
12248
12226
|
};
|
|
12249
12227
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@youdotcom-oss/mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"description": "You.com API Model Context Protocol Server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"engines": {
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
},
|
|
10
10
|
"repository": {
|
|
11
11
|
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/youdotcom-oss/
|
|
12
|
+
"url": "git+https://github.com/youdotcom-oss/dx-toolkit.git",
|
|
13
|
+
"directory": "packages/mcp"
|
|
13
14
|
},
|
|
14
15
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/youdotcom-oss/
|
|
16
|
+
"url": "https://github.com/youdotcom-oss/dx-toolkit/issues"
|
|
16
17
|
},
|
|
17
|
-
"homepage": "https://github.com/youdotcom-oss/
|
|
18
|
+
"homepage": "https://github.com/youdotcom-oss/dx-toolkit/tree/main/packages/mcp#readme",
|
|
18
19
|
"author": "You.com (https://you.com)",
|
|
19
20
|
"keywords": [
|
|
20
21
|
"mcp",
|
|
@@ -23,19 +24,17 @@
|
|
|
23
24
|
],
|
|
24
25
|
"bin": "bin/stdio.js",
|
|
25
26
|
"type": "module",
|
|
26
|
-
"main": "./src/
|
|
27
|
+
"main": "./src/utils.ts",
|
|
27
28
|
"exports": {
|
|
28
|
-
".": "./src/
|
|
29
|
+
".": "./src/utils.ts",
|
|
30
|
+
"./http": "./src/http.ts",
|
|
31
|
+
"./stdio": "./src/stdio.ts"
|
|
29
32
|
},
|
|
30
33
|
"files": [
|
|
31
34
|
"bin/stdio.js",
|
|
32
|
-
"src
|
|
33
|
-
"src
|
|
34
|
-
"src
|
|
35
|
-
"src/shared/format-search-results-text.ts",
|
|
36
|
-
"AGENTS.md",
|
|
37
|
-
"CONTRIBUTING.md",
|
|
38
|
-
"docs/API.md"
|
|
35
|
+
"./src/**",
|
|
36
|
+
"!./src/**/tests/*",
|
|
37
|
+
"!./src/**/*.spec.@(tsx|ts)"
|
|
39
38
|
],
|
|
40
39
|
"publishConfig": {
|
|
41
40
|
"access": "public"
|
|
@@ -54,40 +53,19 @@
|
|
|
54
53
|
"inspect": "bash -c 'source .env 2>/dev/null || true; bunx @modelcontextprotocol/inspector -e YDC_API_KEY=$YDC_API_KEY bun dev'",
|
|
55
54
|
"lint": "biome lint",
|
|
56
55
|
"lint:fix": "biome lint --write",
|
|
57
|
-
"prepare": "git config core.hooksPath .hooks",
|
|
58
56
|
"start": "bun run bin/http",
|
|
59
57
|
"test": "bun test",
|
|
60
58
|
"test:coverage": "bun test --coverage",
|
|
61
59
|
"test:coverage:watch": "bun test --coverage --watch",
|
|
62
60
|
"test:watch": "bun test --watch"
|
|
63
61
|
},
|
|
64
|
-
"lint-staged": {
|
|
65
|
-
"*.{ts,tsx}": [
|
|
66
|
-
"biome check --write --no-errors-on-unmatched"
|
|
67
|
-
],
|
|
68
|
-
"*.{json,md}": [
|
|
69
|
-
"biome format --write --no-errors-on-unmatched"
|
|
70
|
-
],
|
|
71
|
-
"package.json": [
|
|
72
|
-
"format-package --write"
|
|
73
|
-
]
|
|
74
|
-
},
|
|
75
62
|
"dependencies": {
|
|
76
|
-
"zod": "^4.1.13"
|
|
63
|
+
"zod": "^4.1.13",
|
|
64
|
+
"@hono/mcp": "^0.2.0",
|
|
65
|
+
"@modelcontextprotocol/sdk": "^1.24.3",
|
|
66
|
+
"hono": "^4.10.7"
|
|
77
67
|
},
|
|
78
68
|
"devDependencies": {
|
|
79
|
-
"@
|
|
80
|
-
"@commitlint/cli": "20.1.0",
|
|
81
|
-
"@commitlint/config-conventional": "20.0.0",
|
|
82
|
-
"@eslint/js": "9.39.1",
|
|
83
|
-
"@modelcontextprotocol/inspector": "0.17.5",
|
|
84
|
-
"@types/bun": "latest",
|
|
85
|
-
"typescript": "5.9.3",
|
|
86
|
-
"lint-staged": "16.2.7",
|
|
87
|
-
"format-package": "7.0.0",
|
|
88
|
-
"@hono/bun-compress": "0.1.0",
|
|
89
|
-
"@hono/mcp": "0.2.0",
|
|
90
|
-
"@modelcontextprotocol/sdk": "1.24.3",
|
|
91
|
-
"hono": "4.10.7"
|
|
69
|
+
"@modelcontextprotocol/inspector": "0.17.5"
|
|
92
70
|
}
|
|
93
71
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { generateErrorReportLink } from '../shared/generate-error-report-link.ts';
|
|
3
|
+
import { getLogger } from '../shared/get-logger.ts';
|
|
4
|
+
import { ContentsQuerySchema, ContentsStructuredContentSchema } from './contents.schemas.ts';
|
|
5
|
+
import { fetchContents, formatContentsResponse } from './contents.utils.ts';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Register the you-contents tool with the MCP server
|
|
9
|
+
* Extracts and returns full content from multiple URLs in markdown or HTML format
|
|
10
|
+
*/
|
|
11
|
+
export const registerContentsTool = ({
|
|
12
|
+
mcp,
|
|
13
|
+
YDC_API_KEY,
|
|
14
|
+
getUserAgent,
|
|
15
|
+
}: {
|
|
16
|
+
mcp: McpServer;
|
|
17
|
+
YDC_API_KEY?: string;
|
|
18
|
+
getUserAgent: () => string;
|
|
19
|
+
}) => {
|
|
20
|
+
// Register the tool
|
|
21
|
+
mcp.registerTool(
|
|
22
|
+
'you-contents',
|
|
23
|
+
{
|
|
24
|
+
title: 'Extract Web Page Contents',
|
|
25
|
+
description: 'Extract page content in markdown or HTML',
|
|
26
|
+
inputSchema: ContentsQuerySchema.shape,
|
|
27
|
+
outputSchema: ContentsStructuredContentSchema.shape,
|
|
28
|
+
},
|
|
29
|
+
async (toolInput) => {
|
|
30
|
+
const logger = getLogger(mcp);
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
// Validate and parse input
|
|
34
|
+
const contentsQuery = ContentsQuerySchema.parse(toolInput);
|
|
35
|
+
const { urls, format = 'markdown' } = contentsQuery;
|
|
36
|
+
|
|
37
|
+
// Log the request
|
|
38
|
+
await logger({
|
|
39
|
+
level: 'info',
|
|
40
|
+
data: `Contents API call initiated for ${urls.length} URL(s) with format: ${format}`,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// Fetch contents from API
|
|
44
|
+
const response = await fetchContents({
|
|
45
|
+
contentsQuery,
|
|
46
|
+
YDC_API_KEY,
|
|
47
|
+
getUserAgent,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Format response with full content
|
|
51
|
+
const { content, structuredContent } = formatContentsResponse(response, format);
|
|
52
|
+
|
|
53
|
+
// Log success
|
|
54
|
+
await logger({
|
|
55
|
+
level: 'info',
|
|
56
|
+
data: `Contents API call successful: extracted ${response.length} page(s)`,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
content,
|
|
61
|
+
structuredContent,
|
|
62
|
+
};
|
|
63
|
+
} catch (err: unknown) {
|
|
64
|
+
// Handle and log errors
|
|
65
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
66
|
+
const reportLink = generateErrorReportLink({
|
|
67
|
+
errorMessage,
|
|
68
|
+
tool: 'you-contents',
|
|
69
|
+
clientInfo: getUserAgent(),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await logger({
|
|
73
|
+
level: 'error',
|
|
74
|
+
data: `Contents API call failed: ${errorMessage}\n\nReport this issue: ${reportLink}`,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: 'text' as const,
|
|
81
|
+
text: `Error extracting contents: ${errorMessage}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
structuredContent: undefined,
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test';
|
|
2
|
+
import type { ContentsApiResponse } from '../contents.schemas.ts';
|
|
3
|
+
import { fetchContents, formatContentsResponse } from '../contents.utils.ts';
|
|
4
|
+
|
|
5
|
+
const getUserAgent = () => 'MCP/test (You.com; test-client)';
|
|
6
|
+
|
|
7
|
+
// NOTE: The following tests require a You.com API key with access to the Contents API
|
|
8
|
+
// Using example.com/example.org as test URLs since You.com blocks self-scraping
|
|
9
|
+
describe('fetchContents', () => {
|
|
10
|
+
test('returns valid response structure for single URL', async () => {
|
|
11
|
+
const result = await fetchContents({
|
|
12
|
+
contentsQuery: {
|
|
13
|
+
urls: ['https://documentation.you.com/developer-resources/mcp-server'],
|
|
14
|
+
format: 'markdown',
|
|
15
|
+
},
|
|
16
|
+
getUserAgent,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(Array.isArray(result)).toBe(true);
|
|
20
|
+
expect(result.length).toBeGreaterThan(0);
|
|
21
|
+
|
|
22
|
+
const firstItem = result[0];
|
|
23
|
+
expect(firstItem).toBeDefined();
|
|
24
|
+
|
|
25
|
+
// Should have markdown content
|
|
26
|
+
expect(firstItem?.markdown).toBeDefined();
|
|
27
|
+
expect(typeof firstItem?.markdown).toBe('string');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('handles multiple URLs', async () => {
|
|
31
|
+
const result = await fetchContents({
|
|
32
|
+
contentsQuery: {
|
|
33
|
+
urls: [
|
|
34
|
+
'https://documentation.you.com/developer-resources/mcp-server',
|
|
35
|
+
'https://documentation.you.com/developer-resources/python-sdk',
|
|
36
|
+
],
|
|
37
|
+
format: 'markdown',
|
|
38
|
+
},
|
|
39
|
+
getUserAgent,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(Array.isArray(result)).toBe(true);
|
|
43
|
+
expect(result.length).toBe(2);
|
|
44
|
+
|
|
45
|
+
for (const item of result) {
|
|
46
|
+
expect(item).toHaveProperty('url');
|
|
47
|
+
expect(item.markdown).toBeDefined();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('handles html format', async () => {
|
|
52
|
+
const result = await fetchContents({
|
|
53
|
+
contentsQuery: {
|
|
54
|
+
urls: ['https://documentation.you.com/developer-resources/mcp-server'],
|
|
55
|
+
format: 'html',
|
|
56
|
+
},
|
|
57
|
+
getUserAgent,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(Array.isArray(result)).toBe(true);
|
|
61
|
+
const firstItem = result[0];
|
|
62
|
+
expect(firstItem).toBeDefined();
|
|
63
|
+
|
|
64
|
+
expect(firstItem?.html).toBeDefined();
|
|
65
|
+
expect(typeof firstItem?.html).toBe('string');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('formatContentsResponse', () => {
|
|
70
|
+
test('formats single markdown content correctly', () => {
|
|
71
|
+
const mockResponse: ContentsApiResponse = [
|
|
72
|
+
{
|
|
73
|
+
url: 'https://example.com',
|
|
74
|
+
title: 'Example Page',
|
|
75
|
+
markdown: '# Hello\n\nThis is a test page with some content.',
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const result = formatContentsResponse(mockResponse, 'markdown');
|
|
80
|
+
|
|
81
|
+
expect(result).toHaveProperty('content');
|
|
82
|
+
expect(result).toHaveProperty('structuredContent');
|
|
83
|
+
expect(Array.isArray(result.content)).toBe(true);
|
|
84
|
+
expect(result.content[0]).toHaveProperty('type', 'text');
|
|
85
|
+
expect(result.content[0]).toHaveProperty('text');
|
|
86
|
+
|
|
87
|
+
const text = result.content[0]?.text;
|
|
88
|
+
expect(text).toContain('Example Page');
|
|
89
|
+
expect(text).toContain('https://example.com');
|
|
90
|
+
expect(text).toContain('Format: markdown');
|
|
91
|
+
expect(text).toContain('# Hello');
|
|
92
|
+
expect(text).toContain('This is a test page with some content.');
|
|
93
|
+
|
|
94
|
+
expect(result.structuredContent).toHaveProperty('count', 1);
|
|
95
|
+
expect(result.structuredContent).toHaveProperty('format', 'markdown');
|
|
96
|
+
expect(result.structuredContent.items).toHaveLength(1);
|
|
97
|
+
|
|
98
|
+
const item = result.structuredContent.items[0];
|
|
99
|
+
expect(item).toBeDefined();
|
|
100
|
+
|
|
101
|
+
expect(item).toHaveProperty('url', 'https://example.com');
|
|
102
|
+
expect(item).toHaveProperty('title', 'Example Page');
|
|
103
|
+
expect(item).toHaveProperty('content', '# Hello\n\nThis is a test page with some content.');
|
|
104
|
+
expect(item?.contentLength).toBe('# Hello\n\nThis is a test page with some content.'.length);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('formats multiple items correctly', () => {
|
|
108
|
+
const mockResponse: ContentsApiResponse = [
|
|
109
|
+
{
|
|
110
|
+
url: 'https://example1.com',
|
|
111
|
+
title: 'Page 1',
|
|
112
|
+
markdown: 'Content 1',
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
url: 'https://example2.com',
|
|
116
|
+
title: 'Page 2',
|
|
117
|
+
markdown: 'Content 2',
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
const result = formatContentsResponse(mockResponse, 'markdown');
|
|
122
|
+
|
|
123
|
+
expect(result.structuredContent.count).toBe(2);
|
|
124
|
+
expect(result.structuredContent.items).toHaveLength(2);
|
|
125
|
+
|
|
126
|
+
const text = result.content[0]?.text;
|
|
127
|
+
expect(text).toContain('Page 1');
|
|
128
|
+
expect(text).toContain('Page 2');
|
|
129
|
+
expect(text).toContain('https://example1.com');
|
|
130
|
+
expect(text).toContain('https://example2.com');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('handles html format', () => {
|
|
134
|
+
const mockResponse: ContentsApiResponse = [
|
|
135
|
+
{
|
|
136
|
+
url: 'https://example.com',
|
|
137
|
+
title: 'HTML Page',
|
|
138
|
+
html: '<html><body><h1>Hello</h1></body></html>',
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
const result = formatContentsResponse(mockResponse, 'html');
|
|
143
|
+
|
|
144
|
+
expect(result.structuredContent.format).toBe('html');
|
|
145
|
+
const text = result.content[0]?.text;
|
|
146
|
+
expect(text).toContain('Format: html');
|
|
147
|
+
expect(text).toContain('<html>');
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('includes full content for long text', () => {
|
|
151
|
+
const longContent = 'a'.repeat(1000);
|
|
152
|
+
const mockResponse: ContentsApiResponse = [
|
|
153
|
+
{
|
|
154
|
+
url: 'https://example.com',
|
|
155
|
+
title: 'Long Page',
|
|
156
|
+
markdown: longContent,
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
|
|
160
|
+
const result = formatContentsResponse(mockResponse, 'markdown');
|
|
161
|
+
|
|
162
|
+
const text = result.content[0]?.text;
|
|
163
|
+
// Full content should be included (not truncated)
|
|
164
|
+
expect(text).toContain(longContent);
|
|
165
|
+
|
|
166
|
+
// Structured content should have full content and correct length
|
|
167
|
+
const item = result.structuredContent.items[0];
|
|
168
|
+
expect(item?.content).toBe(longContent);
|
|
169
|
+
expect(item?.contentLength).toBe(1000);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test('handles empty content gracefully', () => {
|
|
173
|
+
const mockResponse: ContentsApiResponse = [
|
|
174
|
+
{
|
|
175
|
+
url: 'https://example.com',
|
|
176
|
+
title: 'Empty Page',
|
|
177
|
+
markdown: '',
|
|
178
|
+
},
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
const result = formatContentsResponse(mockResponse, 'markdown');
|
|
182
|
+
|
|
183
|
+
expect(result.structuredContent.items[0]?.contentLength).toBe(0);
|
|
184
|
+
const text = result.content[0]?.text;
|
|
185
|
+
expect(text).toContain('Empty Page');
|
|
186
|
+
expect(text).toContain('Content Length: 0 characters');
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { generateErrorReportLink } from '../shared/generate-error-report-link.ts';
|
|
3
|
+
import { getLogger } from '../shared/get-logger.ts';
|
|
4
|
+
import { ExpressAgentInputSchema, ExpressStructuredContentSchema } from './express.schemas.ts';
|
|
5
|
+
import { callExpressAgent, formatExpressAgentResponse } from './express.utils.ts';
|
|
6
|
+
|
|
7
|
+
export const registerExpressTool = ({
|
|
8
|
+
mcp,
|
|
9
|
+
YDC_API_KEY,
|
|
10
|
+
getUserAgent,
|
|
11
|
+
}: {
|
|
12
|
+
mcp: McpServer;
|
|
13
|
+
YDC_API_KEY?: string;
|
|
14
|
+
getUserAgent: () => string;
|
|
15
|
+
}) => {
|
|
16
|
+
mcp.registerTool(
|
|
17
|
+
'you-express',
|
|
18
|
+
{
|
|
19
|
+
title: 'Express Agent',
|
|
20
|
+
description: 'Fast AI answers with web search',
|
|
21
|
+
inputSchema: ExpressAgentInputSchema.shape,
|
|
22
|
+
outputSchema: ExpressStructuredContentSchema.shape,
|
|
23
|
+
},
|
|
24
|
+
async (agentInput) => {
|
|
25
|
+
const logger = getLogger(mcp);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const response = await callExpressAgent({
|
|
29
|
+
agentInput,
|
|
30
|
+
YDC_API_KEY,
|
|
31
|
+
getUserAgent,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await logger({
|
|
35
|
+
level: 'info',
|
|
36
|
+
data: `Express agent call successful for input: "${agentInput.input}"`,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const { content, structuredContent } = formatExpressAgentResponse(response);
|
|
40
|
+
return { content, structuredContent };
|
|
41
|
+
} catch (err: unknown) {
|
|
42
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
43
|
+
const reportLink = generateErrorReportLink({
|
|
44
|
+
errorMessage,
|
|
45
|
+
tool: 'you-express',
|
|
46
|
+
clientInfo: getUserAgent(),
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
await logger({
|
|
50
|
+
level: 'error',
|
|
51
|
+
data: `Express agent call failed: ${errorMessage}\n\nReport this issue: ${reportLink}`,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text' as const,
|
|
58
|
+
text: `Error: ${errorMessage}`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
structuredContent: undefined,
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
};
|