Meridian documentation
Meridian is a skill router for AI agents — local stdio MCP
for Claude Code/Cursor/Windsurf, hosted remote MCP
at mcp.ask-meridian.uk for Grok/ChatGPT/Claude.ai. It
generates candidate skills with an LLM, then ranks them with a local
orbital classifier that assigns each one a celestial
class (planet · moon · trojan · asteroid · comet · irregular).
Install
1. Stdio MCP — Claude Code, Cursor, Windsurf, Goose, Continue
npm install -g meridian-skills-mcp
claude mcp add meridian meridian-mcp \
-e MERIDIAN_GITHUB_TOKEN=ghp_yourTokenHere
Same install works in any client that speaks stdio MCP. The package is one Node entrypoint plus the bundled orbital classifier — no native deps.
2. Remote MCP — Grok, ChatGPT, Claude.ai connectors
Pick the host below, paste the URL, no install required. Full step-by-step in the Grok connector section.
https://mcp.ask-meridian.uk/mcp
3. Get a GitHub Models token (stdio install only)
The stdio MCP calls GitHub Models
using your own PAT. Create a fine-grained one at
github.com/settings/tokens
with account permissions → Models: Read. That single
permission is enough; the token sees nothing else on your account.
Pass it as MERIDIAN_GITHUB_TOKEN (or GITHUB_TOKEN).
The remote variant (mcp.ask-meridian.uk) handles its
own GitHub Models auth — connector users never see a PAT.
4. Browser miniapp (no install)
Open ask-meridian.uk/miniapp. Type a task or
hit 📷 Scan an object to use real-time camera object
detection as the input. Same orbital classifier, runs in-browser,
routes against the bundled 88-skill corpus.
Grok connector setup
Grok's "Add custom connector" dialog takes an MCP server URL plus four OAuth fields. Paste these:
| Field | Value |
|---|---|
| Server URL | https://mcp.ask-meridian.uk/mcp |
| Authorization endpoint | https://mcp.ask-meridian.uk/authorize |
| Token endpoint | https://mcp.ask-meridian.uk/token |
| Client ID | grok |
| Client secret | (empty) |
| Token auth method | none (PKCE only) |
| Scopes | route_task |
The flow:
- Grok stores the connector and shows an Authorize button.
- Clicking it opens mcp.ask-meridian.uk/authorize in a new tab — a single-button confirmation page. No PAT pasting, no GitHub jargon.
- You click Authorize, the page 302s back to Grok with a
one-time auth code, Grok exchanges it at
/tokenwith PKCE verification, and gets a 1-hour Bearer back. route_taskappears in Grok's tools list. Ask something like "route a task to ranked skills: build a WebSocket-backed presence indicator" and Grok invokes it.
The hosted Worker uses an operator-pays auth model: it holds a single GitHub Models PAT and uses it for every inference call, so connector users see zero credential prompts. Tokens expire after 1 h and can be re-authorized at any time. Same URL works in ChatGPT custom MCPs and Claude.ai connectors — they speak the same MCP Streamable-HTTP + OAuth 2.1 spec.
Source: cf-worker/
in the npm repo. ~250 lines, runs on Cloudflare Workers, deploys
automatically on every push to main.
Quickstart
Once the MCP is registered, your AI client auto-discovers the tool. Just ask:
Use meridian to find skills for setting up a public API rate limiter.
The client calls meridian.route_task({ task, limit }). The
package generates 5 LLM candidates via GitHub Models, classifies them
orbitally, and returns ranked skills with full markdown bodies — which
your client lifts straight into its context window.
Pipeline
meridian.route_task({ task, limit })
│
↓
GitHub Models inference
(default: meta/llama-3.3-70b-instruct)
→ 5 candidate skills
{slug, description, keywords, body}
│
↓
Local orbital classifier (JS, <5 ms)
│
─────────┴──────────
physics: mass · scope · independence ·
cross_domain · fragmentation · drag · dep_ratio
class: planet | moon | trojan | asteroid | comet | irregular
parent: nearest sibling by Jaccard
system: forge | signal | mind (term-set affinity)
lagrange: bridge potential between two strongest systems
│
↓
route_score = (kw·10 + desc·5 + body·1)
× diversity · class_boost · versatility
│
↓
ranked array
Two ways to reach this pipeline. External AI clients
(Grok / ChatGPT / Claude.ai) register the OAuth-gated MCP at
mcp.ask-meridian.uk/mcp and call the
route_task tool — covered in the next section. First-party
browser front-ends (lens, the miniapp, Photon) skip OAuth
entirely and POST mcp.ask-meridian.uk/v1/route — covered
in Browser endpoint below.
MCP tool
The package exposes one tool:
route_task(task: string, limit?: integer)
Generates candidates, classifies them orbitally, and returns the ranked list as a single agent-readable markdown block. Each entry ships with its full skill body so the caller LLM doesn't need a second tool call to read it.
Input
{
"task": "string (≤ 800 chars, required)",
"limit": 5 // 1-10 (default 5)
}
Errors
task required— missing or empty.task too long— over 800 chars.GitHub Models error— token missing, rate limited, or upstream failure. Surfaced verbatim to the agent.
Browser endpoint
First-party Meridian front-ends (the
browser miniapp,
Lens,
Photon)
skip the OAuth dance and POST
mcp.ask-meridian.uk/v1/route directly. The endpoint is
operator-paid — the Cloudflare Worker holds a single
GitHub PAT in a secret and uses it for every inference call — and
Origin-restricted, so only requests carrying an
allowlisted Origin header are accepted. There is no
auth header for the caller to manage; the server enforces both the
allowlist and the upstream credential.
Use this when you're shipping a browser app on an ask-meridian.uk
sub-property and want skill routing without making the user paste a
PAT or run an OAuth callback. Use /mcp instead when the
caller is an external connector host (Grok, ChatGPT, Claude.ai).
Allowlisted origins
https://ask-meridian.ukhttps://lens.ask-meridian.ukhttps://photon.ask-meridian.uk
Bring up a new sub-property by editing
BROWSER_ORIGIN_ALLOWLIST in
cf-worker/worker.mjs
and pushing — the Worker re-deploys automatically.
Request
curl -X POST https://mcp.ask-meridian.uk/v1/route \
-H 'Origin: https://ask-meridian.uk' \
-H 'Content-Type: application/json' \
-d '{"task": "rate limit a public API", "limit": 5}'
Response
Same pipeline as route_task, but returns the
structured classifier output (not the markdown wrapper) so
front-ends can render orbits, score bars, and per-skill physics
panels directly:
{
"task": "rate limit a public API",
"confidence": "strong",
"top_score": 109.3,
"candidates_generated": 5,
"selected": [
/* same shape as the per-entry block in `Response shape` below */
],
"timing": { "llm_ms": 4200, "classify_ms": 12, "total_ms": 4212 }
}
Errors come back as { "error": "..." } with a 4xx/5xx
status: 403 for a non-allowlisted Origin,
500 if the operator's PAT secret is missing,
502 for upstream LLM failure.
Online learning
The browser endpoint /v1/route applies a fitted-correction layer on top of
the heuristic ranking. The correction is trained by a separate POST endpoint that
front-ends fire whenever a user engages a skill (clicks a planet in
Lens, opens a detail panel in the
miniapp, etc.). Constant per-request cost (~1 ms), no batch
training, no GPU, no local execution.
POST /v1/feedback
curl -X POST https://mcp.ask-meridian.uk/v1/feedback \
-H 'Origin: https://ask-meridian.uk' \
-H 'Content-Type: application/json' \
-d '{
"query": "rate limit a public API",
"selected": [ /* same shape as /v1/route's selected[] */ ],
"chosen_slug": "redis-token-bucket",
"action": "click"
}'
Each POST runs one pairwise-ranking SGD step (logistic ranking loss over (chosen,
every-other) pairs, lr 0.02, L2 0.001) against a 24-feature vector per skill: 8 physics
scalars, 6 class one-hot, 3 star-system one-hot, 3 token-hit features, route_score
normalised by batch max, rank position, has-parent, habitable-zone. The fitted
correction multiplies route_score by 1 + tanh(K · w·x) —
bounded to [0, 2] so no individual skill can be silently boosted beyond 2× heuristic.
action values: click, detail_open,
copy, thumbs_up, bootstrap all train (positive
signal). dismiss is recorded but does not update weights — avoids
penalising candidates the user simply didn't have time to look at.
GET /v1/model-info — read-only model state for dashboards
and the eval cron:
curl https://mcp.ask-meridian.uk/v1/model-info
# {
# "version": "v1",
# "n_updates": 64,
# "n_pairs": 1213,
# "cold_start": false,
# "updated_at": "2026-05-07T17:47:14.815Z"
# }
The MCP tool path (/mcp, OAuth 2.1 + PKCE) does not apply
the fitted correction. External connector hosts (Grok, ChatGPT, Claude.ai) get the
deterministic heuristic ranking so per-call behaviour is reproducible. Only the
first-party browser endpoint /v1/route learns from feedback.
For the full story — including the calibration panel that surfaced the bug and the two textbook physics frameworks (Vallado CRTBP, Sears & Zemansky spectral) we tried and abandoned — see the blog post: From 17% to 81%.
Response shape
The MCP renders results as markdown for the agent. The underlying ranked entries look like:
{
"slug": "redis-token-bucket",
"name": "redis-token-bucket",
"description": "Atomic Lua-driven token bucket in Redis for sub-millisecond rate limiting.",
"body": "## Use It For\n- ...\n\n## Workflow\n1. ...\n",
"keywords": ["redis", "lua", "token-bucket", "..."],
"route_score": 109.3,
"classification": {
"class": "trojan",
"class_scores": { "planet": 0.41, "moon": 0.07, "trojan": 0.62, "..." : 0 },
"physics": {
"mass": 0.58,
"scope": 0.71,
"independence": 0.34,
"cross_domain": 0.18,
"fragmentation": 0.12,
"drag": 0.21,
"dep_ratio": 0.64,
"lagrange_potential": 0.22,
"star_system": "forge",
"star_affinity": { "forge": 0.78, "signal": 0.06, "mind": 0.04 }
},
"parent": "api-rate-limiting",
"star_system": "forge",
"lagrange_systems": ["forge"],
"lagrange_potential": 0.22,
"decision_rule": "Companion at L4/L5 of api-rate-limiting — ...",
"habitable_zone": true,
"tidal_lock": true
},
"breakdown": {
"kw_hits": 3,
"desc_hits": 2,
"body_hits": 11,
"diversity_mult": 1.36,
"class_mult": 1.20,
"lagrange_mult": 1.11,
"tokens": ["rate", "limiting", "redis"]
},
"why": "3 keyword · 2 desc · 11 body · trojan×1.20 · ..."
}
Celestial classes
Each generated skill is assigned exactly one class — argmax over six per-class scores:
| Class | Decision rule | Score boost |
|---|---|---|
| Planet | Domain anchor — high mass × scope × independence. Loads as a primary skill. | ×1.30 |
| Trojan | Companion at L4/L5 of a parent — high dep_ratio, low fragmentation. Co-activates permanently with its parent. | ×1.20 |
| Irregular | Cross-domain bridge — spans multiple star systems with high fragmentation. | ×1.10 |
| Moon | Sub-skill orbiting a parent — low independence, dep_ratio drives loading. | ×1.05 |
| Asteroid | Narrow-scope niche tool — low mass but independently useful. | ×0.85 |
| Comet | Specialised / occasional — high drag, high cross_domain, low dep_ratio. Triggers rarely. | ×0.80 |
Physics signature
Per-skill features derived from content alone (no curated lookup tables):
mass— log(body_length) + keyword count, normalized.scope— keyword diversity + cross_domain contribution.independence— 1 − Jaccard with siblings, modulated by mass.cross_domain— Shannon entropy across forge / signal / mind term sets.fragmentation— keyword length stddev + cross_domain.drag— proportion of long / hyphenated specialised terms.dep_ratio— relatedness to siblings in the batch:max(token-Jaccard × 1.5, keyword-Jaccard × 2.2), clamped to [0,1].lagrange_potential— boosted minimum of the two strongest system affinities (min(top₂) × 1.4, clamped to [0,1]).
Star systems
Three system term sets determine which gravitational well a skill orbits:
- Forge — devops/backend:
api · docker · deploy · network · backend · nginx · ssh · ci/cd · build · server · database · redis · cache · auth · test · kubernetes · tunnel · vpn · observability - Signal — growth/marketing:
seo · serp · keyword · content · email · marketing · campaign · analytics · conversion · funnel · brand · publish · backlink · cohort · attribution · linkedin · podcast · persona - Mind — AI/research:
llm · prompt · reasoning · agent · embedding · vector · rag · evaluation · orchestration · memory · knowledge · openai · anthropic · inference · fine-tune · transcript · synthesis
Skills with strong affinity in ≥ 2 systems get a versatility boost (1 + min(0.30, lagrange_potential × 0.5)) on the route score.
Configuration
Environment variables for the MCP:
| Variable | Default | Purpose |
|---|---|---|
MERIDIAN_GITHUB_TOKEN (or GITHUB_TOKEN) |
(required) | Fine-grained PAT with Models: Read. Used to call GitHub Models. |
MERIDIAN_MODEL |
meta/llama-3.3-70b-instruct |
Override the model. Any GitHub Models catalog id works (e.g. openai/gpt-4o). |
MERIDIAN_MODELS_ENDPOINT |
https://models.github.ai/inference/chat/completions |
Override the inference endpoint (e.g. for a self-hosted gateway). |
MERIDIAN_CANDIDATES |
5 |
How many candidate skills the LLM generates per call (3–8). |
MERIDIAN_TIMEOUT_MS |
90000 |
Abort the inference request after this many ms. |
Latency & quota
- Per call — typical 5–15 s. Most of the budget is GitHub Models inference; the local classifier runs in < 5 ms.
- Free tier — GitHub Models has a free per-account daily request budget that resets at 00:00 UTC. Limits depend on the model tier (Llama-3.3-70B is in the higher-cost tier; smaller models burn fewer requests). See GitHub Models limits.
- Failure mode — when the token is missing, expired, or rate-limited, the MCP returns a clear error to the agent instead of silently falling back.
Self-hosting (closed-domain, offline)
If you need local model + curated corpus with no network calls, pin to the legacy line:
npm install -g meridian-skills-mcp@0.3.2
That ships:
- The
skill_orbit.pyPython orbital classifier. - An
@xenova/transformersMiniLM-L6-v2 embedding pre-filter (~23 MB ONNX). - An 88-skill curated corpus of
SKILL.mdfiles. - Four tools:
route_task,get_skill,list_skills,search_skills.
Tradeoff: closed-domain (only the curated 88 skills classify), but no
internet required. Versions 1.x ran the LLM and classifier on
a Cloudflare Worker — that backend was retired during the GitHub Pages
migration; 1.0.1 POSTs to a dead endpoint and is no longer
supported.