JuliusBrussee/caveman · error · TypeError
Provide the native server identity and negotiated MCP…
Error message
Provide the native server identity and negotiated MCP protocol version
What it means
The MCP host adapter constructor in packages/middleware/typescript/src/mcp.ts validates that the caller supplies both a native server identifier and the negotiated MCP protocol version before building its adapter descriptor and recovery binding. These fields are required because the serialization revision is derived from the protocol version and the scope binding is tied to the server identity. Passing an empty/undefined serverId or protocolVersion throws this TypeError immediately at construction time.
Solutions
- Pass the negotiated protocol version from your MCP Server handshake (e.g. `new McpServer(...)` result or the version your server declares, like '2024-11-05').
- Set `serverId` to a stable, non-empty identifier for your MCP server process.
- Read these values from validated config (schema-check env/config before constructing the host).
Example fix
// before
const host = new MCPHost({ runtime, scope, runtime_mode: 'compress' } as any);
// after
const host = new MCPHost({ runtime, scope, serverId: 'my-mcp-server', protocolVersion: '2024-11-05' }); Defensive patterns
Strategy: validation
Validate before calling
function assertMcpOptions(o: { serverId?: string; protocolVersion?: string }): asserts o is { serverId: string; protocolVersion: string } {
if (!o.serverId) throw new Error('serverId is required');
if (!o.protocolVersion) throw new Error('protocolVersion is required');
} Type guard
const hasIdentity = (o: unknown): o is { serverId: string; protocolVersion: string } =>
typeof o === 'object' && o !== null && typeof (o as any).serverId === 'string' && (o as any).serverId.length > 0 && typeof (o as any).protocolVersion === 'string' && (o as any).protocolVersion.length > 0; Try / catch
try {
const host = new MCPHost(options);
} catch (e) {
if (e instanceof TypeError && /server identity/.test(e.message)) {
throw new Error(`MCP host misconfigured: set serverId and protocolVersion (${e.message})`);
}
throw e;
} Prevention
- Derive protocolVersion from the completed MCP handshake, never from a hardcoded or optional config value.
- Validate MCP host options with a schema (zod/valibot) at startup.
- Fail fast in CI with a config smoke test that constructs the host.
When it happens
Trigger: Calling `new MCPHost(options)` (or the library's factory that wraps it) with `options.serverId` undefined/empty, or `options.protocolVersion` undefined/empty.
Common situations: Building the options object from parsed environment config where MCP_SERVER_ID is unset; wiring the adapter before the MCP handshake has produced a negotiated protocol version; TypeScript users bypassing types with `as any` or JSON-parsed config.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- apiKey, baseURL, and agent are required
- cave_tool_sandbox_entry_required
- Recovery requires its scoped handle
- timeoutMs must be a positive integer
- MCP changes require the ownership transaction
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/3bab1c49b007d9d3.
Report an issue: GitHub.
Appendix: source
Thrown at packages/middleware/typescript/src/mcp.ts:29
}
/** Preserve the existing client's transport, IDs, auth and result validation. */
export function bindMCPTool(client: Client, tool: Tool): MCPToolBinding {
return { tool, execute: (arguments_, options) => client.callTool({ name: tool.name, arguments: arguments_ }, undefined, options) as Promise<CallToolResult> };
}
export interface MCPHostOptions { runtime: MiddlewareRuntime; scope: Scope; serverId: string; protocolVersion: string }
/** Native host views only. No MCP framing, server or inference loop is added. */
export class CavemanMCPHost {
readonly recovery: MCPToolBinding;
private readonly binding;
private readonly adapter;
private readonly recoveryExecutor;
private readonly recoveryDefinition;
private readonly versionSupported;
constructor(private readonly options: MCPHostOptions) {
if (!options.serverId || !options.protocolVersion) throw new TypeError('Provide the native server identity and negotiated MCP protocol version');
this.versionSupported = adapterCompatible('mcp');
if (!this.versionSupported && options.runtime.mode !== 'off') options.runtime.decline('unsupported_version');
this.binding = options.runtime.recovery(options.scope);
this.adapter = { id: 'mcp', version: '0.1.0', framework_version: frameworkVersion('@modelcontextprotocol/sdk') ?? 'unknown', serialization_revision: `mcp-native-${options.protocolVersion}-v1` };
this.recovery = {
tool: { name: this.binding.name, description: this.binding.description, inputSchema: structuredClone(this.binding.inputSchema) as Tool['inputSchema'] },
execute: async (arguments_, options) => {
if (typeof arguments_.handle !== 'string') throw new TypeError('Recovery requires its scoped handle');
const page = await this.binding.execute({ ...arguments_, handle: arguments_.handle }, options?.signal ? { signal: options.signal } : undefined);
return { content: [{ type: 'text', text: JSON.stringify(page) }] };
},
};
this.recoveryExecutor = this.recovery.execute;
this.recoveryDefinition = JSON.stringify(this.recovery.tool);
}
register(tools: readonly MCPToolBinding[]): MCPToolBinding[] {
return !this.versionSupported || this.options.runtime.mode !== 'compress' || tools.some(item => item.tool.name === this.recovery.tool.name) ? [...tools] : [...tools, this.recovery];View on GitHub (pinned to 3ee70a1026)