# Building a Robinhood Trading MCP Risk Gateway with TypeScript

> Source: <https://dev.to/hamssog/building-a-robinhood-trading-mcp-risk-gateway-with-typescript-37bl>
> Published: 2026-10-07 05:39:35+00:00

AI agents can now do more than answer questions.

They can interact with external applications through tools.

Robinhood's Trading MCP is one example: an external AI agent can connect to Robinhood and use supported account, portfolio, market-data, watchlist, equities, options, crypto, scanner, alert, and order-related tools. Robinhood also provides trade-approval controls for agentic trading.

That creates a new engineering problem.

The difficult part is not:

```
AI
 ↓
Place Order
```

The difficult part is:

```
AI Agent
   ↓
Trading MCP
   ↓
Permission Layer
   ↓
Risk Gateway
   ↓
Approval / Policy
   ↓
Order Execution
   ↓
Audit
   ↓
State Reconciliation
```

For this project, I am building a reusable **Robinhood Trading MCP Risk Gateway** with TypeScript.

The goal is to demonstrate how I would put deterministic controls between an AI agent and trading execution.

This is not an LLM stock-prediction system.

It is an execution-control system.

An AI model can interpret instructions and select tools.

It should not automatically become the final authority over trading limits.

Consider:

```
User:
"Buy $5,000 of this stock."
```

The agent might generate:

```
symbol = XYZ
side = buy
amount = $5,000
```

But the application's risk policy might say:

```
maximum order = $1,000
```

The gateway should reject the request.

```
AI Agent
   ↓
Trade Intent
   ↓
Risk Gateway
   ↓
REJECTED
```

This is the central design principle:

**The agent can propose an action, but deterministic application code decides whether that action is allowed.**

The initial architecture is:

```
+----------------------+
| AI Agent             |
+----------+-----------+
           |
           v
+----------------------+
| Robinhood Trading    |
| MCP                  |
+----------+-----------+
           |
           v
+----------------------+
| Tool Permission      |
| Layer                |
+----------+-----------+
           |
           v
+----------------------+
| Trade Intent         |
| Validation           |
+----------+-----------+
           |
           v
+----------------------+
| Risk Gateway         |
+----------+-----------+
           |
           v
+----------------------+
| Approval / Policy    |
+----------+-----------+
           |
           v
+----------------------+
| Execution            |
+----------+-----------+
           |
           v
+----------------------+
| Audit / State        |
+----------------------+
```

The gateway sits in the middle.

That makes the execution boundary explicit.

Robinhood's current Trading MCP documentation describes MCP as a mechanism for connecting an external AI agent to Robinhood so the agent can access information and take supported actions. Robinhood documents connections for platforms including Claude, ChatGPT, Codex, Cursor, Grok, Perplexity, OpenClaw, Replit, and others.

The external agent uses a dedicated MCP account for trading.

The agent can read information from the user's Robinhood accounts, while trading through the dedicated Agentic/MCP account.

That distinction is important when designing an application around the integration.

The first permission boundary I want is the separation between reading and changing state.

Conceptually:

```
Read
----
get_accounts
get_portfolio
get_watchlists
get_market_data
get_positions
get_history
```

versus:

```
Write
-----
place order
cancel order
modify order
```

Robinhood's current tool documentation exposes separate categories of account, portfolio, market-data, trading and advanced-order capabilities.

That allows the application to define permissions independently.

A research agent might receive only read permissions.

A portfolio assistant might receive read plus proposal permissions.

A trading agent might receive execution permissions only after additional policy checks.

I want the repository to remain simple:

```
robinhood-mcp-risk-gateway/
├── src/
│   ├── agent/
│   │   └── agent-types.ts
│   │
│   ├── mcp/
│   │   ├── mcp-client.ts
│   │   └── tool-registry.ts
│   │
│   ├── permissions/
│   │   ├── permission.ts
│   │   └── permission-engine.ts
│   │
│   ├── trades/
│   │   ├── trade-intent.ts
│   │   ├── trade-validator.ts
│   │   └── trade-service.ts
│   │
│   ├── risk/
│   │   ├── risk-policy.ts
│   │   └── risk-engine.ts
│   │
│   ├── approvals/
│   │   └── approval-service.ts
│   │
│   ├── audit/
│   │   └── audit-log.ts
│   │
│   ├── state/
│   │   └── execution-state.ts
│   │
│   └── index.ts
│
├── test/
│   ├── unit/
│   └── integration/
│
├── examples/
├── docs/
├── package.json
├── tsconfig.json
├── .env.example
└── README.md
```

