On October 15, 2026, we launched remote MCP servers for three data products: PMWallets (Polymarket traders ranked by on-chain realized PnL), LPSignal (concentrated-liquidity pools ranked by net APR) and OutcomeTick (historical data and backtesting for Polymarket and Predict.fun crypto up/down markets). They share a design with a protected URL that triggers OAuth and a separate anonymous URL. PMWallets uses Streamable HTTP with stateless JSON responses.
We tested them with claude.ai, Claude Code, Codex CLI and ChatGPT developer mode. Most of what broke was in OAuth discovery and client registration; a code review found one more problem in refresh-token cleanup.
Two URLs: one that returns 401, one that doesn't
Every server has a pair:
| Protected | Anonymous | |
|---|---|---|
| PMWallets | https://api.pmwallets.com/mcp |
https://api.pmwallets.com/mcp/public |
| LPSignal | https://api.lpsignal.app/v1/mcp |
https://api.lpsignal.app/v1/mcp/public |
| OutcomeTick | https://outcometick.com/v1/mcp |
https://outcometick.com/v1/mcp/public |
An MCP client starts the OAuth flow when the server answers 401 with a WWW-Authenticate header pointing at the protected-resource metadata (RFC 9728):
HTTP/2 401
www-authenticate: Bearer resource_metadata="https://api.pmwallets.com/.well-known/oauth-protected-resource/mcp", scope="pmwallets"
If an unauthenticated request gets a 200 and a tool list instead, the client stays anonymous. That rules out one URL that serves public tools to everyone and account tools to signed-in users. Each main URL returns 401 without credentials, and the anonymous tools live at /public, which never asks.
Tool annotations in Codex and claude.ai
On PMWallets, the read tools (leaderboard, entity, market, fills, export_quote and others) carry readOnlyHint: true. The subscription and purchase tools, subscribe, buy_export and cancel_subscription, carry destructiveHint: true. LPSignal's 8 tools and OutcomeTick's 14 are all read-only; neither server has spending tools.
{ "name": "leaderboard",
"annotations": { "readOnlyHint": true, "destructiveHint": false, "idempotentHint": true } }
In the recorded tests:
- Codex CLI called PMWallets and OutcomeTick read-only tools without approval flags.
- claude.ai grouped PMWallets read tools under "Read-only tools" in the connector settings.
Annotations are hints that clients may ignore. For PMWallets purchases, the agent quotes first and waits for the user's consent, and buy_export also requires maxPriceCents, the price cap the user agreed to.
Mention every capability in the server instructions
PMWallets sells past fills of a trader as files, one per wallet per UTC day, covering about the last two months. The tools include export_options, export_quote, buy_export and export_file.
Early on, the server instructions didn't mention history exports, and Claude told a user that history from before the subscription was unavailable, although the export tools were already there. The instructions now say that past fills are sold separately as exports and which tool does what.
OAuth: what the authorization server does
PMWallets runs an OAuth 2.1 authorization server with:
- Discovery through RFC 8414 (authorization server metadata) and RFC 9728 (protected resource metadata).
- Client ID Metadata Documents (CIMD), where the
client_idis a URL hosting client metadata, and Dynamic Client Registration (DCR). LPSignal also supports both; its claude.ai, Claude Code and Codex tests used CIMD. OutcomeTick's Claude Code and Codex tests used CIMD. - PKCE with S256 and the RFC 8707
resourceparameter. Tokens are not bound to a URL path; MCP-only access is enforced separately. - Access tokens that last one hour and are accepted for MCP, not the REST API.
- Refresh tokens that last 30 days and rotate on every use. Replaying an old token revokes the whole connection, provided the server still retains the record needed to recognize it.
- RFC 7009 revocation, an authorization page that cannot be framed, and a "Connected AI apps" list where users can disconnect a client.
LPSignal also has one-hour MCP-only access tokens, rotating 30-day refresh tokens, RFC 7009 revocation and a "Connected AI apps" list.
OutcomeTick has separate API keys for its data, smart-money and backtest products. The authorization page has a field for each, and each field accepts only that product's key.
ChatGPT's CIMD document says private_key_jwt
ChatGPT's CIMD document declares:
{ "token_endpoint_auth_method": "private_key_jwt" }
All three products hit a compatibility issue with this declaration. The same document lists none in token_endpoint_auth_methods_supported. Treating ChatGPT as a public client using PKCE worked without implementing RFC 7523 JWT client authentication.
Refresh-token rotation and the cleanup job
Reuse detection depends on retaining records of refresh tokens that have already been exchanged.
PMWallets had a cleanup job that deleted used refresh tokens once they expired. That looks harmless, since an expired token can't be exchanged anyway. But once the row is gone, a replayed token is no longer "used", just unknown. After about 30 days, a replay was rejected as an ordinary bad token, and the connection it belonged to stayed alive.
Codex review found this in PMWallets, and OutcomeTick confirmed the same issue.
A wrong URL that half works
LPSignal and OutcomeTick use /v1/mcp; PMWallets originally used /mcp. A user pasted https://api.pmwallets.com/v1/mcp into ChatGPT. Sign-in succeeded, then ChatGPT showed:
Authentication succeeded, action discovery failed
That route did not exist. Without a token, ChatGPT's POST got a 403 from the Origin check, and the path-specific metadata returned 404. ChatGPT fell back to the root metadata, which described /mcp, and completed OAuth. It then POSTed to /v1/mcp with the token, received 404 and reported a discovery failure.
PMWallets now serves /v1/mcp as an alias with its own protected-resource metadata, with resource set to https://api.pmwallets.com/v1/mcp. The 401 points to that metadata, and the alias is included in the Origin exemption and rate limits.
DNS lookups for URLs users control
PMWallets resolves user-supplied hosts for webhooks and CIMD metadata fetches.
In Node, dns.lookup uses getaddrinfo on the libuv thread pool, which has four threads by default. The same pool runs file system calls and some crypto. A user who controls the hostname can make lookups slow, and a few of them hold up unrelated work in the process. For these lookups we switched to the c-ares-based dns.Resolver, which doesn't use the pool, with an overall deadline.
The second problem was in net.BlockList. We added ::ffff:0:0/96 to block IPv4-mapped IPv6 addresses. With that rule in the list, every plain IPv4 address was classified as internal, public hosts included. Convert a mapped address to IPv4 yourself and check it against the IPv4 rules instead.
Reading large files through MCP
PMWallets' export_file streams and decompresses the download, returning only the requested page. A call scans at most 50,000 lines, concurrent reads are limited, and the read aborts when the client disconnects. Without the disconnect check, the server would keep downloading and decompressing after the client had gone away.
Batches and rate limits
All three servers rate-limit MCP requests. LPSignal and OutcomeTick reject JSON-RPC batches. PMWallets accepts up to 10 messages per batch and executes them one at a time.
Connecting
For Claude Code and Codex CLI, add the protected URL:
# Claude Code
claude mcp add --transport http pmwallets https://api.pmwallets.com/mcp
# Codex CLI
codex mcp add pmwallets --url https://api.pmwallets.com/mcp
Use the LPSignal or OutcomeTick URL from the table to connect those servers. PMWallets was also tested with a claude.ai custom connector and a ChatGPT web developer-mode custom connector. LPSignal supports wallet sign-in with any wallet and has a free account. PMWallets accepts OAuth or a bearer API key; OutcomeTick's authorization page accepts its product-specific keys.
The /public URLs need no account. PMWallets exposes a 20-row leaderboard sample, trader records and market lookups. LPSignal exposes everything except my_account. OutcomeTick exposes coverage and plans, backtest capacity and quotes, strategy checks, and smart-money plans and coverage.
All three are listed in the official MCP Registry (io.github.polymarketwallets/pmwallets, app.lpsignal/lpsignal, io.github.outcometick/outcometick) and have Claude Code plugins. OutcomeTick also ships a local stdio server (ot mcp) with a test that keeps its tool set identical to the remote one. Setup guides: pmwallets.com/ai-agents, LPSignals/lpsignal-mcp, outcometick.com/llms-full.txt.
I build PMWallets, LPSignal and OutcomeTick.
Top comments (0)