Skip to main content
Glama
GreenNode-Samples

VN Stock MCP Server

VN Stock MCP Server — GreenNode AgentBase sample

A pure MCP server (not an agent): no LLM, no memory, no chat. Deploy it as an Agent Runtime on AgentBase and register it as an MCP Connector in MCP Gateway. Every agent on the gateway (as permitted by a Policy Group) can then call the 13 Vietnamese stock-market tools through the gateway.

The same image can run in three places — Agent Runtime, vServer/VKS inside the customer's VPC, or on-prem. See Deploy in three places.

CI License: MIT

Architecture

mcp-stock-server architecture

An agent calls a tool through MCP Gateway → Policy Group → the stock connector attaches an API key retrieved from Access Control → the server validates the key (fail-closed) → it calls the 24hMoney API. The same image runs on Agent Runtime, on vServer / VKS in the customer's VPC, or on-premises. See Deploy in three places.

Related MCP server: FinImpulse MCP Server

Why does this repo exist?

GreenNode AgentBase infrastructure does not only run agents. Agent Runtime can run any MCP server as a dedicated runtime. This is the "tool provider" model:

 Agent (LLM + Memory)
        │  tools/call (MCP JSON-RPC) + the agent's IAM/JWT token
        ▼
 ┌──────────────────────────────────────────────────────────────┐
 │ MCP Gateway                                                  │
 │  ① Inbound Auth (IAM / JWT) — authenticates the agent        │
 │  ② Policy Group — may this agent call stock__<tool>?         │
 │  ③ MCP Connector `stock` — Outbound Auth = API Key           │
 │     (key from Access Control, attached as X-Api-Key header)  │
 └──────────────────────────────┬───────────────────────────────┘
                                │  POST /mcp  + X-Api-Key
                                ▼
 ┌──────────────────────────────────────────────────────────────┐
 │ Agent Runtime: this repo (NO LLM)                            │
 │  Security Settings (IP Access Control · Inbound Identity)    │
 │  → middleware validates the API key (fail-closed) → 13 tools │
 └──────────────────────────────┬───────────────────────────────┘
                                ▼
                       24hMoney (public, unofficial API)
  • Agent = the component that thinks (LLM + memory) and calls tools through the gateway.

  • MCP server (this repo) = the component that holds the data. Any agent can call it without knowing where the server runs; the gateway manages the credential, and the agent never sees the API key.

Tools (13)

Every tool returns JSON (as a string). Errors are returned as {"error": "..."} so the agent can read them; no exception is raised.

Market

Shared source top-stock-all (~90 most liquid symbols), cached for 60s.

Tool

Description

market_top_stocks(limit, sort)

Table of ~90 top market symbols. sort: default / change_percent / value / volume / foreign_net_buy

top_gainers(limit)

Top symbols with the largest gains (positive % only)

top_losers(limit)

Top symbols with the deepest losses (negative % only)

most_active(limit)

Top symbols by liquidity (traded value)

stock_quote(symbol)

Quote for one symbol in the top group: price, +/-%, ceiling/floor, matched volume & value, foreign buy/sell

Results include meta: source, number of tracked symbols, last_update (Vietnam time), and a warning if the data is more than 15 minutes old (market closed).

Companies

Applies to every symbol listed on HOSE · HNX · UPCOM (~1.6k companies). symbol accepts only A–Z, 0–9, 2–10 characters.

Tool

Description

search_company(query, limit)

Find a symbol by name / ticker, accent- and case-insensitive. Ranking: ticker match → name match, then larger companies first (priority), exchange order HOSE > HNX > UPCOM

company_profile(symbol)

Full name (VN/EN), listing exchange, business description

price_history(symbol, days)

Daily closing price, volume, value (≤ 30 sessions)

foreign_trading(symbol, days)

Daily foreign buy/sell (≤ 25 sessions)

valuation(symbol)

P/E, P/B, ROE, ROA, EPS, etc. compared with the industry average

dividend_history(symbol, limit)

Dividend history (cash / stock)

business_plan(symbol)

Annual business plan and % completion

company_announcements(symbol, limit)

Corporate disclosures (with PDF links)

Data source and disclaimer

Data comes from the public APIs used by the 24hMoney web/app (api-finance-t19.24hmoney.vn) and requires no API key. This is an unofficial API and may change or be blocked at any time.

