Skip to content

Danultimate/FinMCP

Repository files navigation

Financial Data MCP Server

A production-grade Model Context Protocol server implementing enterprise security, observability, and AI-agent safety controls.

Stack: Node.js 20 + TypeScript · OAuth 2.1 (RS256 JWT) · OpenTelemetry · Prometheus · Jaeger


What it demonstrates

Capability Implementation
OAuth 2.1 scoped auth Per-tool scope enforcement, RS256 JWT validation, JWKS caching
Human-in-the-loop elicitation Two-phase write pattern: initiate → confirm, with owner binding and idempotency
Full observability OTel spans per tool call, Prometheus metrics, append-only audit log with hash-chain integrity
Security self-scan scan_tools MCP tool (RedForge patterns): 14 checks across tool definitions, injection risk, input validation, scope isolation
Safe financial data Alpha Vantage (stocks) + Open Exchange Rates (forex) — real APIs, no sensitive data

Quick start (mock mode — no API keys needed)

# 1. Install
npm install

# 2. Generate RSA key pair + demo tokens
npm run generate-tokens

# 3. Run the full demo
bash scripts/run-demo.sh

The demo script:

  1. Starts Redis + Jaeger + Prometheus + Grafana via Docker Compose
  2. Exercises all security scenarios (scope denial, elicitation, OWNERSHIP_MISMATCH, self-scan)
  3. Prints the audit log path and observability URLs

Running the server standalone

# Mock data, demo JWT verification
MOCK_EXTERNAL_APIS=true DEMO_MODE=true npm start

The server communicates over stdio (MCP standard). Build first, then add to ~/Library/Application Support/Claude/claude_desktop_config.json:

npm run build   # compiles TypeScript → dist/src/mcp/server.js
{
  "mcpServers": {
    "financial-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/FinMCP/dist/src/mcp/server.js"],
      "env": {
        "DEMO_MODE": "true",
        "DEMO_TOKEN_ROLE": "admin",
        "MOCK_EXTERNAL_APIS": "false",
        "ALPHA_VANTAGE_API_KEY": "your_key",
        "OPEN_EXCHANGE_APP_ID": "your_app_id",
        "OTEL_EXPORTER_OTLP_ENDPOINT": "http://localhost:4318",
        "OTEL_SERVICE_NAME": "financial-mcp-server",
        "AUDIT_LOG_PATH": "/absolute/path/to/FinMCP/logs/audit.log",
        "REDIS_PENDING_OPS_TTL": "600",
        "RATE_LIMIT_READ_BUCKET": "60",
        "RATE_LIMIT_READ_REFILL_SECONDS": "60",
        "RATE_LIMIT_WRITE_BUCKET": "10",
        "RATE_LIMIT_WRITE_REFILL_SECONDS": "60"
      }
    }
  }
}

Note: Use absolute paths throughout — Claude Desktop spawns the process without a shell so relative paths and .env file loading do not work. After any code change run npm run build and restart Claude Desktop.


Auth: passing tokens

Tokens are passed via _meta.authorization in MCP requests:

{
  "method": "tools/call",
  "params": {
    "name": "get_exchange_rates",
    "arguments": { "base_currency": "USD" },
    "_meta": { "authorization": "Bearer <token>" }
  }
}

Demo tokens (after npm run generate-tokens):

Token Scope Can call
read_token read:rates All read tools
write_token read:rates write:alerts All tools + elicitation write flow
admin_token admin All tools including scan_tools

Tools

Read (no elicitation)

Tool Description
get_exchange_rates FX rates for a base currency (up to 10 pairs)
get_stock_quote Live stock quote (price, volume, daily change)
convert_currency Currency conversion with live rate
get_portfolio_summary P&L summary for a list of positions

Write (elicitation required)

Tool Description
set_price_alert Create a price threshold alert
delete_price_alert Delete an existing alert
update_alert_threshold Update the threshold on an alert

Meta

Tool Scope Description
confirm_operation write:alerts Confirm a pending elicitation (Phase 2)
scan_tools admin Run RedForge security scan against all tool definitions

Write flow (elicitation)

Agent → set_price_alert(params)
      ← { type: "elicitation_required", operation_id: "elicit_abc", prompt: "...", expires_at: "..." }

Agent → confirm_operation({ operation_id: "elicit_abc" })
      ← { type: "operation_completed", result: { alert_id: "...", status: "active" } }

Security properties enforced:

  • Owner binding — only the user who initiated can confirm (OWNERSHIP_MISMATCH otherwise)
  • Idempotency — confirming twice returns already_executed with cached result
  • TTL — operations expire after 10 minutes (configurable via REDIS_PENDING_OPS_TTL)

Observability

Endpoint Default What
Prometheus metrics http://localhost:9464/metrics mcp_tool_calls_total, mcp_tool_call_duration_ms, mcp_elicitations_total, mcp_scope_denials_total
Jaeger UI http://localhost:16686 Full OTel traces per tool call with identity + scope attributes
Grafana http://localhost:3001 Dashboards (pre-configured Prometheus + Jaeger datasources)
Audit log ./logs/audit.log Append-only JSONL with hash-chain integrity

With real API keys

# .env
ALPHA_VANTAGE_API_KEY=your_key       # alphavantage.co — free tier: 25 calls/day
OPEN_EXCHANGE_APP_ID=your_app_id     # openexchangerates.org
MOCK_EXTERNAL_APIS=false

Daily quota for Alpha Vantage is tracked in-process. When exhausted, forex tools automatically fall back to Open Exchange Rates.


With a real OAuth provider

OAUTH_ISSUER=https://your-auth-server.com
OAUTH_AUDIENCE=financial-mcp-server
OAUTH_JWKS_URI=https://your-auth-server.com/.well-known/jwks.json
DEMO_MODE=false

The server caches JWKS in memory (TTL: JWKS_CACHE_TTL_SECONDS, default 1h) and serves stale keys if the issuer is temporarily unreachable.


Tests

npm test          # run all tests (vitest)
npm run build     # compile TypeScript → dist/  (required before connecting to Claude Desktop)
npm run dev       # run with tsx watch for local development (no build step needed)

20 tests covering: elicitation store (TTL, idempotency, expiry), scope enforcement (all tool/scope combos), RedForge scanner (7 check scenarios), and mock data clients.


TraceForge integration

TraceForge (pip install traceforge-llm[anthropic]) is a Python observability library. This server produces OTel-native spans in the same format TraceForge expects, so Python-side test harnesses can validate traces using tf_assert and tf_snapshot fixtures against the OTel backend.


RedForge

Security scan checks are implemented inline in src/redforge/ (14 checks across 4 categories). When RedForge is published to npm, scanner.ts becomes a one-line import swap — the SecurityScanResult interface is forward-compatible.

About

Production-grade MCP server with OAuth 2.1, elicitation, OTel observability, and self-scanning security controls. Reference implementation for AI agents over financial data.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors