# Metadata (/docs/code/routes/metadata)

---
title: Metadata
description: One .meta() call names a route for the OpenAPI spec and can hand the same route to an agent as an MCP tool. It changes nothing about how the route runs.
kind: guide
package: questpie
---

How does a route get a decent name in the API reference, and how does an agent
find it? Both answers live in the same object.

## One call, both consumers

```ts title="src/questpie/server/routes/revenue-stats.post.ts"
import { route } from "questpie/services";
import { z } from "zod";

export default route()
	.post()
	.schema(
		z.object({
			startDate: z.string().datetime(),
			endDate: z.string().datetime(),
		}),
	)
	.meta({
		title: "Get revenue stats",
		description: "Revenue for a date range, completed appointments only.",
		tags: ["reports"],
		mcp: {
			expose: true,
			name: "reports.revenue",
			annotations: { readOnlyHint: true },
		},
	})
	.handler(async ({ input, collections }) => {
		const { docs } = await collections.appointments.find({
			where: {
				status: "completed",
				scheduledAt: {
					gte: new Date(input.startDate),
					lte: new Date(input.endDate),
				},
			},
			limit: 10_000,
		});
		return { count: docs.length };
	});
```

## What OpenAPI reads

The spec comes from `@questpie/openapi`. It reads three keys and ignores the
rest.

| You write     | It becomes                                        |
| ------------- | ------------------------------------------------- |
| `title`       | The operation `summary`.                          |
| `description` | The operation `description`.                      |
| `tags`        | The operation's tags, registered on the spec too. |
| nothing       | The route key, tagged `Routes: <first>`.          |

Your `.schema()` and `.outputSchema()` become the request and response schemas
without any help from `.meta()`. A raw route gets a permissive request body and
a note saying it returns whatever it likes.

<Callout type="info" title="`.access(true)` shows up in the spec">
	An explicit `true` emits `security: []` on the operation, which opts it out of
	the spec's security scheme. A function rule, `false`, or no rule at all
	inherits the scheme instead.
</Callout>

## What MCP reads

The tool surface comes from `@questpie/mcp`. A route becomes a tool only when
all of this holds.

| Condition                | Why                                                |
| ------------------------ | -------------------------------------------------- |
| `mcp.expose === true`    | Opt in, one route at a time. Nothing is automatic. |
| The route is JSON mode   | A raw route has no input schema to advertise.      |
| The MCP module allows it | Its own policy still gates the tool.               |

The tool's input schema is your `.schema()`. For a route with URL params, the
tool takes `{ params, input }` instead, so an agent can fill in `:id`.

| `mcp` key     | Effect                                           |
| ------------- | ------------------------------------------------ |
| `expose`      | Set `true` to make the tool exist.               |
| `name`        | The tool name. Defaults to `routes.<key>`.       |
| `title`       | A human label. Falls back to the outer `title`.  |
| `description` | What the tool does. Falls back to the outer one. |
| `annotations` | The standard MCP hints, see below.               |

`annotations` carries `readOnlyHint`, `destructiveHint`, `idempotentHint` and
`openWorldHint`. All four ride out to the agent as standard MCP tool hints.
None of them changes how QUESTPIE runs the route.

<Callout type="warn" title="Your access rule still runs">
	Exposing a route as a tool does not bypass `.access()`. The MCP module checks
	its own policy, then evaluates your route's rule against the agent's session
	before the handler runs.
</Callout>

## Anything else you put there

`RouteMeta` has an open index signature, so extra keys are allowed. They ride
through introspection untouched. Keep them serializable. Nothing in the
framework reads them, which makes them a good seam for a module of your own.

## Related

- [OpenAPI](/docs/agents/openapi), the whole generated spec and the reference
  UI it ships with.
- [MCP](/docs/agents/mcp), the tool surface, its policy, and how an agent
  connects.