For demo/sample use only. This is not investment advice and must not be used for real trading. Data may be delayed or inaccurate.

Units: stock prices are in thousand VND; traded value / market capitalization are in billion VND.

Repo structure

├── Dockerfile                # image: python:3.12-slim, port 8080, /health
├── requirements.txt          # mcp + uvicorn + httpx
├── .env.example              # sample environment variables
├── deploy/                   # deployment outside Agent Runtime (not in the image)
│   ├── vserver/              #   docker compose on vServer (+ optional Caddy TLS)
│   ├── vks/                  #   Kubernetes manifests for VKS
│   └── onprem/               #   on-prem docker compose + check_connectivity.sh
├── src/mcp_server/main.py    # the entire server (single file)
└── tests/                    # 43 hermetic tests (no network calls)

Run locally

export KEY=$(openssl rand -hex 32)          # API key for this run

docker build -t stock-mcp-server .
docker run --rm -p 8080:8080 -e MCP_API_KEYS="$KEY" stock-mcp-server

# health — always open, no key required
curl -s http://localhost:8080/health

Call a tool with an MCP client (Python), passing the key in the X-Api-Key header:

pip install mcp
KEY=$KEY python - <<'PY'
import asyncio, os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

KEY = os.environ["KEY"]

async def main():
    async with streamablehttp_client(
        "http://localhost:8080/mcp", headers={"X-Api-Key": KEY}
    ) as (r, w, _):
        async with ClientSession(r, w) as s:
            await s.initialize()
            tools = await s.list_tools()
            print([t.name for t in tools.tools])
            res = await s.call_tool("search_company", {"query": "hoa phat"})
            print(res.content[0].text)

asyncio.run(main())
PY

Run without Docker: pip install -r requirements.txt && MCP_API_KEYS=$KEY python src/mcp_server/main.py.

Authentication

There are two independent layers of protection; enable both for real deployments.

Layer 1: application-level API key (fail-closed)

The server validates the API key on the /mcp endpoint itself:

Item

Behavior

Configuration

The MCP_API_KEYS environment variable, a comma-separated list of keys. (The legacy STOCK_API_KEY is still accepted)

Accepted headers

X-Api-Key: <key> · Authorization: Bearer <key> · X-Stock-Api-Key: <key> (legacy)

Comparison

Constant-time; keys shorter than 24 characters trigger a log warning → use openssl rand -hex 32

Key configured but caller's key is missing/wrong

401 with WWW-Authenticate

No key configured

503 — the server never opens itself up (fail-closed)

/health, /

Always open (so the runtime can health-probe)

Key rotation: set two keys at the same time, MCP_API_KEYS="old_key,new_key" → update the key on the calling side (Access Control / connector) to new_key → remove old_key from the environment variable. No downtime.

ALLOW_ANONYMOUS=true is only for local runs when you do not want to set a key (/mcp is then fully open if MCP_API_KEYS is empty). Never use it in a deployed environment. If a key is configured, the key is still required even when ALLOW_ANONYMOUS=true.

Layer 2: Agent Runtime Security Settings (platform level)

Configured when you create the Agent Runtime in the console (see the Create runtime documentation). These settings block requests before they reach the container:

  • IP Access Control — the list of source CIDRs allowed to call the runtime endpoint (for example, only the MCP Gateway egress IP range / the corporate network).

  • Inbound Identity — how the runtime authenticates callers: IAM Permissions, JWT, or No authorization.

Note: the MCP Gateway connector calls the runtime with an API Key (see below), so if you enable Inbound Identity on the runtime, choose a mode that is compatible with how the gateway calls in, and re-test tools/list. Layer 1 (API key) remains the mandatory layer for this sample.

Deploy to GreenNode AgentBase

Prerequisites: IAM credentials, an image repository in Container Registry (vCR), and an MCP Gateway (see the skills agentbase-deploy and agentbase-gateway).

1. Build and push the image to Container Registry

docker build --platform linux/amd64 -t vcr.vngcloud.vn/<repo>/stock-mcp-server:v1 .
docker push vcr.vngcloud.vn/<repo>/stock-mcp-server:v1

2. Create the Agent Runtime

Create a runtime from the image above (console or the agentbase-deploy skill), with a flavor such as runtime-s2-general-2x4, and set the environment variable:

