MCP Server — AI Agent Integration
Connect AI agents to Timerise using the Model Context Protocol (MCP) for real booking operations — listing services, querying availability, and managing bookings
Last updated: March 4, 2026
MCP Server — AI Agent Integration
The Timerise API includes a built-in Model Context Protocol (MCP) server that lets AI agents perform real booking operations — listing services, querying availability, and creating or managing bookings — without custom integration code.
How It Works
The MCP server is mounted directly on the Timerise API at /mcp using the Streamable HTTP transport. There is no separate process to run.
The session authentication flow works as follows:
- The agent sends
initializetoPOST /mcpwith anAuthorization: Bearer <api-key>header. - The server creates a private MCP session and returns an
mcp-session-idheader. - Every subsequent tool call in that session uses the same API key, forwarded to the GraphQL layer.
- The agent sends
DELETE /mcp(or disconnects) to end the session.
All Timerise access controls apply — the API key determines which projects and services the agent can see.
Prerequisites
Before connecting an AI agent, you need:
- A Timerise API key — obtain one from your Timerise dashboard under Settings > API, or see the API Keys & Authentication guide.
- The Timerise API running locally (
npm start) or a deployed instance URL. - API keys are prefixed by environment:
SANDBOX-...for sandbox,PROD-...for production.
Available Tools
The MCP server exposes 16 tools organized in four groups.
Service Discovery
| Tool | Description |
|---|---|
list_projects | List all projects accessible with the API key. Use this first to obtain a projectId. |
get_project | Get full details of a single project. |
list_services | List services within a project. |
get_service | Get service details including required formFields — always call before creating a booking. |
list_locations | List physical or virtual locations for a project. |
Slot Availability
| Tool | Description |
|---|---|
list_available_slots | Query available booking slots for a service within a date range. Returns slotId values used when creating bookings. |
Booking Management
| Tool | Description |
|---|---|
create_booking | Create a new booking. Supports both slot-based and range-based services. |
confirm_booking | Move a booking from NEW to CONFIRMED. |
cancel_booking | Cancel a booking. |
reschedule_booking | Move a booking to a different slot or time range. |
list_bookings | List bookings for a project with optional filters (status, service, date range). |
get_booking_details | Get full details of a specific booking. |
Developer Tools
| Tool | Description |
|---|---|
get_api_schema | Get the full GraphQL API schema as SDL for discovery and integration development. |
list_api_users | List API users and their API keys for a project. |
create_api_key | Create a new API key for a project. The key is shown only once. |
delete_api_key | Delete an API key by its ID. |
Client Configuration
Claude Desktop
Claude Desktop has native MCP support via a JSON config file.
macOS — edit ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"timerise": {
"url": "https://sandbox-api.timerise.io/mcp",
"headers": {
"Authorization": "Bearer SANDBOX-your-api-key-here"
}
}
}
}
Windows — edit %APPDATA%\Claude\claude_desktop_config.json with the same content.
Linux — edit ~/.config/Claude/claude_desktop_config.json.
After saving, restart Claude Desktop. A hammer icon in the toolbar confirms MCP tools are loaded. Ask Claude "What Timerise projects can you see?" to verify.
Claude Code (CLI)
Add the server with one command:
claude mcp add timerise \
--transport http \
--url https://sandbox-api.timerise.io/mcp \
--header "Authorization: Bearer SANDBOX-your-api-key-here"
Verify with:
claude mcp list # shows "timerise" in the list
claude mcp get timerise # shows connection details
To remove later: claude mcp remove timerise.
Cursor
Edit ~/.cursor/mcp.json (or use Cursor Settings > MCP > Add Server):
{
"mcpServers": {
"timerise": {
"url": "https://sandbox-api.timerise.io/mcp",
"headers": {
"Authorization": "Bearer SANDBOX-your-api-key-here"
}
}
}
}
For project-scoped config, place .cursor/mcp.json in the project root instead.
Windsurf
Edit ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"timerise": {
"serverType": "http",
"url": "https://sandbox-api.timerise.io/mcp",
"headers": {
"Authorization": "Bearer SANDBOX-your-api-key-here"
}
}
}
}
Restart Windsurf — the Timerise tools will appear in the Cascade panel.
Claude.ai (OAuth 2.1)
Claude.ai connects using OAuth 2.1 Authorization Code + PKCE instead of direct Bearer token auth.
Step 1 — Create OAuth credentials via GraphQL (requires ADMIN role):
mutation {
mcpOAuthClientCreate(projectId: "your-project-id", label: "Claude.ai") {
clientId
clientSecret
label
apiUser {
apiUserId
fullName
projects
}
}
}
Save the clientSecret — it is only shown once. An APIADMIN user is auto-created.
To create credentials for an existing user, pass apiUserId instead of projectId:
mcpOAuthClientCreate(apiUserId: "existing-user-id", label: "Claude.ai") { ... }
Step 2 — In Claude.ai, go to Settings > MCP Servers, add a new server with URL https://sandbox-api.timerise.io/mcp, and enter the clientId and clientSecret in the advanced authentication settings.
Step 3 — Start a conversation and ask Claude to list your projects to verify.
This OAuth flow also works with ChatGPT, Google Gemini, and any MCP client that implements OAuth 2.1 + PKCE.
Managing OAuth clients:
List API users with their OAuth clients and keys:
query {
apiUsers(projectId: "your-project-id") {
apiUserId
fullName
projects
apiKeys {
apiKeyId
label
createdAt
}
oauthClients {
clientId
label
createdAt
}
}
}
Delete an OAuth client (also deletes the associated API key):
mutation {
mcpOAuthClientDelete(clientId: "mcp_xxx")
}
Delete an API user entirely (removes all keys and OAuth clients):
mutation {
apiUserDelete(apiUserId: "the-api-user-id")
}
The OAuth endpoints served automatically include:
| Endpoint | Purpose |
|---|---|
GET /.well-known/oauth-protected-resource | Resource metadata (RFC 9728) |
GET /.well-known/oauth-authorization-server | Authorization server metadata (RFC 8414) |
GET /mcp/oauth/authorize | Authorization endpoint (auto-approve) |
POST /mcp/oauth/token | Token exchange endpoint |
POST /mcp/oauth/register | Returns 405 — use GraphQL API instead |
Expired authorization codes and pending consent requests are automatically cleaned up by a daily cron job.
OpenAI Agents SDK
The OpenAI Agents SDK (Python) has built-in MCP support.
Install the SDK:
pip install openai-agents
Basic agent example:
import asyncio
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def main():
async with MCPServerStreamableHttp(
url="https://sandbox-api.timerise.io/mcp",
headers={"Authorization": "Bearer SANDBOX-your-api-key-here"},
) as timerise:
agent = Agent(
name="Booking Assistant",
instructions=(
"You help users book appointments using the Timerise tools. "
"Always call list_projects first, then list_services, then "
"list_available_slots before creating a booking."
),
mcp_servers=[timerise],
)
result = await Runner.run(
agent,
"Book me a slot for next Monday morning if anything is available.",
)
print(result.final_output)
asyncio.run(main())
With streaming output:
async with MCPServerStreamableHttp(
url="https://sandbox-api.timerise.io/mcp",
headers={"Authorization": "Bearer SANDBOX-your-api-key-here"},
) as timerise:
agent = Agent(
name="Booking Assistant",
instructions="Help users manage Timerise bookings.",
mcp_servers=[timerise],
)
async for event in Runner.run_streamed(agent, "Show me today's bookings"):
if event.type == "text_delta":
print(event.delta, end="", flush=True)
Tool lists are fetched once per connection. Reuse the same context manager across multiple agent invocations rather than opening a new connection per call.
Testing with MCP Inspector
The MCP Inspector is a browser-based tool for testing any MCP server interactively.
npx @modelcontextprotocol/inspector
This opens a local UI at http://localhost:5173. Select Streamable HTTP, enter the server URL (https://sandbox-api.timerise.io/mcp), add the Authorization header, and click Connect.
You can also test with curl. Initialize a session:
curl -si -X POST https://sandbox-api.timerise.io/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SANDBOX-your-api-key-here" \
-d '{
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "test", "version": "0.1" }
},
"id": 1
}'
Copy the mcp-session-id from the response header, then list tools:
curl -s -X POST https://sandbox-api.timerise.io/mcp \
-H "Content-Type: application/json" \
-H "mcp-session-id: <paste-session-id-here>" \
-d '{"jsonrpc":"2.0","method":"tools/list","params":{},"id":2}'
Tutorial: Book a Slot End-to-End
This walkthrough shows the exact tool call sequence an agent follows to complete a booking.
Step 1 — Discover the project
Call list_projects with no arguments. This returns all projects your API key can access:
{
"projects": [{ "projectId": "prj_abc123", "title": "Haircut Studio" }]
}
Step 2 — Find the right service
Call list_services with the project ID:
{ "projectId": "prj_abc123" }
Response:
{
"services": [
{ "serviceId": "svc_xyz789", "title": "Men's Haircut", "price": 35 }
]
}
Step 3 — Check booking form requirements
Call get_service to see the required form fields:
{ "serviceId": "svc_xyz789" }
Response:
{
"service": {
"serviceId": "svc_xyz789",
"formFields": [
{
"fieldId": "f1",
"fieldType": "SYSTEM_FULL_NAME",
"required": true,
"label": "Your name"
},
{
"fieldId": "f2",
"fieldType": "SYSTEM_EMAIL_ADDRESS",
"required": true,
"label": "Email"
},
{
"fieldId": "f3",
"fieldType": "SYSTEM_PHONE_NUMBER",
"required": false,
"label": "Phone"
}
]
}
}
Step 4 — Find an available slot
Call list_available_slots with a date range:
{
"serviceId": "svc_xyz789",
"dateTimeFrom": "2026-03-03T09:00:00Z",
"dateTimeTo": "2026-03-03T18:00:00Z"
}
Response:
{
"service": {
"slots": [
{
"slotId": "slot_a1b2",
"dateTimeFromISO": "2026-03-03T10:00:00Z",
"dateTimeToISO": "2026-03-03T11:00:00Z",
"quantity": 1
}
]
}
}
Step 5 — Create the booking
Call create_booking with the slot ID and required form fields:
{
"serviceId": "svc_xyz789",
"slots": ["slot_a1b2"],
"formFields": {
"SYSTEM_FULL_NAME": "Jane Doe",
"SYSTEM_EMAIL_ADDRESS": "jane@example.com"
},
"locale": "en",
"timeZone": "Europe/Warsaw"
}
Response:
{
"bookingCreate": {
"bookingId": "bkg_9z8y7x",
"status": "NEW",
"dateTimeFromISO": "2026-03-03T10:00:00Z"
}
}
Step 6 — Confirm the booking
If required by the service configuration, call confirm_booking:
{ "bookingId": "bkg_9z8y7x" }
Response:
{
"bookingConfirm": {
"bookingId": "bkg_9z8y7x",
"status": "CONFIRMED"
}
}
Range-based bookings
Some services don't use pre-defined slots. Skip steps 4-5 and pass date ranges directly to create_booking:
{
"serviceId": "svc_range001",
"dateTimeFrom": "2026-03-05T14:00:00Z",
"dateTimeTo": "2026-03-05T15:30:00Z",
"formFields": {
"SYSTEM_FULL_NAME": "Jane Doe",
"SYSTEM_EMAIL_ADDRESS": "jane@example.com"
}
}
Use get_service and check viewConfig.displayType to determine the booking style:
LIST/DAYS/CALENDAR— slot-based, useslots: [slotId]PREORDER— range-based, usedateTimeFrom/dateTimeTo
Session Lifecycle
Each connected agent gets an isolated MCP session:
| HTTP method | Path | mcp-session-id | Purpose |
|---|---|---|---|
POST | /mcp | absent | Initialize new session (must include Authorization header) |
POST | /mcp | present | Send a JSON-RPC message to an existing session |
GET | /mcp | present | Open SSE stream for server-to-client notifications |
DELETE | /mcp | present | Terminate and clean up session |
Sessions are stored in-memory and do not survive API restarts. Agents should re-initialize after a server restart.
Troubleshooting
404 Session not found
The session was not initialized or the API was restarted. Send a new initialize request (without mcp-session-id) to start a fresh session.
400 Missing mcp-session-id header
A GET or DELETE request was sent without the session ID header. Only POST without a session ID is valid (for initialization).
GraphQL errors
The forwarded GraphQL query failed. Common causes:
| Error | Cause | Fix |
|---|---|---|
Not authorized | API key lacks access to the project | Check key permissions in Timerise dashboard |
Project not found | Wrong projectId | Call list_projects again to get valid IDs |
Service not found | Wrong serviceId | Call list_services(projectId) to refresh |
Slot not available | Slot was booked by someone else | Call list_available_slots again |
Tools don't appear in Claude Desktop
- Confirm the API is running:
curl http://localhost:3000/should return{"apiTitle":"Timerise API",...} - Check the config file path — it must be exact (case-sensitive on Linux/macOS).
- Restart Claude Desktop fully (Quit, not just close the window).
- Check Claude Desktop logs:
~/Library/Logs/Claude/(macOS).
Session auth — which key to use
The API key in the Authorization header of the initialize request determines the session's identity. Subsequent requests in the same session use the same key — there is no way to change auth mid-session. Open a new session with a different key if you need different permissions.
For production use, replace the sandbox URL with https://api.timerise.io/mcp and use a PROD- prefixed key.