ninerouter-mcp 1.0.1 → 1.1.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/README.md +315 -182
- package/dist/create-config.js +14 -14
- package/dist/index.js +3 -3
- package/dist/ninerouter-client.js +38 -36
- package/dist/server.js +9 -9
- package/dist/tools/common.js +1 -8
- package/dist/tools/embeddings.js +17 -12
- package/dist/tools/media.js +159 -86
- package/dist/tools/models.js +12 -9
- package/dist/tools/web.js +28 -22
- package/package.json +37 -8
package/dist/tools/web.js
CHANGED
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
import { z } from
|
|
2
|
-
import { requestJson } from
|
|
3
|
-
import {
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { requestJson } from '../ninerouter-client.js';
|
|
3
|
+
import { tryModelsWithFallback, toPrettyJson } from './common.js';
|
|
4
4
|
export function registerWebTools(server, config) {
|
|
5
|
-
server.registerTool(
|
|
6
|
-
description:
|
|
5
|
+
server.registerTool('web_search', {
|
|
6
|
+
description: 'Search the web and return the raw upstream search payload as JSON. Use when the user needs current information, links, or news. Pass `model` (or its alias `provider`) to pick a specific search backend (e.g. `tavily/search`, `brave-search/search`); otherwise the configured `default_models.web_search` fallback chain is tried in order.',
|
|
7
7
|
inputSchema: z.object({
|
|
8
|
-
query: z.string().min(1).describe(
|
|
9
|
-
model: z
|
|
10
|
-
|
|
8
|
+
query: z.string().min(1).describe('Search query to send to the backend.'),
|
|
9
|
+
model: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe('Web-search model id, for example tavily/search or brave-search/search.'),
|
|
13
|
+
provider: z.string().optional().describe('Alias for model.'),
|
|
11
14
|
maxResults: z.number().int().positive().max(20).optional().default(5),
|
|
12
|
-
searchType: z.enum([
|
|
15
|
+
searchType: z.enum(['web', 'news']).optional().default('web'),
|
|
13
16
|
country: z.string().optional(),
|
|
14
17
|
language: z.string().optional(),
|
|
15
18
|
timeRange: z.string().optional(),
|
|
16
19
|
domainFilter: z.string().optional(),
|
|
17
20
|
}),
|
|
18
|
-
}, async ({ query, model, provider, maxResults, searchType, country, language, timeRange, domainFilter }) => {
|
|
21
|
+
}, async ({ query, model, provider, maxResults, searchType, country, language, timeRange, domainFilter, }) => {
|
|
19
22
|
const userModel = model ?? provider;
|
|
20
23
|
const defaultModels = config.defaultModels?.webSearch ?? [];
|
|
21
24
|
const modelsToTry = userModel ? [userModel] : defaultModels;
|
|
22
25
|
if (modelsToTry.length === 0) {
|
|
23
|
-
throw new Error(
|
|
26
|
+
throw new Error('No model specified and no default_models.web_search configured.');
|
|
24
27
|
}
|
|
25
28
|
const payload = await tryModelsWithFallback(modelsToTry, async (selectedModel) => {
|
|
26
|
-
return await requestJson(config,
|
|
29
|
+
return await requestJson(config, '/v1/search', {
|
|
27
30
|
model: selectedModel,
|
|
28
31
|
query,
|
|
29
32
|
max_results: maxResults,
|
|
@@ -35,16 +38,19 @@ export function registerWebTools(server, config) {
|
|
|
35
38
|
});
|
|
36
39
|
});
|
|
37
40
|
return {
|
|
38
|
-
content: [{ type:
|
|
41
|
+
content: [{ type: 'text', text: toPrettyJson(payload) }],
|
|
39
42
|
};
|
|
40
43
|
});
|
|
41
|
-
server.registerTool(
|
|
42
|
-
description:
|
|
44
|
+
server.registerTool('web_fetch', {
|
|
45
|
+
description: 'Fetch a URL and return its content as markdown (default), plain text, or HTML. Use when the user gives a link and you need its readable body. Output is truncated to `maxCharacters` (default 8000). Pass `model` (alias `provider`) to choose a fetcher (e.g. `jina-reader/fetch`, `firecrawl/fetch`); otherwise the configured `default_models.web_fetch` fallback chain is tried in order.',
|
|
43
46
|
inputSchema: z.object({
|
|
44
|
-
url: z.string().url().describe(
|
|
45
|
-
model: z
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
url: z.string().url().describe('URL to fetch and extract.'),
|
|
48
|
+
model: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe('Web-fetch model id, for example jina-reader/fetch or firecrawl/fetch.'),
|
|
52
|
+
provider: z.string().optional().describe('Alias for model.'),
|
|
53
|
+
format: z.enum(['markdown', 'text', 'html']).optional().default('markdown'),
|
|
48
54
|
maxCharacters: z.number().int().nonnegative().optional().default(8000),
|
|
49
55
|
}),
|
|
50
56
|
}, async ({ url, model, provider, format, maxCharacters }) => {
|
|
@@ -52,10 +58,10 @@ export function registerWebTools(server, config) {
|
|
|
52
58
|
const defaultModels = config.defaultModels?.webFetch ?? [];
|
|
53
59
|
const modelsToTry = userModel ? [userModel] : defaultModels;
|
|
54
60
|
if (modelsToTry.length === 0) {
|
|
55
|
-
throw new Error(
|
|
61
|
+
throw new Error('No model specified and no default_models.web_fetch configured.');
|
|
56
62
|
}
|
|
57
63
|
const payload = await tryModelsWithFallback(modelsToTry, async (selectedModel) => {
|
|
58
|
-
return await requestJson(config,
|
|
64
|
+
return await requestJson(config, '/v1/web/fetch', {
|
|
59
65
|
model: selectedModel,
|
|
60
66
|
url,
|
|
61
67
|
format,
|
|
@@ -63,7 +69,7 @@ export function registerWebTools(server, config) {
|
|
|
63
69
|
});
|
|
64
70
|
});
|
|
65
71
|
return {
|
|
66
|
-
content: [{ type:
|
|
72
|
+
content: [{ type: 'text', text: toPrettyJson(payload) }],
|
|
67
73
|
};
|
|
68
74
|
});
|
|
69
75
|
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ninerouter-mcp",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for 9Router with model discovery, automatic fallback, and type-safe validation",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
7
10
|
"author": "Farhan Brillan Widyakirana <https://tojoo.dev>",
|
|
8
11
|
"repository": {
|
|
9
12
|
"type": "git",
|
|
@@ -39,16 +42,42 @@
|
|
|
39
42
|
"prepack": "npm run build",
|
|
40
43
|
"start": "node dist/index.js",
|
|
41
44
|
"dev": "tsx watch src/index.ts",
|
|
42
|
-
"create-config": "tsx src/create-config.ts"
|
|
45
|
+
"create-config": "tsx src/create-config.ts",
|
|
46
|
+
"release": "semantic-release",
|
|
47
|
+
"lint": "eslint .",
|
|
48
|
+
"lint:fix": "eslint . --fix",
|
|
49
|
+
"format": "prettier --write .",
|
|
50
|
+
"format:check": "prettier --check .",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"check": "npm run typecheck && npm run lint && npm run format:check",
|
|
53
|
+
"prepare": "husky"
|
|
43
54
|
},
|
|
44
55
|
"dependencies": {
|
|
45
|
-
"@modelcontextprotocol/server": "^2.0.0-beta.
|
|
46
|
-
"
|
|
47
|
-
"zod": "^4.
|
|
56
|
+
"@modelcontextprotocol/server": "^2.0.0-beta.4",
|
|
57
|
+
"toml": "^5.0.0",
|
|
58
|
+
"zod": "^4.4.3"
|
|
48
59
|
},
|
|
49
60
|
"devDependencies": {
|
|
50
|
-
"@
|
|
51
|
-
"
|
|
52
|
-
"
|
|
61
|
+
"@eslint/js": "^10.0.1",
|
|
62
|
+
"@semantic-release/git": "^10.0.1",
|
|
63
|
+
"@semantic-release/npm": "^13.1.5",
|
|
64
|
+
"@types/node": "^22.20.0",
|
|
65
|
+
"eslint": "^10.7.0",
|
|
66
|
+
"husky": "^9.1.7",
|
|
67
|
+
"lint-staged": "^16.0.0",
|
|
68
|
+
"prettier": "^3.9.5",
|
|
69
|
+
"semantic-release": "^25.0.7",
|
|
70
|
+
"tsx": "^4.23.1",
|
|
71
|
+
"typescript": "^6.0.3",
|
|
72
|
+
"typescript-eslint": "^8.64.0"
|
|
73
|
+
},
|
|
74
|
+
"lint-staged": {
|
|
75
|
+
"*.{ts,tsx,js,cjs,mjs}": [
|
|
76
|
+
"eslint --fix",
|
|
77
|
+
"prettier --write"
|
|
78
|
+
],
|
|
79
|
+
"*.{json,md,yml,yaml}": [
|
|
80
|
+
"prettier --write"
|
|
81
|
+
]
|
|
53
82
|
}
|
|
54
83
|
}
|