export MCP_KEY=$(openssl rand -hex 32)   # RECORD IT — step 3 reuses the same value

# runtime env:
#   MCP_API_KEYS=<value of $MCP_KEY>

When the runtime is ACTIVE (a public endpoint is created automatically):

curl -s https://<endpoint>.agentbase-runtime.aiplatform.vngcloud.vn/health   # → 200, no key required

In the runtime's Security Settings, set IP Access Control (allowed CIDRs) and choose a suitable Inbound Identity (see Layer 2).

About secrets in env: the AgentBase documentation specifies that runtime environment variables are for non-sensitive configuration. An API key is a secret, so the proper place to store it is Access Control (step 3). However, this server reads its key from the environment variable MCP_API_KEYS, so in this sample we set the same key in both places: the runtime env (so the server can validate it) and Access Control (so the gateway can attach it to requests). This is a demo trade-off; for production, rotate keys regularly and restrict who can view the runtime configuration.

3. Store the key in Access Control

Console → Access Control → create an API Key provider named stock-mcp-key whose value is exactly the key $MCP_KEY from step 2 (or use the agentbase-identity skill).

4. Register the MCP Connector in MCP Gateway

Console → MCP Gateway → select the gateway → Add Custom Connector:

  • Name: stock

  • Endpoint: https://<endpoint>.agentbase-runtime.aiplatform.vngcloud.vn/mcp

  • Outbound Auth: API Key (flow 2LO — one shared key for all callers)

    • Header name: X-Api-Key (leave prefix empty)

    • Provider: stock-mcp-key

Or via the API (JSON Merge Patch — send the complete desired targets array):

TOKEN=$(bash ~/.agents/skills/agentbase/scripts/get_token.sh)
curl -X PATCH "https://agentbase.api.vngcloud.vn/gateway/api/v1/gateways/<gw>" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -H 'If-Match: "<resourceVersion>"' \
  -d '{"targets": [ /* existing targets (keep them!) + */ {
        "name": "stock",
        "type": "MCP",
        "endpoint": "https://<endpoint>.agentbase-runtime.aiplatform.vngcloud.vn/mcp",
        "outboundAuth": {
          "type": "APIKEY",
          "flow": "2LO",
          "headerName": "X-Api-Key",
          "headerValuePrefix": "",
          "providerName": "stock-mcp-key"
        }
  }]}'

Warning: targets is a full replacement. Fetch the current list (GET /gateways/<gw>) and send all existing connectors as well, otherwise they will be lost.

5. Policy Group — who can call which tool?

The tools of the stock connector appear on the gateway with actions of the form stock__<tool>. With no Policy Group attached, every tools/call returns 403 (tools/list is always allowed so that agents can discover tools). Example policy allowing one agent to call all 13 tools:

{
  "effect": "allow",
  "principal": "iam:<agent-id>",
  "actions": [
    "stock__market_top_stocks", "stock__top_gainers", "stock__top_losers",
    "stock__most_active", "stock__stock_quote",
    "stock__search_company", "stock__company_profile", "stock__price_history",
    "stock__foreign_trading", "stock__valuation", "stock__dividend_history",
    "stock__business_plan", "stock__company_announcements"
  ],
  "resources": ["gateway:<gateway-name>"]
}

Create a Policy Group containing the policy above and attach it to the gateway (agentbase-policy skill). Do not mix "*" with a list of specific actions — choose one or the other.

End-to-end flow

Agent → MCP Gateway (Inbound Auth: IAM/JWT)
      → Policy Group (is stock__<tool> allowed?)
      → Connector `stock` (Outbound Auth: API Key from Access Control)
      → This Agent Runtime (/mcp, validates the API key)
      → 24hMoney

No agent code changes are needed: the agent only sees the stock__... tools through the gateway and uses its own IAM token.

Deploy in three places

The same image (the Dockerfile at the repo root) can be deployed in three places, matching the three scenarios customers most often ask about. Agent Runtime and MCP Gateway run in the GreenNode-managed AgentBase VPC (172.30.0.0/16), not in the customer's VPC. The gateway's network mode therefore determines where it can reach the MCP server.

(a) Agent Runtime

(b) vServer / VKS in the customer's VPC

(c) On-prem (data center)

