@savvy-web/mcp 1.5.0 → 1.6.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/package.json +3 -3
- package/runtime.js +13 -12
- package/schema/effect-to-zod.js +4 -2
- package/server.js +16 -8
- package/tools/workspace-info.js +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The savvy MCP server — Silk Suite tooling and library knowledge for coding agents",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/mcp",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@effect/rpc": "^0.75.1",
|
|
37
37
|
"@effect/sql": "^0.51.1",
|
|
38
38
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
39
|
-
"@savvy-web/silk-effects": "
|
|
39
|
+
"@savvy-web/silk-effects": "2.0.1",
|
|
40
40
|
"effect": "^3.21.4",
|
|
41
|
-
"workspaces-effect": "^
|
|
41
|
+
"workspaces-effect": "^2.0.1",
|
|
42
42
|
"zod": "^4.4.3"
|
|
43
43
|
}
|
|
44
44
|
}
|
package/runtime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ChangesetConfigReaderLive, Changesets, SilkWorkspaceAnalyzerLive, TagStrategyLive, ToolDiscoveryLive, Turbo, VersioningStrategyLive } from "@savvy-web/silk-effects";
|
|
1
|
+
import { ChangesetConfigLive, ChangesetConfigReaderLive, Changesets, SilkWorkspaceAnalyzerLive, TagStrategyLive, ToolDiscoveryLive, Turbo, VersioningStrategyLive } from "@savvy-web/silk-effects";
|
|
2
2
|
import { Layer } from "effect";
|
|
3
|
-
import { WorkspaceRootLive, WorkspacesLive } from "workspaces-effect";
|
|
3
|
+
import { PointInTimeWorkspaceLive, WorkspaceRootLive, WorkspacesLive } from "workspaces-effect";
|
|
4
4
|
|
|
5
5
|
//#region src/runtime.ts
|
|
6
6
|
/**
|
|
@@ -28,19 +28,20 @@ const InspectorAndAnalyzerLive = Changesets.BranchAnalyzerLive.pipe(Layer.provid
|
|
|
28
28
|
* orchestration service), fully composed except for the services supplied by
|
|
29
29
|
* {@link DepsLive} / the host platform layer.
|
|
30
30
|
*
|
|
31
|
-
* `DepsRegenLive` requires `
|
|
32
|
-
* WorkspaceDiscovery |
|
|
31
|
+
* `DepsRegenLive` requires `PointInTimeWorkspace | ConfigInspector |
|
|
32
|
+
* WorkspaceDiscovery | PublishabilityDetector | ChangesetConfig`. Here
|
|
33
33
|
* `ConfigInspector` is provided via the shared {@link InspectorAndAnalyzerLive}
|
|
34
34
|
* reference (so Effect memoizes the single `ConfigInspector` instance already
|
|
35
|
-
* merged into the runtime), and `
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `
|
|
41
|
-
*
|
|
35
|
+
* merged into the runtime), and `PointInTimeWorkspace` via
|
|
36
|
+
* `PointInTimeWorkspaceLive` (whose `WorkspaceRoot`/`WorkspaceDiscovery` come
|
|
37
|
+
* from `WorkspacesLive` inside {@link DepsLive}). `ChangesetConfig` is provided
|
|
38
|
+
* its own `ChangesetConfigReaderLive` (already present in {@link DepsLive}, but
|
|
39
|
+
* `Layer.mergeAll` does not cross-feed sibling layers). The remaining two —
|
|
40
|
+
* `WorkspaceDiscovery`, `PublishabilityDetector` — are left open and satisfied
|
|
41
|
+
* by `WorkspacesLive` inside {@link DepsLive}. `FileSystem` / `Path` /
|
|
42
|
+
* `CommandExecutor` flow up to the host `NodeContext.layer`.
|
|
42
43
|
*/
|
|
43
|
-
const DepsRegenGroupLive = Changesets.DepsRegenLive.pipe(Layer.provide(InspectorAndAnalyzerLive), Layer.provide(
|
|
44
|
+
const DepsRegenGroupLive = Changesets.DepsRegenLive.pipe(Layer.provide(InspectorAndAnalyzerLive), Layer.provide(PointInTimeWorkspaceLive), Layer.provide(ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive))));
|
|
44
45
|
/**
|
|
45
46
|
* The MCP runtime layer. Provides `SilkWorkspaceAnalyzer`, `WorkspaceRoot`,
|
|
46
47
|
* `Turbo.TurboInspector`, `Changesets.BranchAnalyzer`,
|
package/schema/effect-to-zod.js
CHANGED
|
@@ -17,7 +17,8 @@ import { z } from "zod";
|
|
|
17
17
|
* them.
|
|
18
18
|
*/
|
|
19
19
|
const effectToZodSchema = (schema) => {
|
|
20
|
-
const
|
|
20
|
+
const jsonSchema = JSONSchema.make(schema);
|
|
21
|
+
const inlined = inlineAllRefs(jsonSchema);
|
|
21
22
|
const zodSchema = z.fromJSONSchema(inlined);
|
|
22
23
|
if (isObjectLike(zodSchema)) return zodSchema;
|
|
23
24
|
return z.object({}).catchall(z.unknown());
|
|
@@ -35,7 +36,8 @@ const inlineAllRefs = (root) => {
|
|
|
35
36
|
if (value === null || typeof value !== "object") return value;
|
|
36
37
|
const obj = value;
|
|
37
38
|
if (typeof obj.$ref === "string" && obj.$ref.startsWith(REF_PREFIX)) {
|
|
38
|
-
const
|
|
39
|
+
const defName = obj.$ref.slice(8);
|
|
40
|
+
const target = defs[defName];
|
|
39
41
|
if (target !== void 0) return visit(target);
|
|
40
42
|
}
|
|
41
43
|
const out = {};
|
package/server.js
CHANGED
|
@@ -41,7 +41,8 @@ function buildServer(ctx) {
|
|
|
41
41
|
}, async (args) => {
|
|
42
42
|
const root = args.cwd ?? ctx.cwd;
|
|
43
43
|
const data = await ctx.runtime.runPromise(workspaceInfo(root));
|
|
44
|
-
|
|
44
|
+
const text = Schema.decodeSync(WorkspaceInfoAsMarkdown)(data);
|
|
45
|
+
return structuredResult(text, data);
|
|
45
46
|
});
|
|
46
47
|
server.registerTool("turbo_inspect", {
|
|
47
48
|
description: "Read-only Turborepo inspection. mode=cache diagnoses why a task's cache is hitting/missing (per-package status plus the exact hash contributors: input files, env vars, external-dep hashes, global hash). mode=graph returns the task graph and critical path. mode=affected lists changed packages and their dependents. Never executes tasks (uses --dry).",
|
|
@@ -59,7 +60,8 @@ function buildServer(ctx) {
|
|
|
59
60
|
annotations: { readOnlyHint: true }
|
|
60
61
|
}, async (args) => {
|
|
61
62
|
const data = await ctx.runtime.runPromise(turboInspect(args, ctx.cwd));
|
|
62
|
-
|
|
63
|
+
const text = Schema.decodeSync(TurboInspectAsMarkdown)(data);
|
|
64
|
+
return structuredResult(text, data);
|
|
63
65
|
});
|
|
64
66
|
server.registerTool("changeset_inspect", {
|
|
65
67
|
description: "Read-only changeset analysis for the changeset-manager workflow. mode=branch diffs the current branch against its base and classifies every changed file by owning package (with packagesAffected and the unmapped paths to ask the user about). mode=config surfaces the resolved .changeset/config.json (release surfaces, versionFiles, ignore list). mode=classify maps arbitrary repo-relative paths to their owning package. Prefer this over shelling out to the savvy CLI.",
|
|
@@ -77,7 +79,8 @@ function buildServer(ctx) {
|
|
|
77
79
|
annotations: { readOnlyHint: true }
|
|
78
80
|
}, async (args) => {
|
|
79
81
|
const data = await ctx.runtime.runPromise(changesetInspect(args, ctx.cwd));
|
|
80
|
-
|
|
82
|
+
const text = Schema.decodeSync(ChangesetInspectAsMarkdown)(data);
|
|
83
|
+
return structuredResult(text, data);
|
|
81
84
|
});
|
|
82
85
|
server.registerTool("changeset_validate", {
|
|
83
86
|
description: "Read-only validation of changeset files against the section-aware rules. Pass dir (default .changeset). Returns typed diagnostics (file, rule, line, column, message) plus ok/errorCount in structuredContent. Prefer this over shelling out to savvy changeset lint.",
|
|
@@ -89,7 +92,8 @@ function buildServer(ctx) {
|
|
|
89
92
|
annotations: { readOnlyHint: true }
|
|
90
93
|
}, async (args) => {
|
|
91
94
|
const data = await ctx.runtime.runPromise(changesetValidate(args, ctx.cwd));
|
|
92
|
-
|
|
95
|
+
const text = Schema.decodeSync(ChangesetValidateAsMarkdown)(data);
|
|
96
|
+
return structuredResult(text, data);
|
|
93
97
|
});
|
|
94
98
|
server.registerTool("changeset_deps_detect", {
|
|
95
99
|
description: "Read-only preview of the cumulative dependency diff (merge-base -> working tree) per workspace package. Returns each affected package's resolved dependency-table rows (catalog:/workspace: specifiers resolved to concrete versions; devDependencies retained) as the exact rows a pure-dependency changeset would carry. Does NOT write or delete any file. Prefer this over shelling out to savvy changeset deps detect.",
|
|
@@ -102,7 +106,8 @@ function buildServer(ctx) {
|
|
|
102
106
|
annotations: { readOnlyHint: true }
|
|
103
107
|
}, async (args) => {
|
|
104
108
|
const data = await ctx.runtime.runPromise(changesetDepsDetect(args, ctx.cwd));
|
|
105
|
-
|
|
109
|
+
const text = Schema.decodeSync(ChangesetDepsDetectAsMarkdown)(data);
|
|
110
|
+
return structuredResult(text, data);
|
|
106
111
|
});
|
|
107
112
|
server.registerTool("changeset_preview", {
|
|
108
113
|
description: "Read-only preview of the next release. Runs the genuine changesets engine over the pending changesets and returns each package's version bump (old -> new) plus the rendered CHANGELOG block (dependency tables included), exactly as it would ship. Does not modify the repo. Prefer this over hand-merging changeset files.",
|
|
@@ -111,7 +116,8 @@ function buildServer(ctx) {
|
|
|
111
116
|
annotations: { readOnlyHint: true }
|
|
112
117
|
}, async (args) => {
|
|
113
118
|
const data = await ctx.runtime.runPromise(changesetPreview(args, ctx.cwd));
|
|
114
|
-
|
|
119
|
+
const text = Schema.decodeSync(ChangesetPreviewAsMarkdown)(data);
|
|
120
|
+
return structuredResult(text, data);
|
|
115
121
|
});
|
|
116
122
|
server.registerTool("changeset_deps_regen", {
|
|
117
123
|
description: "Regenerate pure-dependency changesets: delete stale single-package Dependencies-only changesets and write fresh single-package, patch-bump changesets from the cumulative dependency diff (catalog:/workspace: resolved; devDependencies dropped). Mixed changesets (Dependencies plus other content) are left untouched. Set dryRun=true to preview the plan without touching the filesystem. NOTE: without dryRun this tool MUTATES .changeset/*.md (git-reversible). Prefer this over shelling out to savvy changeset deps regen.",
|
|
@@ -128,7 +134,8 @@ function buildServer(ctx) {
|
|
|
128
134
|
}
|
|
129
135
|
}, async (args) => {
|
|
130
136
|
const data = await ctx.runtime.runPromise(changesetDepsRegen(args, ctx.cwd));
|
|
131
|
-
|
|
137
|
+
const text = Schema.decodeSync(ChangesetDepsRegenAsMarkdown)(data);
|
|
138
|
+
return structuredResult(text, data);
|
|
132
139
|
});
|
|
133
140
|
server.registerTool("biome_check", {
|
|
134
141
|
description: "Run Biome over a path and get structured diagnostics back. mode=check (default; lint + format + organize-imports) or mode=lint. Set write=true to apply safe fixes (--write), unsafe=true for unsafe fixes (--write --unsafe). Prefer this over shelling out to biome; the LSP already covers files you've edited. Returns markdown in content[] and a typed object in structuredContent. NOTE: with write/unsafe this tool MUTATES files (git-reversible).",
|
|
@@ -142,7 +149,8 @@ function buildServer(ctx) {
|
|
|
142
149
|
outputSchema: effectToZodSchema(BiomeCheckResult)
|
|
143
150
|
}, async (args) => {
|
|
144
151
|
const data = await runBiomeCheck(args, ctx.cwd);
|
|
145
|
-
|
|
152
|
+
const text = Schema.decodeSync(BiomeCheckAsMarkdown)(data);
|
|
153
|
+
return structuredResult(text, data);
|
|
146
154
|
});
|
|
147
155
|
return server;
|
|
148
156
|
}
|
package/tools/workspace-info.js
CHANGED
|
@@ -85,7 +85,8 @@ const WorkspaceInfoAsMarkdown = Schema.transformOrFail(WorkspaceInfoResult, Sche
|
|
|
85
85
|
*/
|
|
86
86
|
const workspaceInfo = (base) => Effect.gen(function* () {
|
|
87
87
|
const root = yield* (yield* WorkspaceRoot).find(base);
|
|
88
|
-
|
|
88
|
+
const analysis = yield* (yield* SilkWorkspaceAnalyzer).analyze(root);
|
|
89
|
+
return toWorkspaceInfoResult(analysis);
|
|
89
90
|
});
|
|
90
91
|
|
|
91
92
|
//#endregion
|