Mcp

Integrating Your Own MCP Client

Build a custom MCP client that connects to the BookYourPTO MCP server using the Streamable HTTP transport.

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:

TransportEndpointUse case
Streamable HTTP (MCP 2025-11-25)/mcpAll remote connections (POST/GET/DELETE)
SSE (legacy)/sseBackward 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

LanguageSDK
TypeScript/JavaScript@modelcontextprotocol/sdk
Pythonmcp
Gogo-sdk
Rustrust-sdk
Java/Kotlinjava-sdk

Step 1: Connect via Streamable HTTP with OAuth

The MCP SDK handles the OAuth flow automatically. When connecting, the SDK will:

  1. Discover the server's OAuth metadata at /.well-known/oauth-authorization-server (and /.well-known/oauth-protected-resource)
  2. Register your client dynamically via the /register endpoint
  3. Open a browser for the user to sign in on BookYourPTO's domain
  4. Exchange the authorization code for an access token (with PKCE S256)
  5. Connect to the /mcp endpoint with the Bearer token and the MCP-Protocol-Version: 2025-11-25 header

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):

URIDescription
bypto://org/settingsOrganization configuration
bypto://leave-typesAvailable leave types
bypto://team/directoryTeam members with roles
bypto://current-userAuthenticated user's profile
bypto://departmentsDepartments list
bypto://projectsProjects list
bypto://expense-categoriesExpense categories
bypto://document-templatesDocument templates
bypto://onboarding-templatesOnboarding templates
bypto://benefit-plansBenefit plans
bypto://training-coursesTraining courses
bypto://training-categoriesTraining 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