MCP OAuth Integration Guide
Your md-share MCP server is deployed at:
{url}
Overview
The server implements the MCP Streamable HTTP transport with OAuth 2.0 authentication (RFC 8414 + PKCE). Three auth methods are supported:
| Method | How it works | Best for |
|---|---|---|
| OAuth 2.0 (Authorization Code + PKCE) | User signs in via browser, gets access token | ChatGPT, Claude, any OAuth-capable MCP client |
| API Key (Bearer header) | Static mdshare_* key in Authorization: Bearer header | REST tools, curl scripts |
| API Key (query param) | ?apiKey=mdshare_* in URL | Testing, legacy clients |
1. ChatGPT (Built-in MCP OAuth)
ChatGPT natively supports OAuth-based MCP servers. Connect via ChatGPT UI → Settings → MCP Servers → Add MCP Server.
| Field | Value |
|---|---|
| URL | {url}/api/mcp/sse |
| OAuth URL | (leave blank — auto-discovered from /.well-known/oauth-authorization-server) |
ChatGPT will:
- Fetch
/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resourceto discover endpoints - Open a popup to
/oauth/authorize?client_id=...&redirect_uri=https://chatgpt.com/...&response_type=code&code_challenge=... - You sign in with your md-share account (or sign up)
- Code is exchanged at
/oauth/tokenfor an access token - All subsequent MCP calls use
Authorization: Bearer <token>
Already works — this is why ChatGPT connects successfully.
2. Grok (xAI)
Grok connects via Settings → MCP Servers → Add Server. Use the Streamable HTTP (SSE) option:
| Field | Value |
|---|---|
| Name | md-share |
| URL | {url}/api/mcp/sse |
| OAuth | Enabled |
If Grok does not support OAuth auto-discovery, you need an API key instead:
| Field | Value |
|---|---|
| Name | md-share |
| URL | {url}/api/mcp/sse?apiKey=YOUR_API_KEY_HERE |
Troubleshooting Grok 401 errors
The logs show:
authenticateMcp: { hasAuth: false, authPrefix: undefined, hasApiKey: false }
authenticateMcp: no credentials found
POST /api/mcp/sse 401
Grok is sending requests without any credentials. Possible fixes:
- Add
?apiKey=to the URL — this is the most reliable for Grok. Append?apiKey=mdshare_...to the SSE URL. - Check Grok's MCP input fields — some clients have a separate "Auth Token" or "Headers" field. If available, add:
Authorization: Bearer mdshare_... - Create an API key (see section 4 below)
To create an API key for Grok:
- Log into md-share via browser
- Go to Account Settings → API Keys
- Click Create Key, give it a name like
grok - Copy the key (starts with
mdshare_) — it's shown once - Paste it into Grok's config
3. REST / curl (direct API access)
You don't need OAuth for direct API access. Use an API key in the header or query param.
Create an API key
# Requires a session cookie — do this in browser via Account Settings,
# or get a JWT cookie first:
curl -X POST {url}/api/user/api-keys \
-H "Content-Type: application/json" \
-H "Cookie: authToken=YOUR_JWT_COOKIE" \
-d '{"name": "cli"}'
Or via the dashboard UI: Account → API Keys → Create.
List documents (GET)
# Via Bearer header (recommended)
curl -H "Authorization: Bearer mdshare_abc123..." \
{url}/api/mcp/docs
# Via query param (simpler for scripts)
curl "{url}/api/mcp/docs?apiKey=mdshare_abc123..."
Initialize an MCP session (Streamable HTTP)
# Step 1: POST initialize (no session ID)
curl -X POST {url}/api/mcp/sse?apiKey=mdshare_abc123... \
-H "Content-Type: application/json" \
-d '{"method": "initialize", "params": {"protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": {"name": "my-client", "version": "1.0"}}}'
# => Returns session ID in mcp-session-id header + JSON response
# Step 2: GET SSE stream (use the session ID from step 1)
curl -N {url}/api/mcp/sse?apiKey=mdshare_abc123... \
-H "mcp-session-id: YOUR_SESSION_ID"
# Step 3: Send tool calls
curl -X POST {url}/api/mcp/sse?apiKey=mdshare_abc123... \
-H "Content-Type: application/json" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{"method": "tools/call", "params": {"name": "list_documents", "arguments": {}}}'
# Step 4: DELETE to close session
curl -X DELETE {url}/api/mcp/sse?apiKey=mdshare_abc123... \
-H "mcp-session-id: YOUR_SESSION_ID"
4. API Key Management
| Action | Endpoint | Auth |
|---|---|---|
| List keys | GET /api/user/api-keys | Web session (JWT cookie) |
| Create key | POST /api/user/api-keys {"name":"...", "expiresInDays": 30} | Web session |
| Revoke key | DELETE /api/user/api-keys/:id | Web session |
Key format: mdshare_ + 64 hex characters (e.g. mdshare_a1b2c3d4e5f6...)
Limits:
- Max 10 active keys per user
- Default expiry: 30 days
- Key is shown only once on creation — save it immediately
5. OAuth 2.0 (for custom clients)
If you're building a custom MCP client, use the standard Authorization Code + PKCE flow.
Discovery
GET /.well-known/oauth-authorization-server
{
"issuer": "{url}",
"authorization_endpoint": "{url}/oauth/authorize",
"token_endpoint": "{url}/oauth/token",
"scopes_supported": ["mcp:read", "mcp:tools"],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"code_challenge_methods_supported": ["S256"]
}
Flow
- Generate PKCE
code_verifier(43–128 chars, unreserved) - Compute
code_challenge = base64url(sha256(code_verifier)) - Redirect user to:
/oauth/authorize?client_id=&redirect_uri=YOUR_APP&response_type=code&code_challenge=CHALLENGE&code_challenge_method=S256 - User authenticates, gets redirected back with
?code=... - POST to
/oauth/token:grant_type=authorization_code&code=CODE&client_id=CLIENT_ID&redirect_uri=YOUR_APP&code_verifier=VERIFIER - Response:
json
{ "access_token": "...", "token_type": "Bearer", "expires_in": 2592000, "refresh_token": "...", "scope": "mcp:read mcp:tools" } - Use
Authorization: Bearer <access_token>for MCP calls - Refresh with:
grant_type=refresh_token&refresh_token=...&client_id=...
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
authenticateMcp: no credentials found / 401 | No auth sent | Add Authorization: Bearer mdshare_... header or ?apiKey= param |
authenticateMcp: trying OAuth token → token not found | Expired or invalid token | Re-authenticate via OAuth flow or use API key |
405 Method Not Allowed on /api/mcp/mcp | Wrong path | Use /api/mcp/sse |
Mcp-Session-Id header is required | Second request missing session ID | Save session ID from first POST initialize response |
Session not found | Session expired (5-min TTL) | Re-initialize with a new POST |
| OAuth redirect loops | Cookie not set (cross-domain popup) | Try a different browser or enable third-party cookies |
Key difference: ChatGPT vs Grok
- ChatGPT uses native OAuth popup → auto-discovers endpoints → gets token → works
- Grok (and most other clients) may not auto-discover OAuth or send credentials properly. Using an API key with
?apiKey=in the URL is the most reliable fallback.