The core business rules should not be coupled directly to the AI model.

I don't want the rest of the application to consume free-form model output.

Instead, convert it into a typed intent.

For example:

```
type TradeIntent = {
  symbol: string;
  side: "buy" | "sell";
  orderType: "market" | "limit";
  quantity?: number;
  notional?: number;
  limitPrice?: number;
  rationale?: string;
};
```

The application can then validate the object before it reaches execution.

The flow becomes:

```
Natural Language
      ↓
AI Agent
      ↓
Structured Trade Intent
      ↓
Schema Validation
      ↓
Risk Validation
```

The AI's output becomes an input to the software system.

It is not the software system itself.

Compare these two outputs.

Free-form:

```
"Let's buy a meaningful amount of XYZ because momentum looks strong."
```

Structured:

```
{
  symbol: "XYZ",
  side: "buy",
  orderType: "market",
  notional: 500
}
```

The second can be validated.

```
max order = $1,000
requested = $500
result = ALLOWED
```

But:

```
max order = $1,000
requested = $5,000
result = REJECTED
```

This is much easier to test.

The risk policy should be deterministic.

A basic model:

```
type RiskPolicy = {
  maxOrderNotional: number;
  maxPositionNotional: number;
  maxPortfolioExposure: number;
  maxDailyLoss: number;
  maxTradesPerDay: number;
  allowedSymbols: Set<string>;
};
```

The risk engine can evaluate:

```
order size
position size
portfolio exposure
daily trading count
daily loss
symbol restrictions
```

The exact rules can be extended later.

The core API can be small:

```
type RiskDecision =
  | {
      allowed: true;
      checks: string[];
    }
  | {
      allowed: false;
      reason: string;
      failedChecks: string[];
    };
```

Then:

```
function evaluateTrade(
  trade: TradeIntent,
  policy: RiskPolicy,
): RiskDecision {
  // deterministic checks
}
```

The output should explain why a trade was accepted or rejected.

A trade might go through:

``` php
Trade Intent
    |
    +--> symbol allowed?
    |
    +--> order size allowed?
    |
    +--> position limit allowed?
    |
    +--> portfolio exposure allowed?
    |
    +--> daily loss limit allowed?
    |
    +--> trading limit allowed?
```

Only when the required checks pass does the trade continue.

```
All checks pass
      ↓
Execution allowed
```

One simple control is restricting which assets the agent can trade.

``` js
const allowedSymbols = new Set([
  "AAPL",
  "NVDA",
  "MSFT",
]);
if (!policy.allowedSymbols.has(trade.symbol)) {
  return {
    allowed: false,
    reason: "SYMBOL_NOT_ALLOWED",
    failedChecks: ["allowedSymbols"],
  };
}
```

This is intentionally boring.

Risk controls should be boring.

Suppose the application has:

```
maximum position = $2,500
```

and the current position is:

```
$2,200
```

A new:

```
$500
```

order should fail because it would push exposure beyond the configured limit.

``` php
Current position
    $2,200
       +
New order
      $500
       =
    $2,700

Limit = $2,500

Result = REJECTED
```

The important part is that the decision is deterministic.

The gateway can also enforce portfolio-level constraints.

```
Technology exposure: 48%
Maximum:             40%
```

An agent might still propose another technology trade.

The risk engine rejects it.

This creates a portfolio-aware execution boundary:

```
Agent
 ↓
Trade
 ↓
Portfolio State
 ↓
Risk Policy
 ↓
Decision
```

Another useful guardrail is a daily loss limit.

```
maximum daily loss = $500
```

If the application detects that the threshold has already been reached:

```
Trade Intent
     ↓
Risk Engine
     ↓
DAILY_LOSS_LIMIT
     ↓
Rejected
```

The agent does not get to override the policy.

Risk is only one layer.

The agent also needs permissions.

I model capabilities such as:

```
READ_ACCOUNT
READ_PORTFOLIO
READ_MARKET_DATA
READ_WATCHLIST
PROPOSE_TRADE
PLACE_ORDER
CANCEL_ORDER
```

Then define which agent has which capability.

```
type AgentPermissions = {
  readAccount: boolean;
  readPortfolio: boolean;
  readMarketData: boolean;
  proposeTrade: boolean;
  placeOrder: boolean;
  cancelOrder: boolean;
};
```

A research agent can have:

```
readAccount = true
readPortfolio = true
readMarketData = true
placeOrder = false
```

