MCP Authentication

The TradeStaq MCP server requires authentication before you can access portfolio data, deploy bots, or execute trades. This page covers both audiences:

  • End users running an MCP client like Claude Desktop, Cursor, or Claude Code — authenticate via in-chat tools or browser OAuth. OAuth sessions pick a scope at consent that caps what the agent can do.
  • Third-party MCP client developers connecting to mcp.tradestaq.com directly — use the standards-compliant OAuth 2.1 authorization server with Dynamic Client Registration.

Quick Reference

AudienceMethodHow
End user (npx client)login toolEmail + password in chat
End user (any client)authenticate toolBrowser OAuth, pick a scope, credentials never in chat
End user (scripts)set_token toolPaste existing JWT
Any usercheck_auth toolVerify auth, tier capabilities, wallet balance, and token scope
Third-party MCP clientOAuth 2.1 + DCRSelf-register, then Authorization Code + PKCE with scope
Any userConnected AppsRevoke active access tokens

For End Users — In-Chat Authentication

The MCP server exposes four authentication tools. Your AI client calls them based on plain-English requests.

Method 1: Email and Password (login)

"Log me in to TradeStaq with email user@example.com"

The AI calls the login tool, which returns a JWT on success. The token is stored in the MCP server process for subsequent tool calls.

When to use: Local npx setups where you trust the AI client with credentials.

Method 2: Browser OAuth (authenticate)

"Authenticate me with TradeStaq"

The authenticate tool opens your default browser to a TradeStaq authorization page. After you log in and approve, the token is passed back to the MCP server automatically. Credentials never transit the MCP transport.

When to use: When you use SSO / social login, when you have 2FA enabled, or when you prefer not to type credentials in the AI chat.

Method 3: Manual Token (set_token)

"Set my TradeStaq token to eyJhbGciOiJI..."

You can generate a long-lived JWT from the dashboard and paste it in. Useful for CI pipelines or sharing a session across tools.

Checking Authentication Status (check_auth)

"Am I logged in to TradeStaq?"

The AI calls check_auth which returns your authentication state plus the context an agent needs to decide what it can safely do next. The response includes:

  • id, name, email, telegramLinked — basic user info
  • tier — subscription tier name/slug plus a capabilities block (allowLiveTrading, allowAIBuilder, allowNewsTrading, allowMcpServer)
  • strategyLabBalanceUsd — current wallet balance for wallet-charging tools (e.g. generate_strategy)
  • token — present only for MCP Bearer (OAuth) authentication. Carries clientId, clientName, scope, and expiresAt

Agents are expected to preflight check_auth before invoking gated tools so they can surface scope or balance issues to the user instead of hitting a 403 mid-flow. The response is cached per-token for 30 seconds, so frequent checks don't hit the database.


OAuth Scopes

Remote OAuth tokens carry a scope that caps what the token can do. Scopes are hierarchical — higher scopes imply lower ones.

ScopeCan readCan paper-tradeCan live-trade / charge wallet
mcp:readYesNoNo
mcp:paperYesYesNo
mcp:liveYesYesYes

When a tool requires a higher scope than the token holds, the server returns a structured 403 insufficient_scope response with the required scope echoed back. The agent should relay this to the user and ask them to re-authorize with the right scope — it should not automatically call logout + authenticate without explicit user approval, since broader scopes expand what the agent can do on your account.

Session-cookie authentication (dashboard users) has no scope — dashboard sessions can do whatever the user's subscription tier allows.

Wallet-Charging Tools: acknowledgeCost

Tools that charge your Strategy Lab wallet (currently generate_strategy, $0.50 per experiment, up to 30 experiments per call) require the agent to pass acknowledgeCost: true in the request. Without it, the endpoint returns a cost-estimate envelope (no job queued) so the agent can show you the charge and get explicit approval before committing. Dashboard users are exempt — consent happens in the UI.


For Third-Party MCP Clients — OAuth 2.1 + Dynamic Client Registration

