About EVS Meter Tools
EVS Meter Tools is a web app and Telegram bot for supported NUS and SUTD electricity meters, with balance, usage, top-up history, and card top-ups where available. Its read-only MCP server lets an AI assistant check meter information and provide a payment link without handling payments or card details.
Key features
- 01NUS and SUTD meter balance lookup
- 02Usage and recent top-up history
- 03Credit-card top-up links
- 04Telegram bot and web app
- 05Read-only meter tools
- 06No card details passed to MCP
Use cases
- 01Check an NUS or SUTD meter balance
- 02Review recent usage and top-ups
- 03Get a link to top up a supported meter
- 04Ask an AI assistant to check whether credit is low
MCP support
The public, read-only MCP server exposes tools to retrieve meter balances, NUS usage, recent top-ups, and top-up links. It cannot make payments or receive card details, and omits room addresses from responses.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://nusaircon-production.up.railway.app/mcpclaude mcp add --transport http nicholasohjj-nusaircon https://nusaircon-production.up.railway.app/mcp{"mcpServers":{"nicholasohjj-nusaircon":{"type":"http","url":"https://nusaircon-production.up.railway.app/mcp"}}}Open in Cursor{"servers":{"nicholasohjj-nusaircon":{"type":"http","url":"https://nusaircon-production.up.railway.app/mcp"}}}Open in VS Code{"mcpServers":{"nicholasohjj-nusaircon":{"type":"http","url":"https://nusaircon-production.up.railway.app/mcp"}}}{"mcpServers":{"nicholasohjj-nusaircon":{"serverUrl":"https://nusaircon-production.up.railway.app/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
Details
From the project READMEEVS Meter Tools
A Telegram bot and web app for supported EVS electricity meters. Supported NUS and SUTD meters can be topped up by credit card, and supported lookup flows show balance, usage, and top-up history where available.
Supported systems
| Hostel group | EVS system |
|---|---|
| NUS hostels, including PGPR, Houses @ PGP, Residential Colleges, NUS College, UTown Residences, RVRC, Valour House | cp2nus.evs.com.sg |
| SUTD | SUTDMain / EVSSUTDWebPOS |
The legacy NUS CP2 fallback (cp2.evs.com.sg) is hidden from the web app and bot. Its backend routes and services are kept but are not linked from any UI; see CP2 — legacy NUS fallback (hidden).
Features
- Check meter balance, 7-day usage history, and recent top-ups from within Telegram
- Top up supported NUS electricity meters via credit card (SGD $6-$50)
- Top up supported SUTD electricity meters via credit card (SGD $10-$50)
- RSA-encrypted card entry — card details never leave the browser in plaintext
- Works as a Telegram Mini App and as a standalone website
- Cross-system guard: cp2nus bootstrap rejects meters that belong to the legacy cp2 system before initiating payment
- SUTD balance, top-up history, and top-up support
- Analytics tracking and error capture throughout the flow
- Opt-in low-balance alerts on Telegram, with a button to start a top-up
- Read-only MCP server, so people can connect their own Claude and set their own instructions
Architecture
Telegram Bot (telegraf) Website (React, /app/)
│ │
├── /topup └── HomePage
├── /balance └── system selection
├── /usage + meter ID + amount
└── /topups
│ webhook in production/Railway, │
│ polling in development │
▼ ▼
Express (server.js) ←────────────────────────────
│
├── POST /telegram/webhook/* — Telegram webhook receivers
├── GET /webapp — legacy CP2 (hidden from UI): fetches meter summary, redirects to React
├── GET /webapp/bootstrap — runs full payment init, returns token
├── GET /webapp/session — returns session data as JSON for React
├── GET /webapp/pay — redirects to React card entry page
├── POST /webapp/enets_pay — proxies encrypted card data to eNETS
├── POST /webapp/notify — fallback Telegram payment notification
└── GET /webapp/result — redirects to React result page
React Frontend (/app/)
├── / — HomePage (system + meter ID + amount)
├── /balance — HomePage opened in balance-check mode
├── /cp2nus — HomePage preselected for primary NUS CP2NUS flow
├── /cp2nus/loading — cp2nus variant
├── /cp2nus/pay — cp2nus variant
├── /cp2nus/result — cp2nus variant
├── /sutd/loading — SUTD variant
├── /sutd/pay — SUTD variant
└── /sutd/result — SUTD variant
Payment flows
CP2 — legacy NUS fallback (hidden)
Hidden. This flow is no longer offered in the web app or bot. The
/app/cp2,/app/loading,/app/pay, and/app/resultReact routes are commented out infrontend/src/App.jsx,/app/cp2is excluded from the sitemap and robots.txt, and the bot no longer shows a hostel picker. The backend routes and services below are kept for reference and possible reinstatement.
Scrapes the legacy EVS WebPOS portal to create a transaction, then proxies through eNETS. It was used only for NUS meters that had not migrated to CP2NUS.
/webapp— fetches meter address and balance from ORE, redirects to React loading page with address/balance in query params- Bootstrap (
/webapp/bootstrap) — runsrunPurchaseFlowandgetMeterSummaryin parallel:GET /EVSWebPOS/→ login →POST /loginServlet(meter validation)POST /selectOfferServlet(amount selection)GET /paymentServlet→ extractmerchant_txn_refPOST creditpayment.jsp→ extract eNETSmessagePOST /enets2/PaymentListener.do→ extract RSA public key (n,e),netsMid,netsTxnRef- Creates a payment session (10-min TTL), redirects to React card page
- Card page (
/app/pay) — React component; fetches session via/webapp/session; encryptscardNo + cvvwith eNETS RSA scripts client-side - Payment proxy (
/webapp/enets_pay):POST https://www.enets.sg/GW2/uCredit/pay- Preferred path: extracts EVS callback form →
POST /EVSWebPOS/transSumServlet→parseEvsTransactionSummary - Fallback:
parseEnetsResultscrapes the eNETS receipt HTML directly - Writes outcome (
status,merchantTxnRef,reason) back to the server-side session
- Result page (
/app/result) — React component; reads outcome from server session via/webapp/session
CP2NUS — primary NUS hostel flow
Uses the EVS JSON API and the eNETS Payment Page (enetspp) host directly. This is the primary NUS hostel flow for migrated meters.
/webapp— same as cp2; fetches meter info, redirects to/app/cp2nus/loading- Bootstrap (
/cp2nus/webapp/bootstrap) —runBootstrapruns sequentially:- Meter system check —
isCp2Meter()guard: rejects cp2 meters with aWRONG_SYSTEMerror; on network failure the check is skipped and flow proceeds init_pay—POST /enets/init_pay→{ txn_identifier, req, sign }meter_info—getMeterSummary→buildPayDisplayAddressenetspp_pay—buildEnetsPayUrl→GET enetspp/pay?p=…→ extracttxnReq,keyId,hmacTxnReqListener—POST /GW2/TxnReqListener→ RSA key,netsTxnRef,netsMid,paymtNetsMid,txnRand,keyId,hmac
- Meter system check —
- Card page (
/app/cp2nus/pay) — same RSA encryption;paymtNetsMid(acquiring MID) used inpanSubmitForm, not top-levelnetsMid - Payment proxy (
/cp2nus/webapp/enets_pay):GET /GW2/pluginpages/env.jsp→ seedJSESSIONIDPOST /GW2/credit/init;jsessionid=…POST /GW2/credit/panSubmitForm- Preferred path:
netsTxnStatusin response →preParsedresult (no b2s call) - Fallback:
POST /enets/b2s→ 303 redirect →parsePayResultbase64-decodes params
- Result page (
/app/cp2nus/result) — reads outcome from server session
SUTD
Uses the SUTD EVS WebPOS pages and the SUTD credit payment bridge.
/sutd/webapp— fetches SUTD meter credit, redirects to/app/sutd/loading- Bootstrap (
/sutd/webapp/bootstrap) — runs the SUTD WebPOS flow:POST /EVSSUTDWebPOS/loginServletwith the meter IDPOST /EVSSUTDWebPOS/selectOfferServletwith the selected amountGET /EVSSUTDWebPOS/paymentServlet→ extractmerchant_txn_refPOST http://120.50.44.233/payment_sutd_credit/creditpayment.jspPOST /enets2/PaymentListener.do→ extract RSA public key,netsMid,netsTxnRef
- Card page (
/app/sutd/pay) — same RSA-encrypted card entry as NUS flows - Payment proxy (
/sutd/webapp/enets_pay) — posts encrypted card data to eNETS, then posts the callback to/EVSSUTDWebPOS/transSumServlet - Result page (
/app/sutd/result) — reads outcome from server session
Setup
Prerequisites
- Node.js 20.19+ (22.12+ on Node 22)
- A Telegram bot token (from @BotFather)
- A publicly accessible HTTPS server (required for Telegram WebApp buttons)
Installation
# Install backend dependencies
npm install
# Install and build the frontend
npm run build:frontend
Environment variables
Create a .env file:
TELEGRAM_BOT_TOKEN=your_bot_token_here # legacy single-bot token
NUS_TELEGRAM_BOT_TOKEN=your_nus_bot_token # optional, for concurrent nusairconbot
SUTD_TELEGRAM_BOT_TOKEN=your_sutd_bot_token # optional, for concurrent sutdairconbot
PAYMENT_SESSION_SECRET=replace_with_openssl_rand_hex_32 # stable secret for signed payment/result tokens
SERVER_URL=https://your-public-server.example.com
OWNER_CHAT_ID=your_telegram_chat_id # receives feedback notifications
MAINTENANCE_MODE=false # set to "true" to put the app and bot in global maintenance mode
MAINTENANCE_MESSAGE= # optional custom global maintenance message
OWNER_STATUS_TIMEOUT_MS=4000 # optional timeout for owner /status EVS/eNETS checks
TOPUP_DISABLED=false # set to "true" to show maintenance message
DB_DIR=. # local SQLite dir; use /data on Railway with a mounted volume
TELEGRAM_BOT_MODE= # production/Railway defaults to webhook; dev defaults to polling
TELEGRAM_BOT_AUDIENCE=nus # legacy single-bot mode: "nus" or "sutd"
LOW_BALANCE_CRON_SECRET= # optional, enables the low-balance check endpoint
MCP_ALLOWED_ORIGINS= # optional, comma-separated extra browser origins allowed to call /mcp
GOOGLE_SITE_VERIFICATION_FILE= # optional Search Console HTML file name, e.g. googleabc123.html
GOOGLE_SITE_VERIFICATION_CONTENT= # optional exact file body if Google provides non-default content
SERVER_URL must be HTTPS for the Telegram WebApp payment button to work. If it is HTTP, the bot falls back to a plain browser link instead. On Railway, set it to https://${{RAILWAY_PUBLIC_DOMAIN}} or leave it unset and the app will derive it from RAILWAY_PUBLIC_DOMAIN.
PAYMENT_SESSION_SECRET should be a stable random value, for example from openssl rand -hex 32. If it is omitted, the app falls back to the configured bot token, which still survives Railway sleeps as long as the bot token does not change.
For Google Search Console URL-prefix verification, use the HTML file method and set GOOGLE_SITE_VERIFICATION_FILE to the downloaded file name. The default response body is google-site-verification: <file name>. If Google's downloaded file contains different text, set GOOGLE_SITE_VERIFICATION_CONTENT to the exact file body.
In production, or when RAILWAY_PUBLIC_DOMAIN is present, each configured bot uses a Telegram webhook by default so Railway Serverless can sleep and wake from inbound Telegram requests. Set TELEGRAM_BOT_MODE=polling only for an always-on deployment. Optional webhook variables are TELEGRAM_WEBHOOK_PATH, TELEGRAM_WEBHOOK_SECRET, and TELEGRAM_DROP_PENDING_UPDATES for single-bot mode. For concurrent bots, use NUS_TELEGRAM_WEBHOOK_PATH, NUS_TELEGRAM_WEBHOOK_SECRET, SUTD_TELEGRAM_WEBHOOK_PATH, and SUTD_TELEGRAM_WEBHOOK_SECRET only if you need custom paths; otherwise unique paths are generated automatically.
For concurrent Telegram bots in one deployment, set both NUS_TELEGRAM_BOT_TOKEN and SUTD_TELEGRAM_BOT_TOKEN. The same service starts both nusairconbot and sutdairconbot, with separate command menus, webhook paths, in-memory sessions, and payment notifications. Single-bot deployments can still use TELEGRAM_BOT_TOKEN plus TELEGRAM_BOT_AUDIENCE.
Running
# Development (two terminals)
npm run dev # Express backend on :3000
npm run dev:frontend # Vite dev server on :5173
# Production
npm run build:frontend
npm start
The frontend is served at /app/ by Express in production. In development, Vite proxies /webapp, /cp2nus, and /sutd to the backend.
For local development, keep DB_DIR=. or omit it. On Railway, set DB_DIR=/data only after attaching a volume mounted at /data.
Railway Serverless
The repo includes railway.json with the production build, start command, and /health check. Railway Serverless itself is enabled in the dashboard:
- Add a public domain for the service.
- Set either
TELEGRAM_BOT_TOKENfor one bot, orNUS_TELEGRAM_BOT_TOKENandSUTD_TELEGRAM_BOT_TOKENfor concurrent bots. Also setPAYMENT_SESSION_SECRET,SERVER_URL=https://${{RAILWAY_PUBLIC_DOMAIN}}, and any optional variables such asOWNER_CHAT_ID. - Attach a volume mounted at
/dataand setDB_DIR=/dataso saved users survive restarts. - Go to service settings > Deploy > Serverless and enable Serverless.
- Deploy. Startup will register the Telegram webhook automatically.
Cold starts can make the first Telegram or web request slower, and Railway may return a first-request 502 while waking the service. Bot sessions and owner reply threads are in-memory and reset by restarts or sleeps; payment/result tokens are sealed with PAYMENT_SESSION_SECRET and can still be read after a restart until their TTL expires. Saved meter IDs remain in SQLite.
Testing
# Backend tests
npm test
# Frontend tests
cd frontend && npm test
# Lint backend and frontend
npm run lint
Connect your own Claude (MCP)
The service exposes a public, read-only MCP server at POST /mcp, so people can connect their own AI assistant and decide how it behaves. No account or API key is needed.
| Tool | What it does |
|---|---|
get_meter_balance | Current balance (NUS and SUTD) |
get_meter_usage | Last 7 days of usage and analysis (NUS only) |
get_recent_topups | Up to 10 recent top-ups (NUS and SUTD) |
get_topup_link | Link to the top-up page, where the user pays by card themselves |
The tools cannot make payments and never receive card details. Responses leave out the room address, because they are passed on to the user's AI provider.
To connect, add https://<your-domain>/mcp as a custom connector in Claude's settings. In Claude Code, run:
claude mcp add --transport http evs-meter https://<your-domain>/mcp
Each person writes their own instructions in their assistant, for example in a Claude Project:
My NUS meter ID is 12345678. When I ask about aircon credit, check the balance
and work out how many days it will last from my recent usage. If it will last
under 3 days, suggest a top-up amount and give me the top-up link.
The server is stateless, so it works with Railway Serverless. Tool calls are limited to 120 per 5 minutes per IP address, and can't be sent as a batch. Hosted clients share IP addresses, so that limit is higher than the website's lookup limit.
Tool errors are JSON, so an agent can decide what to do next:
{ "code": "METER_NOT_FOUND", "message": "EVS has no NUS meter with this ID. ...", "retryable": false }
| Code | Retryable | Meaning |
|---|---|---|
METER_NOT_FOUND | No | EVS doesn't recognise the meter on that system |
INVALID_AMOUNT | No | The amount is outside the system's top-up limits |
TOPUPS_PAUSED | No | Top-ups are turned off for now |
EVS_UNAVAILABLE | Yes | EVS could not be reached or returned no details |
RATE_LIMITED | Yes | Too many calls from this IP address; includes retryAfterSeconds |
get_topup_link checks the meter first. If EVS can't be reached, it still returns a link with meterVerified: false.
Every tool declares an output schema and returns structuredContent, with the same JSON as text for clients that don't read it.
To have an agent check your balance daily and send you a payment link when it runs low, see scheduled balance check. To run your own copy, or build your own automation on top of it, see the self-hosting guide.
Bot commands
| Command | Description |
|---|---|
/start | Show the main menu |
/topup | Start an electricity top-up |
/balance | Check meter balance |
/usage | Show last 7 days of usage |
/topups | Show recent top-ups |
/alerts | Show or change low-balance alerts |
/feedback | Share feedback or report an issue |
/cancel | Cancel the current flow |
/help | Show help and supported systems |
/status | Owner-only live service health |
/stats | Owner-only runtime snapshot |
Low-balance alerts
People opt in with /alerts on, which covers the saved meters on that bot. Meters are saved when someone starts a top-up in the bot. Once a day, each opted-in meter is checked and its chat gets a message if the balance is below SGD 5. The message has a button that starts the bot's normal top-up flow, so the person still chooses the amount and pays by card.
Each meter gets one alert per low-balance episode, and can alert again once a check sees the balance back at SGD 5 or more. A failed lookup is skipped and never counts as a zero balance. Alerts stop if someone blocks the bot, and /forget removes them along with the meter.
To turn on the daily check:
- Generate a secret with
openssl rand -hex 32. - Set it as
LOW_BALANCE_CRON_SECRETon Railway. - Add the same value as a repository secret with the same name on GitHub.
.github/workflows/low-balance-check.yml then calls POST /internal/low-balance-check at 09:00 Singapore time, which also wakes the service if Railway Serverless has put it to sleep. To test it, run the workflow manually from the GitHub Actions tab. GitHub can delay scheduled runs, and it disables them after 60 days without repository activity.
Bot session flow
Sessions are stored in-memory with a 15-minute TTL. All messages for a given chat are serialised through a per-chat lock to prevent race conditions. On serverless deployments, sessions reset when Railway stops and later restarts the service. The top-up flow stages are:
idle
→ awaiting_meter_id (8-digit ID; prefetches balance + 7-day usage;
NUS always uses cp2nus, no hostel picker)
→ awaiting_amount ($6–$50 SGD)
→ awaiting_payment (WebApp Pay button; re-prompts on text)
→ idle (reset after WebApp closes)
→ awaiting_meter_id_balance (/balance with no saved meter)
→ awaiting_meter_id_usage (/usage with no saved meter)
→ awaiting_meter_id_topups (/topups with no saved meter)
→ awaiting_feedback_rating (/feedback — star rating keyboard)
→ awaiting_feedback_text (free-text or ⏭ Skip)
→ idle
/balance, /usage, and /topups use single-step stages that return to idle after one response.
Payment Session
Payment sessions (created by /webapp/bootstrap) are sealed encrypted tokens with a 10-minute TTL, separate from bot sessions. The token holds the meter ID, amount, address, balance, and eNETS gateway fields needed for payment. This avoids losing an active payment session when Railway Free wakes the service on a new process.
Once /webapp/enets_pay has a final outcome, the server sends the Telegram payment notification immediately and returns a sealed result token with a 24-hour TTL. Payment and result tokens carry an internal tokenKind; payment routes reject result tokens, and result routes reject payment tokens. The React result page reads outcome data from GET /webapp/session?token=; query params are never trusted for payment results. CP2NUS receipt PDFs are still held in a volatile in-memory cache, so the result page only shows the receipt link when that cache entry is available.
User store
Saved meter IDs and hostels are persisted in a SQLite database (evs_users.db) using better-sqlite3. The database is written to DB_DIR (Railway Volume at /data if present, otherwise the project root). Unlike bot sessions and payment sessions, the user store survives restarts.
Owner reply threading
When a user submits feedback, the bot forwards a notification to OWNER_CHAT_ID. The owner can reply directly to that Telegram notification and the bot forwards the reply back to the user. Reply threads are tracked via an in-memory pendingReplies map with a 7-day TTL.
Note: threading only follows the original notification message. If the owner replies to their own reply, that message is not automatically routed back to the user — only replies to the original forwarded notification are intercepted.
Project structure
├── server.js # Express entry point; serves React at /app/
├── routes/
│ ├── cp2.js # WebApp + API routes for legacy cp2 (hidden from UI)
│ ├── cp2nus.js # WebApp + API routes for cp2nus
│ ├── sutd.js # WebApp + API routes for SUTD
│ ├── mcp.js # Read-only MCP server for AI assistants
│ ├── alerts.js # Secret-protected trigger for the low-balance check
│ └── website.js # Standalone website lookup + feedback API
├── services/
│ ├── cp2Service.js # Legacy cp2 purchase flow: EVS WebPOS scraping + eNETS proxy
│ ├── cp2nusService.js # Purchase flow: EVS JSON API + eNETS PP + NETS API
│ ├── sutdService.js # SUTD balance, history, and WebPOS purchase flow
│ ├── ore.js # ORE API: meter summary, usage history, top-up history
│ ├── meterLookup.js # Balance, usage, and top-up lookups shared by the website and MCP
│ ├── paymentSession.js # Sealed payment/result tokens and receipt cache
│ ├── paymentNotification.js # Telegram payment result notification helper
│ ├── paymentSubmitLock.js # Short-lived duplicate payment submit guard
│ ├── utils.js # HTML parsing, result normalisation, XSS escaping
│ ├── validators.js # Meter ID validation and strict amount parsing
│ ├── config.js # Base URLs and shared HTTP headers
│ └── analytics.js # Event tracking and exception capture
├── bot/
│ ├── index.js # Telegraf handlers + webhook/polling runtime
│ ├── handlers/ # Command and text message handlers
│ ├── services/ # Bot session, user store, lookup helpers
│ └── constants.js # Stage names, keyboards, shared messages
├── views/
│ └── errorPage.js # Shared HTML error page for Express error responses
└── frontend/ # React + Vite frontend
├── src/
│ ├── App.jsx # React Router routes
│ ├── pages/
│ │ ├── HomePage.jsx # System selection + meter ID + amount entry
│ │ ├── LoadingPage.jsx # Spinner; calls /webapp/bootstrap
│ │ ├── CardPaymentPage.jsx # RSA card form; calls /webapp/enets_pay
│ │ ├── ResultPage.jsx # Payment outcome
│ │ └── TermsPage.jsx # Terms of Use
│ ├── components/ # Card, DetailRow, Logo, ErrorCard
│ └── lib/ # rsa.js, cardBrand.js, validation.js
└── __tests__/ # Vitest + Testing Library tests
Notes
- Bot sessions are in-memory and expire after 15 minutes. Payment tokens are sealed and restart-safe if
PAYMENT_SESSION_SECRETis stable; pending payment tokens expire after 10 minutes, completed result tokens after 24 hours. The user store (saved meter IDs) is SQLite-backed and persists across restarts. /webapp/enets_payuses a process-local submit lock keyed by merchant transaction reference to reject duplicate in-flight payment submits with HTTP 409.- Card details are RSA-encrypted in the browser before being sent to the server. The server never sees plaintext card numbers or CVVs.
- The cp2nus flow distinguishes between the top-level
netsMid(UMID_xxx) andpaymtNetsMid(acquiring MID frompaymtSvcInfoList[0]). Using the wrong MID will cause the payment to fail silently. - ORE (
ore.evs.com.sg) and the eNETS init endpoint (p-1.evs.com.sg) are currently called with an emptyAuthorization: Bearerheader and succeed — no token is required. The EVS2 portal also runs a user-auth service (evs2u.evs.com.sg/login) that issues short-lived (~1h) JWTs, so EVS could start enforcing a real token on these endpoints in future. If lookups or top-ups begin returning401/403, this is the likely cause and the flow would need to obtain and attach a JWT. - Accepted top-up amount input is strict: plain numbers plus common currency prefixes such as
$10orS$ 10.00; malformed mixed strings are rejected. NUS minimum top-up is $6.00 SGD, SUTD minimum top-up is $10.00 SGD, and all systems currently cap top-ups at $50.00 SGD. - The website entry point (
/app/) and the Telegram Mini App use the same Express routes and React pages — no separate codepaths. - Global maintenance mode can be enabled at runtime with
/maintenanceon(owner command) or at startup withMAINTENANCE_MODE=true. Users see the global maintenance message across the app and bot;/healthand Telegram webhooks remain available so the service stays reachable and the owner can run/maintenanceoff. SetMAINTENANCE_MESSAGEto customize the copy.GLOBAL_MAINTENANCE=trueis accepted as a legacy alias. - Top-ups can be disabled at runtime with
/topupoff(owner command) or at startup withTOPUP_DISABLED=true. Bot users in an active top-up session when the flag is set will have their session reset and see the maintenance message. Website payment start, loading, card, bootstrap, and card-submit routes also return the maintenance response while top-ups are disabled; lookup, terms, result, and feedback routes remain available.
Licence
MIT. This is an unofficial tool, not affiliated with NUS, SUTD, EVS or eNETS.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.