mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-14 06:47:00 +10:00
b4aaf9712f
* feat(mcp): add OAuth 2.1 authentication for claude.ai MCP connector Enable OAuth 2.1 (RFC 8414 + RFC 7591) for the MCP endpoint using better-auth's MCP plugin. This allows claude.ai and other MCP clients to authenticate via Dynamic Client Registration and Authorization Code flow with PKCE, using the existing login page. - Add `mcp()` plugin to better-auth config with login page redirect - Add `.well-known/oauth-authorization-server` discovery endpoint - Add `.well-known/oauth-protected-resource` metadata endpoint - Update MCP handler to accept Bearer tokens via `getMcpSession` - Retain `x-api-key` fallback for backward compatibility - Return proper HTTP 401 + WWW-Authenticate header for unauthed requests - Add `oauthApplication`, `oauthAccessToken`, `oauthConsent` tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): use typed AuthError and suppress noisy verifyApiKey throws - Replace string-matching error detection with instanceof AuthError - Wrap verifyApiKey in try-catch to avoid logging malformed key errors - Move console.error below auth check so 401s don't pollute logs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(mcp): add database migration for OAuth tables Creates oauth_application, oauth_access_token, and oauth_consent tables required for MCP OAuth 2.1 Dynamic Client Registration flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): resolve OAuth Bearer token auth for oRPC tool calls The oRPC context only checked session cookies and API keys, causing MCP tool calls from OAuth clients (claude.ai) to fail with Unauthorized even though the MCP endpoint itself authenticated successfully. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): look up user by userId from OAuth access token getMcpSession returns OAuthAccessToken (with userId), not a session object with a user property. Must query the user table by userId. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(mcp): migrate from deprecated mcp() plugin to @better-auth/oauth-provider The better-auth MCP plugin is marked for deprecation in favor of the OAuth Provider plugin. This refactors the entire OAuth 2.1 flow to use @better-auth/oauth-provider with JWT-based token verification, replacing the opaque token lookup via getMcpSession(). Key changes: - Replace mcp() with jwt() + oauthProvider() in auth config - Replace getMcpSession() with verifyAccessToken() (JWT/JWKS) - Replace oauthApplication table with oauthClient (RFC 7591 compliant) - Add oauthRefreshToken table and jwks table for JWT signing keys - Extract shared authBaseUrl and verifyOAuthToken helper - Hoist McpServer to module scope (avoid per-request reconstruction) - Update .well-known discovery endpoints for OAuth Provider Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): resolve OAuth 2.1 flow for claude.ai MCP connector Multiple fixes required to make the full MCP OAuth flow work with claude.ai's implementation: - Add RFC 8414 discovery route at /.well-known/oauth-authorization-server/api/auth (claude.ai appends the issuer path per spec) - Add /auth/oauth server route to handle login/consent flow (generates auth codes directly, bypassing h3 cookie issues) - Default token_endpoint_auth_method to "none" via onRequest plugin hook (claude.ai omits this field, causing confidential client rejection) - Strip prompt=consent from authorize requests via onRequest hook (better-auth checks prompt before skipConsent, causing redirect loops) - Add validAudiences for MCP resource URL (JWT aud claim contains the MCP URL, not the base URL) - Disable CSRF check for cross-origin OAuth flows - Log token endpoint errors for debugging - Set skipConsent on OAuth clients via /auth/oauth route Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(mcp): harden OAuth security and enforce lock on delete - Scope CSRF bypass to OAuth2 paths only instead of disabling globally - Validate redirect_uri against registered client URIs (prevents code interception) - Use pathname matching instead of fragile url.includes() for route guards - Replace biased modulo code generation with crypto.randomBytes - Enforce resume lock check on delete (previously silently ignored) - Remove debug console.error logging of OAuth token response bodies - Use Response.json() consistently for MCP 401 response Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update dependencies, refine ignore patterns, and enhance documentation - Updated various dependencies in package.json and pnpm-lock.yaml for improved stability and features. - Adjusted ignore patterns in knip.json to include specific component directories. - Enhanced documentation for the MCP server, clarifying authentication methods and configuration options. - Made minor adjustments to VSCode settings for better code organization. * fix(mcp): resolve OAuth client registration and stale token handling Claude.ai sends token_endpoint_auth_method: "client_secret_post" without a client_secret during Dynamic Client Registration, causing Better Auth to reject it as an unauthenticated confidential client. Force to "none" for unauthenticated registrations. Also catch JWKS verification errors (e.g. key rotation after redeployment) so stale Bearer tokens return 401 instead of 200 with an error body, allowing clients to re-initiate the OAuth flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * reiterate on tests --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
304 lines
13 KiB
Plaintext
304 lines
13 KiB
Plaintext
---
|
||
title: "Using the MCP Server"
|
||
description: "Connect Reactive Resume to AI tools like Claude Desktop, Cursor, and Codex using the Model Context Protocol"
|
||
---
|
||
|
||
The Reactive Resume MCP server lets you manage and modify your resumes through any MCP-compatible AI tool — Claude Desktop, Cursor, Codex, and more. It connects to the Reactive Resume API and exposes tools for listing, reading, and patching resumes using natural language.
|
||
|
||
## What is MCP?
|
||
|
||
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is a standard that lets LLM-powered tools connect to external services. Instead of being limited to the built-in chat UI, you can use any MCP client to interact with your resumes.
|
||
|
||
## Prerequisites
|
||
|
||
<Steps>
|
||
<Step title="Choose your authentication method">
|
||
Reactive Resume MCP supports two authentication methods:
|
||
|
||
- **OAuth2 (recommended):** best user experience for clients that support MCP OAuth.
|
||
- **API key (fallback):** works in all clients that can send custom headers.
|
||
|
||
Use OAuth2 whenever your MCP client supports it. Use API key only when OAuth is unavailable in that client.
|
||
|
||
</Step>
|
||
|
||
<Step title="If using API key, create one">
|
||
Head over to [https://rxresu.me](https://rxresu.me) (or your self-hosted instance), sign in, and navigate to **Settings → API Keys**. Click **Create a new API key**, give it a name, and copy the secret — it's only shown once.
|
||
|
||
For the full walkthrough, see [Using the API](/guides/using-the-api).
|
||
|
||
</Step>
|
||
</Steps>
|
||
|
||
## Configuration
|
||
|
||
There are two transport options, and each can use either OAuth2 or API key depending on your client capabilities.
|
||
|
||
### Method 1: Streamable HTTP (recommended)
|
||
|
||
If your client supports the `url` field (e.g. **Cursor**, **Codex**, Claude custom connectors), use this.
|
||
|
||
#### Option A: OAuth2 (recommended)
|
||
|
||
Most OAuth-capable clients only need the MCP URL:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"reactive-resume": {
|
||
"url": "https://rxresu.me/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Then connect/sign in from the client UI (or with the client's OAuth login command).
|
||
|
||
#### Option B: API key (fallback)
|
||
|
||
If OAuth is not supported in your client, send `x-api-key`:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"reactive-resume": {
|
||
"url": "https://rxresu.me/mcp",
|
||
"headers": {
|
||
"x-api-key": "your-api-key"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Method 2: mcp-remote
|
||
|
||
If your client only supports `command` / `args` (for example, local-only Claude Desktop config), use [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) as a bridge. This requires [Node.js](https://nodejs.org) **20 or later**.
|
||
|
||
`mcp-remote` is most commonly used with API keys:
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"reactive-resume": {
|
||
"command": "npx",
|
||
"args": ["mcp-remote", "https://rxresu.me/mcp", "--header", "x-api-key:your-api-key"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
<Info>Replace `your-api-key` with the API key you created in the prerequisites step.</Info>
|
||
|
||
### Where to put the config
|
||
|
||
| Client | Config file |
|
||
| ----------------- | ------------------------------------------------------------------------------------------------ |
|
||
| Cursor | `.cursor/mcp.json` in your project or home directory |
|
||
| Claude Desktop | `claude_desktop_config.json` ([docs](https://modelcontextprotocol.io/quickstart/user)) |
|
||
| Codex | `~/.codex/config.toml` or `.codex/config.toml` ([docs](https://developers.openai.com/codex/mcp)) |
|
||
| Other MCP clients | Refer to the client's documentation |
|
||
|
||
## Authentication Details (How Reactive Resume MCP Works)
|
||
|
||
Reactive Resume MCP accepts authentication in this order:
|
||
|
||
1. **Bearer token (OAuth2 access token)** via `Authorization: Bearer <token>`
|
||
2. **API key fallback** via `x-api-key: <key>`
|
||
|
||
If neither is valid, the MCP endpoint responds with `401` and advertises OAuth metadata using:
|
||
|
||
- `WWW-Authenticate: Bearer resource_metadata="<instance>/.well-known/oauth-protected-resource"`
|
||
|
||
This lets OAuth-capable MCP clients discover and complete the OAuth flow automatically.
|
||
|
||
### OAuth2 flow used by this server
|
||
|
||
Reactive Resume is configured as an OAuth authorization server for MCP clients:
|
||
|
||
- The MCP endpoint is `https://rxresu.me/mcp`.
|
||
- OAuth discovery metadata is exposed under `/.well-known/*` endpoints.
|
||
- The login/authorization route is `/auth/oauth`.
|
||
- If the user is not signed in, `/auth/oauth` redirects to `/auth/login`, then resumes OAuth.
|
||
- If the user is signed in, `/auth/oauth` validates `client_id` and `redirect_uri`, issues an authorization code, and redirects back to the client.
|
||
- PKCE parameters (`code_challenge`, `code_challenge_method`) are preserved in the authorization flow.
|
||
|
||
## Popular Client Setup
|
||
|
||
### Cursor
|
||
|
||
**OAuth2 (recommended):**
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"reactive-resume": {
|
||
"url": "https://rxresu.me/mcp"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**API key fallback:**
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"reactive-resume": {
|
||
"url": "https://rxresu.me/mcp",
|
||
"headers": {
|
||
"x-api-key": "your-api-key"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### Codex (CLI / IDE extension)
|
||
|
||
Add server:
|
||
|
||
```bash
|
||
codex mcp add reactive-resume --url https://rxresu.me/mcp
|
||
```
|
||
|
||
Then log in with OAuth:
|
||
|
||
```bash
|
||
codex mcp login reactive-resume
|
||
```
|
||
|
||
API key fallback (`config.toml`):
|
||
|
||
```toml
|
||
[mcp_servers."reactive-resume"]
|
||
url = "https://rxresu.me/mcp"
|
||
http_headers = { "x-api-key" = "your-api-key" }
|
||
```
|
||
|
||
### Claude (web app custom connector)
|
||
|
||
Add `https://rxresu.me/mcp` as a custom remote MCP connector, then connect with OAuth in Claude's connector UI.
|
||
|
||
### Claude Desktop (local config file)
|
||
|
||
Use `mcp-remote` bridge with API key (example shown above in **Method 2**).
|
||
|
||
## External References
|
||
|
||
- [Cursor MCP docs](https://cursor.sh/docs/mcp)
|
||
- [MCP quickstart for users (Claude Desktop example)](https://modelcontextprotocol.io/quickstart/user)
|
||
- [OpenAI Codex MCP docs](https://developers.openai.com/codex/mcp)
|
||
- [Claude custom connectors (remote MCP)](https://claude.com/docs/connectors/custom/remote-mcp)
|
||
- [MCP Authorization spec](https://modelcontextprotocol.io/specification/latest/basic/authorization)
|
||
|
||
## Self-Hosting
|
||
|
||
If you're running a self-hosted Reactive Resume instance, replace `https://rxresu.me/mcp` with your instance URL:
|
||
|
||
```json
|
||
{
|
||
"url": "https://resume.example.com/mcp",
|
||
"headers": {
|
||
"x-api-key": "your-api-key"
|
||
}
|
||
}
|
||
```
|
||
|
||
## Available Tools
|
||
|
||
The MCP server exposes the following tools:
|
||
|
||
| Tool | Description |
|
||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||
| `get_resume` | Get the full data of a specific resume by ID |
|
||
| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||
| `duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||
| `patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||
| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||
| `lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||
| `unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||
| `export_resume_pdf` | Generate a PDF from the resume and return a download URL |
|
||
| `get_resume_screenshot` | Get a visual preview of the resume's first page as a WebP image URL |
|
||
| `get_resume_statistics` | Get view and download statistics for a resume |
|
||
|
||
## Available Resources
|
||
|
||
| Resource | Description |
|
||
| ----------------- | --------------------------------------------------------------------------------------------------------------- |
|
||
| `resume://{id}` | The full resume data as a readable JSON resource. Lists all resumes and supports reading individual ones by ID |
|
||
| `resume://schema` | The ResumeData JSON Schema — reference this to understand valid paths and value types for JSON Patch operations |
|
||
|
||
## Available Prompts
|
||
|
||
Prompts are pre-built workflows that provide the AI with structured instructions and context. Each prompt embeds the resume data and schema automatically.
|
||
|
||
| Prompt | Description |
|
||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| `build_resume` | Guide you step-by-step through building a resume from scratch — basics, summary, experience, education, skills, and design |
|
||
| `improve_resume` | Review your resume and suggest concrete improvements to wording, impact, metrics, and structure |
|
||
| `tailor_resume` | Adapt your resume to match a specific job description with keyword optimization and ATS targeting. Requires the job description as input |
|
||
| `review_resume` | Get a structured, professional critique with a scorecard (1–10 across seven dimensions) and prioritized recommendations. **Read-only** — no changes are made |
|
||
|
||
## Usage Examples
|
||
|
||
Once your MCP client is connected, you can use natural language to interact with your resumes:
|
||
|
||
### Browsing
|
||
|
||
- "List my resumes"
|
||
- "Show me my resume named 'Software Engineer'"
|
||
- "What skills are listed on my resume?"
|
||
- "Show me the stats for my resume"
|
||
|
||
### Creating & Managing
|
||
|
||
- "Create a new resume called 'Frontend Engineer 2026'"
|
||
- "Duplicate my 'Software Engineer' resume for a product manager role"
|
||
- "Lock my finalized resume so it can't be accidentally edited"
|
||
- "Delete my old draft resume"
|
||
|
||
### Editing
|
||
|
||
- "Update my name to Jane Doe"
|
||
- "Change my headline to Senior Software Engineer"
|
||
- "Add TypeScript to my skills with an Advanced proficiency level"
|
||
- "Add a new experience entry for my role as Staff Engineer at Acme Corp from Jan 2024 to Present"
|
||
- "Remove the third item from my skills section"
|
||
|
||
### Styling
|
||
|
||
- "Change the template to bronzor"
|
||
- "Set the primary color to blue"
|
||
- "Hide the interests section"
|
||
|
||
### Exporting
|
||
|
||
- "Export my resume as a PDF"
|
||
- "Show me a screenshot of my resume"
|
||
|
||
### Using Prompts
|
||
|
||
- "Help me build my resume from scratch" (uses `build_resume`)
|
||
- "Review my resume and give me a score" (uses `review_resume`)
|
||
- "Improve the wording on my resume" (uses `improve_resume`)
|
||
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
|
||
|
||
<Tip>
|
||
The AI will use `get_resume` to inspect your current resume before making changes with `patch_resume`. This ensures
|
||
the correct JSON paths are used.
|
||
</Tip>
|
||
|
||
## Troubleshooting
|
||
|
||
| Issue | Solution |
|
||
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||
| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) |
|
||
| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection |
|
||
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
|
||
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
|
||
| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard |
|
||
| Connection refused | Check that the URL is correct and the instance is running |
|
||
| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later** — upgrade with `nvm use 20` or `nvm alias default 20` |
|