Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions demos/remote-mcp-pingfederate/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.vscode/
**/node_modules
**/dist
**/build
**/lib
**/coverage
**/*.tsbuildinfo
.wrangler
worker-configuration.d.ts
.dev.vars
76 changes: 76 additions & 0 deletions demos/remote-mcp-pingfederate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Model Context Protocol (MCP) Server + PingFederate

This is a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/introduction) server that supports remote MCP connections, with PingFederate built in.

Powered by [Cloudflare Workers](https://developers.cloudflare.com/workers/), the MCP server functions as a resource server within the OAuth architecture. It validates incoming requests and facilitate token exchange, ensuring secure communication between the MCP client and downstream APIs.

> [!WARNING]
> This is a demo template designed to help you get started quickly. While we have implemented several security controls, **you must implement all preventive and defense-in-depth security measures before deploying to production**. Please review the MCP spec: [Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices)

## Getting Started

This demo shows how an MCP server can securely call a protected API on behalf of an end user. To begin:

1. Deploy the [Todo API](./api/)
2. Deploy the [MCP server](./mcp/)

> [!NOTE]
> The API used here could be any PingFederate-secured API. We use a Cloudflare workers API for this example, but the goal is to demonstrate how any API can be connected to an MCP server using this architecture.

### Access the remote MCP server from the Cloudflare Workers AI LLM Playground

Navigate to [https://playground.ai.cloudflare.com](https://playground.ai.cloudflare.com) and connect to your MCP server using the following URL pattern:

```
https://remote-mcp-ping-federate.<your-subdomain>.workers.dev/mcp
```

<img src="_docs/workers_ai_playground_demo.png" width="60%" alt="MCP server demo with workers ai playground">

### Access the remote MCP server from Claude Desktop

Open Claude Desktop and navigate to Settings -> Developer -> Edit Config. This opens the configuration file that controls which MCP servers Claude can access.

Replace the content with the following configuration. Once you restart Claude Desktop, a browser window will open showing your OAuth login page. Complete the authentication flow to grant Claude access to your MCP server. After you grant access, the tools will become available for you to use.

```
{
"mcpServers": {
"todo-mcp": {
"command": "npx",
"args": [
"mcp-remote",
"https://remote-mcp-ping-federate.<ENV>.workers.dev/mcp"
]
}
}
}
```

Once the Tools (under 🔨) show up in the interface, you can ask Claude to use them. For example: "Could you tell me what is in my Todo list?". Claude should invoke the tool and show the result generated by the MCP server.

## How does it work?

This architecture bridges the stateless nature of Cloudflare workers with the stateful requirements of an authenticated MCP session.

### Authentication & Client Bootstrapping

When an unregistered MCP client tries to connect to the MCP server without a token, the server provides the necessary details for the client to perform Dynamic Client Registration (DCR) on the fly. This allows the client to handle the user login and consent via PingFederate automatically, provisioning the tokens required to both connect to the MCP server and for the MCP server to execute delegated token exchanges.

> [!NOTE]
> This implementation utilizes Dynamic Client Registration (DCR) to handle client onboarding. While the MCP protocol recommends CIMD as the new standard, DCR remains the only production-ready option currently supported by enterprise identity providers like PingFederate. Future versions of this architecture may transition to CIMD as support becomes available.

### Cloudflare Agents (State & Transport)

The MCP server extends the McpAgent class, which automatically wraps the MCP logic in a durable object. This handles the complex infrastructure requirements:
- **Session Persistence:** It creates a dedicated, isolated environment for each MCP connection and securely persists the PingFederate tokens in the durable object's storage (`this.props`).
- **Network Transport:** The agent manages the raw HTTP connection. It accepts incoming requests and keeps the response open as a Server-Sent Events (SSE) stream, enabling the Durable Object to push real-time updates back to the client over the single endpoint.

### MCP SDK (Tool Logic)

The official `@modelcontextprotocol/sdk` is used to define the actual capabilities of the MCP server. Inside the agent, an McpServer instance:
- **Handles Protocol:** Manages the serialization of JSON-RPC messages and tool definitions.

## Use Cases & Extensibility

This architecture demonstrates a pattern for connecting AI agents to APIs protected by PingFederate. The remote MCP server enables LLMs to interact with APIs on behalf of an authenticated user, without requiring modifications to the target API's existing permission models. By leveraging standard OAuth 2.0 patterns like Dynamic Client Registration (DCR) and Token Exchange, this project provides a blueprint for enabling natural language interactions with enterprise data while enforcing identity-based access controls and consent.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added demos/remote-mcp-pingfederate/_docs/policy.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions demos/remote-mcp-pingfederate/api/.dev.vars.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
API_ISSUER=
API_AUDIENCE=
65 changes: 65 additions & 0 deletions demos/remote-mcp-pingfederate/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Todo API

A protected API providing basic Todo CRUD. Access is granted only with a PingFederate token.

### Stack

| Role | Name | Description |
| :--- | :--- | :--- |
| **Platform** | [Cloudflare Workers](https://workers.cloudflare.com) | Serverless execution |
| **Framework** | [Hono](https://hono.dev) | Lightweight API endpoints |
| **Data Storage** | [Cloudflare Workers KV](https://developers.cloudflare.com/kv) | User-scoped Todo list data |

### Requirements

* Node.js (v20+)
* PingFederate server
* Cloudflare account & [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/install-and-update)

### Structure

```text
api/
├── src/
│ ├── index.ts # Worker entry point, defines the routes
│ ├── config.ts # Worker bindings, request-scoped variables, and scopes
│ ├── todoService.ts # Todo CRUD with Cloudflare KV
│ └── auth.ts # PingFederate token verification
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript compiler settings
└── wrangler.jsonc # Worker configuration
```

## 🚀 Deploy to Cloudflare

1. Install dependencies and build
```zsh
npm install
npm run build
```

2. Set remote environment variables using wrangler

| Name | Description | Example |
| :--- | :--- | :--- |
| API_ISSUER | PingFederate server domain | `https://<ENV>.com:9031` |
| API_AUDIENCE | `aud` claim this API expects in JWTs | `https://todo-api-ping-federate.<ENV>.workers.dev` |

```bash
wrangler secret put API_ISSUER
wrangler secret put API_AUDIENCE
```

3. Configure remote KV storage using wrangler

```bash
wrangler kv namespace create TODO_KV_PING_FEDERATE
```

> Note: After running this command, you must update `wrangler.jsonc` with the generated KV namespace ID

4. Deploy

```bash
npm run deploy
```
Loading