Make @imqueue first-class inside AI coding agents. When a developer asks their assistant to "build an @imqueue service" or "how do I expose a method", the agent should reach for authoritative docs and correct scaffolding rather than hallucinating an API. This is the GEO (Generative Engine Optimization) counterpart to SEO: instead of ranking in a search page, we rank at code-time, inside the tools developers already use.
Three capabilities, thirteen tools locally:
- Docs access —
search_docs,get_doc,list_packages - Offline scaffolding —
scaffold_service,scaffold_client(templates, no deps) - CLI bridge —
cli_status,cli_install,cli_help,create_service,generate_client,fleet(imq ctl),config(imq config),logs(imq log) (drive the installedimqbinary — install it, create projects, generate clients, manage the local fleet and CLI configuration)
The first two capabilities are read-only and run anywhere; the CLI bridge acts on the
machine the server runs on, so the hosted endpoint carries the first two plus a
local_install_guide and nothing else — six read-only tools.
AI agent (Claude Code / Cursor / …)
│ MCP (JSON-RPC over stdio)
▼
@imqueue/mcp ── fetch ──▶ imqueue.org
├─ docs.ts (/llms.txt, /<page>/index.md)
├─ packages.ts (static catalog)
└─ scaffold.ts (code templates)
- Transport: stdio (the universal local-MCP transport; works with every host
today). The hosted Streamable HTTP variant since planned in §7 now exists at
mcp.imqueue.org/mcp; it shares this code and serves the read-only subset (docs + scaffolding + a local-install guide), because the CLI-backed tools act on the caller's own machine. Seeworker/README.md. - Runtime: Node ≥ 18, TypeScript,
@modelcontextprotocol/sdkhigh-levelMcpServer,zodinput schemas. Ships as an npm bin (npx -y @imqueue/mcp). - Docs source: fetched live from the sites' existing machine-readable feeds and
cached in-process (1 h TTL), with a 5 s per-request timeout and a fall back to the
stale copy rather than an error. No docs are bundled, so the server can never go
stale against a release. Only
imqueue.organdimqueue.comare ever fetched (host-checked).
imqueue.org already emits, for GEO:
/llms.txt— curated index (## Section+- [Title](url): description)/api/search-index.json— every exported symbol of the current majors/<page-url>index.mdand/<page-url>.md— a plain-markdown mirror of every page/blog/search-index.json— structured post index
imqueue.com emits its own /llms.txt and mirrors, covering licensing, pricing and
support — the questions the framework docs deliberately do not answer, and therefore
the ones this server could not answer while it read one site.
The MCP server is a thin, agent-facing adapter over those — one source of truth.
Parse both /llms.txt feeds (imqueue.org for the framework, imqueue.com for
licensing/pricing/support) plus /api/search-index.json into
{title, url, description, section, symbol?} entries, then rank them.
Ranking is IDF-weighted, not flat overlap: every term's contribution is scaled by
how rare it is across the corpus, because every page here is about @imqueue services
and imqueue/service/queue say nothing about which page answers the question.
On top of that:
- Titles match on identifier segments —
MigrateDownOptionsyieldsmigrate/down/options— so a term cannot match the middle of a longer word. An exact segment scores as a whole token, a prefix of one scores less, and fragments under four characters score nothing. - Inflections (
-s/-es/-ies,-ing,-ed,-tion/-sion) are expanded on both the query and the corpus and matched form-to-form, and discounted relative to the word as written. No synonym map. - A symbol page can match on its summary alone, weakly; a symbol's package
(which lives in its section) is scored, so
package-style queries work and the optionalpackagefilter scopes a search to one of a mutually exclusive pair. - Blog posts are demoted by a multiplier, not a sort key, so a decisively better article can still lead while a doc page wins a near-tie.
- Per-parent AND per-member-name caps stop one class, or one method name repeated across six classes, filling the answer.
- A miss returns the corpus's section names and the indexed terms nearest the query, because zero results with no guidance is positive evidence to a model that the topic is not covered.
→ "how do I expose a method" → the RPC guide + API pages.
Resolve a page URL to its markdown mirror (…/index.md, or <page>.md) and return
the raw markdown for reading/quoting. A URL that already names a file
(/llms-full.txt, /blog/feed.xml) is fetched as-is. Host-restricted to
imqueue.org and imqueue.com; a commercial page with no mirror returns a pointer
rather than an error. Bodies over 200 kB are truncated, which the result reports.
Static catalog of every documented package with one-liners, install commands and,
where two packages cover the same ground, an explicit pick rule — so the agent
picks the right one first instead of choosing on wording. @imqueue/js,
@imqueue/travis and @imqueue/mcp are published but deliberately undocumented and
are not listed.
Emit an IMQService subclass with @expose()d, JSDoc-typed methods (JSDoc is
@imqueue's type source) + a bootstrap that start()s it, and — for any non-primitive
parameter or return type — a types.ts with the @classType()/@property()
declarations that type needs. Omitting methods yields a starter template. Points to
imq service create for a fully provider-wired project.
@imqueue generates the real typed client from a running service, so types
never drift. The tool returns that command plus an illustrative usage snippet (it
does not fabricate a client that could go stale). Every identifier it emits is pinned
by test/scaffold.test.ts against IMQClient's generator and the CLI's writer: the
file is named after the service, the CLI argument is the service's class name
because that is its queue name, and the module's only export is a namespace
holding the client class.
The server runs locally, so when @imqueue/cli is on PATH it can drive the real
imq (see src/cli.ts). Safety posture:
- Every call runs
imqwith stdin closed and a timeout, so an interactive prompt (a missing flag) fails fast with guidance rather than hanging the server. create_servicerunsimq service create … --dry-runby default (writes nothing); a real run requires an explicitapply: true— an agent must never create repos / push to remotes silently.cli_helpsurfaces the exact flags to pass so the run is non-interactive.generate_clientrunsimq client generate(the service must be running).cli_installrunsnpm install -g @imqueue/clito bootstrap the CLI when absent.fleetwrapsimq ctl <start|stop|restart|status>(status is read-only; the others change running processes).configwrapsimq config <check|get|set|init>(get/check read-only; set writes a single value; init is interactive so automation should prefer set).- If
imqis absent, the tools return an install hint and the offlinescaffold_*tools remain available.
All four hints the spec defines are set explicitly on all thirteen tools — never
omitted, never null. test/annotations.test.ts asserts that through a real
tools/list call and pins every value in the table below, so a change of judgement
has to be made deliberately and shows up in a diff as a claim about behaviour.
Two things worth stating because they are easy to read as contradictions:
destructiveandidempotentare not opposites.cli_installreplaces whateverimqwas installed, which is destructive; installing the same version twice lands the same binary, so all the destruction happens in the first call and the second adds none.openWorldandidempotentanswer different questions.get_docmay return different bytes on two calls because the page changed — that is whatopenWorldHint: truesays. It still changes nothing by being called twice.
| Tool | read-only | destructive | idempotent | open-world | Justification |
|---|---|---|---|---|---|
search_docs |
✅ | ❌ | ✅ | ✅ | Fetches public pages from imqueue.org/imqueue.com and ranks them. Reads only; the sites change between calls, so open-world. |
get_doc |
✅ | ❌ | ✅ | ✅ | Fetches one page's markdown. Host-locked to the two @imqueue domains; refuses anything else. |
list_packages |
✅ | ❌ | ✅ | ❌ | Renders a catalogue compiled into the build. No network, no filesystem. |
scaffold_service |
✅ | ❌ | ✅ | ❌ | Returns source code as text. Writes no file, creates no project, runs no command — the caller decides whether anything is ever written. create_service is the tool that writes. |
scaffold_client |
✅ | ❌ | ✅ | ❌ | Returns text, including the imq client generate command as a string. It does not run it. generate_client is the tool that does. |
local_install_guide |
✅ | ❌ | ✅ | ❌ | Returns static setup instructions. Installs nothing — which is why it is not called install_locally. |
cli_status |
✅ | ❌ | ✅ | ❌ | Detects the local imq binary and reports its version. |
cli_help |
✅ | ❌ | ✅ | ❌ | Runs imq <command> --help, which prints and exits. |
create_service |
❌ | ❌ | ❌ | ✅ | Dry-run by default; with apply: true it writes a project and may init git, wire CI and push to a remote — hence open-world. Not destructive: it creates rather than damages. Not idempotent: a second apply meets a populated directory and can create a second repository. |
generate_client |
❌ | ❌ | ✅ | ✅ | Introspects a running service over its queue (open-world) and overwrites two files at a fixed path. A repeat leaves the same state, so idempotent. |
cli_install |
❌ | ✅ | ✅ | ✅ | npm install -g @imqueue/cli — downloads from the npm registry and replaces any existing imq, so destructive. Idempotent: see the note above. |
fleet |
❌ | ✅ | ❌ | ❌ | imq ctl over local service repos. status is read-only but stop/restart kill running processes, and a hint describes the tool's worst case. Not idempotent: restart twice restarts twice, dropping in-flight work each time. |
config |
❌ | ✅ | ❌ | ❌ | imq config set overwrites a value and init rewrites the file. Not idempotent because init is interactive and a repeat is not guaranteed to be a no-op. |
logs |
❌ | ✅ | ❌ | ❌ | dump reads; clean deletes the collected logs. Not idempotent: a running fleet writes logs continuously, so a second clean deletes different data — a client must not auto-retry it. |
The hosted surface is the first eight rows minus the two cli_* entries — six tools,
every one readOnlyHint: true and idempotentHint: true. Nothing that changes state
is registered there at all, because a server on Cloudflare's edge cannot reach the
caller's machine; see §2 and worker/README.md.
On the two scaffold_* names. "Scaffold" means "write files" in most tooling, so
a reader who stops at the name would conclude readOnlyHint: true is wrong. It is
not — they return strings — but appearance is what a reviewer judges, so both
descriptions now open with READ-ONLY: returns … and writes nothing to disk, and
each points at the CLI-backed tool that does the real thing.
Input:
search_docs:{ query: string (1..200), limit?: 1..20, package?: string }get_doc:{ url: string }list_packages:{}scaffold_service/scaffold_client:{ name|service: string, methods?: Method[] }whereMethod = { name, description?, params?: {name,type,description?,optional?}[], returns? }.
Output: every tool returns { content: [{ type: "text", text }] }, and errors return
the same shape with isError: true (so the agent sees a message, not a transport
failure). The five shared tools additionally declare an outputSchema and return
structuredContent:
| Tool | structuredContent |
|---|---|
search_docs |
{ query, count, results[{title, section, description, url, symbol?}] } |
get_doc |
{ url, mimeType, bytes, truncated } — metadata only; the page body is in content and never repeated |
list_packages |
{ packages[{name, install, summary, pick?}] } |
scaffold_service |
{ service, install, files[{path, language, content}], types[], cliAlternative } |
scaffold_client |
{ service, client, namespace, generateCommand, output, example{language, content} } |
For the scaffolders and the catalogue the markdown is rendered from that structure,
so prose and data cannot disagree. The CLI-bridge tools declare no outputSchema:
they return imq stdout, which has no shape worth promising.
Publish @imqueue/mcp to npm, then list it everywhere agents discover servers:
A channel with no status is a channel nobody can tell is done, so every row carries one. Re-verify before quoting any of them: a listing can be auto-indexed and then go stale, which is how Context7 came to serve a superseded safety model in the project's own voice.
| Channel | Artifact / action | Status (verified 2026-08-03) |
|---|---|---|
| Official MCP registry | server.json (this repo) → publish via mcp-publisher. Namespace org.imqueue/mcp (DNS auth on imqueue.org). |
done, automated in postpublish |
| Smithery | smithery.yaml (this repo) → connect the GitHub repo. |
listed, but advertises CLI tools the hosted server does not serve — convert to a declared remote |
| Glama / LobeHub | Auto-index from npm + GitHub. | indexed, stale — needs a re-scrape |
| Context7 | Submit imqueue.org; context7.json is the rules block it serves to agents. |
indexed at an old revision; refresh needed, and a refresh ping belongs in postpublish |
| mcp.so / PulseMCP / Docker MCP Catalog / mcp-get | Submit the listing. | absent — verified 404/zero-result on all four |
| awesome-mcp-servers (appcypher, wong2) | PR the repo into the list. | absent |
| Cline marketplace | llms-install.md (this repo, current). |
absent; optional, since it already works from a repo URL |
| Cursor / VS Code directories | The GitHub/VS Code gallery pulls from the official registry. | expected to arrive on its own |
| Anthropic Connectors Directory | No auth, six read-only tools with hints, privacy + terms + docs pages all 200. | technically ready; blocked on a non-technical prerequisite — the portal needs a Team/Enterprise org with directory-management access |
| OpenAI apps | Domain-verification token live at /.well-known/openai-apps-challenge. |
draft, under review — the reason this branch does not publish |
| imqueue.org | "MCP server" section on /using-ai-assistants/ with the install snippet. |
done |
Install snippet promoted everywhere:
{ "mcpServers": { "imqueue": { "command": "npx", "args": ["-y", "@imqueue/mcp"] } } }Three layers, because each catches what the others cannot.
npm test runs the unit tests (node:test under tsx, type-checked by
tsconfig.test.json). This is where anything whose correctness does not depend on the
network lives: the ranker on a fixed corpus, the scaffolders' emitted identifiers, URL
resolution, telemetry, and the Worker's HTTP surface — worker.ts is a plain
Web-Standard fetch handler, so it is called with a Request and asserted on the
Response, with no wrangler and no deploy.
npm run smoke spawns the built stdio server and drives the JSON-RPC handshake:
initialize (asserting instructions and a title) → tools/list (the exact local
tool list, a title and all three behaviour hints on every tool, and which tools do and
do not declare an outputSchema) → tools/call for the offline tools, plus live
ranking checks that only a real 1,500-entry corpus exercises.
npm run smoke:remote [url] does the same for the hosted endpoint, where the
contract is stricter: the exact six-tool list, readOnlyHint: true on all of them,
the search → get_doc chain on structured data alone, and the method handling
(GET/DELETE → 405, HEAD / → 200) that a hang would otherwise reach production
with.
npm run verify runs all of it plus both type-checks.
Streamable HTTP deployment (a hosted endpoint) for zero-install use and for hosts that prefer remote servers.— shipped:mcp.imqueue.org/mcp.Richer search — light stemming to improve recall.— shipped: inflection expansion, segment-aware title matching, summary and package scoring, and apackagefilter. Folding in/blog/search-index.jsontopics is still open.generate_clientfor real — spin up against a reachable running service and return the actual generated client.- Resources — expose docs pages as MCP resources (not just tool results) so
hosts can surface them in their UI. Deferred rather than planned: these are the
attach/@-mention picker, a user-initiated surface, not a citation path, and call
volume does not yet justify it. The wiring is nearly free —
loadIndex()already yields title/description/section/url andgetDocis the read callback. - Prompts — ship an "author an @imqueue service" prompt template. Same reasoning.
- Protocol revision — the current revision is 2026-07-28; the SDK's
LATEST_PROTOCOL_VERSIONis behind that and this server cannot speak it. Do not hand-rollserver/discoveror-32022: the spec's own compatibility matrix says a non-modern 400 body is what makes a dual-era client fall back toinitializeand connect, so emitting-32022would advertise modern support and suppress the recovery path. Track the SDK release that moves the constant, raise the^1.12.0floor then, and assert the negotiated version in a scheduled smoke run.
GPL-3.0, matching the framework; commercial licensing via imqueue.com.