When to use

Fastest; GreenNode manages the infrastructure

The MCP server must not be exposed to the Internet and sits in a private VPC

Data/servers must stay in the data center

Gateway Network mode

Public

Private (select the VPC + Subnet of the vServer/VKS)

Private + Route CIDRs including the on-prem CIDR

Connector URL

https://<endpoint>.agentbase-runtime.aiplatform.vngcloud.vn/mcp

https://xx.xx.x.x:8443/mcp

https://xx.xx.x.x:8443/mcp (IP of the on-prem MCP host)

Outbound Auth

API Key — header X-Api-Key, provider in Access Control

API Key — header X-Api-Key, provider in Access Control

API Key — header X-Api-Key, provider in Access Control

Additional connectivity

—

The VPC must already be privately connected to AgentBase (contact GreenNode support), with DNS resolution enabled; security group allowing 172.30.0.0/16

Site-to-Site VPN / Interconnect (customer side), bidirectional routes including 172.30.0.0/16, firewall allowing 172.30.0.0/16

Guide

Deploy to GreenNode AgentBase (above)

deploy/vserver/ · deploy/vks/

deploy/onprem/

Notes:

  • The gateway's Network mode is chosen at creation time (step ⑤ Network & Compute) and cannot be changed afterward. A Private gateway keeps traffic on the private network, so an MCP server reachable only over the Internet (as in (a)) requires a separate Public gateway.

  • A Private gateway lists only VPCs that are already privately connected to AgentBase; if yours is not listed, contact GreenNode support to enable it.

  • Wherever the server runs, the Policy Group still uses the same stock__<tool> actions (e.g. stock__search_company). Only the connector's Endpoint changes; there is no need to rewrite policies or agent code.

  • On the on-prem side, confirm with GreenNode whether the request source is NATed (it may no longer be 172.30.0.0/16) before opening the firewall.

  • Addresses of the form xx.xx.x.x/xx in the documentation are placeholders — replace them with your actual ranges.

Technical architecture

  • FastMCP (stateless_http=True) — MCP streamable HTTP at /mcp.

  • Shared fetch_api(): GET requests use a TTL cache (session data 60s; valuation / dividends / business plan / announcements 15 minutes; company directory 6 hours) and 3 retries with backoff; cache_hits / upstream_calls metrics are exposed in /health.

  • symbol validation (^[A-Z0-9]{2,10}$) before it is composed into the upstream request; limit/days are clamped to the allowed range.

  • Vietnam timezone (Asia/Ho_Chi_Minh) for last_update and for detecting stale data after market close.

  • Runtime contract: port 8080 (override with PORT), GET /health → 200, GET / describes the server.

Environment variables

Variable

Default

Description

MCP_API_KEYS

(required)

API keys protecting /mcp; multiple keys separated by , (rotation). Empty ⇒ /mcp returns 503

ALLOW_ANONYMOUS

(empty)

true ⇒ allows /mcp without a key when no key is configured. Local development only

STOCK_API_BASE_URL

https://api-finance-t19.24hmoney.vn

Override the data source (internal proxy/mirror)

HTTP_TIMEOUT_SECONDS

10

Timeout for upstream API calls

CACHE_TTL_SECONDS

60

Cache TTL for session data (top stocks, prices, foreign trading)

SLOW_CACHE_TTL_SECONDS

900

Cache TTL for slow-changing data (dividends, business plan, valuation, announcements)

COMPANY_CACHE_TTL_SECONDS

21600

Cache TTL for the company directory (6 hours)

PORT

8080

Listening port

Test

pip install -r requirements.txt pytest pytest-asyncio
python -m pytest tests/ -v   # 43 tests, hermetic — no network calls

Coverage: market tool sorting/limits, search_company ranking, symbol validation, registration of all 13 tools, and the fail-closed auth middleware (503 / 401 / 3 header styles / 2-key rotation).

  • Skill agentbase-deploy — Agent Runtime + Container Registry

  • Skill agentbase-identity — Access Control: storing the API key provider

  • Skill agentbase-gateway — MCP Connector, inbound/outbound auth, attaching a Policy Group

  • Skill agentbase-policy — writing stock__<tool> policies

  • Two sample agents that share the same infrastructure: sample-travel-buddy, sample-zalo-restaurant

