Shared Forest API and MCP developer portal
Shared Forest is a free, shared illustrated forest that grows from its traffic. Every page load adds growth energy, and every visitor, human or AI agent, can plant one tree per day.
This page is the reference for the Shared Forest REST API, the Shared Forest MCP servers and the in-page WebMCP tools. Reading and planting work without an account and without API keys. Sponsoring trees over MCP needs a Shared Forest account connected with OAuth 2.1.
Quickstart
- Read the live forest:
GET /api/forest. - Plant a tree for your user:
POST /api/visitwith{"plant": true}. - Watch the tree appear on the live canvas within seconds.
curl -s "https://sharedforest.com/api/forest?limit=5"
curl -s -X POST https://sharedforest.com/api/visit -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" -d '{"plant":true}'
curl -s -X POST https://sharedforest.com/ask -H 'Content-Type: application/json' -d '{"query":"how many trees grow in the forest?"}'
Authentication
The REST read endpoints, POST /api/visit, /ask and the docs MCP server need no credentials. CORS allows every origin.
The product MCP server at https://sharedforest.com/mcp is an OAuth 2.1 protected resource. Its read tools stay anonymous. start_sponsorship needs scope sponsor:write; get_order needs account:read or sponsor:write; my_orders and my_trees need account:read. Without a token these tools answer HTTP 401 with WWW-Authenticate: Bearer resource_metadata="https://sharedforest.com/.well-known/oauth-protected-resource/mcp". MCP clients such as Claude, Cursor and VS Code then run the flow by themselves.
- Discover: protected resource metadata (RFC 9728) and authorization server metadata (RFC 8414).
- Register:
POST /oauth/register(Dynamic Client Registration, RFC 7591). Redirect URIs: https, loopback http or a native app scheme. - Authorize: send the user to
/oauth/authorizewithresponse_type=code, PKCEcode_challenge_method=S256(required,plainis rejected) and the exact redirect URI. The user logs in, checks your client name, redirect host and scopes, and sets a spending limit (max EUR per order and per day). - Exchange:
POST /oauth/tokenwithgrant_type=authorization_codeand thecode_verifier. Access tokens last 15 minutes. Refresh tokens rotate on every use. - Call:
Authorization: Bearer <access_token>onPOST /mcp. Orders from start_sponsorship belong to the user's account. The user still confirms and pays each order onconfirm_url. - Revoke:
POST /oauth/tokenwithtoken=<refresh_token>(RFC 7009).
Step-by-step walkthrough with curl: auth.md.
Endpoints
| Method and path | Purpose | Side effects |
|---|---|---|
GET /api/forest | Live snapshot of trees, groves and traffic stats | None |
POST /api/visit | Record a visit or plant a tree | Queues one visit |
GET /ask, POST /ask | NLWeb question endpoint | None |
POST /mcp | Product MCP server (JSON-RPC) | plant_tree queues a visit, design_tree and start_sponsorship create records |
POST /mcp/docs | Docs MCP server (JSON-RPC) | None |
GET /api/v1/pricing, GET /api/v1/pricing/quote | Sponsorship prices | None |
POST /api/v1/designs, GET /api/v1/designs/{id} | Design a sponsored tree | Creates a design (3 per day) |
GET /api/v1/spots, POST /api/v1/holds, DELETE /api/v1/holds/{id} | Find, hold and release spots | Holds spots for 30 minutes |
POST /api/v1/orders, GET /api/v1/orders/{id} | Start and read a sponsorship order | Creates an unpaid order |
POST /api/v1/orders/{id}/confirm | Human confirmation (confirm page only) | Opens Stripe Checkout |
POST /api/v1/orders/{id}/extend | 30 more minutes for an unconfirmed agent order, once | Extends the holds |
POST /api/v1/orders/{id}/withdraw | Withdrawal function (§ 356a BGB) | Cancels the order |
Machine-readable reference: OpenAPI 3.1 and the API catalog. /api/forest and /api/visit also answer under the version prefix /api/v1/.
GET /api/forest
Returns the trees and groves nearest the centre plus traffic stats. truncated is true when the world holds more: read them with GET /api/v1/chunks?bbox=. The response is cached for 30 seconds. Query parameters, all optional:
limit: trees per page, 1-500. Turns on pagination.cursor: thenextCursorvalue of the previous page.bbox:minX,minY,maxX,maxYin world coordinates. Only trees inside the box.
curl -s "https://sharedforest.com/api/forest?limit=2"
Example response (values are illustrative):
{
"trees": [
{ "id": "3f9a1c2e", "type": "oak", "x": 4012.5, "y": 3988.1, "plantedAt": 1790150400000, "growthStage": 3, "origin": "human" },
{ "id": "b71d04aa", "type": "pine", "x": 4101.0, "y": 4050.7, "plantedAt": 1790150460000, "growthStage": 1, "origin": "agent" }
],
"groves": [{ "id": "c20e9f11", "x": 3950, "y": 4020, "level": 1, "treesAbsorbed": 6 }],
"stats": { "humans": 1520, "agents": 88, "bots": 301, "plantedToday": 12 },
"visitors": 1909,
"uniqueVisitors": 640,
"generatedAt": "2026-09-23T10:00:00.000Z",
"nextCursor": "bzoy",
"limit": 2,
"totalTrees": 57
}
POST /api/visit
Records one visit. {"plant": false} adds anonymous growth energy and stores no identifier. {"plant": true} plants one tree per visitor per day. Bots never plant: their response has "plant": false.
curl -s -X POST https://sharedforest.com/api/visit -H 'Content-Type: application/json' -H 'Idempotency-Key: my-retry-key-1' -d '{"plant":true}'
{ "queued": true, "visitId": "<uuid v4>", "plant": true }
The tree appears within seconds. The live WebSocket feed sends a visit_result event with the same visitId and the outcome planted, grew, absorbed, duplicate or counted.
GET or POST /ask
NLWeb endpoint. It answers from the live stats and these documents. Set streaming=true (GET) or prefer.streaming (POST) for Server-Sent Events.
curl -s "https://sharedforest.com/ask?query=how+many+agents+visited"
{ "query_id": "q-1", "_meta": { "response_type": "list", "version": "1.0" }, "results": [{ "url": "https://sharedforest.com/api/forest", "name": "Shared Forest live stats", "site": "https://sharedforest.com", "score": 1, "description": "The forest has 57 trees and 3 groves." }] }
Sponsor trees via API or MCP
A sponsorship puts 1-100 custom-designed trees in the forest for 12 months. Each tree shows a display name (1-32 characters) and an optional https link, labelled "Sponsored". The name goes live after automatic checks. The link goes live after a manual review. Prices include VAT. See pricing.
| Trees per order | Unit price | Renewal per tree | Discount | Example |
|---|---|---|---|---|
| 1-4 | 9.00 EUR | 4.50 EUR | 0 % | 1 tree = 9.00 EUR |
| 5-9 | 7.80 EUR | 3.90 EUR | 13 % | 5 trees = 39.00 EUR |
| 10-24 | 6.90 EUR | 3.45 EUR | 23 % | 10 trees = 69.00 EUR |
| 25-99 | 5.96 EUR | 2.98 EUR | 34 % | 25 trees = 149.00 EUR |
| 100 | 4.90 EUR | 2.45 EUR | 46 % | 100 trees = 490.00 EUR |
Steps:
- Quote: MCP
get_price_quoteorGET /api/v1/pricing/quote?quantity=5. - Design: MCP
design_treewith a wish (1-200 characters) orPOST /api/v1/designs{"wish": "..."}. Returns adesign_id(dsn_...). Max 3 designs per caller per day. One design for all trees, or one per tree. - Place (optional): MCP
find_spotsorGET /api/v1/spots?quantity=5&near=4096,4096. REST callers hold spots withPOST /api/v1/holdsand free unused ones withDELETE /api/v1/holds/{id}. Omit spots and the server picks free ones. - Order: MCP
start_sponsorship(needs an OAuth token with scopesponsor:write; the order belongs to the user's account and must fit the spending limit set at consent) orPOST /api/v1/orderswithchannel: "mcp"or"api"and anIdempotency-Key. The order is unpaid. It expires 30 minutes after creation unless the human confirms.POST /api/v1/orders/{id}/extendadds 30 minutes once; the confirm page does this when it opens. The response hasconfirmUrl(https://sharedforest.com/sponsor/confirm/ord_...). - Hand over: give
confirmUrlto your user. The user accepts the terms, gives the withdrawal consent and pays on Stripe. That page callsPOST /api/v1/orders/{id}/confirm. - Follow up: MCP
get_orderorGET /api/v1/orders/{id}every 10 seconds or more untilstatusisfulfilled.refund_pendingmeans paid but not plantable: the operator refunds.treeIdsthen lists the new trees. Share https://sharedforest.com/?tree=<id>.
curl -s "https://sharedforest.com/api/v1/pricing/quote?quantity=5"
curl -s -X POST https://sharedforest.com/api/v1/designs -H 'Content-Type: application/json' -d '{"wish":"a silver birch with warm lanterns"}'
curl -s -X POST https://sharedforest.com/api/v1/orders -H 'Content-Type: application/json' -H "Idempotency-Key: $(uuidgen)" \
-d '{"quantity":5,"designIds":["dsn_abc123"],"displayName":"Ada","email":"ada@example.com","channel":"api"}'
What an agent can do: quote, design, select spots, create an unpaid order, read its status. What an agent cannot do: accept the terms, give the withdrawal consent or pay for a consumer. German consumer law requires the consumer's own click on "Order and pay" (§ 312j (3) BGB) and the consumer's own express consent before the display starts (§ 356 (5) BGB). So a human always confirms on confirmUrl.
Business buyers (phase 2, off until announced): POST /api/v1/orders/{id}/pay with the buyer's EU VAT id answers 402 with a Machine Payments Protocol challenge (WWW-Authenticate: Payment). Retry with a Stripe Shared Payment Token. This path is B2B only because the consumer rules above do not apply to businesses. It appears in the OpenAPI document only when it is on.
Errors are problem+json: 422 blocked name, link or wish; 409 spot taken or sold out; 429 rate limit or cap (3 designs per caller per day, 100 held spots per IP, 3 open orders per email, 300 unconfirmed spots in total); 502 payment provider down; 503 sales not open yet.
Pagination
GET /api/forest without parameters returns the trees nearest the centre (truncated shows when more exist). A bbox covers the whole world. Add limit, cursor or bbox to get one page. The page adds nextCursor, limit and totalTrees. Send nextCursor as cursor until it is null. The MCP tool list_trees uses the same cursor.
Idempotency
Send an Idempotency-Key header on POST /api/visit (1-255 visible ASCII characters, a UUID is best). A repeat with the same key within 24 hours returns the first response and the header Idempotency-Replayed: true. It queues no second visit. The MCP tool plant_tree accepts the same key as idempotency_key.
Rate limits
- 120 requests per minute per IP address on the API, the MCP servers and /ask.
- Every response carries
RateLimitandRateLimit-Policyheaders. - A
429response carriesRetry-Afterin seconds. - One planted tree per visitor per day. More plant requests on the same day add growth energy only.
- Sponsorship caps (429): 3 designs per caller per day, 100 held spots per IP address, 3 open orders per email, 300 unconfirmed spots in total. Release unused holds with
DELETE /api/v1/holds/{id}. - MCP request bodies over 16 KiB and /ask bodies over 4 KiB return
413.
Errors
Every API error is application/problem+json (RFC 9457). The detail field says how to fix the request.
{ "type": "about:blank", "title": "Bad Request", "status": 400, "detail": "limit must be an integer between 1 and 500." }
| Status | When |
|---|---|
| 400 | Invalid query parameter, body or Idempotency-Key |
| 402 | MPP pay endpoint without a valid payment credential (business buyers only) |
| 403 | MCP request with an untrusted Origin header |
| 404 | Unknown API path, design or order |
| 405 | Wrong method. The Allow header lists the correct methods |
| 409 | Spot taken, sponsor layer full, or order not open |
| 413 | Request body too large |
| 422 | Blocked wish, name or link, or a missing consent |
| 429 | Rate limit reached. Wait Retry-After seconds |
| 503 | Forest state unavailable, or sponsorship sales not open yet. Wait Retry-After seconds |
Versioning
The REST API is version 1. Responses carry API-Version: 1. The paths /api/v1/forest and /api/v1/visit are stable aliases. Changes inside a version are additive only. A breaking change gets a new path version. The old version then returns Deprecation (RFC 9745) and Sunset (RFC 8594) headers for at least 90 days before removal. The OpenAPI document states the same policy in x-versioning-policy.
MCP servers
Both servers use Streamable HTTP with JSON responses and no sessions. The docs server needs no authentication. The product server needs an OAuth token only for its account tools (see Authentication).
| Server | URL | Tools | Server card |
|---|---|---|---|
| Product | https://sharedforest.com/mcp | get_forest_stats, get_forest, list_trees, plant_tree, explain_shared_forest, get_price_quote, design_tree, get_design, find_spots, start_sponsorship, get_order, my_orders, my_trees | server-card.json |
| Docs | https://sharedforest.com/mcp/docs | search_docs, read_doc | server-card.json |
curl -s -X POST https://sharedforest.com/mcp -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_forest_stats","arguments":{}}}'
Client config for Claude Code, Cursor or VS Code:
{ "mcpServers": { "shared-forest": { "type": "http", "url": "https://sharedforest.com/mcp" }, "shared-forest-docs": { "type": "http", "url": "https://sharedforest.com/mcp/docs" } } }
WebMCP
The homepage registers two in-page tools with document.modelContext (fallback navigator.modelContext): get_forest_stats and plant_tree. Browser agents call them without scraping the canvas.
SDKs and agent plugin
The public agent plugin repository holds zero-dependency SDKs for TypeScript (sdk/js) and Python (sdk/python) with one SharedForestClient: getForest, listTrees, plant, quote, designTree, findSpots, startSponsorship, getOrder and waitForOrder. The packages are not on npm or PyPI yet: install from the repository. It also holds AGENTS.md, the Agent Plugin manifest (plugin.json), MCP client config (mcp.json, server.json) and the skills. Any OpenAPI generator can read the OpenAPI document.
Sandbox
There is no separate sandbox. All read endpoints have no side effects. To test writes without planting, send {"plant": false}: it only adds growth energy.
Changelog
- 2026-09-23: relaunch. Public REST API, product and docs MCP servers, NLWeb /ask, cursor pagination and Idempotency-Key.
- 2026-09-23: tree sponsorship for agents. MCP tools get_price_quote, design_tree, get_design, find_spots, start_sponsorship and get_order. REST endpoints under /api/v1/ for pricing, designs, spots, holds and orders. Skill shared-forest-sponsor.
- 2026-09-23: the docs MCP server gets its own server card.
list_treesrequireslimit. The OpenAPI document states the versioning and deprecation policy.
Discovery
- llms.txt, API llms.txt, developers llms.txt
- Agent skill and sponsor skill
- Agent plugin repository
- Pricing
Support
Arne Kellmann, contact@ghardenlab.com. See contact.