gasp-standard-mcp 1.18.0 → 1.19.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/README.md +12 -2
- package/build/http.d.ts +30 -0
- package/build/http.js +92 -0
- package/build/index.js +3 -857
- package/build/ossie/adapt.d.ts +6 -1
- package/build/ossie/adapt.js +11 -4
- package/build/ossie/check.js +7 -9
- package/build/ossie/company.js +2 -12
- package/build/ossie/demo.js +2 -12
- package/build/ossie/types.d.ts +25 -1
- package/build/ossie/types.js +58 -10
- package/build/server.d.ts +52 -0
- package/build/server.js +914 -0
- package/build/standard-version.json +1 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -29,6 +29,16 @@ Add to your project `.mcp.json` or `~/.claude/settings.json`:
|
|
|
29
29
|
|
|
30
30
|
Restart your MCP client after adding the config.
|
|
31
31
|
|
|
32
|
+
### Hosted endpoint
|
|
33
|
+
|
|
34
|
+
The same server runs over Streamable HTTP for clients that connect to a remote server, such as the Claude connectors directory. Every tool is read-only and the standard is public, so the endpoint takes no authentication.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx gasp-standard-mcp-http # PORT=3000, endpoint /mcp, /health for the version and tool count
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`PORT`, `HOST` and `MCP_PATH` set where it listens. `MCP_ALLOWED_HOSTS` and `MCP_ALLOWED_ORIGINS` (comma-separated) refuse requests whose Host or Origin header is not listed, the DNS-rebinding guard for a server reachable from a browser; set `MCP_ALLOWED_HOSTS` to the public hostname in production. The `Dockerfile` in this directory serves the published package the same way. Every tool carries a `title` and `readOnlyHint: true`, so a client can run them without a per-call prompt. The HTTP server writes one JSON line to stderr per tool call (the tool, the duration, success or error and the client's name and version; never the arguments or the result), so the host's logs count calls per tool.
|
|
41
|
+
|
|
32
42
|
## Tools
|
|
33
43
|
|
|
34
44
|
| Tool | Description |
|
|
@@ -59,7 +69,7 @@ The traversal tools (`infer_drivers`, `explain_path`, `coverage_gap`) return pat
|
|
|
59
69
|
|
|
60
70
|
## Data
|
|
61
71
|
|
|
62
|
-
Version 1.
|
|
72
|
+
Version 1.19.0.
|
|
63
73
|
|
|
64
74
|
- 306 metrics across 13 departments
|
|
65
75
|
- 94 glossary definitions (81 SaaS domain + 13 GASP framework)
|
|
@@ -89,7 +99,7 @@ Version 1.18.0.
|
|
|
89
99
|
- The drift scorer (`src/ossie/drift.ts`, behind `score_drift`) runs a company's metrics and the canonical ones side by side on the synthetic SaaS dataset (`src/synthetic.ts`, also the test fixture) in PGlite, loaded on first use from the optional dependency `@electric-sql/pglite`; without it the tool says so and runs nothing
|
|
90
100
|
- The metric compiler (`src/ossie/company.ts`, behind `compile_model`) writes every covered GASP metric into a company's own Ossie model, direct or staged, over their datasets, sources and field expressions. The test suite compiles the fixture adapter and checks every direct and staged value against the reference export on the fixture rows
|
|
91
101
|
- Adapter schema 1.1 (`standard/gasp-adapter-schema.json`, JSON Schema draft 2020-12, served at https://gaspwiki.com/schemas/gasp-adapter-v1.json): a `datasets` map, per-dataset mappings (a mapping names its `dataset`), informational fields and `$schema`. The schema travels in the bundled data as `adapterSchema` and `validate_adapter` applies it with Ajv, so validation works offline. `npm run derive` writes a ready template to `standard/exports/gasp-adapter-template.json` (served at https://gaspwiki.com/downloads/gasp-adapter-template.json) and the tests validate it against the schema
|
|
92
|
-
- GASP Ossie extension schema 1 (`standard/gasp-ossie-extension-v1.json`, JSON Schema draft 2020-12, served at https://gaspwiki.com/schemas/gasp-ossie-extension-v1.json): the parsed `data` of every `custom_extensions` entry GASP writes under `vendor_name: GASP`, one definition per placement (model, dataset, metric, mapping), strict so each key GASP adds outside Ossie is listed. It travels in the bundled data as `extensionSchema` and the cross-document checks (`checkExport` in `src/ossie/check.ts`) validate every GASP payload against it in the export, `validate_mapping`, `compile_model`, `propose_mapping` and `record_decision`
|
|
102
|
+
- GASP Ossie extension schema 1 (`standard/gasp-ossie-extension-v1.json`, JSON Schema draft 2020-12, served at https://gaspwiki.com/schemas/gasp-ossie-extension-v1.json): the parsed `data` of every `custom_extensions` entry GASP writes under `vendor_name: GASP`, one definition per placement (model, dataset, metric, mapping), strict so each key GASP adds outside Ossie is listed. It travels in the bundled data as `extensionSchema` and the cross-document checks (`checkExport` in `src/ossie/check.ts`) validate every GASP payload against it in the export, `validate_mapping`, `compile_model`, `propose_mapping` and `record_decision`. Every tool reads `data` as the JSON string the pinned Ossie schema defines or as an object (the form apache/ossie #522 proposes), under `custom_extensions` or the `nonportable_extensions` key the same proposal renames it to, and writes the pinned form under the pinned key
|
|
93
103
|
- Sources: the canonical [GASP Standard](https://gaspwiki.com) files
|
|
94
104
|
|
|
95
105
|
## Examples
|
package/build/http.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The GASP Standard MCP server over Streamable HTTP, for a hosted endpoint
|
|
4
|
+
* such as https://mcp.gaspwiki.com/mcp that Claude connects to as a remote
|
|
5
|
+
* server. Stateless: every request gets its own transport and server over the
|
|
6
|
+
* data loaded once, so any number of instances can sit behind one hostname.
|
|
7
|
+
* No authentication: the standard is public and every tool is read-only.
|
|
8
|
+
*
|
|
9
|
+
* Run `gasp-standard-mcp-http` (or `node build/http.js`). Environment:
|
|
10
|
+
* `PORT` (default 3000), `HOST` (default 0.0.0.0), `MCP_PATH` (default /mcp),
|
|
11
|
+
* `MCP_ALLOWED_HOSTS` and `MCP_ALLOWED_ORIGINS` (comma-separated; when either
|
|
12
|
+
* is set, requests whose Host or Origin header is not listed are refused, the
|
|
13
|
+
* DNS-rebinding guard for a server reachable from a browser).
|
|
14
|
+
*
|
|
15
|
+
* Every tool call is written to stderr as one JSON line, `{"event":"tool",
|
|
16
|
+
* "tool":"lookup_metric","ms":12,"ok":true,"client":"claude-ai/1.0"}`, so the
|
|
17
|
+
* host's log stream counts calls per tool. Arguments and results are never
|
|
18
|
+
* logged, which is what the privacy policy at gaspwiki.com/privacy promises.
|
|
19
|
+
*/
|
|
20
|
+
import { type Server } from "node:http";
|
|
21
|
+
export interface HttpOptions {
|
|
22
|
+
port?: number;
|
|
23
|
+
host?: string;
|
|
24
|
+
/** The path the MCP endpoint answers on; everything else is the health page or 404. */
|
|
25
|
+
path?: string;
|
|
26
|
+
allowedHosts?: string[];
|
|
27
|
+
allowedOrigins?: string[];
|
|
28
|
+
}
|
|
29
|
+
/** The listening server. `port: 0` picks a free port (tests). */
|
|
30
|
+
export declare function startHttpServer(options?: HttpOptions): Server;
|
package/build/http.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The GASP Standard MCP server over Streamable HTTP, for a hosted endpoint
|
|
4
|
+
* such as https://mcp.gaspwiki.com/mcp that Claude connects to as a remote
|
|
5
|
+
* server. Stateless: every request gets its own transport and server over the
|
|
6
|
+
* data loaded once, so any number of instances can sit behind one hostname.
|
|
7
|
+
* No authentication: the standard is public and every tool is read-only.
|
|
8
|
+
*
|
|
9
|
+
* Run `gasp-standard-mcp-http` (or `node build/http.js`). Environment:
|
|
10
|
+
* `PORT` (default 3000), `HOST` (default 0.0.0.0), `MCP_PATH` (default /mcp),
|
|
11
|
+
* `MCP_ALLOWED_HOSTS` and `MCP_ALLOWED_ORIGINS` (comma-separated; when either
|
|
12
|
+
* is set, requests whose Host or Origin header is not listed are refused, the
|
|
13
|
+
* DNS-rebinding guard for a server reachable from a browser).
|
|
14
|
+
*
|
|
15
|
+
* Every tool call is written to stderr as one JSON line, `{"event":"tool",
|
|
16
|
+
* "tool":"lookup_metric","ms":12,"ok":true,"client":"claude-ai/1.0"}`, so the
|
|
17
|
+
* host's log stream counts calls per tool. Arguments and results are never
|
|
18
|
+
* logged, which is what the privacy policy at gaspwiki.com/privacy promises.
|
|
19
|
+
*/
|
|
20
|
+
import { createServer as createHttp } from "node:http";
|
|
21
|
+
import { realpathSync } from "node:fs";
|
|
22
|
+
import { pathToFileURL } from "node:url";
|
|
23
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
24
|
+
import { PKG_VERSION, TOOL_COUNT, createServer } from "./server.js";
|
|
25
|
+
const CORS = {
|
|
26
|
+
"Access-Control-Allow-Origin": "*",
|
|
27
|
+
"Access-Control-Allow-Methods": "GET, POST, DELETE, OPTIONS",
|
|
28
|
+
"Access-Control-Allow-Headers": "Content-Type, Accept, Authorization, Mcp-Session-Id, Mcp-Protocol-Version",
|
|
29
|
+
"Access-Control-Expose-Headers": "Mcp-Session-Id, Mcp-Protocol-Version",
|
|
30
|
+
};
|
|
31
|
+
/** The listening server. `port: 0` picks a free port (tests). */
|
|
32
|
+
export function startHttpServer(options = {}) {
|
|
33
|
+
const path = options.path ?? "/mcp";
|
|
34
|
+
const guard = options.allowedHosts?.length || options.allowedOrigins?.length
|
|
35
|
+
? { enableDnsRebindingProtection: true, allowedHosts: options.allowedHosts, allowedOrigins: options.allowedOrigins }
|
|
36
|
+
: {};
|
|
37
|
+
const handle = async (req, res) => {
|
|
38
|
+
const url = new URL(req.url ?? "/", "http://localhost");
|
|
39
|
+
if (req.method === "OPTIONS") {
|
|
40
|
+
res.writeHead(204, CORS);
|
|
41
|
+
res.end();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (url.pathname === path) {
|
|
45
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true, ...guard });
|
|
46
|
+
const server = createServer({ onToolCall: (r) => console.error(JSON.stringify(r)) });
|
|
47
|
+
res.on("close", () => { void transport.close(); void server.close(); });
|
|
48
|
+
for (const [k, v] of Object.entries(CORS))
|
|
49
|
+
res.setHeader(k, v);
|
|
50
|
+
await server.connect(transport);
|
|
51
|
+
await transport.handleRequest(req, res);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/health")) {
|
|
55
|
+
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
56
|
+
res.end(JSON.stringify({ name: "gasp-standard", version: PKG_VERSION, tools: TOOL_COUNT, endpoint: path, documentation: "https://gaspwiki.com/v1/tools" }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
60
|
+
res.end(`Not found. The MCP endpoint is ${path}.`);
|
|
61
|
+
};
|
|
62
|
+
const server = createHttp((req, res) => {
|
|
63
|
+
handle(req, res).catch((err) => {
|
|
64
|
+
console.error("[gasp-standard-mcp-http]", err);
|
|
65
|
+
if (!res.headersSent) {
|
|
66
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
67
|
+
}
|
|
68
|
+
if (!res.writableEnded)
|
|
69
|
+
res.end(JSON.stringify({ jsonrpc: "2.0", error: { code: -32603, message: "Internal error" }, id: null }));
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
server.listen(options.port ?? 3000, options.host ?? "0.0.0.0");
|
|
73
|
+
return server;
|
|
74
|
+
}
|
|
75
|
+
const list = (v) => v?.split(",").map((s) => s.trim()).filter(Boolean);
|
|
76
|
+
const invokedDirectly = (() => {
|
|
77
|
+
try {
|
|
78
|
+
return process.argv[1] !== undefined && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
if (invokedDirectly) {
|
|
85
|
+
const port = Number(process.env.PORT ?? 3000);
|
|
86
|
+
const path = process.env.MCP_PATH ?? "/mcp";
|
|
87
|
+
const server = startHttpServer({ port, host: process.env.HOST, path, allowedHosts: list(process.env.MCP_ALLOWED_HOSTS), allowedOrigins: list(process.env.MCP_ALLOWED_ORIGINS) });
|
|
88
|
+
server.on("listening", () => console.error(`[gasp-standard-mcp-http] ${PKG_VERSION}: ${TOOL_COUNT} tools on port ${port}${path}`));
|
|
89
|
+
const stop = () => server.close(() => process.exit(0));
|
|
90
|
+
process.on("SIGTERM", stop);
|
|
91
|
+
process.on("SIGINT", stop);
|
|
92
|
+
}
|