@skeletiq/mcp 0.6.1 → 0.7.1

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
@@ -56,6 +56,8 @@ Run `npx -y @skeletiq/mcp` over stdio with `SKELETIQ_API_KEY` in the environment
56
56
  | `SKELETIQ_API_KEY` | yes | — | A personal API token, starting `skq_`. Not a browser session token. |
57
57
  | `SKELETIQ_API_URL` | no | `https://api.skeletiq.com` | For self-hosted installs. A trailing `/api/v1` is accepted and trimmed. |
58
58
 
59
+ Every request carries a User-Agent of the form `skeletiq-mcp/<version> (<host name>/<host version>)`, naming the MCP host that launched the server.
60
+
59
61
  ## Scopes
60
62
 
61
63
  A token grants only what you tick. The server's tools need:
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { serveStdio } from "@modelcontextprotocol/server/stdio";
5
5
 
6
6
  // src/config.ts
7
7
  var SERVER_NAME = "skeletiq";
8
- var SERVER_VERSION = "0.6.1";
8
+ var SERVER_VERSION = "0.7.1";
9
9
  var AGENT_SCHEMA_VERSION = 2;
10
10
  function schemaNote(serverVersion) {
11
11
  if (serverVersion === void 0 || serverVersion <= AGENT_SCHEMA_VERSION) return null;
@@ -199,12 +199,28 @@ function describeApiError(error) {
199
199
 
200
200
  // src/http/client.ts
201
201
  var API_PREFIX = "/api/v1";
202
+ var HOST_TOKEN_MAX = 64;
203
+ function hostToken(value) {
204
+ return value.replace(/[^A-Za-z0-9._~+-]/g, "-").slice(0, HOST_TOKEN_MAX);
205
+ }
206
+ function userAgent(host) {
207
+ const base = `${SERVER_NAME}-mcp/${SERVER_VERSION}`;
208
+ const name = hostToken(host?.name ?? "");
209
+ if (!name) return base;
210
+ const version = hostToken(host?.version ?? "");
211
+ return version ? `${base} (${name}/${version})` : `${base} (${name})`;
212
+ }
202
213
  var READ_TIMEOUT_MS = 6e5;
203
214
  var SkeletiqClient = class {
204
215
  config;
216
+ hostSource;
205
217
  constructor(config) {
206
218
  this.config = config;
207
219
  }
220
+ /** Where to read the connected host's name and version from, for the User-Agent. */
221
+ setHostSource(source) {
222
+ this.hostSource = source;
223
+ }
208
224
  /** A parsed JSON response, or a thrown `SkeletiqApiError` carrying the server's own words. */
209
225
  async request(path2, options = {}) {
210
226
  const response = await this.fetch(path2, options);
@@ -223,7 +239,7 @@ var SkeletiqClient = class {
223
239
  headers: {
224
240
  Authorization: `Bearer ${this.config.apiKey}`,
225
241
  Accept: "application/json",
226
- "User-Agent": `${SERVER_NAME}-mcp/${SERVER_VERSION}`,
242
+ "User-Agent": userAgent(this.hostSource?.()),
227
243
  ...options.body === void 0 ? {} : { "Content-Type": "application/json" },
228
244
  ...options.headers
229
245
  },
@@ -625,6 +641,11 @@ var CritiqueFindingSchema = z.looseObject({
625
641
  remediation: z.string().optional(),
626
642
  /** Reported in full and never charged against the score — advice, not a problem to fix. */
627
643
  advisory_only: z.boolean().optional(),
644
+ /**
645
+ * Whether this finding can hold a design back from Ready. Absent or null means not stated, and
646
+ * the standing Ready rule applies to it.
647
+ */
648
+ holds_ready: z.boolean().nullable().optional(),
628
649
  /**
629
650
  * The problem this finding is about. Stable across rewording, and shared with the advisor
630
651
  * suggestion and decision record raised by the same problem — the key a dismissal is recorded
@@ -1455,6 +1476,7 @@ var input3 = z5.object({
1455
1476
  });
1456
1477
  var SCORE_BASIS_FINDINGS_ONLY = "findings_only";
1457
1478
  var SCORE_BASIS_NOTE = "This score is findings only. It is not held to a traceability ceiling, because this tool is given a design and no requirement set to trace it against. The app can show a lower number for the same stored design for exactly that reason.";
1479
+ var COMPLIANCE_INDICATIVE_NOTE = "Indicative automated check based on architecture structure \u2014 not a certified compliance audit or legal advice.";
1458
1480
  var output3 = z5.object({
1459
1481
  architecture_score: z5.number(),
1460
1482
  score_basis: z5.literal(SCORE_BASIS_FINDINGS_ONLY).describe(SCORE_BASIS_NOTE),
@@ -1537,7 +1559,7 @@ function registerCritique(server, client) {
1537
1559
  // An agent that reads only the text has to see this too. Without it, "the app
1538
1560
  // said 59 and the tool said 74" is an unexplained contradiction it will either
1539
1561
  // resolve wrongly or report as a bug.
1540
- complianceAssessed ? `Compliance checked against ${(critique.frameworks_checked ?? []).join(", ")} \u2014 the frameworks systems in this domain are usually held to, inferred from the domain you sent rather than from a regime anyone named. Confirm them.` : critique.compliance_note ?? "Compliance was not assessed.",
1562
+ complianceAssessed ? `Compliance checked against ${(critique.frameworks_checked ?? []).join(", ")} \u2014 the frameworks systems in this domain are usually held to, inferred from the domain you sent rather than from a regime anyone named. Confirm them. ` + COMPLIANCE_INDICATIVE_NOTE : `${critique.compliance_note ?? "Compliance was not assessed."} ${COMPLIANCE_INDICATIVE_NOTE}`,
1541
1563
  // Only when the exposure used is not the one the agent chose. When a
1542
1564
  // non-public exposure *was* applied the server returns an
1543
1565
  // `exposure_scoped_checks` finding naming exactly which checks it took off
@@ -1660,13 +1682,13 @@ function frameDetail(frame) {
1660
1682
  var constraintsInput = z6.strictObject({
1661
1683
  budget: z6.enum(["low", "medium", "high", "unlimited"]).optional(),
1662
1684
  max_latency_ms: z6.number().int().positive().optional(),
1663
- // Free strings, not an enum, and the description has to say why. SkeletIQ assesses five
1685
+ // Free strings, not an enum, and the description has to say why. SkeletIQ assesses six
1664
1686
  // frameworks; naming anything else is still worth doing, because an unassessed regime is
1665
1687
  // recorded and reaches the design brief as a requirement the agent still has to satisfy.
1666
1688
  // The old text read 'e.g. HIPAA, SOC2, PCI-DSS, GDPR' — it omitted SOX, which is the one
1667
1689
  // an agent is least likely to guess, and said nothing about what happens to the rest.
1668
1690
  compliance: z6.array(z6.string()).optional().describe(
1669
- "Regimes the design must satisfy. SOC2, HIPAA, PCI-DSS, GDPR and SOX are checked against requirement catalogues; any other regime is recorded and carried into the design brief as a requirement to satisfy, rather than checked."
1691
+ "Regimes the design must satisfy. SOC2, HIPAA, PCI-DSS, GDPR, SOX and DPDP (India) are checked against requirement catalogues; any other regime is recorded and carried into the design brief as a requirement to satisfy, rather than checked."
1670
1692
  ),
1671
1693
  preferred_cloud: z6.enum(["aws", "gcp", "azure", "any"]).optional(),
1672
1694
  preferred_technologies: z6.array(z6.string()).optional(),
@@ -2833,6 +2855,7 @@ function buildServer(client, resolver) {
2833
2855
  { name: SERVER_NAME, version: SERVER_VERSION },
2834
2856
  { instructions: INSTRUCTIONS, capabilities: { tools: {} } }
2835
2857
  );
2858
+ client.setHostSource(() => server.server.getClientVersion());
2836
2859
  registerListProjects(server, resolver);
2837
2860
  registerGetDesign(server, client, resolver);
2838
2861
  registerGenerate(server, client, resolver);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skeletiq/mcp",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Design in SkeletIQ, build with your coding agent. MCP server for the SkeletIQ architecture platform.",
5
5
  "license": "MIT",
6
6
  "author": "Sabhahith Works Private Limited",