An execution agent can be given a narrower set of explicitly required write permissions.

Robinhood currently supports trade approvals for agentic trading. With approvals enabled, the agent can propose trades but the user must review and manually place them in Robinhood; with approvals disabled, eligible trades can be placed by the agent without manual confirmation of each order. Robinhood notes that certain trades may still require approval.

That maps naturally to two application modes.

```
Agent
  ↓
Trade Intent
  ↓
Risk
  ↓
Approval Request
  ↓
Human
  ↓
Robinhood
Agent
  ↓
Trade Intent
  ↓
Risk
  ↓
Policy
  ↓
Robinhood
```

I would build assisted mode first.

The application can represent approval explicitly:

```
type ApprovalStatus =
  | "NOT_REQUIRED"
  | "PENDING"
  | "APPROVED"
  | "REJECTED";
```

The trade lifecycle then becomes:

```
PROPOSED
   ↓
VALIDATED
   ↓
RISK_CHECKED
   ↓
APPROVAL_PENDING
   ↓
APPROVED
   ↓
SUBMITTED
```

Or:

```
RISK_CHECKED
   ↓
REJECTED
```

This makes the execution path easy to audit.

Once an order is submitted, the application still needs to track it.

A useful state machine:

```
id="robinhood-order-state"
PROPOSED
   ↓
VALIDATED
   ↓
APPROVED
   ↓
SUBMITTED
   ↓
OPEN
   ↓
FILLED
   ↓
RECONCILED
```

Failure paths:

```
SUBMITTED
   ├── REJECTED
   ├── CANCELED
   └── FAILED
```

The important distinction is:

```
order submitted
order filled
```

The second requires actual execution state.

Every meaningful agent action should produce an audit record.

```
type AuditRecord = {
  id: string;
  timestamp: string;
  agentId: string;
  action: string;
  tool: string;
  inputHash?: string;
  riskDecision?: string;
  approvalStatus?: string;
  result?: string;
  error?: string;
};
```

The goal is to answer:

Why did this order happen?

An audit trail should let us reconstruct:

```
User Request
     ↓
Agent Decision
     ↓
Tool Call
     ↓
Risk Decision
     ↓
Approval
     ↓
Order
     ↓
Result
```

The architecture should enforce one execution route.

Not:

```
Agent
 ├── MCP
 ├── Direct API
 └── Another execution path
```

Instead:

```
Agent
   ↓
Trading MCP
   ↓
Risk Gateway
   ↓
Execution
```

There should be one clearly defined path to a trading action.

That makes permissions, logging, testing, and monitoring much easier.

Robinhood's current agent tooling includes account, portfolio, watchlist, market-data, equities, options, crypto, scanner, alerts, and advanced order capabilities.

That means the agent can potentially build decisions from multiple sources:

```
Portfolio
     +
Market Data
     +
Watchlist
     +
Trading Rules
     ↓
Trade Proposal
```

The important engineering boundary remains:

```
Data
 ↓
Analysis
 ↓
Trade Intent
 ↓
Risk
 ↓
Execution
```

A simple trading bot might know only:

```
symbol
price
signal
```

A portfolio-aware agent can incorporate:

```
current positions
buying power
existing exposure
trade history
P&L
Agent:
"Buy $500 of XYZ."

Portfolio:
Existing XYZ = $2,200

Risk:
Maximum XYZ position = $2,500

Result:
REJECT
```

The agent does not need to be trusted to remember the limit.

The policy engine enforces it.

Here is an example from start to finish:

```
User
 |
 | "Find a momentum opportunity
 |  and propose a $500 trade."
 |
 v
AI Agent
 |
 | market-data tools
 v
Robinhood MCP
 |
 v
Trade Intent
 |
 v
Risk Gateway
 |
 +--> symbol check
 +--> order-size check
 +--> position check
 +--> portfolio check
 |
 v
Approval
 |
 v
Order
 |
 v
Robinhood
 |
 v
Execution State
 |
 v
Audit Log
```

This is the architecture I want the GitHub project to demonstrate.

The core service can have one main operation:

```
async function processTradeIntent(
  intent: TradeIntent,
): Promise<TradeDecision> {
  validateTradeIntent(intent);

  const riskDecision = riskEngine.evaluate(intent);

  if (!riskDecision.allowed) {
    return {
      status: "REJECTED",
      riskDecision,
    };
  }

  return approvalService.handle(intent);
}
```

The important part is that the AI model does not call the broker directly.

It submits a typed request into the application's controlled pipeline.

