@microlink/mcp 2.2.0 → 2.3.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/package.json +2 -2
- package/src/output-schemas.js +108 -0
- package/src/schemas.js +26 -4
- package/src/tools/register.js +9 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@microlink/mcp",
|
|
3
3
|
"description": "MCP server for Microlink API",
|
|
4
4
|
"homepage": "https://github.com/microlinkhq/microlink",
|
|
5
|
-
"version": "2.
|
|
5
|
+
"version": "2.3.0",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js"
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"access": "public"
|
|
67
67
|
},
|
|
68
68
|
"type": "module",
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "9e479486674289cd4f5257b4c1fd5ad527f6ebb1"
|
|
70
70
|
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
// Output schemas for every tool's `structuredContent.data` value.
|
|
4
|
+
// They mirror the TypeScript definitions that ship with the library
|
|
5
|
+
// (packages/core/src/index.d.ts, packages/search/src/index.d.ts), which are
|
|
6
|
+
// the canonical contract for response shapes. Index signatures map to
|
|
7
|
+
// `.catchall(z.unknown())` so forward-compatible API fields always validate.
|
|
8
|
+
|
|
9
|
+
// `Asset` (packages/core/src/index.d.ts).
|
|
10
|
+
const assetSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
url: z.string(),
|
|
13
|
+
type: z.string().optional(),
|
|
14
|
+
size: z.number().optional(),
|
|
15
|
+
size_pretty: z.string().optional(),
|
|
16
|
+
width: z.number().optional(),
|
|
17
|
+
height: z.number().optional()
|
|
18
|
+
})
|
|
19
|
+
.catchall(z.unknown())
|
|
20
|
+
|
|
21
|
+
// logo/video/audio return `Asset | null`: the primary media field is null
|
|
22
|
+
// when the page has no detectable asset.
|
|
23
|
+
const nullableAssetSchema = assetSchema.nullable()
|
|
24
|
+
|
|
25
|
+
// `Metadata` (packages/core/src/index.d.ts).
|
|
26
|
+
const metadataSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
title: z.string().nullable().optional(),
|
|
29
|
+
description: z.string().nullable().optional(),
|
|
30
|
+
url: z.string().nullable().optional()
|
|
31
|
+
})
|
|
32
|
+
.catchall(z.unknown())
|
|
33
|
+
|
|
34
|
+
// `Embed` (packages/core/src/index.d.ts). `embed()` returns `data.iframe`
|
|
35
|
+
// verbatim; a successful response omits it when oEmbed discovery fails,
|
|
36
|
+
// and asToolResult stores that as null.
|
|
37
|
+
const embedSchema = z
|
|
38
|
+
.object({
|
|
39
|
+
html: z.string(),
|
|
40
|
+
scripts: z.array(z.unknown()).optional()
|
|
41
|
+
})
|
|
42
|
+
.catchall(z.unknown())
|
|
43
|
+
.nullable()
|
|
44
|
+
|
|
45
|
+
// `FunctionResult<unknown>` (packages/core/src/index.d.ts); profiling and
|
|
46
|
+
// logging are optional because the API may omit them.
|
|
47
|
+
const functionResultSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
isFulfilled: z.boolean(),
|
|
50
|
+
value: z.unknown(),
|
|
51
|
+
profiling: z.record(z.string(), z.unknown()).optional(),
|
|
52
|
+
logging: z.record(z.string(), z.unknown()).optional()
|
|
53
|
+
})
|
|
54
|
+
.catchall(z.unknown())
|
|
55
|
+
|
|
56
|
+
// Search pages (packages/search/src/index.d.ts) projected to JSON: the lazy
|
|
57
|
+
// helpers (`html()`, `markdown()`, `next()`) are functions and never reach
|
|
58
|
+
// the wire. One tolerant shape covers every vertical, including autocomplete
|
|
59
|
+
// (`{ value }` results).
|
|
60
|
+
const searchResultSchema = z
|
|
61
|
+
.object({
|
|
62
|
+
title: z.string().optional(),
|
|
63
|
+
url: z.string().optional(),
|
|
64
|
+
description: z.string().optional(),
|
|
65
|
+
value: z.string().optional()
|
|
66
|
+
})
|
|
67
|
+
.catchall(z.unknown())
|
|
68
|
+
|
|
69
|
+
const searchPageSchema = z
|
|
70
|
+
.object({
|
|
71
|
+
results: z.array(searchResultSchema),
|
|
72
|
+
knowledgeGraph: z.record(z.string(), z.unknown()).optional(),
|
|
73
|
+
peopleAlsoAsk: z.array(z.record(z.string(), z.unknown())).optional(),
|
|
74
|
+
relatedSearches: z.array(z.record(z.string(), z.unknown())).optional()
|
|
75
|
+
})
|
|
76
|
+
.catchall(z.unknown())
|
|
77
|
+
|
|
78
|
+
// Content rules return null when the selector matches nothing
|
|
79
|
+
// (verified against the live API: data.markdown/text are null on no match).
|
|
80
|
+
const stringSchema = z.string().nullable()
|
|
81
|
+
const stringArraySchema = z.array(z.string())
|
|
82
|
+
const recordSchema = z.record(z.string(), z.unknown())
|
|
83
|
+
const unknownArraySchema = z.array(z.unknown())
|
|
84
|
+
// Default Lighthouse JSON is an object; `output: 'html' | 'csv'` returns a string.
|
|
85
|
+
const lighthouseSchema = z.union([z.string(), recordSchema])
|
|
86
|
+
|
|
87
|
+
export const outputSchemas = {
|
|
88
|
+
metadata: metadataSchema,
|
|
89
|
+
logo: nullableAssetSchema,
|
|
90
|
+
markdown: stringSchema,
|
|
91
|
+
html: stringSchema,
|
|
92
|
+
text: stringSchema,
|
|
93
|
+
screenshot: assetSchema,
|
|
94
|
+
pdf: assetSchema,
|
|
95
|
+
embed: embedSchema,
|
|
96
|
+
video: nullableAssetSchema,
|
|
97
|
+
audio: nullableAssetSchema,
|
|
98
|
+
links: stringArraySchema,
|
|
99
|
+
images: stringArraySchema,
|
|
100
|
+
videos: stringArraySchema,
|
|
101
|
+
audios: stringArraySchema,
|
|
102
|
+
emails: stringArraySchema,
|
|
103
|
+
technologies: unknownArraySchema,
|
|
104
|
+
lighthouse: lighthouseSchema,
|
|
105
|
+
search: searchPageSchema,
|
|
106
|
+
function: functionResultSchema,
|
|
107
|
+
extract: recordSchema
|
|
108
|
+
}
|
package/src/schemas.js
CHANGED
|
@@ -215,8 +215,19 @@ export const metaConfigSchema = objectLikeSchema(
|
|
|
215
215
|
)
|
|
216
216
|
|
|
217
217
|
const baseSchema = z.object({
|
|
218
|
-
url: z
|
|
219
|
-
|
|
218
|
+
url: z
|
|
219
|
+
.string()
|
|
220
|
+
.url()
|
|
221
|
+
.describe(
|
|
222
|
+
'Public URL of the page to process. Include the protocol, for example https://example.com.'
|
|
223
|
+
),
|
|
224
|
+
apiKey: z
|
|
225
|
+
.string()
|
|
226
|
+
.min(1)
|
|
227
|
+
.optional()
|
|
228
|
+
.describe(
|
|
229
|
+
'Microlink PRO API key. Omit it unless you have one: requests then use the MICROLINK_API_KEY environment variable or the free endpoint.'
|
|
230
|
+
)
|
|
220
231
|
})
|
|
221
232
|
|
|
222
233
|
const fullShape = {
|
|
@@ -386,8 +397,19 @@ export const lighthouseInputSchema = baseSchema
|
|
|
386
397
|
|
|
387
398
|
export const searchInputSchema = z
|
|
388
399
|
.object({
|
|
389
|
-
query: z
|
|
390
|
-
|
|
400
|
+
query: z
|
|
401
|
+
.string()
|
|
402
|
+
.min(1)
|
|
403
|
+
.describe(
|
|
404
|
+
'Google search query. Operators like site:, filetype: or quotes work as-is.'
|
|
405
|
+
),
|
|
406
|
+
apiKey: z
|
|
407
|
+
.string()
|
|
408
|
+
.min(1)
|
|
409
|
+
.optional()
|
|
410
|
+
.describe(
|
|
411
|
+
'Microlink API key. Required for this tool: Google search runs on the PRO endpoint.'
|
|
412
|
+
),
|
|
391
413
|
type: z
|
|
392
414
|
.enum([
|
|
393
415
|
'search',
|
package/src/tools/register.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
client,
|
|
5
5
|
resolveApiKey
|
|
6
6
|
} from '../microlink-client.js'
|
|
7
|
+
import { outputSchemas } from '../output-schemas.js'
|
|
7
8
|
|
|
8
9
|
function getHeaderValueCaseInsensitive (headers, headerName) {
|
|
9
10
|
if (!headers || typeof headers !== 'object') {
|
|
@@ -79,9 +80,16 @@ export function register (
|
|
|
79
80
|
invoke,
|
|
80
81
|
annotations = READ_ONLY_ANNOTATIONS
|
|
81
82
|
) {
|
|
83
|
+
// Every tool wraps its result as `structuredContent.data`; the output
|
|
84
|
+
// schema describes that `data` value (see output-schemas.js). Error
|
|
85
|
+
// results are exempt: the SDK skips output validation when `isError`.
|
|
86
|
+
const key = name.replace(/^microlink_/, '')
|
|
87
|
+
const dataSchema = outputSchemas[key]
|
|
88
|
+
const outputSchema = dataSchema ? { data: dataSchema } : undefined
|
|
89
|
+
|
|
82
90
|
server.registerTool(
|
|
83
91
|
name,
|
|
84
|
-
{ description, inputSchema, annotations },
|
|
92
|
+
{ description, inputSchema, outputSchema, annotations },
|
|
85
93
|
async (args, extra) => {
|
|
86
94
|
const parsed = inputSchema.safeParse(args)
|
|
87
95
|
|