Skip to content

Exception rendering #143

@bilfeldt

Description

@bilfeldt

Laravel Package Version

0.5.3

Laravel Version

12.49.0

PHP Version

8.4

Database Driver & Version

No response

Description

Current Behavior

Currently, any exceptions thrown is either caught by this package or bobbles up to Laravel\Mcp\Server where they are converted to a proper response. See

mcp/src/Server.php

Lines 204 to 220 in 592140d

} catch (Throwable $e) {
report($e);
$config = Container::getInstance()->make('config');
if ($config->get('app.debug', false)) {
throw $e;
}
$jsonRpcResponse = JsonRpcResponse::error(
$request->id ?? null,
-32603,
'Something went wrong while processing the request.',
);
$this->transport->send($jsonRpcResponse->toJson());
}

Currently only ValidationException's are caught and converted to well structured Tool Execution Errors while any other exception is converted to a generic protocol error:

try {
// @phpstan-ignore-next-line
$response = Container::getInstance()->call([$tool, 'handle']);
} catch (ValidationException $validationException) {
$response = Response::error(ValidationMessages::from($validationException));
}

mcp/src/Server.php

Lines 202 to 220 in 592140d

} catch (JsonRpcException $e) {
$this->transport->send($e->toJsonRpcResponse()->toJson());
} catch (Throwable $e) {
report($e);
$config = Container::getInstance()->make('config');
if ($config->get('app.debug', false)) {
throw $e;
}
$jsonRpcResponse = JsonRpcResponse::error(
$request->id ?? null,
-32603,
'Something went wrong while processing the request.',
);
$this->transport->send($jsonRpcResponse->toJson());
}

The Problem

The generic error message provides no context about what actually went wrong, causing client retry loops and little way for the AI/user to resolve any issues.

Possible solutions

The solution is to render the exception to a proper response - question is just how.

MCP Rendering

Laravels exception rendering capabilities already allows for HTML and JSON rendering depending on the Accept header. We could extend this with a "plain text" rendering used for MCPs.

That would be a big task, since basically all existing exception rendering should not just deal with html and json, but also plaintext.

Rely on JSON

Any exception rendering to JSON is already "safe" in the sense that it should not contain any sensitive data.

Because of that, then we could render the exception and map any 5xx server errors to Protocol Errors and any 4xx errors to Tool Execution Errors - in both cases including the rendered json in the response message.

Steps To Reproduce

Try to throw these different types of exceptions within an MCP tool:

throw ValidationException::withMessages(['field' => 'Example message']);

throw new AuthorizationException('Not allowed');

throw new Exception('Something went wrong');

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions