greennode-agentbase-mcp
MCP server that exposes GreenNode AgentBase REST APIs as three searchable meta-tools (list_servers, search_tools, execute), reducing tool-definition overhead by ~95%. Runs locally over stdio or remotely over streamable HTTP with any MCP client.
README
greennode-agentbase-mcp
An MCP server that exposes the GreenNode AgentBase REST APIs as 3 searchable meta-tools — a search→execute gateway that cuts the MCP tool-definition tax ~95%+ versus flattening every operation into its own tool. Runs locally over stdio (default) or remotely over streamable HTTP, with any MCP-speaking client.
Table of contents
- Quick start
- How it works
- Transports: stdio vs. streamable HTTP
- Configuration
- Development & operations
- Further reading
- License
Quick start
Connect a local MCP client (Claude Code, Cursor, Windsurf, …) to the gateway over stdio in under a minute.
Prerequisites
- Node.js ≥ 20 (see
package.jsonengines) - A GreenNode AgentBase bearer token — one token is valid across all six services
1. Install
git clone https://github.com/GreenNodeHub/greennode-agentbase-mcp.git
cd greennode-agentbase-mcp
npm ci
2. Run (stdio is the default transport — no need to set TRANSPORT)
GREENNODE_MCP_TOKEN=<your-token> npm start
3. Wire up your client. Claude Code — .mcp.json:
{
"mcpServers": {
"agentbase": {
"command": "npx",
"args": ["tsx", "src/index.ts"],
"env": { "GREENNODE_MCP_TOKEN": "<your-token>" }
}
}
}
For Cursor, Windsurf, Cline, Roo Code, Claude Desktop, and other clients, see docs/mcp-client-quickstart.html — same command + env, each client's own config key.
Optional — auto-rotating token (external). If you have the
agentbaseskill installed (it ships.claude/skills/agentbase/scripts/get_token.sh, which is not part of this repo) plusGREENNODE_CLIENT_ID/GREENNODE_CLIENT_SECRET(or a.greennode.json), point your client atscripts/mcp-launch.shinstead. It mints a fresh ~30-minute IAM JWT on every (re)start, so reconnecting rotates the token automatically — no manual re-export, no stale-token 401s.
First call flow: list_servers → search_tools → execute (see How it works).
How it works
Instead of exposing 100+ operations as individual MCP tools (a large manifest the model pays for every turn), the server exposes 3 meta-tools. The full operation set lives in a generated registry the model searches on demand.
┌───────────────────────────────────────────────────────────────┐
│ Generated layer (from specs, committed, never hand-edited) │
│ registry.generated.json │
│ every operation: { id, service, method, path, │
│ summary, tags, inputSchema, … } │
└───────────────────────────────────────────────────────────────┘
▲ consumed by
┌───────────────────────────────────────────────────────────────┐
│ Meta layer (hand-written TypeScript) │
│ • 3 meta-tools: list_servers, search_tools, execute │
│ • BM25 search engine │
│ • JMESPath field projection + response byte cap │
│ • inbound auth + downstream token pass-through │
│ • env resolver (base URLs, transport, limits) │
└───────────────────────────────────────────────────────────────┘
Meta-tools
| Tool | Args | Returns |
|---|---|---|
list_servers |
— | the services, each with its operation count + tags |
search_tools |
query, server?, limit? |
BM25-ranked operations, each with its full inputSchema inline |
execute |
id, args?, fields? |
the real HTTP response, projected by fields and byte-capped |
Discovery is two steps: search_tools returns enough to call execute directly (the input schema is inline), so there's no separate describe step.
// 1) orient on the six services
list_servers()
// → [{ "name": "policy", "description": "policy service (… operations)", "operationCount": …, "tags": […] }, …]
// 2) search by intent — the id and full inputSchema come back together
search_tools({ query: "list policy groups" })
// → [{ "id": "policy.get_api_v1_policy_groups", "service": "policy",
// "summary": "List policy groups", "inputSchema": { "type": "object",
// "properties": { "page": {…}, "page_size": {…}, "name": {…} } } }, …]
// 3) execute; `fields` is an optional JMESPath projection to shrink the response
execute({ id: "policy.get_api_v1_policy_groups", args: { page: 1, page_size: 10 } })
// → the live response (omit `fields` to see the whole body; pass e.g. fields:"items[].name" to project it)
Operation ids look like service.<method>_<slugified-path> (e.g. policy.get_api_v1_policy_groups). Always take an id from search_tools — never type one by hand.
Why meta-tools: 3 tool definitions (~1–2K resident tokens) instead of one tool per operation. See benchmarks/report-2026-07-06.md for the token math — a 36.8× smaller manifest and 7–41% fewer input tokens end-to-end versus the flat (one-tool-per-op) variant.
Transports: stdio vs. streamable HTTP
| stdio | streamable HTTP | |
|---|---|---|
| Use case | local, any MCP client | deployed runtime / remote clients |
| Default | yes (TRANSPORT=stdio) |
opt-in (TRANSPORT=http) |
| Lifecycle | one server for the process lifetime | fresh server + transport per request (stateless) |
| Token source | env var named by TOKEN_ENV (default GREENNODE_MCP_TOKEN) |
Authorization: Bearer header, per request |
| Endpoint | stdin/stdout (JSON-RPC) | POST /mcp |
| Health | — | GET /healthz, GET /health |
stdio (default)
The server reads JSON-RPC from stdin and writes responses to stdout. stdout is the protocol — all diagnostics and the one-line startup banner go to stderr, so they never corrupt the stream.
GREENNODE_MCP_TOKEN=<your-token> npm start # TRANSPORT=stdio is the default
The token is read once at startup from the env var named by TOKEN_ENV (default GREENNODE_MCP_TOKEN). The server runs for the process lifetime and exits when the client closes stdin. See Quick start for the client-wiring snippet.
Streamable HTTP
For a deployed runtime or remote clients. Each POST /mcp builds a fresh server + StreamableHTTPServerTransport for that request (stateless) and authenticates from the Authorization header. The token is not read from the environment in this mode.
TRANSPORT=http npm start # listens on :8080 (PORT); pass the token per request, not via env
Smoke-test it:
curl http://localhost:8080/healthz # → {"ok":true}
# a raw initialize request to /mcp (clients normally build this JSON-RPC envelope for you)
curl -X POST http://localhost:8080/mcp \
-H "Authorization: Bearer <your-token>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
Configuration
All config is via environment variables, read once at startup by loadEnvConfig (src/config/env.ts).
| Var | Default | Notes |
|---|---|---|
TRANSPORT |
stdio |
stdio or http. Any other value throws at boot — the process exits non-zero, nothing listens. |
GREENNODE_MCP_TOKEN |
— | Upstream bearer token, stdio only. Forwarded to all six services on execute. |
TOKEN_ENV |
GREENNODE_MCP_TOKEN |
Name of the env var that holds the token, stdio only. Set this to read the token from a differently-named var. |
PORT |
8080 |
HTTP transport listen port. |
MAX_RESPONSE_BYTES |
25000 |
Hard cap on execute responses; over-cap responses are truncated with a notice. |
SEARCH_LIMIT_DEFAULT |
5 |
Default limit for search_tools when the caller omits it. |
In streamable HTTP mode the token is not read from env at all — clients supply it per request via
Authorization: Bearer.GREENNODE_MCP_TOKEN/TOKEN_ENVapply only to stdio.
Development & operations
Scripts (package.json):
| Script | What it does |
|---|---|
npm start |
Run the server (tsx src/index.ts) |
npm run dev |
Run with reload (tsx watch src/index.ts) |
npm run build |
Typecheck only (tsc --noEmit). There is no compiled dist/ — the runnable form is tsx. |
npm test / npm run test:watch |
Vitest |
npm run fetch-specs |
Refresh specs/ from the AgentBase spec endpoints |
npm run generate-registry |
Rebuild registry.generated.json from specs/ |
Regenerate the registry when the upstream specs change:
npm run fetch-specs && npm run generate-registry
Then commit both specs/ and registry.generated.json. Both are generated — never hand-edit them.
Docker:
docker build -t greennode-agentbase-mcp .
docker run -e TRANSPORT=http -p 8080:8080 greennode-agentbase-mcp
The bearer token is supplied per request via the Authorization header (same as HTTP mode) — not via env.
⚠️
TRANSPORTdefaults tostdio. A deployed HTTP runtime — and the shippedDockerfile(which setsPORTbut notTRANSPORT) — must setTRANSPORT=httpexplicitly. Without it the process starts in stdio mode and listens on no port. Thedocker runcommand above passes-e TRANSPORT=http; for a production image, bakeENV TRANSPORT=httpinto the Dockerfile.
Further reading
docs/mcp-client-quickstart.html— per-client wiring (Claude Desktop/Code, Cursor, Windsurf, Claude.ai, Cline, Roo Code, agent frameworks)benchmarks/report-2026-07-06.md— gateway vs. flat token mathspecs/README.md— spec sources and regenerate notesdocs/superpowers/specs/— design docs (gateway, stdio local server, flatten baseline)
License
See LICENSE.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.