@systemdox/mcp-server 0.1.18 → 0.1.20

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 CHANGED
@@ -1,8 +1,38 @@
1
1
  # @systemdox/mcp-server
2
2
 
3
- MCP server that exposes your SystemDox context — checks, specs, docs, tech
4
- stack, project rules, and prompt templates — to AI coding tools (Claude Code,
5
- Cursor, and any MCP-compatible client).
3
+ > Your architecture, in your agents' context window.
4
+
5
+ [SystemDox](https://www.systemdox.com) is the document plane for AI-assisted
6
+ engineering: your standards — specs, architecture decision records, checks,
7
+ project rules, docs — versioned in git and delivered into your coding agents'
8
+ context at write time. This MCP server is how Claude Code, Cursor, and any
9
+ MCP-compatible tool read that context — and write decisions back.
10
+
11
+ **Why teams use it**
12
+
13
+ - **Write-time context** — `get_context` ranks the checks, specs, and docs
14
+ relevant to the task at hand and fetches their bodies within a token budget,
15
+ so the agent starts from your architecture instead of guessing it.
16
+ - **Both directions** — agents call `report_decision` and `update_spec` as they
17
+ work, so decision records exist as a by-product of shipping, not as
18
+ archaeology afterwards.
19
+ - **One source for every agent** — no per-developer rules-file forks; every
20
+ session in every repository starts from the same governed, versioned context.
21
+ - **Scoped by key** — the API key is the boundary: a department-scoped key sees
22
+ only its own space's checks, specs, and docs.
23
+
24
+ **Get started in about a minute**
25
+
26
+ 1. [Create a SystemDox workspace](https://app.systemdox.com/signup) — the free
27
+ tier covers one repository, permanently.
28
+ 2. Connect a GitHub repository and generate an API key (workspace settings).
29
+ 3. Add the server to your tool — hosted (Option A) or local (Option B) below.
30
+
31
+ Docs: [MCP setup guide](https://www.systemdox.com/guides/mcp-setup) ·
32
+ [Documentation standard](https://www.systemdox.com/guides/documentation-standard) ·
33
+ [Why write-time context](https://www.systemdox.com/blog/architecture-only-exists-at-write-time)
34
+
35
+ ---
6
36
 
7
37
  There are two ways to run it. Both authenticate with a SystemDox API key
8
38
  (generate one on your SystemDox settings page) and act strictly as the caller —
package/dist/cli.js CHANGED
@@ -1979,7 +1979,7 @@ var init_server = __esm({
1979
1979
  init_create_project_rule();
1980
1980
  init_get_prompt_templates();
1981
1981
  init_create_prompt_template();
1982
- SERVER_VERSION = true ? "0.1.18" : "0.0.0-dev";
1982
+ SERVER_VERSION = true ? "0.1.20" : "0.0.0-dev";
1983
1983
  }
1984
1984
  });
1985
1985
 
package/dist/http.js CHANGED
@@ -117,6 +117,41 @@ var SystemDoxApiClient = class {
117
117
  }
118
118
  };
119
119
 
120
+ // src/lib/metrics.ts
121
+ var NAMESPACE = "SystemDox/Mcp";
122
+ var METRIC_NAME = "McpToolCall";
123
+ function buildMcpToolCallMetric(tenant, tool, timestamp = Date.now()) {
124
+ return {
125
+ _aws: {
126
+ Timestamp: timestamp,
127
+ CloudWatchMetrics: [
128
+ {
129
+ Namespace: NAMESPACE,
130
+ Dimensions: [["tenant"]],
131
+ Metrics: [{ Name: METRIC_NAME, Unit: "Count" }]
132
+ }
133
+ ]
134
+ },
135
+ tenant,
136
+ tool,
137
+ [METRIC_NAME]: 1
138
+ };
139
+ }
140
+ function emitMcpToolCalls(tenant, rawBody, log = console.log) {
141
+ try {
142
+ const parsed = JSON.parse(rawBody);
143
+ const messages = Array.isArray(parsed) ? parsed : [parsed];
144
+ for (const message of messages) {
145
+ if (typeof message === "object" && message !== null && message.method === "tools/call") {
146
+ const params = message.params;
147
+ const tool = typeof params?.name === "string" ? params.name : "unknown";
148
+ log(JSON.stringify(buildMcpToolCallMetric(tenant || "unknown", tool)));
149
+ }
150
+ }
151
+ } catch {
152
+ }
153
+ }
154
+
120
155
  // src/server.ts
121
156
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
122
157
 
@@ -1790,7 +1825,7 @@ function registerCreatePromptTemplate(server, apiClient) {
1790
1825
  }
1791
1826
 
1792
1827
  // src/server.ts
1793
- var SERVER_VERSION = true ? "0.1.18" : "0.0.0-dev";
1828
+ var SERVER_VERSION = true ? "0.1.20" : "0.0.0-dev";
1794
1829
  function createServer(apiClient) {
1795
1830
  const server = new McpServer({
1796
1831
  name: "systemdox-mcp-server",
@@ -1836,9 +1871,9 @@ function jsonError(statusCode, message) {
1836
1871
  async function handler(event) {
1837
1872
  const headers = lowercaseHeaders(event.headers);
1838
1873
  const host = (headers["host"] ?? "").toLowerCase();
1874
+ const keyTenant = event.requestContext?.authorizer?.tenant_slug ?? "";
1839
1875
  if (host.endsWith(MCP_HOST_SUFFIX)) {
1840
1876
  const hostTenant = host.slice(0, -MCP_HOST_SUFFIX.length);
1841
- const keyTenant = event.requestContext?.authorizer?.tenant_slug ?? "";
1842
1877
  if (!hostTenant || hostTenant.includes(".") || hostTenant !== keyTenant) {
1843
1878
  return jsonError(403, "API key does not belong to this tenant");
1844
1879
  }
@@ -1860,6 +1895,7 @@ async function handler(event) {
1860
1895
  return jsonError(401, "Missing bearer API key");
1861
1896
  }
1862
1897
  const rawBody = event.isBase64Encoded ? Buffer.from(event.body ?? "", "base64").toString("utf8") : event.body ?? "";
1898
+ emitMcpToolCalls(keyTenant, rawBody);
1863
1899
  const apiClient = new SystemDoxApiClient({
1864
1900
  apiKey,
1865
1901
  apiUrl: process.env.SYSTEMDOX_API_URL ?? "https://api.puglieseweb.com/systemdox",
package/dist/index.js CHANGED
@@ -1806,7 +1806,7 @@ function registerCreatePromptTemplate(server2, apiClient2) {
1806
1806
  }
1807
1807
 
1808
1808
  // src/server.ts
1809
- var SERVER_VERSION = true ? "0.1.18" : "0.0.0-dev";
1809
+ var SERVER_VERSION = true ? "0.1.20" : "0.0.0-dev";
1810
1810
  function createServer(apiClient2) {
1811
1811
  const server2 = new McpServer({
1812
1812
  name: "systemdox-mcp-server",
@@ -0,0 +1,41 @@
1
+ /**
2
+ * CloudWatch EMF telemetry for hosted MCP tool calls (issue #978).
3
+ *
4
+ * One metric answers two standing questions:
5
+ * - activation: "which tenants had ≥1 agent MCP call this week?" —
6
+ * `McpToolCall` dimensioned by `tenant`, joined against the repo
7
+ * `connectedAt` cohort in DynamoDB;
8
+ * - the deprecated-alias removal gate (README "Deprecated tool names"):
9
+ * per-tool-name counts come from the `tool` property via Logs Insights,
10
+ * kept out of the dimensions so metric cardinality stays = tenants.
11
+ *
12
+ * EMF is a formatted console.log — no SDK dependency, no latency, and the
13
+ * Lambda's existing log group is the transport. Telemetry must never break
14
+ * the request path: emitters swallow their own failures.
15
+ */
16
+ declare const METRIC_NAME = "McpToolCall";
17
+ export interface McpToolCallMetric {
18
+ _aws: {
19
+ Timestamp: number;
20
+ CloudWatchMetrics: {
21
+ Namespace: string;
22
+ Dimensions: string[][];
23
+ Metrics: {
24
+ Name: string;
25
+ Unit: string;
26
+ }[];
27
+ }[];
28
+ };
29
+ tenant: string;
30
+ tool: string;
31
+ [METRIC_NAME]: number;
32
+ }
33
+ /** Build the EMF envelope for a single tool call. Pure — unit-testable. */
34
+ export declare function buildMcpToolCallMetric(tenant: string, tool: string, timestamp?: number): McpToolCallMetric;
35
+ /**
36
+ * Parse a JSON-RPC request body (single message or batch) and emit one EMF
37
+ * line per `tools/call`. Anything unparseable or non-tool (initialize,
38
+ * tools/list, notifications) is ignored. Never throws.
39
+ */
40
+ export declare function emitMcpToolCalls(tenant: string, rawBody: string, log?: (line: string) => void): void;
41
+ export {};
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/metrics.ts
4
+ var NAMESPACE = "SystemDox/Mcp";
5
+ var METRIC_NAME = "McpToolCall";
6
+ function buildMcpToolCallMetric(tenant, tool, timestamp = Date.now()) {
7
+ return {
8
+ _aws: {
9
+ Timestamp: timestamp,
10
+ CloudWatchMetrics: [
11
+ {
12
+ Namespace: NAMESPACE,
13
+ Dimensions: [["tenant"]],
14
+ Metrics: [{ Name: METRIC_NAME, Unit: "Count" }]
15
+ }
16
+ ]
17
+ },
18
+ tenant,
19
+ tool,
20
+ [METRIC_NAME]: 1
21
+ };
22
+ }
23
+ function emitMcpToolCalls(tenant, rawBody, log = console.log) {
24
+ try {
25
+ const parsed = JSON.parse(rawBody);
26
+ const messages = Array.isArray(parsed) ? parsed : [parsed];
27
+ for (const message of messages) {
28
+ if (typeof message === "object" && message !== null && message.method === "tools/call") {
29
+ const params = message.params;
30
+ const tool = typeof params?.name === "string" ? params.name : "unknown";
31
+ log(JSON.stringify(buildMcpToolCallMetric(tenant || "unknown", tool)));
32
+ }
33
+ }
34
+ } catch {
35
+ }
36
+ }
37
+ export {
38
+ buildMcpToolCallMetric,
39
+ emitMcpToolCalls
40
+ };
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@systemdox/mcp-server",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
- "description": "MCP server that exposes SystemDox checks, specs, docs, and tech stack to AI coding tools",
5
+ "description": "Give AI coding agents your architecture context over MCP — checks, specs, ADRs, project rules, and docs from SystemDox, delivered at write time",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
8
8
  "bin": {
@@ -44,11 +44,18 @@
44
44
  "keywords": [
45
45
  "mcp",
46
46
  "model-context-protocol",
47
+ "mcp-server",
47
48
  "systemdox",
48
49
  "ai",
49
50
  "claude",
51
+ "claude-code",
52
+ "cursor",
53
+ "context",
54
+ "architecture",
55
+ "adr",
50
56
  "checks",
51
- "specs"
57
+ "specs",
58
+ "governance"
52
59
  ],
53
60
  "author": "Emanuele Pugliese",
54
61
  "license": "ISC",