@imqueue/mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +65 -0
- package/SPEC.md +119 -0
- package/dist/docs.d.ts +17 -0
- package/dist/docs.js +103 -0
- package/dist/docs.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/packages.d.ts +7 -0
- package/dist/packages.js +18 -0
- package/dist/packages.js.map +1 -0
- package/dist/scaffold.d.ts +14 -0
- package/dist/scaffold.js +106 -0
- package/dist/scaffold.js.map +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@imqueue/mcp — Model Context Protocol server for @imqueue
|
|
2
|
+
Copyright (C) 2026 imqueue.com
|
|
3
|
+
|
|
4
|
+
This program is free software: you can redistribute it and/or modify it under
|
|
5
|
+
the terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
Foundation, either version 3 of the License, or (at your option) any later
|
|
7
|
+
version.
|
|
8
|
+
|
|
9
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
11
|
+
PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
12
|
+
|
|
13
|
+
You should have received a copy of the GNU General Public License along with
|
|
14
|
+
this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
15
|
+
|
|
16
|
+
Commercial licensing and support for use in closed-source products is available
|
|
17
|
+
at https://imqueue.com.
|
|
18
|
+
|
|
19
|
+
NOTE: replace this notice with the full GPL-3.0 license text before publishing
|
|
20
|
+
(https://www.gnu.org/licenses/gpl-3.0.txt).
|
package/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# @imqueue/mcp
|
|
2
|
+
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server for **[@imqueue](https://imqueue.org)**. It lets AI coding agents (Claude Code, Cursor, Windsurf, VS Code, …) **search the @imqueue documentation** and **scaffold typed services & clients** — so they generate correct, idiomatic @imqueue code instead of guessing.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Tool | What it does |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `search_docs` | Search the official docs (guides, tutorial, CLI manual, API reference, articles) and return the most relevant pages + URLs. |
|
|
10
|
+
| `get_doc` | Fetch the full markdown of a doc page by URL. |
|
|
11
|
+
| `list_packages` | List the main @imqueue packages with install commands. |
|
|
12
|
+
| `scaffold_service` | Generate an `IMQService` subclass with `@expose()`d, JSDoc-typed methods + a bootstrap. |
|
|
13
|
+
| `scaffold_client` | Show how to generate and use the fully-typed client for a service. |
|
|
14
|
+
|
|
15
|
+
Docs are fetched live from imqueue.org's machine-readable feeds (`/llms.txt`, per-page `…/index.md` mirrors), so the server never ships stale content. It only ever fetches `imqueue.org`.
|
|
16
|
+
|
|
17
|
+
## Install
|
|
18
|
+
|
|
19
|
+
Requires Node.js ≥ 18. No build step for users — run straight from npm:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx -y @imqueue/mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Claude Code
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
claude mcp add imqueue -- npx -y @imqueue/mcp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Cursor / Windsurf / VS Code / Claude Desktop
|
|
32
|
+
|
|
33
|
+
Add to your MCP config (`.cursor/mcp.json`, `claude_desktop_config.json`, …):
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"mcpServers": {
|
|
38
|
+
"imqueue": {
|
|
39
|
+
"command": "npx",
|
|
40
|
+
"args": ["-y", "@imqueue/mcp"]
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Develop
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install
|
|
50
|
+
npm run build # tsc -> dist/
|
|
51
|
+
npm run dev # run from source with tsx
|
|
52
|
+
npm run smoke # JSON-RPC handshake + tools/list + tool calls
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Example
|
|
56
|
+
|
|
57
|
+
> **User:** *"Create an @imqueue user service with a getUser(id) method."*
|
|
58
|
+
>
|
|
59
|
+
> The agent calls `scaffold_service({ name: "user", methods: [{ name: "getUser", params: [{ name: "id", type: "number" }], returns: "User" }] })` and gets a ready-to-paste `UserService` + bootstrap, then `search_docs("run a service")` / `get_doc(...)` to wire it up.
|
|
60
|
+
|
|
61
|
+
## License
|
|
62
|
+
|
|
63
|
+
GPL-3.0. Commercial licensing & support for closed-source products: [imqueue.com](https://imqueue.com).
|
|
64
|
+
|
|
65
|
+
See [SPEC.md](./SPEC.md) for the full design and registry-distribution plan.
|
package/SPEC.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# @imqueue/mcp — design spec
|
|
2
|
+
|
|
3
|
+
## 1. Purpose
|
|
4
|
+
|
|
5
|
+
Make @imqueue **first-class inside AI coding agents**. When a developer asks their
|
|
6
|
+
assistant to "build an @imqueue service" or "how do I expose a method", the agent
|
|
7
|
+
should reach for authoritative docs and correct scaffolding rather than
|
|
8
|
+
hallucinating an API. This is the GEO (Generative Engine Optimization) counterpart
|
|
9
|
+
to SEO: instead of ranking in a search page, we rank **at code-time**, inside the
|
|
10
|
+
tools developers already use.
|
|
11
|
+
|
|
12
|
+
Two capabilities, five tools:
|
|
13
|
+
|
|
14
|
+
- **Docs access** — `search_docs`, `get_doc`, `list_packages`
|
|
15
|
+
- **Scaffolding** — `scaffold_service`, `scaffold_client`
|
|
16
|
+
|
|
17
|
+
## 2. Architecture
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
AI agent (Claude Code / Cursor / …)
|
|
21
|
+
│ MCP (JSON-RPC over stdio)
|
|
22
|
+
▼
|
|
23
|
+
@imqueue/mcp ── fetch ──▶ imqueue.org
|
|
24
|
+
├─ docs.ts (/llms.txt, /<page>/index.md)
|
|
25
|
+
├─ packages.ts (static catalog)
|
|
26
|
+
└─ scaffold.ts (code templates)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- **Transport:** stdio (the universal local-MCP transport; works with every host
|
|
30
|
+
today). A hosted **Streamable HTTP** variant is a later option (§7).
|
|
31
|
+
- **Runtime:** Node ≥ 18, TypeScript, `@modelcontextprotocol/sdk` high-level
|
|
32
|
+
`McpServer`, `zod` input schemas. Ships as an npm bin (`npx -y @imqueue/mcp`).
|
|
33
|
+
- **Docs source:** fetched live from imqueue.org's existing machine-readable feeds
|
|
34
|
+
and cached in-process (1 h TTL). No docs are bundled, so the server can never go
|
|
35
|
+
stale against a release. **Only `imqueue.org` is ever fetched** (host-checked).
|
|
36
|
+
|
|
37
|
+
### Why reuse the site feeds
|
|
38
|
+
imqueue.org already emits, for GEO:
|
|
39
|
+
- `/llms.txt` — curated index (`## Section` + `- [Title](url): description`)
|
|
40
|
+
- `/<page-url>index.md` — a plain-markdown mirror of every page
|
|
41
|
+
- `/blog/search-index.json` — structured post index
|
|
42
|
+
|
|
43
|
+
The MCP server is a thin, agent-facing adapter over those — one source of truth.
|
|
44
|
+
|
|
45
|
+
## 3. Tools
|
|
46
|
+
|
|
47
|
+
### `search_docs(query, limit?=6)`
|
|
48
|
+
Parse `/llms.txt` into `{title, url, description, section}` entries; rank by query-term
|
|
49
|
+
overlap (title ×3, section/description/url ×1); return the top *N* with URLs.
|
|
50
|
+
→ *"how do I expose a method" → the RPC guide + API pages.*
|
|
51
|
+
|
|
52
|
+
### `get_doc(url)`
|
|
53
|
+
Resolve a page URL to its markdown mirror (`…/index.md`) and return the raw markdown
|
|
54
|
+
for reading/quoting. Host-restricted to imqueue.org.
|
|
55
|
+
|
|
56
|
+
### `list_packages()`
|
|
57
|
+
Static catalog (rpc, core, cli, job, pg-pubsub, pg-cache, async-logger, http-protect)
|
|
58
|
+
with one-liners + install commands, so the agent picks the right package first.
|
|
59
|
+
|
|
60
|
+
### `scaffold_service(name, methods?)`
|
|
61
|
+
Emit an `IMQService` subclass with `@expose()`d, **JSDoc-typed** methods (JSDoc is
|
|
62
|
+
@imqueue's type source) + a bootstrap that `start()`s it. Omitting `methods` yields a
|
|
63
|
+
starter template. Points to `imq service create` for a fully provider-wired project.
|
|
64
|
+
|
|
65
|
+
### `scaffold_client(service, methods?)`
|
|
66
|
+
@imqueue generates the **real** typed client from a **running** service
|
|
67
|
+
(`imq client generate <Name>`), so types never drift. The tool returns that command
|
|
68
|
+
plus an illustrative usage snippet (it does not fabricate a client that could go stale).
|
|
69
|
+
|
|
70
|
+
## 4. Input schemas (zod)
|
|
71
|
+
|
|
72
|
+
- `search_docs`: `{ query: string, limit?: 1..20 }`
|
|
73
|
+
- `get_doc`: `{ url: string }`
|
|
74
|
+
- `list_packages`: `{}`
|
|
75
|
+
- `scaffold_service` / `scaffold_client`: `{ name|service: string, methods?: Method[] }`
|
|
76
|
+
where `Method = { name, description?, params?: {name,type,description?}[], returns? }`.
|
|
77
|
+
|
|
78
|
+
Every tool returns `{ content: [{ type: "text", text }] }`; errors return the same
|
|
79
|
+
shape with `isError: true` (so the agent sees a message, not a transport failure).
|
|
80
|
+
|
|
81
|
+
## 5. Distribution to registries
|
|
82
|
+
|
|
83
|
+
Publish `@imqueue/mcp` to npm, then list it everywhere agents discover servers:
|
|
84
|
+
|
|
85
|
+
| Channel | Artifact / action |
|
|
86
|
+
|---|---|
|
|
87
|
+
| **Official MCP registry** | `server.json` (this repo) → publish via `mcp-publisher`. Namespace `io.github.imqueue/mcp`. |
|
|
88
|
+
| **Smithery** | `smithery.yaml` (this repo) → connect the GitHub repo. |
|
|
89
|
+
| **mcp.so / PulseMCP / Glama** | Auto-index from npm + GitHub; submit/claim the listing. |
|
|
90
|
+
| **Cursor / VS Code directories** | Add the `mcpServers` JSON snippet to their community lists. |
|
|
91
|
+
| **awesome-mcp-servers** | PR the repo into the list. |
|
|
92
|
+
| **imqueue.org** | Add an "MCP server" section to `/using-ai-assistants/` with the install snippet. |
|
|
93
|
+
|
|
94
|
+
Install snippet promoted everywhere:
|
|
95
|
+
```json
|
|
96
|
+
{ "mcpServers": { "imqueue": { "command": "npx", "args": ["-y", "@imqueue/mcp"] } } }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 6. Verification
|
|
100
|
+
|
|
101
|
+
`npm run smoke` spawns the built server and drives the JSON-RPC handshake:
|
|
102
|
+
`initialize` → `tools/list` (asserts all five) → `tools/call` for `scaffold_service`
|
|
103
|
+
and `list_packages` (offline) and `search_docs` (live). CI can run it on every push.
|
|
104
|
+
|
|
105
|
+
## 7. Roadmap
|
|
106
|
+
|
|
107
|
+
- **Streamable HTTP** deployment (a hosted endpoint) for zero-install use and for
|
|
108
|
+
hosts that prefer remote servers.
|
|
109
|
+
- **`generate_client` for real** — spin up against a reachable running service and
|
|
110
|
+
return the actual generated client.
|
|
111
|
+
- **Resources** — expose docs pages as MCP *resources* (not just tool results) so
|
|
112
|
+
hosts can surface them in their UI.
|
|
113
|
+
- **Prompts** — ship an "author an @imqueue service" prompt template.
|
|
114
|
+
- **Richer search** — fold in `/blog/search-index.json` topics and light stemming to
|
|
115
|
+
improve recall (e.g. "delayed jobs" → job/scheduling pages).
|
|
116
|
+
|
|
117
|
+
## 8. Licensing
|
|
118
|
+
|
|
119
|
+
GPL-3.0, matching the framework; commercial licensing via imqueue.com.
|
package/dist/docs.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface DocEntry {
|
|
2
|
+
title: string;
|
|
3
|
+
url: string;
|
|
4
|
+
description: string;
|
|
5
|
+
section: string;
|
|
6
|
+
}
|
|
7
|
+
/** Fetch + parse /llms.txt into a flat list of doc entries (cached). */
|
|
8
|
+
export declare function loadIndex(): Promise<DocEntry[]>;
|
|
9
|
+
/** Rank doc entries by overlap of query terms with title (weighted), section, description and url. */
|
|
10
|
+
export declare function searchDocs(query: string, limit?: number): Promise<DocEntry[]>;
|
|
11
|
+
/** Resolve a page URL/path to its markdown-mirror URL (`<page-url>index.md`). */
|
|
12
|
+
export declare function mirrorUrl(pageUrl: string): string;
|
|
13
|
+
/** Fetch the full markdown of a doc page by its page URL or path. */
|
|
14
|
+
export declare function getDoc(pageUrl: string): Promise<{
|
|
15
|
+
url: string;
|
|
16
|
+
markdown: string;
|
|
17
|
+
}>;
|
package/dist/docs.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Docs access for the @imqueue MCP server.
|
|
2
|
+
//
|
|
3
|
+
// The docs live on imqueue.org as machine-readable feeds:
|
|
4
|
+
// * /llms.txt — curated index: `## Section` + `- [Title](url): description`
|
|
5
|
+
// * /<page-url>index.md — a plain-markdown mirror of every page
|
|
6
|
+
// * /blog/search-index.json — [{ title, url, summary, topics, ... }]
|
|
7
|
+
//
|
|
8
|
+
// We fetch these at runtime (so the server never ships stale copies) and cache
|
|
9
|
+
// them in-process. Only imqueue.org is ever fetched.
|
|
10
|
+
const SITE = "https://imqueue.org";
|
|
11
|
+
const TTL_MS = 60 * 60 * 1000; // 1h in-process cache
|
|
12
|
+
let indexCache = null;
|
|
13
|
+
function assertImqueueUrl(u) {
|
|
14
|
+
const url = new URL(u, SITE);
|
|
15
|
+
if (url.hostname !== "imqueue.org") {
|
|
16
|
+
throw new Error(`Refusing to fetch non-imqueue.org URL: ${url.href}`);
|
|
17
|
+
}
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
/** Fetch + parse /llms.txt into a flat list of doc entries (cached). */
|
|
21
|
+
export async function loadIndex() {
|
|
22
|
+
if (indexCache && Date.now() - indexCache.at < TTL_MS)
|
|
23
|
+
return indexCache.entries;
|
|
24
|
+
const res = await fetch(`${SITE}/llms.txt`);
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
throw new Error(`Failed to fetch llms.txt (HTTP ${res.status})`);
|
|
27
|
+
const text = await res.text();
|
|
28
|
+
const entries = [];
|
|
29
|
+
let section = "General";
|
|
30
|
+
for (const line of text.split("\n")) {
|
|
31
|
+
const h = line.match(/^##\s+(.+?)\s*$/);
|
|
32
|
+
if (h) {
|
|
33
|
+
section = h[1].trim();
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const m = line.match(/^-\s+\[([^\]]+)\]\(([^)]+)\)(?::\s*(.*))?$/);
|
|
37
|
+
if (m) {
|
|
38
|
+
entries.push({
|
|
39
|
+
title: m[1].trim(),
|
|
40
|
+
url: m[2].trim(),
|
|
41
|
+
description: (m[3] || "").trim(),
|
|
42
|
+
section,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
indexCache = { at: Date.now(), entries };
|
|
47
|
+
return entries;
|
|
48
|
+
}
|
|
49
|
+
const STOP = new Set([
|
|
50
|
+
"the", "a", "an", "and", "or", "of", "to", "in", "for", "on", "with",
|
|
51
|
+
"is", "are", "how", "do", "does", "i", "my", "me", "it", "that", "this",
|
|
52
|
+
]);
|
|
53
|
+
function tokenize(s) {
|
|
54
|
+
return s
|
|
55
|
+
.toLowerCase()
|
|
56
|
+
.split(/[^a-z0-9@/+.-]+/)
|
|
57
|
+
.filter((t) => t.length > 1 && !STOP.has(t));
|
|
58
|
+
}
|
|
59
|
+
/** Rank doc entries by overlap of query terms with title (weighted), section, description and url. */
|
|
60
|
+
export async function searchDocs(query, limit = 6) {
|
|
61
|
+
const entries = await loadIndex();
|
|
62
|
+
const terms = tokenize(query);
|
|
63
|
+
if (!terms.length)
|
|
64
|
+
return entries.slice(0, limit);
|
|
65
|
+
const scored = entries.map((e) => {
|
|
66
|
+
const title = e.title.toLowerCase();
|
|
67
|
+
const hay = `${e.section} ${e.description} ${e.url}`.toLowerCase();
|
|
68
|
+
let score = 0;
|
|
69
|
+
for (const t of terms) {
|
|
70
|
+
if (title.includes(t))
|
|
71
|
+
score += 3;
|
|
72
|
+
if (hay.includes(t))
|
|
73
|
+
score += 1;
|
|
74
|
+
}
|
|
75
|
+
return { e, score };
|
|
76
|
+
});
|
|
77
|
+
return scored
|
|
78
|
+
.filter((x) => x.score > 0)
|
|
79
|
+
.sort((a, b) => b.score - a.score)
|
|
80
|
+
.slice(0, limit)
|
|
81
|
+
.map((x) => x.e);
|
|
82
|
+
}
|
|
83
|
+
/** Resolve a page URL/path to its markdown-mirror URL (`<page-url>index.md`). */
|
|
84
|
+
export function mirrorUrl(pageUrl) {
|
|
85
|
+
const url = assertImqueueUrl(pageUrl);
|
|
86
|
+
let p = url.pathname;
|
|
87
|
+
if (p.endsWith("index.md"))
|
|
88
|
+
return url.href;
|
|
89
|
+
if (p.endsWith(".md"))
|
|
90
|
+
return url.href;
|
|
91
|
+
if (!p.endsWith("/"))
|
|
92
|
+
p += "/";
|
|
93
|
+
return `${SITE}${p}index.md`;
|
|
94
|
+
}
|
|
95
|
+
/** Fetch the full markdown of a doc page by its page URL or path. */
|
|
96
|
+
export async function getDoc(pageUrl) {
|
|
97
|
+
const mUrl = mirrorUrl(pageUrl);
|
|
98
|
+
const res = await fetch(mUrl);
|
|
99
|
+
if (!res.ok)
|
|
100
|
+
throw new Error(`Failed to fetch ${mUrl} (HTTP ${res.status}). Use search_docs to find a valid page URL.`);
|
|
101
|
+
return { url: mUrl, markdown: await res.text() };
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=docs.js.map
|
package/dist/docs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../src/docs.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// @imqueue MCP server — exposes @imqueue docs search and service/client
|
|
3
|
+
// scaffolding to AI coding agents (Claude Code, Cursor, …) over stdio.
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { searchDocs, getDoc } from "./docs.js";
|
|
8
|
+
import { renderPackages } from "./packages.js";
|
|
9
|
+
import { scaffoldService, scaffoldClient } from "./scaffold.js";
|
|
10
|
+
const text = (t) => ({ content: [{ type: "text", text: t }] });
|
|
11
|
+
const fail = (e) => ({
|
|
12
|
+
content: [{ type: "text", text: `Error: ${e instanceof Error ? e.message : String(e)}` }],
|
|
13
|
+
isError: true,
|
|
14
|
+
});
|
|
15
|
+
const methodSchema = z
|
|
16
|
+
.object({
|
|
17
|
+
name: z.string().describe("Method name"),
|
|
18
|
+
description: z.string().optional().describe("What the method does"),
|
|
19
|
+
params: z
|
|
20
|
+
.array(z.object({
|
|
21
|
+
name: z.string(),
|
|
22
|
+
type: z.string().describe("TypeScript type, e.g. 'string' or 'number[]'"),
|
|
23
|
+
description: z.string().optional(),
|
|
24
|
+
}))
|
|
25
|
+
.optional(),
|
|
26
|
+
returns: z.string().optional().describe("TypeScript return type WITHOUT Promise<> — e.g. 'User' or 'string'"),
|
|
27
|
+
})
|
|
28
|
+
.strict();
|
|
29
|
+
const server = new McpServer({ name: "imqueue", version: "0.1.0" });
|
|
30
|
+
server.registerTool("search_docs", {
|
|
31
|
+
title: "Search @imqueue documentation",
|
|
32
|
+
description: "Search the official @imqueue docs (guides, tutorial, CLI manual, API reference, articles) and return the most relevant pages with their URLs. Use this first when asked how to do something in @imqueue, then get_doc to read a page in full.",
|
|
33
|
+
inputSchema: {
|
|
34
|
+
query: z.string().describe("What you want to find, e.g. 'expose a service method' or 'delayed jobs'"),
|
|
35
|
+
limit: z.number().int().min(1).max(20).optional().describe("Max results (default 6)"),
|
|
36
|
+
},
|
|
37
|
+
}, async ({ query, limit }) => {
|
|
38
|
+
try {
|
|
39
|
+
const hits = await searchDocs(query, limit ?? 6);
|
|
40
|
+
if (!hits.length)
|
|
41
|
+
return text(`No matches for "${query}". Try broader terms or call list_packages.`);
|
|
42
|
+
const body = hits
|
|
43
|
+
.map((h) => `### ${h.title} _(${h.section})_\n${h.description}\n${h.url}`)
|
|
44
|
+
.join("\n\n");
|
|
45
|
+
return text(`${hits.length} result(s) for "${query}":\n\n${body}\n\nRead any page in full with get_doc(url).`);
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
return fail(e);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
server.registerTool("get_doc", {
|
|
52
|
+
title: "Read an @imqueue doc page",
|
|
53
|
+
description: "Fetch the full markdown of an @imqueue documentation page by its URL (as returned by search_docs). Returns plain markdown suitable for reading and quoting.",
|
|
54
|
+
inputSchema: {
|
|
55
|
+
url: z.string().describe("An imqueue.org page URL, e.g. https://imqueue.org/get-started/"),
|
|
56
|
+
},
|
|
57
|
+
}, async ({ url }) => {
|
|
58
|
+
try {
|
|
59
|
+
const doc = await getDoc(url);
|
|
60
|
+
return text(`Source: ${doc.url}\n\n${doc.markdown}`);
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
return fail(e);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
server.registerTool("list_packages", {
|
|
67
|
+
title: "List @imqueue packages",
|
|
68
|
+
description: "Return the main @imqueue packages with a one-line summary and install command, so you can pick the right one.",
|
|
69
|
+
inputSchema: {},
|
|
70
|
+
}, async () => {
|
|
71
|
+
try {
|
|
72
|
+
return text(renderPackages());
|
|
73
|
+
}
|
|
74
|
+
catch (e) {
|
|
75
|
+
return fail(e);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
server.registerTool("scaffold_service", {
|
|
79
|
+
title: "Scaffold an @imqueue service",
|
|
80
|
+
description: "Generate an idiomatic @imqueue/rpc service (an IMQService subclass with @expose()d, JSDoc-typed methods) plus a bootstrap that starts it. Provide the methods you want, or omit them for a starter template.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
name: z.string().describe("Service name, e.g. 'user' or 'UserService'"),
|
|
83
|
+
methods: z.array(methodSchema).optional().describe("Methods to expose"),
|
|
84
|
+
},
|
|
85
|
+
}, async ({ name, methods }) => {
|
|
86
|
+
try {
|
|
87
|
+
return text(scaffoldService(name, methods));
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
return fail(e);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
server.registerTool("scaffold_client", {
|
|
94
|
+
title: "Scaffold an @imqueue typed client",
|
|
95
|
+
description: "Show how to generate and use the fully-typed client for an @imqueue service. @imqueue generates the real client from a running service (via `imq client generate`), so this returns that command plus an illustrative usage snippet.",
|
|
96
|
+
inputSchema: {
|
|
97
|
+
service: z.string().describe("The service to call, e.g. 'user' or 'UserService'"),
|
|
98
|
+
methods: z.array(methodSchema).optional().describe("Known methods (used to shape the example call)"),
|
|
99
|
+
},
|
|
100
|
+
}, async ({ service, methods }) => {
|
|
101
|
+
try {
|
|
102
|
+
return text(scaffoldClient(service, methods));
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
return fail(e);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
async function main() {
|
|
109
|
+
const transport = new StdioServerTransport();
|
|
110
|
+
await server.connect(transport);
|
|
111
|
+
// stderr is safe for logs; stdout is the JSON-RPC channel.
|
|
112
|
+
console.error("@imqueue MCP server running on stdio");
|
|
113
|
+
}
|
|
114
|
+
main().catch((err) => {
|
|
115
|
+
console.error("Fatal:", err);
|
|
116
|
+
process.exit(1);
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/packages.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Static catalog of the main @imqueue packages, so an agent can pick the right
|
|
2
|
+
// one before scaffolding. Kept deliberately short — the full ecosystem lives on
|
|
3
|
+
// imqueue.org and is reachable via search_docs.
|
|
4
|
+
export const PACKAGES = [
|
|
5
|
+
{ name: "@imqueue/rpc", install: "npm i @imqueue/rpc", summary: "Type-safe RPC over a message queue — decorators, services and generated clients. The package you build services with." },
|
|
6
|
+
{ name: "@imqueue/core", install: "npm i @imqueue/core", summary: "The Redis-backed messaging-queue engine shared by the framework (usually a transitive dependency of rpc)." },
|
|
7
|
+
{ name: "@imqueue/cli", install: "npm i -g @imqueue/cli", summary: "The `imq` CLI: scaffolds services, wires VCS/CI/registry providers, generates typed clients and runs a local fleet." },
|
|
8
|
+
{ name: "@imqueue/job", install: "npm i @imqueue/job", summary: "Simple, safe-by-default Redis job queue — delayed/scheduled jobs, guaranteed processing, retries." },
|
|
9
|
+
{ name: "@imqueue/pg-pubsub", install: "npm i @imqueue/pg-pubsub", summary: "Reliable PostgreSQL LISTEN/NOTIFY with inter-process lock support." },
|
|
10
|
+
{ name: "@imqueue/pg-cache", install: "npm i @imqueue/pg-cache", summary: "PostgreSQL-managed cache on Redis for @imqueue service methods." },
|
|
11
|
+
{ name: "@imqueue/async-logger", install: "npm i @imqueue/async-logger", summary: "Configurable async logger over winston for @imqueue services." },
|
|
12
|
+
{ name: "@imqueue/http-protect", install: "npm i @imqueue/http-protect", summary: "HTTP DDoS-protection middleware." },
|
|
13
|
+
];
|
|
14
|
+
export function renderPackages() {
|
|
15
|
+
const lines = PACKAGES.map((p) => `- **${p.name}** — ${p.summary}\n \`${p.install}\``);
|
|
16
|
+
return `# @imqueue packages\n\n${lines.join("\n")}\n\nFull ecosystem & docs: https://imqueue.org`;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=packages.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packages.js","sourceRoot":"","sources":["../src/packages.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,gFAAgF;AAChF,gDAAgD;AAQhD,MAAM,CAAC,MAAM,QAAQ,GAAc;IACjC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,uHAAuH,EAAE;IACzL,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,qBAAqB,EAAE,OAAO,EAAE,2GAA2G,EAAE;IAC/K,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,uBAAuB,EAAE,OAAO,EAAE,qHAAqH,EAAE;IAC1L,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,mGAAmG,EAAE;IACrK,EAAE,IAAI,EAAE,oBAAoB,EAAE,OAAO,EAAE,0BAA0B,EAAE,OAAO,EAAE,oEAAoE,EAAE;IAClJ,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,yBAAyB,EAAE,OAAO,EAAE,iEAAiE,EAAE;IAC7I,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,6BAA6B,EAAE,OAAO,EAAE,+DAA+D,EAAE;IACnJ,EAAE,IAAI,EAAE,uBAAuB,EAAE,OAAO,EAAE,6BAA6B,EAAE,OAAO,EAAE,kCAAkC,EAAE;CACvH,CAAC;AAEF,MAAM,UAAU,cAAc;IAC5B,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,QAAQ,CAAC,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC;IACxF,OAAO,0BAA0B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gDAAgD,CAAC;AACpG,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface MethodSpec {
|
|
2
|
+
name: string;
|
|
3
|
+
description?: string;
|
|
4
|
+
params?: {
|
|
5
|
+
name: string;
|
|
6
|
+
type: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
}[];
|
|
9
|
+
returns?: string;
|
|
10
|
+
}
|
|
11
|
+
/** Generate an @imqueue/rpc service class + a bootstrap that starts it. */
|
|
12
|
+
export declare function scaffoldService(name: string, methods?: MethodSpec[]): string;
|
|
13
|
+
/** Generate a typed-client usage snippet for a service. */
|
|
14
|
+
export declare function scaffoldClient(service: string, methods?: MethodSpec[]): string;
|
package/dist/scaffold.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Code scaffolding for @imqueue — mirrors what `imq service create` /
|
|
2
|
+
// `imq client generate` produce, but as inline templates an agent can drop into
|
|
3
|
+
// a project. @imqueue is decorator-driven and JSDoc is the source of truth for
|
|
4
|
+
// types, so the generated code documents each method with JSDoc.
|
|
5
|
+
function pascal(s) {
|
|
6
|
+
return s
|
|
7
|
+
.replace(/[^a-zA-Z0-9]+/g, " ")
|
|
8
|
+
.trim()
|
|
9
|
+
.split(/\s+/)
|
|
10
|
+
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
11
|
+
.join("");
|
|
12
|
+
}
|
|
13
|
+
const DEFAULT_METHODS = [
|
|
14
|
+
{
|
|
15
|
+
name: "hello",
|
|
16
|
+
description: "Example method — replace with your own.",
|
|
17
|
+
params: [{ name: "name", type: "string", description: "Who to greet" }],
|
|
18
|
+
returns: "string",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
function renderMethod(m) {
|
|
22
|
+
const params = m.params ?? [];
|
|
23
|
+
const ret = m.returns ?? "void";
|
|
24
|
+
const sig = params.map((p) => `${p.name}: ${p.type}`).join(", ");
|
|
25
|
+
const jsdoc = [
|
|
26
|
+
" /**",
|
|
27
|
+
` * ${m.description ?? m.name}`,
|
|
28
|
+
...params.map((p) => ` * @param ${p.name} - ${p.description ?? p.type}`),
|
|
29
|
+
` * @return {Promise<${ret}>}`,
|
|
30
|
+
" */",
|
|
31
|
+
].join("\n");
|
|
32
|
+
const body = ret === "void"
|
|
33
|
+
? " // TODO: implement"
|
|
34
|
+
: ` // TODO: implement\n throw new Error('${m.name}() not implemented');`;
|
|
35
|
+
return `${jsdoc}\n @expose()\n public async ${m.name}(${sig}): Promise<${ret}> {\n${body}\n }`;
|
|
36
|
+
}
|
|
37
|
+
/** Generate an @imqueue/rpc service class + a bootstrap that starts it. */
|
|
38
|
+
export function scaffoldService(name, methods) {
|
|
39
|
+
const cls = pascal(name).endsWith("Service") ? pascal(name) : `${pascal(name)}Service`;
|
|
40
|
+
const list = methods && methods.length ? methods : DEFAULT_METHODS;
|
|
41
|
+
const body = list.map(renderMethod).join("\n\n");
|
|
42
|
+
const service = `import { IMQService, expose } from '@imqueue/rpc';
|
|
43
|
+
|
|
44
|
+
export class ${cls} extends IMQService {
|
|
45
|
+
${body}
|
|
46
|
+
}
|
|
47
|
+
`;
|
|
48
|
+
const bootstrap = `import { ${cls} } from './${cls}';
|
|
49
|
+
|
|
50
|
+
// Start the service so other services can call its @expose()d methods.
|
|
51
|
+
(async () => {
|
|
52
|
+
const service = new ${cls}({ name: '${cls}' });
|
|
53
|
+
await service.start();
|
|
54
|
+
console.log('${cls} is up');
|
|
55
|
+
})();
|
|
56
|
+
`;
|
|
57
|
+
return [
|
|
58
|
+
`Install: \`npm i @imqueue/rpc\` (needs a running Redis).`,
|
|
59
|
+
"",
|
|
60
|
+
`**${cls}.ts**`,
|
|
61
|
+
"```typescript",
|
|
62
|
+
service.trimEnd(),
|
|
63
|
+
"```",
|
|
64
|
+
"",
|
|
65
|
+
`**index.ts** (bootstrap)`,
|
|
66
|
+
"```typescript",
|
|
67
|
+
bootstrap.trimEnd(),
|
|
68
|
+
"```",
|
|
69
|
+
"",
|
|
70
|
+
`Tip: scaffold a full, provider-wired project (VCS/CI/Docker) with the CLI instead: \`imq service create ${name}\`.`,
|
|
71
|
+
].join("\n");
|
|
72
|
+
}
|
|
73
|
+
/** Generate a typed-client usage snippet for a service. */
|
|
74
|
+
export function scaffoldClient(service, methods) {
|
|
75
|
+
const cls = pascal(service).endsWith("Service") ? pascal(service) : `${pascal(service)}Service`;
|
|
76
|
+
const clientCls = cls.replace(/Service$/, "Client");
|
|
77
|
+
const sample = (methods && methods[0]) || DEFAULT_METHODS[0];
|
|
78
|
+
const args = (sample.params ?? []).map((p) => `/* ${p.name}: ${p.type} */`).join(", ");
|
|
79
|
+
const usage = `import { ${clientCls} } from './clients/${clientCls}';
|
|
80
|
+
|
|
81
|
+
(async () => {
|
|
82
|
+
const client = new ${clientCls}();
|
|
83
|
+
await client.start();
|
|
84
|
+
|
|
85
|
+
// Fully-typed remote call — signature comes from ${cls}:
|
|
86
|
+
const result = await client.${sample.name}(${args});
|
|
87
|
+
console.log(result);
|
|
88
|
+
})();
|
|
89
|
+
`;
|
|
90
|
+
return [
|
|
91
|
+
`@imqueue generates the **real**, fully-typed client from a **running** service, so its types can never drift:`,
|
|
92
|
+
"",
|
|
93
|
+
"```bash",
|
|
94
|
+
`# with ${cls} running:`,
|
|
95
|
+
`imq client generate ${cls.replace(/Service$/, "")}`,
|
|
96
|
+
"```",
|
|
97
|
+
"",
|
|
98
|
+
`That emits \`./clients/${clientCls}\`. Use it like:`,
|
|
99
|
+
"```typescript",
|
|
100
|
+
usage.trimEnd(),
|
|
101
|
+
"```",
|
|
102
|
+
"",
|
|
103
|
+
`The snippet above is illustrative — prefer the generated client so method signatures stay in sync with the service.`,
|
|
104
|
+
].join("\n");
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=scaffold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@imqueue/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Model Context Protocol server for @imqueue — lets AI coding agents search the docs and scaffold typed services & clients.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"imqueue",
|
|
9
|
+
"ai",
|
|
10
|
+
"coding-assistant",
|
|
11
|
+
"cursor",
|
|
12
|
+
"claude",
|
|
13
|
+
"rpc",
|
|
14
|
+
"microservices",
|
|
15
|
+
"nodejs",
|
|
16
|
+
"typescript"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://imqueue.org",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/imqueue/mcp.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "GPL-3.0",
|
|
24
|
+
"author": "imqueue.com <support@imqueue.com>",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"bin": {
|
|
27
|
+
"imqueue-mcp": "dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"SPEC.md"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"start": "node dist/index.js",
|
|
39
|
+
"dev": "tsx src/index.ts",
|
|
40
|
+
"smoke": "node scripts/smoke.mjs",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
45
|
+
"zod": "^3.23.8"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@types/node": "^22.0.0",
|
|
49
|
+
"tsx": "^4.19.0",
|
|
50
|
+
"typescript": "^5.6.0"
|
|
51
|
+
}
|
|
52
|
+
}
|