VN Stock MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@VN Stock MCP Servershow me today's top gainers in the Vietnamese stock market"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
Architecture
The same image runs in three places, and only the gateway's network mode and the connector URL change:
(a) Agent Runtime on AgentBase: a Public MCP Gateway calls the runtime's public endpoint, which is protected by the fail-closed API key and the runtime's IP Access Control.
(b) vServer / VKS in your VPC: a Private gateway reaches the server's private IP over the private connection between the AgentBase VPC (
172.30.0.0/16) and your VPC.(c) On-premises: a Private gateway with Route CIDRs for the on-premises range goes through your VPC and VPN Site-to-Site / Interconnect to the data center.
In every case the server calls the public 24hMoney API, so (b) and (c) need outbound HTTPS through NAT or a proxy
(or an internal mirror set in STOCK_API_BASE_URL). The call flow inside the gateway is shown in
End-to-end flow; the per-option settings are in Deploy in three places.
Related MCP server: Vnstock 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 a JSON object (MCP structured output: structuredContent, with the same JSON as text in content).
A failure is a normal MCP tool error: the result has isError: true and a short, readable message in content (for
example Invalid stock symbol: 'x'... or 24hMoney API is unavailable (HTTP 503) after 3 attempts). Error messages never
contain the upstream URL. All tools are annotated readOnlyHint and openWorldHint.
Common result conventions:
source("24hmoney.vn") is at the top level of every result;unitsis at the top level of every result that contains numbers.Every list result has a
count. Missing upstream values arenull, never0.Dates are
YYYY-MM-DD(trading days in Vietnam time); timestamps are ISO 8601 with the+07:00offset.Limits (
limit,days) are clamped to the documented range instead of being rejected.
Market
Shared source top-stock-all (~90 most liquid symbols), cached for 60s.
Tool | Description |
| Table of ~90 top market symbols. |
| Top symbols with the largest gains (positive % only) |
| Top symbols with the deepest losses (negative % only) |
| Top symbols by liquidity (traded value) |
| Quote for one symbol in the top group: price, +/-%, ceiling/floor, matched volume & value, foreign buy/sell |
Market results also include stocks_tracked, last_update (ISO 8601, Vietnam time) and a note when the data is
more than 15 minutes old (market closed). state is one of up, down, unchanged, at_ceiling, at_floor, unknown.
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 |
| Find a symbol by name / ticker ( |
| Full name (VN/EN), listing exchange, business description |
| Per trading session (newest first): close, reference, +/-%, volume, value. |
| Daily foreign buy/sell and net value ( |
| P/E, P/B, ROE, ROA, EPS, etc. compared with the industry average |
| Dividend history ( |
| Annual business plan and % completion |
| 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 is in billion VND; volumes are in shares. Each result
states the units it uses in its units object.
Repo structure
├── Dockerfile # image: python:3.12-slim, non-root (uid 10001), port 8080, /health
├── requirements.txt # runtime dependencies (bounded)
├── requirements-dev.txt # + pytest, pytest-asyncio, ruff, pyyaml
├── .env.example # sample environment variables
├── deploy/ # deployment outside Agent Runtime (not in the image)
│ ├── vserver/ # docker compose on vServer (+ optional Caddy TLS override)
│ ├── vks/ # Kubernetes manifests for VKS (NodePort, internal only)
│ └── onprem/ # on-prem docker compose + check_connectivity.sh
├── src/mcp_server/main.py # the entire server (single file)
├── src/mcp_server/healthcheck.py # container healthcheck (HOST/PORT aware)
└── tests/ # 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 # {"status":"ok","tools":13}The MCP endpoint is exactly http://localhost:8080/mcp; /mcp/ (trailing slash) is not redirected and returns 404,
so use the URL without the trailing slash in every connector / client.
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())
PYRun without Docker (Python 3.11+): 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 |
Accepted headers |
|
Key requirements | At least 32 characters and not a placeholder (a key containing |
Invalid key configured |
|
Key configured but caller's key is missing/wrong |
|
No key configured |
|
| Always open (so the runtime can health-probe); |
Key rotation: keys are read once at startup, so every change needs a restart / redeploy of the runtime.
Set two keys at the same time, MCP_API_KEYS="old_key,new_key" (restart) → update the key on the
calling side (Access Control / connector) to new_key → remove old_key from the environment variable (restart).
No downtime as long as the restarts are rolling.
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; an invalid key (placeholder / too short) keeps /mcp locked.
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, orNo 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-deployandagentbase-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:v12. 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
# (the server rejects placeholders and keys shorter than 32 characters)
# 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 requiredIn 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:
stockEndpoint:
https://<endpoint>.agentbase-runtime.aiplatform.vngcloud.vn/mcpOutbound 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):
# IAM token: client-credentials grant with the client id / secret of your IAM service account
# (the same call as scripts/print_token.py in the sample-byo-agent-mcp-gateway repo)
TOKEN=$(curl -s -u "$GREENNODE_CLIENT_ID:$GREENNODE_CLIENT_SECRET" -d grant_type=client_credentials \
https://iam.api.vngcloud.vn/accounts-api/v2/auth/token \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["access_token"])')
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:
targetsis 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)
→ 24hMoneyNo 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 never run in the customer's VPC: in Public mode they use AgentBase's shared public
endpoint, and in Private mode they run in the GreenNode-managed AgentBase VPC (172.30.0.0/16). 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 |
| vServer: |
|
Outbound Auth | API Key — header | API Key — header | API Key — header |
Additional connectivity | — | The VPC must already be privately connected to AgentBase (contact GreenNode support), with DNS resolution enabled; security group allowing | Site-to-Site VPN / Interconnect (customer side), bidirectional routes including |
Guide | Deploy to GreenNode AgentBase (above) |
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.
The server always calls the public 24hMoney API: on vServer / VKS allow outbound HTTPS through NAT or a proxy, on-premises through the data center's proxy / NAT, or point
STOCK_API_BASE_URLat an internal mirror. See the diagram in Architecture.In (b) and (c) the URL is plain
http://unless you add TLS: the traffic stays on the private connection, and the repo ships a TLS option only for vServer (deploy/vserver/docker-compose.tls.yml). The connector URL is always exactly/mcp(no trailing slash).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/xxin the documentation are placeholders — replace them with your actual ranges.
Technical architecture
FastMCP (
stateless_http=True) — MCP streamable HTTP at/mcp; tools return dicts (structured output) and raiseToolErroron failure.Shared
fetch_api(): one lazily createdhttpx.AsyncClient(closed on shutdown by the app lifespan); the upstream payload shape is validated before it is cached, and errors or empty payloads are never cached. The TTL cache (session data 60s; valuation / dividends / business plan / announcements 15 minutes; company directory 6 hours) drops expired entries and holds at most 512 entries. Concurrent cold calls for the same key share one upstream request.Retries: at most 3 attempts per call (back-off 0.5s, then 1.5s), only for network errors, HTTP 5xx and 429. A 4xx answer or a non-JSON body fails immediately with a short, status-specific message.
HTTP_TIMEOUT_SECONDSis the total budget for all attempts of one call.symbolvalidation (^[A-Z0-9]{2,10}$, case-insensitive) before it is composed into the upstream request;limit/daysare clamped to the allowed range.Untrusted upstream data: numbers are coerced (
"62.1"→62.1) or becomenull; unexpected shapes become a clean tool error, never a raw exception.Vietnam timezone (
Asia/Ho_Chi_Minh) forlast_update, trading dates and for detecting stale data after market close.Runtime contract: port
8080(override withPORT, bind address withHOST),GET /health→ 200 liveness only,GET /describes the server.The container runs as the non-root user
10001and has aHEALTHCHECKthat runshealthcheck.py: it probes/healthonHOST:PORT(127.0.0.1 whenHOSTis unset or a wildcard address).
Environment variables
Variable | Default | Description |
| (required) | API keys protecting |
| (empty) |
|
|
| Override the data source (internal proxy/mirror) |
|
| Total time budget for the upstream work of one tool call (all attempts and back-offs included) |
|
| Cache TTL for session data (top stocks, prices, foreign trading) |
|
| Cache TTL for slow-changing data (dividends, business plan, valuation, announcements) |
|
| Cache TTL for the company directory (6 hours) |
|
| Listening port |
|
| Bind address |
Test
pip install -r requirements-dev.txt
python -m pytest -v # hermetic — the 24hMoney API is faked, no network calls
ruff check --select F,E9,B,UP,SIM --target-version py312 src testsCoverage: tool ranking / validation / output shapes, upstream type drift and malformed payloads, cache (no bad payloads,
expiry, bounded size), request de-duplication, retry policy and time budget, fail-closed auth (placeholder / short keys,
503 / 401, header styles, key rotation), /health not leaking configuration, isError results and structured output over
real MCP calls, and the deploy files (docker compose config when Docker is installed).
Related resources
Skill
agentbase-deploy— Agent Runtime + Container RegistrySkill
agentbase-identity— Access Control: storing the API key providerSkill
agentbase-gateway— MCP Connector, inbound/outbound auth, attaching a Policy GroupSkill
agentbase-policy— writingstock__<tool>policiesRelated samples:
sample-travel-buddy(an agent that calls tools through a Public gateway, like thestockconnector here) andsample-zalo-restaurant(a Private runtime with its own Private gateway and an MCP server in the customer VPC)
License
MIT — the data belongs to 24hMoney and is for demo purposes only.
This server cannot be deployed
Maintenance
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
- AlicenseBqualityDmaintenanceMCP server for Vietnamese stock market data, enabling AI assistants to access stock history, financials, company info, and global market data through 21 tools.21104MIT
- AlicenseNot gradedqualityDmaintenanceProvides seamless access to Vietnamese stock market data, enabling real-time prices, historical data, company fundamentals, and market analysis through natural language queries.22 npmMIT
- AlicenseBqualityCmaintenanceMCP 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.21MIT
- AlicenseNot gradedqualityCmaintenanceFastMCP server for Vietnamese stock analysis, providing 35+ tools for fetching quotes, financials, and technical indicators (144+), and generating AI-driven investment reports. Integrates with Claude/Cursor via stdio/SSE and supports LangGraph workflows.MIT