@cloudgrid-io/mcp 0.20.52 → 0.20.53

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/src/docs.js DELETED
@@ -1,147 +0,0 @@
1
- // CloudGrid MCP server — docs edition (public, read-only, no auth).
2
- //
3
- // A public documentation search server exposing search_cloudgrid_documentation
4
- // (and an optional cloudgrid_quickstart_guide) over MCP Streamable HTTP. The
5
- // same Express + StreamableHTTP pattern as the web edition (src/web.js),
6
- // stripped to the minimum: no auth, no OAuth, no session identity, no secrets.
7
- // Safe to expose anonymously.
8
- //
9
- // The corpus (src/corpus/*.md) is indexed at startup with BM25 keyword search.
10
- // The search backend is behind a clean interface so it can be swapped to
11
- // semantic/embedding search later without changing the tool contract.
12
- //
13
- // Run: PORT=8080 node src/docs.js Health: GET /healthz
14
-
15
- import { readFileSync } from "node:fs";
16
- import { randomUUID } from "node:crypto";
17
- import express from "express";
18
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
19
- import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
20
- import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
21
- import { z } from "zod";
22
- import { DocsSearch, loadCorpus } from "./docs-search.js";
23
-
24
- const { version } = JSON.parse(readFileSync(new URL("../package.json", import.meta.url)));
25
-
26
- const PORT = Number(process.env.PORT || 8080);
27
-
28
- // Build the search index once at startup.
29
- const search = loadCorpus(new DocsSearch());
30
-
31
- // Load the quickstart guide verbatim.
32
- const QUICKSTART = readFileSync(new URL("./corpus/cookbook.md", import.meta.url), "utf-8");
33
-
34
- // ── Tool helpers (same convention as tools.js) ────────────────────────────────
35
- function ok(text) {
36
- return { content: [{ type: "text", text }] };
37
- }
38
-
39
- function createDocsServer() {
40
- const server = new McpServer({ name: "cloudgrid-docs", version });
41
-
42
- // Naming cleanup (spec §6): the docs tools move to the grid_* family. Both
43
- // legacy names stay registered as deprecated aliases (same handler) so nothing
44
- // breaks mid-migration; aliases are removed in a later major.
45
- // search_cloudgrid_documentation → grid_search_docs
46
- // cloudgrid_quickstart_guide → grid_quickstart
47
- const dual = (name, alias, description, schema, handler) => {
48
- server.tool(name, description, schema, handler);
49
- server.tool(alias, `(deprecated: use ${name}) ${description}`, schema, handler);
50
- };
51
-
52
- dual(
53
- "grid_search_docs",
54
- "search_cloudgrid_documentation",
55
- "Search CloudGrid documentation — guides, tutorials, CLI reference, MCP setup, and skill descriptions. Returns the most relevant doc chunks with title, snippet, and source path.",
56
- {
57
- query: z
58
- .string()
59
- .describe(
60
- "The search query, e.g. 'deploy a database app' or 'add the MCP to Cursor'",
61
- ),
62
- },
63
- async ({ query }) => {
64
- const results = search.search(query, 5);
65
- if (results.length === 0) {
66
- return ok("No documentation matched your query. Try different keywords.");
67
- }
68
- const text = results
69
- .map(
70
- (r, i) => `### ${i + 1}. ${r.title}\n${r.snippet}\n_Source: ${r.source}_`,
71
- )
72
- .join("\n\n---\n\n");
73
- return ok(text);
74
- },
75
- );
76
-
77
- dual(
78
- "grid_quickstart",
79
- "cloudgrid_quickstart_guide",
80
- "Get the CloudGrid quickstart guide — the canonical build-and-ship loop from scaffold to deploy to feedback.",
81
- {},
82
- async () => ok(QUICKSTART),
83
- );
84
-
85
- return server;
86
- }
87
-
88
- // ── HTTP server (mirrors src/web.js, no auth) ─────────────────────────────────
89
- const app = express();
90
- app.use(express.json());
91
-
92
- app.get("/healthz", (_req, res) => res.json({ ok: true, edition: "docs" }));
93
-
94
- const transports = Object.create(null);
95
-
96
- app.post("/mcp", async (req, res) => {
97
- const sessionId = req.headers["mcp-session-id"];
98
- let transport = sessionId ? transports[sessionId] : undefined;
99
-
100
- if (transport) {
101
- await transport.handleRequest(req, res, req.body);
102
- return;
103
- }
104
-
105
- if (sessionId || !isInitializeRequest(req.body)) {
106
- res.status(400).json({
107
- jsonrpc: "2.0",
108
- error: { code: -32000, message: "No valid session. Send an initialize request first." },
109
- id: null,
110
- });
111
- return;
112
- }
113
-
114
- const newSessionId = randomUUID();
115
- transport = new StreamableHTTPServerTransport({
116
- sessionIdGenerator: () => newSessionId,
117
- onsessioninitialized: (sid) => {
118
- transports[sid] = transport;
119
- },
120
- });
121
- transport.onclose = () => {
122
- if (transport.sessionId) delete transports[transport.sessionId];
123
- };
124
- const server = createDocsServer();
125
- await server.connect(transport);
126
- await transport.handleRequest(req, res, req.body);
127
- });
128
-
129
- async function handleSessionRequest(req, res) {
130
- const sessionId = req.headers["mcp-session-id"];
131
- const transport = sessionId ? transports[sessionId] : undefined;
132
- if (!transport) {
133
- res.status(400).send("Invalid or missing session id");
134
- return;
135
- }
136
- await transport.handleRequest(req, res);
137
- }
138
-
139
- app.get("/mcp", handleSessionRequest);
140
- app.delete("/mcp", handleSessionRequest);
141
-
142
- app.listen(PORT, () => {
143
- // eslint-disable-next-line no-console
144
- console.error(
145
- `cloudgrid-docs edition listening on :${PORT} (POST /mcp, GET /healthz) — ${search.size} chunks indexed`,
146
- );
147
- });