diff --git a/package-lock.json b/package-lock.json index b53a7bf..6fae115 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@linkedapi/mcp", - "version": "2.3.18", + "version": "2.3.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@linkedapi/mcp", - "version": "2.3.18", + "version": "2.3.19", "license": "MIT", "dependencies": { "@linkedapi/node": "^2.3.19", diff --git a/package.json b/package.json index 1a3f313..70540aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@linkedapi/mcp", - "version": "2.3.18", + "version": "2.3.19", "description": "MCP server that lets AI assistants control LinkedIn accounts and retrieve real-time data.", "main": "dist/index.js", "bin": { diff --git a/src/tools/accept-invitation.ts b/src/tools/accept-invitation.ts index 20f36bc..a591fb2 100644 --- a/src/tools/accept-invitation.ts +++ b/src/tools/accept-invitation.ts @@ -59,32 +59,6 @@ export class AcceptInvitationTool extends OperationTool { .optional(), reactionsRetrievalConfig: z .object({ - limit: z.number().min(1).max(1000).optional(), + limit: z.number().min(1).max(500).optional(), }) .optional(), }); @@ -84,7 +84,7 @@ export class FetchPostTool extends OperationTool { limit: { type: 'number', description: - 'Optional. Max number of reactions to retrieve. Defaults to 10, with a maximum value of 1000.', + 'Optional. Max number of reactions to retrieve. Defaults to 10, with a maximum value of 500.', }, }, }, diff --git a/src/tools/ignore-invitation.ts b/src/tools/ignore-invitation.ts index c38a31a..93d7b89 100644 --- a/src/tools/ignore-invitation.ts +++ b/src/tools/ignore-invitation.ts @@ -59,32 +59,6 @@ export class IgnoreInvitationTool extends OperationTool 'Optional. URL copied from a LinkedIn content search page after configuring filters. When specified, filter is ignored entirely.', }, }, - anyOf: [{ required: ['term'] }, { required: ['customSearchUrl'] }], } as Tool['inputSchema'], }; } diff --git a/src/utils/admin-tool.ts b/src/utils/admin-tool.ts index f3bf34e..711b81d 100644 --- a/src/utils/admin-tool.ts +++ b/src/utils/admin-tool.ts @@ -2,6 +2,8 @@ import { LinkedApiAdmin } from '@linkedapi/node'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import z from 'zod'; +import { unwrapValueEnvelope } from './unwrap-value-envelope.js'; + export abstract class AdminTool { public abstract readonly name: string; protected abstract readonly schema: z.ZodSchema; @@ -9,7 +11,7 @@ export abstract class AdminTool { public abstract getTool(): Tool; public validate(args: unknown): TParams { - return this.schema.parse(args) as TParams; + return this.schema.parse(unwrapValueEnvelope(args, this.getTool().inputSchema)) as TParams; } public abstract execute({ diff --git a/src/utils/linked-api-tool.ts b/src/utils/linked-api-tool.ts index 5e88297..5b41724 100644 --- a/src/utils/linked-api-tool.ts +++ b/src/utils/linked-api-tool.ts @@ -4,6 +4,8 @@ import z from 'zod'; import type { TLinkedApiToolResult, TWorkflowAck } from '../types/linked-api-tool-result.type.js'; +import { unwrapValueEnvelope } from './unwrap-value-envelope.js'; + interface TLinkedApiToolExecuteOptions { linkedapi: LinkedApi; args: TParams; @@ -18,7 +20,7 @@ export abstract class LinkedApiTool { public abstract getTool(): Tool; public validate(args: unknown): TParams { - return this.schema.parse(args) as TParams; + return this.schema.parse(unwrapValueEnvelope(args, this.getTool().inputSchema)) as TParams; } public abstract execute( diff --git a/src/utils/public-tool.ts b/src/utils/public-tool.ts index 5075253..43d27ac 100644 --- a/src/utils/public-tool.ts +++ b/src/utils/public-tool.ts @@ -1,6 +1,8 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js'; import z from 'zod'; +import { unwrapValueEnvelope } from './unwrap-value-envelope.js'; + // Tools backed by public Linked API endpoints: they get no client and run without any token. export abstract class PublicTool { public abstract readonly name: string; @@ -9,7 +11,7 @@ export abstract class PublicTool { public abstract getTool(): Tool; public validate(args: unknown): TParams { - return this.schema.parse(args) as TParams; + return this.schema.parse(unwrapValueEnvelope(args, this.getTool().inputSchema)) as TParams; } public abstract execute({ args }: { args: TParams }): Promise; diff --git a/src/utils/unwrap-value-envelope.ts b/src/utils/unwrap-value-envelope.ts new file mode 100644 index 0000000..8257189 --- /dev/null +++ b/src/utils/unwrap-value-envelope.ts @@ -0,0 +1,22 @@ +import { Tool } from '@modelcontextprotocol/sdk/types.js'; + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +// ChatGPT cannot take a tool whose inputSchema has anyOf/oneOf at its root, and calls such a tool with +// its arguments wrapped as { value: {...} }. No root combinators are listed any more, but a client +// can keep calling with a tool list it fetched earlier, so a lone `value` object is still unwrapped +// for every tool that does not declare a parameter of that name. +export function unwrapValueEnvelope(args: unknown, inputSchema: Tool['inputSchema']): unknown { + if (!isRecord(args) || inputSchema.properties?.['value'] !== undefined) { + return args; + } + + const { value, ...rest } = args; + if (Object.keys(rest).length > 0 || !isRecord(value)) { + return args; + } + + return value; +}