@coldiq/mcp 5.4.4 → 5.4.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/dist/index.js +44 -36
- package/dist/index.js.map +1 -1
- package/dist/tool-json-schema.d.ts +8 -0
- package/dist/tool-json-schema.d.ts.map +1 -0
- package/dist/tool-json-schema.js +19 -0
- package/dist/tool-json-schema.js.map +1 -0
- package/dist/tools/call-endpoint.d.ts +7 -1
- package/dist/tools/call-endpoint.d.ts.map +1 -1
- package/dist/tools/call-endpoint.js +1 -1
- package/dist/tools/call-endpoint.js.map +1 -1
- package/dist/tools/enrich-company-bulk.d.ts +1 -9
- package/dist/tools/enrich-company-bulk.d.ts.map +1 -1
- package/dist/tools/enrich-company.d.ts +1 -1
- package/dist/tools/enrich-person-bulk.d.ts +1 -15
- package/dist/tools/enrich-person-bulk.d.ts.map +1 -1
- package/dist/tools/enrich-person.d.ts +1 -1
- package/dist/tools/extract-post-engagement.d.ts +5 -1
- package/dist/tools/extract-post-engagement.d.ts.map +1 -1
- package/dist/tools/fetch-page-content.d.ts +2 -2
- package/dist/tools/find-email.d.ts +1 -1
- package/dist/tools/find-emails-bulk.d.ts +1 -15
- package/dist/tools/find-emails-bulk.d.ts.map +1 -1
- package/dist/tools/find-emails.d.ts +2 -14
- package/dist/tools/find-emails.d.ts.map +1 -1
- package/dist/tools/find-influencers.d.ts +28 -8
- package/dist/tools/find-influencers.d.ts.map +1 -1
- package/dist/tools/find-influencers.js +1 -1
- package/dist/tools/find-influencers.js.map +1 -1
- package/dist/tools/find-people.d.ts +11 -8
- package/dist/tools/find-people.d.ts.map +1 -1
- package/dist/tools/find-phone-bulk.d.ts +1 -13
- package/dist/tools/find-phone-bulk.d.ts.map +1 -1
- package/dist/tools/find-phone.d.ts +1 -1
- package/dist/tools/find-signals.d.ts +16 -8
- package/dist/tools/find-signals.d.ts.map +1 -1
- package/dist/tools/get-endpoint-details.d.ts +7 -1
- package/dist/tools/get-endpoint-details.d.ts.map +1 -1
- package/dist/tools/get-place-reviews.d.ts +8 -3
- package/dist/tools/get-place-reviews.d.ts.map +1 -1
- package/dist/tools/get-website-visitors.d.ts +4 -1
- package/dist/tools/get-website-visitors.d.ts.map +1 -1
- package/dist/tools/search-ads.d.ts +14 -6
- package/dist/tools/search-ads.d.ts.map +1 -1
- package/dist/tools/search-companies.d.ts +15 -12
- package/dist/tools/search-companies.d.ts.map +1 -1
- package/dist/tools/search-jobs.d.ts +54 -22
- package/dist/tools/search-jobs.d.ts.map +1 -1
- package/dist/tools/search-knowledge.d.ts +6 -2
- package/dist/tools/search-knowledge.d.ts.map +1 -1
- package/dist/tools/search-knowledge.js +1 -1
- package/dist/tools/search-knowledge.js.map +1 -1
- package/dist/tools/search-places.d.ts +16 -8
- package/dist/tools/search-places.d.ts.map +1 -1
- package/dist/tools/search-reddit.d.ts +24 -5
- package/dist/tools/search-reddit.d.ts.map +1 -1
- package/dist/tools/search-seo.d.ts +33 -8
- package/dist/tools/search-seo.d.ts.map +1 -1
- package/dist/tools/search-web.d.ts +7 -2
- package/dist/tools/search-web.d.ts.map +1 -1
- package/dist/tools/verify-email.d.ts +1 -1
- package/dist/tools/verify-emails-bulk.d.ts +2 -2
- package/package.json +7 -5
- package/src/index.ts +51 -37
- package/src/tool-json-schema.ts +21 -0
- package/src/tools/call-endpoint.ts +1 -1
- package/src/tools/find-influencers.ts +1 -1
- package/src/tools/search-knowledge.ts +1 -1
- package/tests/gtm/harness.ts +2 -2
- package/tests/instructions.test.ts +3 -3
- package/tests/sdk-zod4-compatibility.test.ts +59 -0
- package/tests/tool-json-schema.test.ts +94 -0
package/src/index.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
3
|
+
import { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
4
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
5
|
+
import { z } from 'zod'
|
|
5
6
|
import { initClient } from './client.js'
|
|
6
7
|
import { MCP_SERVER_INSTRUCTIONS } from './instructions.js'
|
|
7
8
|
import { withKnowledgeFirstRequirement } from './knowledge-policy.js'
|
|
@@ -220,48 +221,61 @@ const server = new McpServer(
|
|
|
220
221
|
},
|
|
221
222
|
)
|
|
222
223
|
|
|
224
|
+
function registerPublicTool<Fields extends Record<string, z.ZodType>>(
|
|
225
|
+
name: string,
|
|
226
|
+
description: string,
|
|
227
|
+
schema: Fields,
|
|
228
|
+
handler: ToolCallback<z.ZodObject<Fields>>,
|
|
229
|
+
): void {
|
|
230
|
+
const inputSchema = z.strictObject(schema)
|
|
231
|
+
server.registerTool<never, typeof inputSchema>(name, {
|
|
232
|
+
description,
|
|
233
|
+
inputSchema,
|
|
234
|
+
}, handler)
|
|
235
|
+
}
|
|
236
|
+
|
|
223
237
|
// Register knowledge retrieval before every operational tool so clients that
|
|
224
238
|
// preserve registration order surface the mandatory prerequisite first.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
239
|
+
registerPublicTool(searchKnowledgeName, searchKnowledgeDescription, searchKnowledgeSchema, searchKnowledgeHandler)
|
|
240
|
+
registerPublicTool(searchCompaniesName, withKnowledgeFirstRequirement(searchCompaniesDescription), searchCompaniesSchema, searchCompaniesHandler)
|
|
241
|
+
registerPublicTool(findPeopleName, withKnowledgeFirstRequirement(findPeopleDescription), findPeopleSchema, findPeopleHandler)
|
|
242
|
+
registerPublicTool(findEmailName, withKnowledgeFirstRequirement(findEmailDescription), findEmailSchema, findEmailHandler)
|
|
243
|
+
registerPublicTool(enrichPersonName, withKnowledgeFirstRequirement(enrichPersonDescription), enrichPersonSchema, enrichPersonHandler)
|
|
244
|
+
registerPublicTool(findEmailsName, withKnowledgeFirstRequirement(findEmailsDescription), findEmailsSchema, findEmailsHandler)
|
|
245
|
+
registerPublicTool(verifyEmailName, withKnowledgeFirstRequirement(verifyEmailDescription), verifyEmailSchema, verifyEmailHandler)
|
|
246
|
+
registerPublicTool(verifyEmailsBulkName, withKnowledgeFirstRequirement(verifyEmailsBulkDescription), verifyEmailsBulkSchema, verifyEmailsBulkHandler)
|
|
247
|
+
registerPublicTool(findEmailsBulkName, withKnowledgeFirstRequirement(findEmailsBulkDescription), findEmailsBulkSchema, findEmailsBulkHandler)
|
|
248
|
+
registerPublicTool(enrichPersonBulkName, withKnowledgeFirstRequirement(enrichPersonBulkDescription), enrichPersonBulkSchema, enrichPersonBulkHandler)
|
|
249
|
+
registerPublicTool(enrichCompanyBulkName, withKnowledgeFirstRequirement(enrichCompanyBulkDescription), enrichCompanyBulkSchema, enrichCompanyBulkHandler)
|
|
250
|
+
registerPublicTool(findPhoneBulkName, withKnowledgeFirstRequirement(findPhoneBulkDescription), findPhoneBulkSchema, findPhoneBulkHandler)
|
|
251
|
+
registerPublicTool(getBulkJobName, withKnowledgeFirstRequirement(getBulkJobDescription), getBulkJobSchema, getBulkJobHandler)
|
|
252
|
+
registerPublicTool(cancelBulkJobName, withKnowledgeFirstRequirement(cancelBulkJobDescription), cancelBulkJobSchema, cancelBulkJobHandler)
|
|
253
|
+
registerPublicTool(findPhoneName, withKnowledgeFirstRequirement(findPhoneDescription), findPhoneSchema, findPhoneHandler)
|
|
254
|
+
registerPublicTool(enrichCompanyName, withKnowledgeFirstRequirement(enrichCompanyDescription), enrichCompanySchema, enrichCompanyHandler)
|
|
255
|
+
registerPublicTool(searchWebName, withKnowledgeFirstRequirement(searchWebDescription), searchWebSchema, searchWebHandler)
|
|
256
|
+
registerPublicTool(searchJobsName, withKnowledgeFirstRequirement(searchJobsDescription), searchJobsSchema, searchJobsHandler)
|
|
257
|
+
registerPublicTool(searchAdsName, withKnowledgeFirstRequirement(searchAdsDescription), searchAdsSchema, searchAdsHandler)
|
|
258
|
+
registerPublicTool(searchPlacesName, withKnowledgeFirstRequirement(searchPlacesDescription), searchPlacesSchema, searchPlacesHandler)
|
|
259
|
+
registerPublicTool(findInfluencersName, withKnowledgeFirstRequirement(findInfluencersDescription), findInfluencersSchema, findInfluencersHandler)
|
|
260
|
+
registerPublicTool(searchRedditName, withKnowledgeFirstRequirement(searchRedditDescription), searchRedditSchema, searchRedditHandler)
|
|
261
|
+
registerPublicTool(searchSeoName, withKnowledgeFirstRequirement(searchSeoDescription), searchSeoSchema, searchSeoHandler)
|
|
262
|
+
registerPublicTool(findSignalsName, withKnowledgeFirstRequirement(findSignalsDescription), findSignalsSchema, findSignalsHandler)
|
|
263
|
+
registerPublicTool(fetchPageContentName, withKnowledgeFirstRequirement(fetchPageContentDescription), fetchPageContentSchema, fetchPageContentHandler)
|
|
264
|
+
registerPublicTool(getCreditBalanceName, getCreditBalanceDescription, getCreditBalanceSchema, getCreditBalanceHandler)
|
|
265
|
+
registerPublicTool(getSessionUsageName, getSessionUsageDescription, getSessionUsageSchema, getSessionUsageHandler)
|
|
266
|
+
registerPublicTool(listDataSourcesName, withKnowledgeFirstRequirement(listDataSourcesDescription), listDataSourcesSchema, listDataSourcesHandler)
|
|
267
|
+
registerPublicTool(listSkillsName, listSkillsDescription, listSkillsSchema, listSkillsHandler)
|
|
268
|
+
registerPublicTool(loadSkillName, loadSkillDescription, loadSkillSchema, loadSkillHandler)
|
|
269
|
+
registerPublicTool(extractPostEngagementName, withKnowledgeFirstRequirement(extractPostEngagementDescription), extractPostEngagementSchema, extractPostEngagementHandler)
|
|
270
|
+
registerPublicTool(getPlaceReviewsName, withKnowledgeFirstRequirement(getPlaceReviewsDescription), getPlaceReviewsSchema, getPlaceReviewsHandler)
|
|
257
271
|
// ColdIQ Visitor ID: read the leads identified on the account's own websites.
|
|
258
272
|
// Listing the websites themselves needs no tool — search_endpoints/call_endpoint
|
|
259
273
|
// already reach GET /visitor-id/websites.
|
|
260
|
-
|
|
274
|
+
registerPublicTool(getWebsiteVisitorsName, withKnowledgeFirstRequirement(getWebsiteVisitorsDescription), getWebsiteVisitorsSchema, getWebsiteVisitorsHandler)
|
|
261
275
|
// Generic endpoint discovery + invoke: reach any integrated provider without a dedicated tool.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
276
|
+
registerPublicTool(searchEndpointsName, withKnowledgeFirstRequirement(searchEndpointsDescription), searchEndpointsSchema, searchEndpointsHandler)
|
|
277
|
+
registerPublicTool(getEndpointDetailsName, withKnowledgeFirstRequirement(getEndpointDetailsDescription), getEndpointDetailsSchema, getEndpointDetailsHandler)
|
|
278
|
+
registerPublicTool(callEndpointName, withKnowledgeFirstRequirement(callEndpointDescription), callEndpointSchema, callEndpointHandler)
|
|
265
279
|
|
|
266
280
|
// Connect via stdio transport
|
|
267
281
|
const transport = new StdioServerTransport()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
const ToolInputJsonSchema = z.object({
|
|
4
|
+
type: z.literal('object'),
|
|
5
|
+
properties: z.json().optional(),
|
|
6
|
+
}).passthrough()
|
|
7
|
+
|
|
8
|
+
/** Convert a tool input schema without widening Zod object contracts. */
|
|
9
|
+
export function toOpenApiInputSchema(schema: z.ZodObject) {
|
|
10
|
+
const generated = z.toJSONSchema(schema, {
|
|
11
|
+
target: 'openapi-3.0',
|
|
12
|
+
io: 'input',
|
|
13
|
+
override: ({ zodSchema, jsonSchema }) => {
|
|
14
|
+
if (zodSchema instanceof z.ZodObject && jsonSchema.additionalProperties === undefined) {
|
|
15
|
+
jsonSchema.additionalProperties = false
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
return ToolInputJsonSchema.parse(generated)
|
|
21
|
+
}
|
|
@@ -16,7 +16,7 @@ export const callEndpointSchema = {
|
|
|
16
16
|
method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).optional()
|
|
17
17
|
.describe('HTTP method from search_endpoints. Set this when one path supports more than one method.'),
|
|
18
18
|
params: z
|
|
19
|
-
.record(z.unknown())
|
|
19
|
+
.record(z.string(), z.unknown())
|
|
20
20
|
.optional()
|
|
21
21
|
.describe('Provider-native request params (request body fields, plus any path/query params), as shown in get_endpoint_details.'),
|
|
22
22
|
}
|
|
@@ -13,7 +13,7 @@ export const findInfluencersSchema = {
|
|
|
13
13
|
.describe('Target platform.'),
|
|
14
14
|
limit: z.number().int().min(1).max(100).default(25)
|
|
15
15
|
.describe('Max creators to return (1–100). Pricing is returned in the response credit headers.'),
|
|
16
|
-
page: z.number().int().min(1).
|
|
16
|
+
page: z.number().int().min(1).optional().meta({ default: 1 })
|
|
17
17
|
.describe('Page number for pagination.'),
|
|
18
18
|
sort_by: z.enum(['relevancy', 'number_of_followers', 'engagement_rate']).optional(),
|
|
19
19
|
sort_order: z.enum(['asc', 'desc']).optional(),
|
package/tests/gtm/harness.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk'
|
|
2
|
-
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
3
2
|
import * as fs from 'node:fs'
|
|
4
3
|
import * as path from 'node:path'
|
|
5
4
|
import * as url from 'node:url'
|
|
6
5
|
|
|
7
6
|
import { MODEL_PROFILES } from '../../../src/agent/model-policy.js'
|
|
7
|
+
import { toOpenApiInputSchema } from '../../src/tool-json-schema.js'
|
|
8
8
|
import { MCP_TOOLS } from './tools-bridge.js'
|
|
9
9
|
|
|
10
10
|
export interface ToolCall {
|
|
@@ -49,7 +49,7 @@ export async function runAgent(options: RunAgentOptions): Promise<AgentResult> {
|
|
|
49
49
|
const anthropicTools: Anthropic.Tool[] = MCP_TOOLS.map((t) => ({
|
|
50
50
|
name: t.name,
|
|
51
51
|
description: t.description,
|
|
52
|
-
input_schema:
|
|
52
|
+
input_schema: toOpenApiInputSchema(t.schema),
|
|
53
53
|
}))
|
|
54
54
|
|
|
55
55
|
const messages: Anthropic.MessageParam[] = [{ role: 'user', content: prompt }]
|
|
@@ -50,7 +50,7 @@ describe('MCP knowledge-first policy', () => {
|
|
|
50
50
|
const indexPath = fileURLToPath(new URL('../src/index.ts', import.meta.url))
|
|
51
51
|
const registrations = readFileSync(indexPath, 'utf8')
|
|
52
52
|
.split('\n')
|
|
53
|
-
.filter((line) => line.trimStart().startsWith('
|
|
53
|
+
.filter((line) => line.trimStart().startsWith('registerPublicTool('))
|
|
54
54
|
const unguardedCompatibilityTools = [
|
|
55
55
|
'getCreditBalanceName',
|
|
56
56
|
'getSessionUsageName',
|
|
@@ -58,9 +58,9 @@ describe('MCP knowledge-first policy', () => {
|
|
|
58
58
|
'loadSkillName',
|
|
59
59
|
]
|
|
60
60
|
|
|
61
|
-
expect(registrations[0]).toContain('
|
|
61
|
+
expect(registrations[0]).toContain('registerPublicTool(searchKnowledgeName, searchKnowledgeDescription')
|
|
62
62
|
for (const registration of registrations.slice(1)) {
|
|
63
|
-
if (unguardedCompatibilityTools.some((name) => registration.includes(`
|
|
63
|
+
if (unguardedCompatibilityTools.some((name) => registration.includes(`registerPublicTool(${name},`))) continue
|
|
64
64
|
expect(registration).toContain('withKnowledgeFirstRequirement(')
|
|
65
65
|
}
|
|
66
66
|
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
2
|
+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
4
|
+
import { afterEach, describe, expect, it } from 'vitest'
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
|
|
7
|
+
describe('MCP SDK Zod 4 compatibility', () => {
|
|
8
|
+
const closeCallbacks: Array<() => Promise<void>> = []
|
|
9
|
+
|
|
10
|
+
afterEach(async () => {
|
|
11
|
+
await Promise.all(closeCallbacks.splice(0).map((close) => close()))
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('publishes populated input properties and required fields over the wire', async () => {
|
|
15
|
+
const server = new McpServer({ name: 'schema-contract-server', version: '1.0.0' })
|
|
16
|
+
server.registerTool(
|
|
17
|
+
'schema_contract',
|
|
18
|
+
{
|
|
19
|
+
description: 'Verify Zod 4 tool schema conversion.',
|
|
20
|
+
inputSchema: z.strictObject({
|
|
21
|
+
platform: z.enum(['linkedin', 'instagram']),
|
|
22
|
+
limit: z.number().int().min(1).max(100).default(25),
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
async () => ({ content: [{ type: 'text', text: 'ok' }] }),
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
const client = new Client({ name: 'schema-contract-client', version: '1.0.0' })
|
|
29
|
+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair()
|
|
30
|
+
closeCallbacks.push(() => client.close(), () => server.close())
|
|
31
|
+
|
|
32
|
+
await server.connect(serverTransport)
|
|
33
|
+
await client.connect(clientTransport)
|
|
34
|
+
|
|
35
|
+
const result = await client.listTools()
|
|
36
|
+
expect(result.tools).toHaveLength(1)
|
|
37
|
+
expect(result.tools[0]?.inputSchema).toMatchObject({
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
platform: { type: 'string', enum: ['linkedin', 'instagram'] },
|
|
41
|
+
limit: { type: 'integer', minimum: 1, maximum: 100, default: 25 },
|
|
42
|
+
},
|
|
43
|
+
required: ['platform'],
|
|
44
|
+
additionalProperties: false,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const invalidCall = await client.callTool({
|
|
48
|
+
name: 'schema_contract',
|
|
49
|
+
arguments: { platform: 'linkedin', unexpected: true },
|
|
50
|
+
})
|
|
51
|
+
expect(invalidCall.isError).toBe(true)
|
|
52
|
+
expect(invalidCall.content).toEqual([
|
|
53
|
+
expect.objectContaining({
|
|
54
|
+
type: 'text',
|
|
55
|
+
text: expect.stringContaining('Unrecognized key: "unexpected"'),
|
|
56
|
+
}),
|
|
57
|
+
])
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readdirSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
import { toOpenApiInputSchema } from '../src/tool-json-schema.js'
|
|
6
|
+
import { MCP_TOOLS } from './gtm/tools-bridge.js'
|
|
7
|
+
|
|
8
|
+
const PublicToolSchema = z.record(z.string(), z.instanceof(z.ZodType))
|
|
9
|
+
|
|
10
|
+
describe('public MCP tool JSON schemas', () => {
|
|
11
|
+
it('converts every registered public tool schema', async () => {
|
|
12
|
+
const toolFiles = readdirSync(new URL('../src/tools/', import.meta.url))
|
|
13
|
+
.filter((file) => file.endsWith('.ts'))
|
|
14
|
+
.sort()
|
|
15
|
+
const indexSource = readFileSync(new URL('../src/index.ts', import.meta.url), 'utf8')
|
|
16
|
+
|
|
17
|
+
expect(indexSource.match(/registerPublicTool\(/g)).toHaveLength(toolFiles.length)
|
|
18
|
+
expect(indexSource).not.toContain('server.tool(')
|
|
19
|
+
|
|
20
|
+
for (const file of toolFiles) {
|
|
21
|
+
const exports = await import(new URL(`../src/tools/${file}`, import.meta.url).href)
|
|
22
|
+
const schemaEntries = Object.entries(exports).filter(([name]) => name.endsWith('Schema'))
|
|
23
|
+
const nameEntries = Object.entries(exports).filter(([name]) => name.endsWith('Name'))
|
|
24
|
+
|
|
25
|
+
expect(schemaEntries, file).toHaveLength(1)
|
|
26
|
+
expect(nameEntries, file).toHaveLength(1)
|
|
27
|
+
|
|
28
|
+
const schema = toOpenApiInputSchema(z.object(PublicToolSchema.parse(schemaEntries[0][1])))
|
|
29
|
+
expect(schema, z.string().parse(nameEntries[0][1])).toMatchObject({
|
|
30
|
+
type: 'object',
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
properties: expect.any(Object),
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('converts every tool schema without widening object inputs', () => {
|
|
38
|
+
for (const tool of MCP_TOOLS) {
|
|
39
|
+
const schema = toOpenApiInputSchema(tool.schema)
|
|
40
|
+
|
|
41
|
+
expect(schema, tool.name).toMatchObject({
|
|
42
|
+
type: 'object',
|
|
43
|
+
additionalProperties: false,
|
|
44
|
+
properties: expect.any(Object),
|
|
45
|
+
})
|
|
46
|
+
expect(schema, tool.name).not.toHaveProperty('$schema')
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('closes only Zod objects and does not change record or default data', () => {
|
|
51
|
+
const schema = toOpenApiInputSchema(z.object({
|
|
52
|
+
nested: z.object({ value: z.string() }),
|
|
53
|
+
labels: z.record(z.string(), z.string()),
|
|
54
|
+
settings: z.object({ type: z.literal('object') }).default({ type: 'object' }),
|
|
55
|
+
}))
|
|
56
|
+
|
|
57
|
+
expect(schema).toMatchObject({
|
|
58
|
+
properties: {
|
|
59
|
+
nested: { additionalProperties: false },
|
|
60
|
+
labels: { additionalProperties: { type: 'string' } },
|
|
61
|
+
settings: {
|
|
62
|
+
additionalProperties: false,
|
|
63
|
+
default: { type: 'object' },
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('preserves required fields, defaults, limits, and optional defaults', () => {
|
|
70
|
+
const tool = MCP_TOOLS.find((candidate) => candidate.name === 'find_influencers')
|
|
71
|
+
expect(tool).toBeDefined()
|
|
72
|
+
if (!tool) throw new Error('find_influencers is not registered')
|
|
73
|
+
|
|
74
|
+
const schema = toOpenApiInputSchema(tool.schema)
|
|
75
|
+
|
|
76
|
+
expect(schema.required).toEqual(['platform'])
|
|
77
|
+
expect(schema).toMatchObject({
|
|
78
|
+
properties: {
|
|
79
|
+
limit: {
|
|
80
|
+
type: 'integer',
|
|
81
|
+
minimum: 1,
|
|
82
|
+
maximum: 100,
|
|
83
|
+
default: 25,
|
|
84
|
+
},
|
|
85
|
+
page: {
|
|
86
|
+
type: 'integer',
|
|
87
|
+
minimum: 1,
|
|
88
|
+
default: 1,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
expect(schema.properties?.page).not.toHaveProperty('nullable')
|
|
93
|
+
})
|
|
94
|
+
})
|