License

MIT — the data belongs to 24hMoney and is for demo purposes only.

Related MCP Connectors

  • Real SEC, 13F, insider, congress & macro data your AI agent can cite. Hosted MCP, 24 tools.

  • A Model Context Protocol server exposing real-time and historical Colombo Stock Exchange (CSE) data to AI agents and LLM applications. Provides quotes and OHLCV price history, full financial statements (income, balance sheet, cash flow), pre-computed technicals (moving averages, RS ratings, volume signals), macroeconomic indicators, corporate actions, and rule-based screening across CSE stocks and sector indices, everything needed to build CSE-aware trading assistants, research tools, and market-analysis agents. This is the official MCP server of www.ceyloncharts.com

  • Your agent needs markets — prices and fundamentals for listed companies, the filings behind them, crypto, and what the prediction markets put the odds at. **What you can ask for** • "Pull this company's income statement, cash flow and balance sheet for the last 8 quarters." • "What did insiders buy or sell, and when?" • "Snapshot prices for these 50 tickers, then the OHLC history for the three that moved." • "What are the current odds on this event across Kalshi and Polymarket?" • "Screen for companies matching these financial criteria." **How to use it** Point any MCP client at https://mcp.aisa.one/finance/mcp and sign in with OAuth — there is no key to create or paste. 49 tools: prices and snapshots, income statements, balance sheets and cash flows, metrics and ratios, earnings and analyst estimates, filings and line-item search, insider trades, macro interest rates, news, a screener; CoinGecko spot prices, market tables, OHLC, per-venue tickers and trending; Kalshi and Polymarket markets and trades; plus EDINET filings for Japan. **Why this rather than the source** Equities, crypto and event markets behind one account, so a cross-asset question is one conversation. **It is also a door to the rest** The same login reaches 26 sources and 580+ operations. Read the number here, then ask the same agent what X is saying about the ticker today — without adding a second server. **What it costs** Finding and inspecting an operation is free. Running one is billed per call at API prices, with no seat and no monthly minimum, and every call takes max_price_usd so an agent cannot overspend by accident. **Where else it reaches** https://mcp.aisa.one/marketpulse/mcp · /crypto-market-data/mcp · /prediction-market-data/mcp · /stock-pulse/mcp for one slice each.

  • Your agent needs company financials it can compute on — statements, ratios, earnings, estimates, filings and insider activity as structured data, not a PDF. **What you can ask for** • "Give me 8 quarters of income statement, balance sheet and cash flow for this ticker." • "What do analysts estimate for next quarter, and how did the last four surprise?" • "Find this exact line item across every filing." • "Who bought or sold as an insider in the last 90 days?" • "Screen for profitable companies under this valuation with growing revenue." **How to use it** Point any MCP client at https://mcp.aisa.one/marketpulse/mcp and sign in with OAuth — there is no key to create or paste. 21 tools: prices and snapshots, income statements, balance sheets, cash-flow statements, financial metrics and snapshots, earnings, analyst estimates, company facts, filings and filing items, line-item search, a screener, insider trades, macro interest rates, news, plus EDINET documents and filing digests for Japanese issuers. **Why this rather than the source** Statements as fields you can compute on, and a screener in the same place. **It is also a door to the rest** The same login reaches 26 sources and 580+ operations. Read the fundamentals here, then ask the same agent what social is saying about the ticker — without adding a second server. **What it costs** Finding and inspecting an operation is free. Running one is billed per call at API prices, with no seat and no monthly minimum, and every call takes max_price_usd so an agent cannot overspend by accident. **Where else it reaches** https://mcp.aisa.one/finance/mcp for equities, crypto and prediction markets in one place.

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Provides structured financial market data (stocks, ETFs, mutual funds, fundamentals, market indicators) to AI systems via MCP, enabling natural language access to financial datasets with both hosted and local deployment options.
    31
    111 npm
    ISC
  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides seamless access to Vietnamese stock market data, enabling real-time prices, historical data, company fundamentals, and market analysis through natural language queries.
    13 npm
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    MCP server providing Vietnamese stock market data including historical prices, financial statements, company info, trading data, forex, crypto, world indices, and mutual funds through 21 MCP tools and 22 CLI commands with support for stdio, SSE, and Streamable HTTP transports.
    21
    MIT