Third-party MCP clients (Claude.ai, ChatGPT, custom MCP hosts) that want to connect to mcp.tradestaq.com/mcp on behalf of their users should use the standards-compliant OAuth 2.1 authorization server. No contact with TradeStaq needed to get started — clients self-register.

Discovery

TradeStaq publishes standard metadata files under the production origin (https://www.tradestaq.com):

EndpointSpecPurpose
/.well-known/oauth-authorization-serverRFC 8414Advertises issuer, endpoints, response_types, grant_types, PKCE support, token-endpoint auth methods
/.well-known/oauth-protected-resourceRFC 9728Advertises the API resource (https://www.tradestaq.com/api) and its authorization server. Also carries mcp_resource for strict MCP 2025-06-18 clients
/.well-known/mcp/server-card.jsonSEP-1649Advertises both the hosted streamable-http endpoint and the stdio npm package

See Discovery & .well-known endpoints for the full catalog.

Dynamic Client Registration (RFC 7591)

POST https://www.tradestaq.com/api/oauth/register
Content-Type: application/json

{
  "client_name": "My MCP Client",
  "redirect_uris": ["https://mcp-client.example.com/oauth/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code"],
  "response_types": ["code"],
  "scope": "mcp"
}

Response returns a client_id. Supports public (PKCE-only) and confidential clients. Register once per client, then reuse the client_id across all end-user authorizations.

Authorization Code Flow with PKCE

  1. Redirect the user to:

    https://www.tradestaq.com/api/oauth/authorize
      ?response_type=code
      &client_id=<client_id>
      &redirect_uri=<redirect_uri>
      &code_challenge=<code_challenge>
      &code_challenge_method=S256
      &state=<random-nonce>
      &scope=mcp
    

    The user logs in (if not already), lands on /oauth/consent, and approves. PKCE is mandatory — plain is banned.

  2. User is redirected back to your redirect_uri with ?code=<single-use-code>&state=<nonce>. Codes expire after 60 seconds and are single-use (atomic GETDEL).

  3. Exchange the code for a JWT:

    POST https://www.tradestaq.com/api/oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=authorization_code
    &code=<code>
    &redirect_uri=<redirect_uri>
    &client_id=<client_id>
    &code_verifier=<code_verifier>
    

    Response: { "access_token": "eyJ...", "token_type": "Bearer", "expires_in": 3600, "scope": "mcp", "refresh_token": "tsr_..." } — a 60-minute access JWT plus a rotating 30-day refresh token.

  4. Send the token on MCP requests as Authorization: Bearer <access_token>.

  5. Refresh before the access token expires (or on a 401) with the rotating refresh token — no browser re-consent:

    POST https://www.tradestaq.com/api/oauth/token
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=refresh_token
    &refresh_token=<refresh_token>
    &client_id=<client_id>
    

    The response returns a fresh access_token and a new refresh_token (rotation) — persist the new one and discard the old. Presenting an already-used refresh token is treated as theft and revokes the whole session (RFC 9700 reuse detection).

Errors follow RFC 6749 §5.2 (JSON bodies with error + error_description).

Loopback Redirect URIs (RFC 8252 §7.3)

For native or CLI MCP clients that listen on http://127.0.0.1:<random_port>/callback, the authorization server accepts any port on the loopback interface at authorization time, not just the exact port registered. This lets a desktop MCP client register once with a placeholder port and bind to a random available port per session.

Confidential Clients

Clients that can keep a secret (server-side MCP hosts) can request token_endpoint_auth_method: "client_secret_basic" or "client_secret_post" at registration. The server issues a client_secret in the registration response. Confidential flows still require PKCE.


Managing Access (Revoke)

Visit Dashboard → Settings → Connected Apps to see every MCP client that has an active token for your account. Each row shows:

  • Client name (from the client_name at registration)
  • Client ID
  • Scope
  • Issued time, expiry time, last used time

Click Revoke to immediately invalidate the token. Revocation is enforced on the hot path (Redis denylist with TTL matching the JWT's remaining lifetime) — subsequent API calls with that token fail with 401. The token's Mongo record is stamped with revokedAt for audit.

Revoking a token ends its entire session — the access token, its rotating refresh-token chain, and any access tokens issued under the same authorization are invalidated together. Other independent sessions (separate authorizations) are unaffected; revoke each to sign out everywhere.


How Tokens Are Stored

DetailValue
FormatJWT (JSON Web Token)
Storage (npx)In-memory during the MCP server process
Storage (remote)Stateless — the client re-sends its bearer per request; nothing is held server-side
Storage (third-party client)Your client's responsibility — treat like any OAuth access token
ExpiryAccess token 60 min; rotating refresh token 30 days (sliding) — silent renewal, no periodic re-consent
Scopemcp:read, mcp:paper, or mcp:live — whichever you granted at the OAuth consent screen (or the legacy mcp for pre-0.3.13.0 tokens, treated server-side as mcp:live)
RevocableYes, at /dashboard/settings/connected-apps (hot-path Redis denylist enforcement)

The npm MCP server does not persist tokens to disk. Close the client and you re-authenticate on next use.


Authentication Flows

In-chat flow (login / authenticate / set_token):

┌──────────┐   login/authenticate   ┌──────────────┐   JWT   ┌──────────┐
│ AI Client│────────────────────────▶│ MCP Server   │────────▶│ TradeStaq│
│          │◀────────────────────────│              │◀────────│   API    │
└──────────┘   token stored in       └──────────────┘  verify └──────────┘
               MCP process memory

Third-party OAuth 2.1 + DCR flow:

┌────────────────┐  1. POST /api/oauth/register       ┌──────────────┐
│ Third-party    │────────────────────────────────────▶│ TradeStaq    │
│ MCP Client     │◀──── client_id ────────────────────│ Auth Server  │
│                │                                    │              │
│                │  2. redirect user → /authorize     │              │
│                │────────────────────────────────────▶│              │
│                │                                    │              │
│                │  3. user consents, get code        │              │
│                │◀───────────────────────────────────│              │
│                │                                    │              │
│                │  4. POST /api/oauth/token (PKCE)   │              │
│                │────────────────────────────────────▶│              │
│                │◀─ access JWT (60m) + refresh (30d) ─│              │
│                │                                    │              │
│                │  5. Bearer JWT on MCP requests     │  ┌──────────┐│
│                │────────────────────────────────────▶│  │TradeStaq ││
│                │                                    │  │   API    ││
│                │◀───────────────────────────────────│  └──────────┘│
└────────────────┘                                    └──────────────┘

Security Considerations

  • Email/password login sends credentials through the MCP transport. On local npx, they stay on your machine. On remote, TLS protects them. Prefer OAuth when your 2FA or SSO requires it.
  • Browser OAuth (authenticate tool or standards-compliant flow) never exposes your password to the AI client.
  • Grant the narrowest scope that works. Research/summary agents only need mcp:read. Paper-trading agents should ask for mcp:paper. Reserve mcp:live for agents you trust with real money.
  • PKCE is mandatory for the OAuth 2.1 server. Clients cannot opt out. code_challenge_method=plain is banned per OAuth 2.1 §7.5.
  • Short authorization code lifetime. Codes expire after 60 seconds and are single-use (atomic GETDEL). A stolen code is useless after one exchange.
  • Tokens are revocable. A compromised token can be invalidated immediately from the dashboard. Revocation hits the hot path — no lag.
  • Refresh tokens with rotation. Access tokens are short-lived (60 min); a rotating 30-day refresh token renews them silently — no periodic browser re-consent. Each refresh returns a new refresh token and invalidates the old one; presenting a used token is treated as theft and revokes the whole session (RFC 9700 reuse detection).
  • Tokens from the deprecated /api/oauth/mcp/token endpoint are rejected as of v0.3.13.0 because they were minted without a JWT identifier and silently bypassed scope enforcement. Clients on @the-staq/tradestaq-mcp v0.2.0+ already use the RFC-compliant /api/oauth/token flow; older configs must re-authenticate.

Next Steps