@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 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.2.0",
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": "6a9b3891741143671b096d53c0d9e69f6b62cb94"
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.string().url(),
219
- apiKey: z.string().min(1).optional()
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.string().min(1),
390
- apiKey: z.string().min(1).optional(),
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',
@@ -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