A starter template for building an MCP server with human-approved, provider-scoped access to third-party APIs using WorkOS Pipes. It shows one way to let AI assistants securely interact with services like Linear, Notion, Snowflake, and more with a human in the loop, without trying to prescribe the full deployment story or how approval requests should be delivered.
- Human-in-the-loop approval flow — AI assistants request access, humans approve via a browser-based consent screen
- Provider-scoped authority — humans select exactly which integrations to authorize (not all-or-nothing)
- Unified authority grants — broad and per-request approvals share one
AuthorityGrantmodel - Time-limited sessions — authority expires after 5 minutes, configurable
- Read/write gating — read and write operations are gated separately
- Approval polling — assistants poll for results and receive user instructions
sequenceDiagram
participant AI as AI Assistant
participant Server as This Server
participant Human as Human
AI->>Server: request_elevated_access<br>(reason, providers)
Server-->>AI: approval URL + request ID
AI->>Human: "Open this URL"
Human->>Server: select providers
Human->>Server: add notes (optional)
Human->>Server: approve / deny
AI->>Server: check_access_request
Server-->>AI: approved + providers
Server-->>AI: user instructions
AI->>Server: call_integration_api
Server-->>AI: API response
Broad authority and per-request approval now use the same underlying grant record:
kind: "broad"grants temporary session authority for selected providerskind: "request"approves one exact API call and is consumed on execution
Every MCP request is authenticated first, then authorized from the current Pipes session and grant state:
app/[transport]/route.tsrequires AuthKit authentication for every MCP request.lib/mcp/with-authkit.tsverifies the bearer token, loads the WorkOS user, and loads or creates a Redis-backedPipesSessionkeyed bysid + organizationId.- Effective MCP scopes are derived from the session's current broad authority, not directly from JWT claims.
request_elevated_accesscreates pendingAuthorityGrantrecords:- broad grants request temporary
readorwriteauthority for selected providers - request grants store the exact
url,method, and optionalbodyfor one API call
- broad grants request temporary
- The approval UI resolves that grant:
- approved broad grants become
session.activeGrant - approved request grants stay in Redis as single-use approved grants
- approved broad grants become
call_integration_apienforces access by either:- checking the active broad grant for read/write level and provider access
- or consuming a matching approved request grant bound to the same
sid,userId, andorganizationId
pnpm installRedis is required for session and approval state storage.
# macOS (Homebrew)
brew install redis
brew services start redis
# Docker
docker run -d -p 6379:6379 redis
# Or run directly
redis-serverVerify it's running:
redis-cli ping
# PONGcp .env.local.example .env.localFill in your values:
WORKOS_API_KEY=sk_test_...
WORKOS_CLIENT_ID=client_...
WORKOS_COOKIE_PASSWORD=<generate with: openssl rand -base64 32>
WORKOS_REDIRECT_URI=http://localhost:5711/callback
AUTHKIT_DOMAIN=auth.workos.com
REDIS_URL=redis://127.0.0.1:6379Optional tuning:
SESSION_TTL_MS=604800000 # Session store TTL (default: 7 days)
SESSION_AUTHORITY_TTL_MS=300000 # Authority expiry (default: 5 minutes)pnpm devThe server starts at http://localhost:5711.
Point your MCP client (Claude Code, Claude Desktop, Cursor, etc.) at the server URL. The server uses Streamable HTTP transport at http://localhost:5711/mcp.
- Add a new provider module in
lib/mcp/providers/that exports aProviderDefinition - Implement
matchesUrl,buildHeaders, and, if needed,isWriteOperationfor provider-specific write detection - Register the provider in
lib/mcp/providers/index.ts - Update
lib/mcp/token-injection.tsonly if the provider needs auth behavior beyond the default Bearer token injection - Configure the integration in your WorkOS dashboard
The call_integration_api tool auto-detects the provider from the API URL domain and injects the correct authentication headers.
| Key pattern | Data | TTL |
|---|---|---|
pipes:mcp:session:{sid}:{orgId} |
Stored session record with active broad, pending broad, and active request grant IDs | Default 7 days; may be deleted earlier if idle |
pipes:mcp:authority-grant:{id} |
Unified broad/request grant record in pending, approved, or denied state |
Up to 5 min; preserved as remaining grant lifetime after resolution |
pipes:mcp:approval-token:{jti} |
One-time approval-token consumption guard (not the encrypted token itself) | Remaining approval-token lifetime, up to 5 min |
pnpm dev # Start dev server (Turbopack)
pnpm typecheck # Type check
pnpm lint # Lint (Biome)
pnpm format # Format (Biome)See CONTRIBUTING.md for guidelines on how to contribute to this project.
MIT License. See LICENSE for details.