Integrating Your Own MCP Client
This guide walks you through building a custom MCP client that connects to the BookYourPTO MCP server. Use this if you're building your own AI tool, automation, or integration.
Overview
The BookYourPTO MCP server is reached over Streamable HTTP:
| Transport | Endpoint | Use case |
|---|---|---|
Streamable HTTP (MCP 2025-11-25) | /mcp | All remote connections (POST/GET/DELETE) |
| SSE (legacy) | /sse | Backward compatibility only — available for one release, do not build new clients on it |
BookYourPTO MCP uses OAuth 2.0 with PKCE (S256) for authentication. Your client must implement the standard MCP OAuth flow: discover server metadata, register dynamically, open a browser for user login, and exchange the authorization code for an access token. The MCP SDK handles most of this automatically.
Prerequisites
- The hosted BookYourPTO MCP server at
mcp.bookyourpto.com - An MCP client library for your language
MCP SDKs by language
| Language | SDK |
|---|---|
| TypeScript/JavaScript | @modelcontextprotocol/sdk |
| Python | mcp |
| Go | go-sdk |
| Rust | rust-sdk |
| Java/Kotlin | java-sdk |
Step 1: Connect via Streamable HTTP with OAuth
The MCP SDK handles the OAuth flow automatically. When connecting, the SDK will:
- Discover the server's OAuth metadata at
/.well-known/oauth-authorization-server(and/.well-known/oauth-protected-resource) - Register your client dynamically via the
/registerendpoint - Open a browser for the user to sign in on BookYourPTO's domain
- Exchange the authorization code for an access token (with PKCE S256)
- Connect to the
/mcpendpoint with the Bearer token and theMCP-Protocol-Version: 2025-11-25header
TypeScript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.bookyourpto.com/mcp")
);
const client = new Client({
name: "my-custom-client",
version: "1.0.0",
});
// The SDK handles OAuth automatically — a browser window will open
// for the user to sign in on BookYourPTO's domain
await client.connect(transport);
console.log("Connected to BookYourPTO MCP");
Python
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
# The SDK handles OAuth automatically
async with streamablehttp_client("https://mcp.bookyourpto.com/mcp") as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
print("Connected to BookYourPTO MCP")
Step 2: Discover Available Tools
Once connected (and authenticated via OAuth), list the available tools — there are 346 across 19 categories:
TypeScript
const tools = await client.listTools();
for (const tool of tools.tools) {
console.log(`${tool.name}: ${tool.description}`);
console.log(" annotations:", tool.annotations); // readOnlyHint / destructiveHint / ...
}
Python
tools = await session.list_tools()
for tool in tools.tools:
print(f"{tool.name}: {tool.description}")
Each tool carries MCP annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint) so your client can decide when to confirm a destructive or high-risk action. See the Tools Reference for the full list.
Step 3: Call a Tool
Authentication is already complete (handled by OAuth during connection). You can call any tool immediately:
TypeScript
// Check leave balance
const result = await client.callTool({
name: "get_leave_balance",
arguments: {
year: 2026,
},
});
console.log(result.content);
Python
# Check leave balance
result = await session.call_tool(
"get_leave_balance",
arguments={"year": 2026},
)
print(result.content)
Example: Create a leave request
const result = await client.callTool({
name: "create_leave_request",
arguments: {
userId: "user-id-here",
leaveTypeId: "leave-type-id-here",
startDate: "2026-03-15",
endDate: "2026-03-16",
reason: "Personal day",
},
});
Step 4: Read Resources
The MCP server exposes read-only resources for context:
// List available resources
const resources = await client.listResources();
// Read a specific resource
const orgSettings = await client.readResource({
uri: "bypto://org/settings",
});
console.log(orgSettings.contents);
Available resources (12 total):
| URI | Description |
|---|---|
bypto://org/settings | Organization configuration |
bypto://leave-types | Available leave types |
bypto://team/directory | Team members with roles |
bypto://current-user | Authenticated user's profile |
bypto://departments | Departments list |
bypto://projects | Projects list |
bypto://expense-categories | Expense categories |
bypto://document-templates | Document templates |
bypto://onboarding-templates | Onboarding templates |
bypto://benefit-plans | Benefit plans |
bypto://training-courses | Training courses |
bypto://training-categories | Training categories |
Connection Lifecycle
Reconnection
Network connections may drop. Implement reconnection logic:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
async function connectWithRetry(maxRetries = 5) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const transport = new StreamableHTTPClientTransport(
new URL("https://mcp.bookyourpto.com/mcp")
);
const client = new Client({
name: "my-client",
version: "1.0.0",
});
await client.connect(transport);
return client;
} catch (error) {
console.error(`Attempt ${attempt} failed:`, error.message);
if (attempt < maxRetries) {
await new Promise((r) => setTimeout(r, 2000 * attempt));
}
}
}
throw new Error("Failed to connect after retries");
}
Clean shutdown
// Always close the connection when done
await client.close();
Key References
- MCP Specification — Full protocol standard
- Building an MCP Client — Official client guide
- BookYourPTO MCP Tools — All 346 tools reference
- Security & Permissions — OAuth flow and role-based access details