Skip to content

prompts/get rejects omitted arguments when all argsSchema fields are optional; tools fix (#1404) not in 1.x #1869

@dgon-jd

Description

@dgon-jd

Describe the bug

The same "missing arguments rejected when all schema fields are optional" pattern fixed for tools in #1404 still affects prompts, and #1404 itself is not in any 1.x release.

Two distinct gaps:

  1. Prompts handler is unfixed (all versions, including main). prompts/get calls safeParseAsync(argsObj, request.params.arguments) without the ?? {} defensive pattern that fix(server): handle undefined arguments for tools with all optional params #1404 introduced for tools. A prompt registered with argsSchema: { context: z.string().optional() } and called with arguments omitted returns MCP error -32602: Invalid arguments for prompt <name>: [{ "code": "invalid_type", "expected": "object", "received": "undefined" }].

  2. Tools fix (fix(server): handle undefined arguments for tools with all optional params #1404) is not in the 1.x line. The fix landed on main on 2026-01-23 but none of the v1.x backport releases (1.27.0, 1.27.1, 1.28.0, 1.29.0 — all tagged as "[v1.x backport]") include it. Users on @modelcontextprotocol/sdk@^1.27.0 still hit the tools-side bug.

To Reproduce

Server (using 1.27.0 or any 1.x):

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const server = new McpServer({ name: 'test', version: '1.0.0' }, {
  capabilities: { prompts: {}, tools: {} },
});

server.registerPrompt(
  'echo',
  {
    title: 'Echo',
    description: 'Echoes optional context',
    argsSchema: { context: z.string().optional() },
  },
  async (args) => ({
    messages: [{
      role: 'user',
      content: { type: 'text', text: `context: ${args.context ?? 'none'}` },
    }],
  }),
);

Client — any of these fail in the same way:

// Fails: arguments omitted
await client.getPrompt({ name: 'echo' });

// Fails: arguments explicitly undefined
await client.getPrompt({ name: 'echo', arguments: undefined });

Client workaround that succeeds:

await client.getPrompt({ name: 'echo', arguments: {} });

Expected behavior

Per the MCP spec, arguments on prompts/get is OPTIONAL at the request level. A registered prompt where every declared argument is optional should accept a request that omits arguments entirely. The same applies to tools/call (addressed upstream by #1404 on main).

Actual behavior

safeParseAsync(argsObj, undefined) fails because argsObj is the non-optional z.object(...) returned by objectFromShape(), which rejects undefined at the top level. The error propagates to the client as JSON-RPC -32602 "Invalid params".

Root cause is _createRegisteredPrompt in src/server/mcp.ts (around line 567):

argsSchema: argsSchema === undefined ? undefined : objectFromShape(argsSchema),

Wrapped once, the top-level object is never optional, and there's no ?? {} coercion at the parse site (unlike tools, where #1404 added it).

Proposed fix

Mirror #1404 in the prompts handler:

- const parseResult = await safeParseAsync(argsObj, request.params.arguments);
+ const parseResult = await safeParseAsync(argsObj, request.params.arguments ?? {});

Plus backport both the tools fix (#1404) and this prompts fix to the v1.x branch so users on @modelcontextprotocol/sdk@^1.x get consistent behavior.

Affected versions

Workaround we're using

Transport-layer normalization that sets req.body.params.arguments ??= {} before the request reaches the SDK, for both prompts/get and tools/call. Works across all 1.x versions but is obviously a wart we'd like to drop.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions