The app exposes a Model Context Protocol (MCP) server, so AI agents
such as Claude Code can read and write site content directly: create and edit Pages (blog posts)
and upload, rename and attach media (Attachments).
POST /mcp (streamable HTTP transport)game-object.de Content (App\Mcp\Servers\ContentServer)laravel/mcp, registered in routes/ai.phpThe endpoint uses the same Sanctum API tokens as the /api/v1 routes, sent as a Bearer token.
It is rate limited to 120 requests per minute.
<id>|<secret>).GET /api/v1/validate-token.A missing or invalid token returns 401 with WWW-Authenticate: Bearer realm="mcp".
Warning: a token grants full write access to all pages and media, including private ones. Treat it like a password.
Claude Code:
claude mcp add --transport http game-object http://localhost/mcp \
--header "Authorization: Bearer <token>"
If Claude Code runs inside a Docker sandbox, localhost is the sandbox itself. Use
http://host.docker.internal/mcp instead. Check the connection with claude mcp get game-object
or /mcp inside a session.
Any other MCP client: configure an HTTP server with URL <APP_URL>/mcp and the header
Authorization: Bearer <token>.
Quick check with curl:
curl -X POST http://localhost/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'Authorization: Bearer <token>' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
| Tool | Purpose |
|---|---|
list_pages |
List pages, newest first. Filters: search (title/uri), publish_state, page, per_page (max 100). Without content. |
get_page |
One page by id or uri, including HTML content, attachments, related_page_ids and project_ids. |
create_page |
Create a page: title, uri (unique, [a-z0-9-/]), content (HTML), optional publish_state, allow_comments, related_page_ids, project_ids. |
update_page |
Change only the fields that are passed. content replaces the whole body; relation lists replace the full set. |
| Tool | Purpose |
|---|---|
list_media |
List attachments, newest first. Filters: search (file name), type, page_id. |
get_media |
One attachment and the pages it is attached to. With include_preview: true, png/jpeg/gif/webp images up to 2 MB are returned as image content. |
upload_media |
Upload from content_base64 (with file_name) or source_url. Optional page_id + relation attach it right away. Returns the public url. |
update_media |
Change publish_state and/or rename file_name (moves the file, so the url changes). |
attach_media |
Attach an existing attachment to a page with a relation (default attachment). |
detach_media |
Remove the link between an attachment and a page. The file is kept. |
There are no delete tools on purpose. Deleting pages and media is still done in Nova.
Writing a post with a cover image:
upload_media with the image. Note the returned attachment.id and attachment.url.create_page with <img src="{url}"> in the HTML content. It is created as private.attach_media with relation: "thumbnail" to use the image as the page thumbnail in excerpts.update_page with publish_state: "published".Base64 through an MCP tool call is only practical for small files. For anything larger, use the multipart endpoint with the same token:
curl -X POST http://localhost/api/v1/attachments \
-H 'Accept: application/json' \
-H 'Authorization: Bearer <token>' \
-F file=@cover.png \
-F page_id=13 \
-F relation=thumbnail
| Field | Required | Notes |
|---|---|---|
file |
yes | Limited by upload_max_filesize / post_max_size in docker/app/php.ini. |
prefix |
no | Storage folder, default media. |
page_id |
no | Attach to this page. |
relation |
no | Default attachment; thumbnail sets the page thumbnail. |
Responds with 201 and the same attachment fields as upload_media.
<h1> in page content becomes <h2>, because the page title is the only <h1>.create_page creates pages as private unless publish_state is given.
Authenticated requests see private pages and media too.AttachmentService::createFromUploadedSingleFile. Uploading
an identical file again returns the existing attachment.My Cover.PNG becomes my-cover.png).upload_media accepts up to 50 MB. source_url downloads time out after 60 seconds.source_url: the server fetches any http(s) URL it is given. This is acceptable because only
token holders can call it.update_page.thumbnail attachment, then its newest image, then its newest attachment.| Path | Contents |
|---|---|
routes/ai.php |
Registers /mcp with auth:sanctum and throttling. |
app/Mcp/Servers/ContentServer.php |
Server name, instructions and tool list. |
app/Mcp/Tools/ |
One class per tool. |
app/Mcp/Concerns/ |
Shared presenters (PresentsModels) and page write logic (WritesPages). |
app/Http/Controllers/AttachmentController.php |
The multipart /api/v1/attachments endpoint. |
tests/Feature/McpContentServerTest.php |
Feature tests for all tools and both endpoints. |
To add a tool: create a class in app/Mcp/Tools/ extending Laravel\Mcp\Server\Tool, give it
#[Name] and #[Description] attributes, and add it to $tools in ContentServer.