fix(mcp): use JSON-compatible invitation dates (#6814)

This commit is contained in:
Thomas authored and GitHub committed 2026-09-10 17:53:38 +02:00
1 parent 61d3fd5f08
commit 3358b6337f
7 files changed
+25 -5

No files matched your search

+6
View File
@@ -27,6 +27,12 @@ session cookie.
API keys do not expire automatically and act as the user that created them. Every request uses that user's group and
resource permissions.
## Invitation dates
`POST /api/invites` and the tRPC `invite.createInvite` procedure accept `expirationDate` as an ISO 8601 timestamp with a
timezone, for example `2026-12-01T18:00:00Z`. Homarr validates the timestamp and stores it as a date. tRPC callers should
send the ISO string rather than a JavaScript `Date` object.
## Health probes
The liveness endpoint does not require authentication:
+11
View File
@@ -55,6 +55,9 @@ the catalog by namespace, tool name, or description; matching categories expand
MCP supports API keys and OAuth 2.1 with PKCE. API keys are simpler for direct client configuration; OAuth is available
for clients that support dynamic authorization.
Copy the complete generated API key, including both parts and the dot: `<id>.<token>`. Sending only the ID or token
returns `invalid_token`. Keep the full key private when sharing client configuration or diagnostics.
When Homarr is behind a reverse proxy that does not preserve the public host and protocol, set `BASE_URL` to the exact
public origin, for example `https://homarr.example.com`. Do not include a path. OAuth clients discover the authorization
server and protected resource through Homarr's `/.well-known/` routes.
@@ -79,12 +82,20 @@ curl -X POST \
The removed HTTP+SSE `/sse` and `/message` transports return `410 Gone`.
Successful `server/discover` or `initialize` responses confirm connectivity, but do not validate the tool catalog.
Use `tools/list` above to check that the client can discover tools. If it returns
`Date cannot be represented in JSON Schema`, the server failed to generate a tool schema; changing the API key will
not resolve that error. Update Homarr to a release containing the invitation date schema fix.
## Tools and resources
The generated catalog includes available board, app, integration, Docker, Kubernetes, media, DNS, download, and other
Homarr procedures. Queries are read-only; mutations change state. The exact catalog depends on the release and current
user permissions, so inspect `tools/list` or the table in Homarr instead of relying on a static list.
The `invite_createInvite` tool accepts `expirationDate` as an ISO 8601 timestamp with a timezone, such as
`2026-12-01T18:00:00Z` or `2026-12-01T19:00:00+01:00`.
For Custom Widgets, install the official skill and follow the preview-and-evidence workflow described in
[Connect an agent](./custom-widgets/agent-authoring). Secret values are configured in Homarr and are never returned
through MCP.
+3 -1
View File
@@ -2572,7 +2572,9 @@
"type": "object",
"properties": {
"expirationDate": {
"type": "string"
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
}
},
"required": [
@@ -45,7 +45,7 @@ export const InviteCreateForm = ({ onClose }: InviteCreateFormProps) => {
onSubmit={form.onSubmit((values) => {
mutate(
{
expirationDate: new Date(values.expirationDate),
expirationDate: new Date(values.expirationDate).toISOString(),
},
{
onSuccess: (result) => {
+1
View File
@@ -109,6 +109,7 @@ export function extractMcpToolsFromProcedures<TRoot extends AnyRootTypes, TRecor
inputValidator,
inputSchema: normalizeJsonSchema(
z.toJSONSchema(inputValidator, {
io: "input",
unrepresentable: "any",
}),
),
+2 -2
View File
@@ -56,7 +56,7 @@ export const inviteRouter = createTRPCRouter({
.requiresPermission("admin")
.input(
z.object({
expirationDate: z.date(),
expirationDate: z.iso.datetime({ offset: true }),
}),
)
.output(z.object({ id: z.string(), token: z.string() }))
@@ -80,7 +80,7 @@ export const inviteRouter = createTRPCRouter({
await ctx.db.insert(invites).values({
id,
expirationDate: input.expirationDate,
expirationDate: new Date(input.expirationDate),
creatorId: ctx.session.user.id,
token,
});
+1 -1
View File
@@ -123,7 +123,7 @@ describe("create should create a new invite expiring on the specified date with
// Act
const result = await caller.createInvite({
expirationDate,
expirationDate: expirationDate.toISOString(),
});
// Assert