Risk logic should be highly testable.

```
max order = $1,000

$500  → allowed
$1,000 → allowed
$1,001 → rejected
```

Position limits:

```
current = $2,000
limit   = $2,500

order = $400 → allowed
order = $500 → allowed
order = $501 → rejected
```

Symbol restrictions:

```
AAPL → allowed
XYZ  → rejected
```

Daily loss:

```
daily loss = $490
limit      = $500

new trade allowed if other constraints pass
```

Tests should cover boundary values.

Permission tests should verify that an agent cannot use tools it does not have.

```
Research Agent
    ↓
get_portfolio
    ↓
ALLOW
Research Agent
    ↓
place_order
    ↓
DENY
```

This gives the permission layer a simple and deterministic contract.

Test both modes.

Assisted:

```
Trade
 ↓
Risk passes
 ↓
Approval pending
 ↓
User approves
 ↓
Execution
```

Rejected:

```
Trade
 ↓
Risk passes
 ↓
Approval pending
 ↓
User rejects
 ↓
No execution
```

Automated:

```
Trade
 ↓
Risk passes
 ↓
Policy permits automation
 ↓
Execution
```

These tests should be independent of the actual AI model.

The system should also handle:

```
MCP unavailable
tool timeout
invalid tool result
risk rejection
approval timeout
order rejected
order canceled
execution failure
state synchronization failure
```

The goal is not to make the AI appear perfect.

The goal is to make the surrounding system behave predictably when the AI or external infrastructure is imperfect.

The gateway must not become a place where secrets are casually stored.

Do not:

```
log credentials
commit .env files
store private secrets in audit records
print authentication tokens
```

Use environment variables or an appropriate secret-management layer.

The repository should contain:

```
.env.example
```

but never actual credentials.

The risk gateway does not have to be limited to one strategy.

It can sit underneath:

```
Momentum Agent
     ↓
Risk Gateway

Rebalancing Agent
     ↓
Risk Gateway

Portfolio Agent
     ↓
Risk Gateway

Research + Execution Agent
     ↓
Risk Gateway
```

The agent changes.

The controls remain.

The project fits into a larger trading system:

```
                         USER
                           |
                           v
                       AI AGENT
                           |
                           v
                 ROBINHOOD TRADING MCP
                           |
                           v
                 +--------------------+
                 | Permission Layer   |
                 +----------+---------+
                            |
                            v
                 +--------------------+
                 | Trade Intent       |
                 | Validation         |
                 +----------+---------+
                            |
                            v
                 +--------------------+
                 | Risk Gateway       |
                 +----------+---------+
                            |
                            v
                 +--------------------+
                 | Approval / Policy  |
                 +----------+---------+
                            |
                            v
                 +--------------------+
                 | Robinhood Order    |
                 +----------+---------+
                            |
                            v
                      ORDER STATE
                            |
                            v
                       AUDIT LOG
```

The gateway is the control point.

A traditional bot often looks like:

```
Signal
 ↓
Order
```

A controlled agentic trading system looks more like:

```
User Intent
     ↓
AI Agent
     ↓
Tools
     ↓
Structured Intent
     ↓
Permission
     ↓
Risk
     ↓
Approval / Policy
     ↓
Execution
     ↓
Audit
     ↓
Reconciliation
```

The valuable statement is not:

"I can connect ChatGPT to Robinhood."

The stronger statement is:

**I can build the controlled infrastructure around an AI trading agent.**

That includes:

```
AI integration
+
MCP
+
Tool permissions
+
Structured outputs
+
Risk controls
+
Approval workflows
+
Execution
+
Auditability
+
State management
+
Failure handling
```

That is much closer to what a real trading product requires.

```
                       ROBINHOOD AI AGENT
                               |
                               v
                       TRADING MCP
                               |
                               v
                      TOOL PERMISSIONS
                               |
                               v
                     STRUCTURED INTENT
                               |
                               v
                        RISK ENGINE
                               |
                     +---------+---------+
                     |                   |
                     v                   v
                  REJECT              APPROVE
                                         |
                                         v
                                  TRADE APPROVAL
                                         |
                                         v
                                    ROBINHOOD
                                         |
                                         v
                                   ORDER STATE
                                         |
                                         v
                                    AUDIT LOG
                                         |
                                         v
                                  RECONCILIATION
```

The core principle is simple:

**AI proposes. Software validates. Policy controls. Robinhood executes.**

That is the architecture I am using to explore the new generation of **Robinhood agentic trading infrastructure**.
