ln-ashlar-mcp
OfficialClick on "Install 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., "@ln-ashlar-mcpsearch ln-ashlar documentation for OAuth setup"
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.
MCP Server
Node.js (ESM) HTTP server exposing Model Context Protocol (MCP) tools (Streamable HTTP + legacy SSE transport) along with a custom OAuth 2.0 + PKCE authorization flow and a searchable knowledge base (ln-ashlar documentation).
Technologies
Node.js, Express 5
@modelcontextprotocol/sdk(Streamable HTTP and SSE transports)jsonwebtokenfor authorization code / access tokenfuse.jsfor fuzzy documentation searchwinston+winston-daily-rotate-filefor logging
Related MCP server: Markdown RAG MCP
Installation
npm installCopy the example configuration files and populate them with real values (both files are in .gitignore and are NOT committed):
cp config/auth.example.json config/auth.json
cp config/jwt.example.json config/jwt.jsonconfig/auth.json— list of users ({ users: { <username>: { clientId, token } } }).config/jwt.json— secret (secret) for signing JWT codes/tokens. Generate a strong random value (e.g.,openssl rand -base64 48).config/oauth.json— already present in the repository (contains no secrets), defines allowedredirect_urivalues (allowedRedirects) and whether loopback (localhost/127.0.0.1) redirects are allowed.config/gemini.json— configuration for thereview_plantool (copy fromconfig/gemini.example.json); contains no secrets in the file — authentication for gemini-cli uses gemini-cli's own encrypted credentials in the runner'sHOME(~/.gemini/gemini-credentials.json), not via this repo.
Corpus — Where Documentation Lives
The server DOES NOT have a built-in location for documentation. Where ln-ashlar (and any other product repository with a docs-mcp/ folder) resides is determined by a single environment variable:
DOCS_CORPUS_ROOTS— comma-separated list of repository roots (not thedocs-mcpsubfolder). Each root is read only if it containsdocs-mcp/.ASHLAR_DOCS_REPO— legacy single-root fallback, used only whenDOCS_CORPUS_ROOTSis not set.
The same setting feeds all consumers — ashlar tools (tools/ashlar/corpus.js, configuredRoots()), legacy knowledge index (tools/knowledge/loader.js), and get_ln_schema. There is no backup copy in this repo: if no root contains the schema, get_ln_schema returns an error instead of a stale response.
If the variable is not set, the server still boots up — tools report "not configured" instead of crashing.
Routing Contract — docs-mcp/component-router.md
Every root should carry docs-mcp/component-router.md — a matrix for component selection (what it is used for, what it is NOT used for). This is the only top-level file served by the server without indexing it as a document:
Its body is injected verbatim into MCP
instructionsduringinitialize(tools/ashlar/instructions.js,buildInstructions()). This is the only push channel in MCP — the client places it into the model's system prompt before the first token, without any tool call. This allows the model to know the exact component in advance, rather than browsing documentation and guessing.The same matrix is also served on demand via the
get_component_routerMCP tool (optionalrootfor a single root).Descriptions of
get_markup,get_component,list_components, andsearch_docsrepeat the rule (ROUTER_FIRST_HINT) — a second safeguard at the moment a component is chosen.
It is read raw: no frontmatter, does not pass through parseDoc(), and intentionally does NOT enter docs/registry/byName/fuse. Two reasons: it does not need frontmatter, and its name must not collide with the actual ln-router component in components/.
Federation: N roots ⇒ N sections in instructions, each with a header --- <rootLabel> / component router ---, in the order of DOCS_CORPUS_ROOTS. A root without such a file is valid — it simply does not contribute a section (logs a console.warn). When no root has one, the server does not send instructions at all.
instructions are built per session, not on startup — meaning a commit to the corpus reaches the next session without restarting the server (following the same git-HEAD rule from "Documentation Refresh" below).
A clone in resources/ is one possible setup, not a requirement. Locally, it can directly point to your working checkout:
DOCS_CORPUS_ROOTS=/home/mcp/ln-ashlar node server.js$env:DOCS_CORPUS_ROOTS = 'c:/laragon/www/ln-ashlar'; node server.jsRunning
Linux Startup (CLI)
Start the server directly on Linux with the ln-ashlar corpus path:
DOCS_CORPUS_ROOTS=/home/mcp/ln-ashlar PORT=8080 node server.jsOr using ASHLAR_DOCS_REPO:
ASHLAR_DOCS_REPO=/home/mcp/ln-ashlar PORT=8080 npm startLinux Service (Systemd)
To run the server continuously in the background on Linux as a systemd service:
Create
/etc/systemd/system/ln-ashlar-mcp.service:
[Unit]
Description=LN Ashlar MCP Server
After=network.target
[Service]
Type=simple
User=mcp
WorkingDirectory=/home/mcp/server
Environment=PORT=8080
Environment=DOCS_CORPUS_ROOTS=/home/mcp/ln-ashlar
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetEnable and start the service:
sudo systemctl daemon-reload
sudo systemctl enable ln-ashlar-mcp
sudo systemctl start ln-ashlar-mcpThe server listens on 0.0.0.0:<PORT> (default 8080).
Note: The server does NOT hot-reload JS code — any changes in server.js, routes/, middleware/, or tools/ require a process restart to take effect. Exceptions are corpus contents and users in config/auth.json — they can be refreshed without a restart (see below).
Endpoints Overview
OAuth 2.0 + PKCE Flow
GET /authorize— displays an HTML login page (login template inviews/login.html). Acceptsclient_id,redirect_uri,state,response_type,code_challenge,code_challenge_method(query).POST /authorize— handles login (username/token). On success, issues a short-lived (5 min) one-time authorizationcode— either redirects toredirect_uriwithcode/state, or returnscodedirectly in a JSON response ifredirect_uriis omitted. Protected by rate-limiting (10 attempts / 15 min per IP).POST /token— exchangescode(+code_verifierfor PKCE S256) for anaccess_token(valid 24h). Eachcodeis single-use. Protected by rate-limiting (30 attempts / 15 min per IP).
Only redirect_uri values from config/oauth.json (allowedRedirects) or loopback addresses (if allowLoopbackRedirects: true) are accepted.
MCP Transports (Require Authentication)
ALL /andALL /mcp— Streamable HTTP transport (protocol version2025-11-25). Sessions (mcp-session-id) are bound to the user who initiated them — attempting to use another user's session returns403.GET /sseandPOST /messages— legacy SSE transport (protocol version2024-11-05), also bound to user per session.
Authentication (for all endpoints above + /knowledge/*): Authorization: Bearer <token> or as an API key (along with X-Client-Id header, or client-id/token query parameters — only on /sse and /messages) or as a JWT access_token issued by /token.
Knowledge Base
Legacy index over all .md files in configured roots — including the internals layer (js/ln-*/README.md, docs/architecture/) which the ashlar corpus intentionally does not index. It is complementary to search_docs, not a duplicate. node_modules/ and .git/ are skipped.
Paths in results are prefixed with the root label (ln-ashlar/docs/css/mixins.md) to remain unambiguous when multiple roots exist; knowledge_read accepts both prefixed and standard repo-relative paths.
GET /knowledge/search?q=<term>— fuzzy search. The same sharedsearch()function (tools/knowledge/search.js) is used by both the REST route and theknowledge_searchMCP tool. Returns503withnot_configuredwhen no root is configured.POST /knowledge/reload— reloads.mdfiles from disk and rebuilds the Fuse index without a process restart. Returns{ reloaded: true, docs: <doc_count> }. Every reload is logged with Winston.
Healthcheck
MCP tool
healthcheck(seetools/healthcheck.js), available via MCP transports once a session is established.
review_plan
MCP tool (tools/review_plan.js) that sends a plan (architectural or implementation) to an independent Gemini reviewer via gemini-cli. Authentication to Gemini uses gemini-cli's own credentials stored securely in the runner's HOME (~/.gemini/gemini-credentials.json, currently API key) — no secrets or environment variables exist in this repo. The tool is stateless — the calling agent manages the loop: draft → review → revise, up to 3 iterations; on iterations 2–3, previous_feedback is passed along; stops on APPROVE or iteration 3. Configuration: config/gemini.json (model, timeout, concurrency, max iterations, isolated runner HOME/cwd). Logs: api-key id, plan_type, iteration, chars in/out, duration, verdict, model. Security: gemini-cli is restricted to pure text input/text output (coreTools: [], isolated HOME/empty cwd, never --yolo). The reviewer has read-only MCP access to the docs corpus on the same server (gemini-reviewer key, review_plan excluded from its tools to prevent recursion); due to these agentic tool round-trips, server timeout is 240s (config/gemini.json, timeoutMs). Each call is also logged to a dedicated audit log (logs/review-audit-*.log) with full prompt and response content, which can be disabled via auditLog: false in config/gemini.json. After loop completion (APPROVE or iteration 3), the calling agent can make an optional call with wrap_up: true (passing all previous feedback in previous_feedback) to receive a brief final summary of the entire review process.
User Management
Users are loaded from config/auth.json via middleware/user-store.js, which caches the parsed file and automatically reloads it when the file's mtime changes. This means: adding/deleting a user in config/auth.json takes effect immediately, WITHOUT restarting the server (applies to /authorize, /token, and MCP authentication via middleware/auth.js).
Documentation Refresh
The two indices have different freshness models — neither requires a restart, but they do not refresh in the same way:
Legacy Knowledge Index (
/knowledge/*,knowledge_search,knowledge_read) — on-demand reload: callPOST /knowledge/reloadwith valid authentication. Also catches uncommitted disk changes.Ashlar Corpus (
search_docs,get_component,get_markup,validate_docs,get_ln_schema, …) — reloads automatically when the root's git HEAD changes (gitSignature(),tools/ashlar/corpus.js). Thus, an uncommitted change in the root is NOT visible, even afterreload; the change only reflects after it is committed (and on the server — after it pulls).
Checks
Four independent checks — each covering aspects the other three do not:
npm test # 1. behavior
npm run sync:ln-attrs -- --check --root=/home/mcp/ln-ashlar # 2. drift
npm run lint:snippets # 3. ghost attributes + ATTR.*
npm run smoke:generators # 4. contract template↔builderBehavior —
node --test, 203 tests. The only suite covering the security surface: full OAuth + PKCE flow,/mcpauthentication, binding MCP sessions to users, path traversal inknowledge_read. The integration test boots a real server onPORT=8099and reads real credentials fromconfig/auth.jsonat runtime — do not hardcode them.Drift —
attributes.generated.jsmust be fresh against ln-ashlarjs/**+scss/**. Exit 1 when ln-ashlar moves ahead of generators. Run after every pull of ln-ashlar.Ghost attributes +
ATTR.*— everydata-ln-*in_src/**.htmland in builders must exist in that set, and everyATTR.xreference must resolve. Unresolved references outputundefined="…"— valid HTML, dead attribute.Contract template↔builder — all 19 generators render under strict mode: every
{{key}}provided by a builder must exist in the template.
Run all four checks before creating a PR.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- FlicenseAqualityDmaintenanceEnables users to search and fetch Google's Gemini API documentation directly within an MCP-compliant environment. It provides structured access to guides and references for features like function calling, embeddings, and text generation.2
- AlicenseNot gradedqualityDmaintenanceProvides semantic search over markdown documentation using RAG, allowing natural language queries and integration with MCP clients.1MIT
- AlicenseNot gradedqualityDmaintenanceProvides RAG (Retrieval Augmented Generation) access to technical documentation through MCP, enabling LLMs to search and retrieve relevant documentation on-demand.4MIT
- FlicenseNot gradedqualityCmaintenanceEnables building and querying knowledge graphs by ingesting documents into Neo4j using Gemini for entity extraction, and exposes MCP tools for graph health, document ingestion, and knowledge base querying.
Related MCP Connectors
Agentic search over your Dewey document collections from any MCP-compatible client.
MCP Hub: AI service discovery, per-user OAuth, and multi-service workflow orchestration
Query any docs site via MCP. Submit a URL, ask questions, get cited answers.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/livenetworks/ln-ashlar-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server