@systemdox/mcp-server 0.1.19 → 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/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.19" : "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.19" : "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.19" : "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,6 +1,6 @@
1
1
  {
2
2
  "name": "@systemdox/mcp-server",
3
- "version": "0.1.19",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
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",