[BUG] A tool argument named `device` never reaches a local stdio MCP server (Claude Desktop)
Summary
In Claude Desktop, any MCP tool whose input schema declares an argument nameddevice receives the call without that field. The tool then fails with
whatever "missing argument" error it raises. Tools that take no device
work normally, and the very same server answers correctly when the same
JSON-RPC request is written to it directly.
Renaming the argument to device_id, changing nothing else, fixes it.
I could not find an existing issue for this. The closest reports through the
same bridge (sbroenne/mcp-server-excel#850 with session_id, and #4188 where
all arguments were dropped) have different shapes, so I am filing it
separately with an isolated reproduction.
Environment
- Claude Desktop on macOS (Apple Silicon), September 2026 build
- Local stdio MCP server registered in
claude_desktop_config.json - Node.js v26,
@modelcontextprotocol/sdkserver, stdio transport - Reproduced on two different machines and two different accounts
Minimal reproduction
server.mjs — two identical tools whose only difference is the name of their
single argument. Each echoes back exactly what it received:
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
const tools = [
{
name: 'echo_device',
description: 'Echoes the arguments it receives. Takes a required `device`.',
inputSchema: {
type: 'object',
properties: { device: { type: 'string', description: 'Any string' } },
required: ['device'],
},
},
{
name: 'echo_device_id',
description: 'Echoes the arguments it receives. Takes a required `device_id`.',
inputSchema: {
type: 'object',
properties: { device_id: { type: 'string', description: 'Any string' } },
required: ['device_id'],
},
},
];
const server = new Server({ name: 'arg-repro', version: '1.0.0' }, { capabilities: { tools: {} } });
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools }));
server.setRequestHandler(CallToolRequestSchema, async (request) => ({
content: [{ type: 'text', text: `received arguments: ${JSON.stringify(request.params.arguments ?? null)}` }],
}));
await server.connect(new StdioServerTransport());
Register it:
{ "mcpServers": { "arg-repro": { "command": "node", "args": ["/absolute/path/to/server.mjs"] } } }
Then, in a new conversation, ask for both calls:
Callecho_devicewith device "abc", then callecho_device_idwith device_id "abc".
Observed: echo_device reports received arguments: {} (the field is
absent), echo_device_id reports received arguments: {"device_id":"abc"}.
Expected: both echo the value they were given.
Control: the same server over plain JSON-RPC
{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"echo_device","arguments":{"device":"abc"}}}
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"echo_device_id","arguments":{"device_id":"abc"}}}
id 2 -> received arguments: {"device":"abc"}
id 3 -> received arguments: {"device_id":"abc"}
Both fields arrive. The server is not the variable.
How it showed up in practice
A CLI that exposes ~84 MCP tools, 27 of which took a device argument. Every
one of those failed with device is required; thinr_devices,thinr_monitoring in fleet mode and thinr_playbook_run (which names its
target target_devices) all worked. The assistant reported passing the
argument, and the server logged the call arriving without it.
Sequence on one machine, one binary, one account:
- Direct JSON-RPC call with
device→ works. - Same call through Claude Desktop →
device is required. - Rename the argument to
device_idin the schema, restart the app, new
conversation → works, nothing else changed.
Note for anyone reproducing: refreshing the tool list inside an open
conversation is not enough. The tool definitions already in that conversation
stay as they were, and the assistant keeps describing the old schema. It takes
quitting the app and starting a new conversation.
Hypothesis
The observable fact is that the field does not arrive. The likely cause is a
name collision: the remote-devices bridge appears to use device as its own
routing key for the computer_* tools and consumes it, so it is removed from
the payload before the request is forwarded to the MCP server.
If that is right, the same would apply to every other name the bridge reserves,
and server authors have no way to know which those are.
Impact
Any MCP server that names an argument device is silently broken in Claude
Desktop, with an error that points at the server rather than at the transport.
It cost us a day of debugging on the server side before we thought to blame the
argument's name.
Suggestions
- Namespace the bridge's own parameters so they cannot collide with a server's
(for example an internal prefix), or strip them after routing rather than
before forwarding.
- Failing that, document the reserved names so authors can avoid them.
- When the bridge removes a field a tool declares as required, surface that in
the error instead of letting the server report a missing argument.
Workaround
Rename the argument. We moved ours to device_id and it works.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