@shrkcrft/mcp-server 0.1.0-alpha.8 → 0.1.0-alpha.9
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 +1 -1
- package/dist/main.d.ts +1 -1
- package/dist/main.js +1 -1
- package/dist/server/create-mcp-server.d.ts.map +1 -1
- package/dist/server/create-mcp-server.js +15 -21
- package/dist/server/http-transport.d.ts +7 -2
- package/dist/server/http-transport.d.ts.map +1 -1
- package/dist/server/http-transport.js +10 -6
- package/dist/tools/all-tools.d.ts.map +1 -1
- package/dist/tools/all-tools.js +7 -5
- package/dist/tools/r19-extras.tool.js +1 -1
- package/dist/tools/r28-plugin-lifecycle.tool.d.ts +4 -0
- package/dist/tools/r28-plugin-lifecycle.tool.d.ts.map +1 -0
- package/dist/tools/r28-plugin-lifecycle.tool.js +94 -0
- package/dist/tools/r32-profiles.tool.d.ts +3 -0
- package/dist/tools/r32-profiles.tool.d.ts.map +1 -1
- package/dist/tools/r32-profiles.tool.js +54 -3
- package/package.json +20 -21
- package/dist/tools/diff-check.tool.d.ts +0 -15
- package/dist/tools/diff-check.tool.d.ts.map +0 -1
- package/dist/tools/diff-check.tool.js +0 -157
- package/dist/tools/file-advice.tool.d.ts +0 -22
- package/dist/tools/file-advice.tool.d.ts.map +0 -1
- package/dist/tools/file-advice.tool.js +0 -88
- package/dist/tools/primary-tools.d.ts +0 -24
- package/dist/tools/primary-tools.d.ts.map +0 -1
- package/dist/tools/primary-tools.js +0 -70
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
SharkCraft MCP server: 25 tools over @modelcontextprotocol/sdk's stdio transport.
|
|
4
4
|
|
|
5
|
-
Part of [SharkCraft](https://github.com/
|
|
5
|
+
Part of [SharkCraft](https://github.com/sharkcraft/sharkcraft) — a deterministic, local-first toolkit that gives AI coding agents durable project context. See the main repo for documentation, examples, and the `shrk` CLI.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
package/dist/main.d.ts
CHANGED
package/dist/main.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/create-mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAQnE,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"create-mcp-server.d.ts","sourceRoot":"","sources":["../../src/server/create-mcp-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAQnE,OAAO,EAAqB,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,qBAAqB;IACpC,0DAA0D;IAC1D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED,MAAM,WAAW,6BAA8B,SAAQ,gBAAgB;IACrE,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sDAAsD;IACtD,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACzC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC1C,mCAAmC;IACnC,YAAY,CAAC,EAAE,eAAe,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,iBAAiB,EAAE,MAAM,GAAG,SAAS,EACrC,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,MAAM,CAIR;AAqBD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,6BAA6B,GAAG;IAC7E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;CACpB,CAqHA;AAED,wBAAsB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0EvF"}
|
|
@@ -3,9 +3,9 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
6
|
+
import { initTokenizer } from '@shrkcrft/context';
|
|
6
7
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION, } from "./mcp-server-config.js";
|
|
7
8
|
import { ALL_TOOLS } from "../tools/index.js";
|
|
8
|
-
import { PRIMARY_MCP_TOOLS, shouldAdvertiseFullToolset } from "../tools/primary-tools.js";
|
|
9
9
|
import { validateToolInput } from "./tool-input-validators.js";
|
|
10
10
|
import { buildResourceList, readResource } from "../resources/index.js";
|
|
11
11
|
/**
|
|
@@ -53,24 +53,13 @@ export function createSharkcraftServer(config) {
|
|
|
53
53
|
resources: { listChanged: true },
|
|
54
54
|
},
|
|
55
55
|
});
|
|
56
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const advertised = advertiseFull
|
|
64
|
-
? ALL_TOOLS
|
|
65
|
-
: ALL_TOOLS.filter((t) => PRIMARY_MCP_TOOLS.has(t.name));
|
|
66
|
-
return {
|
|
67
|
-
tools: advertised.map((t) => ({
|
|
68
|
-
name: t.name,
|
|
69
|
-
description: t.description,
|
|
70
|
-
inputSchema: t.inputSchema,
|
|
71
|
-
})),
|
|
72
|
-
};
|
|
73
|
-
});
|
|
56
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
57
|
+
tools: ALL_TOOLS.map((t) => ({
|
|
58
|
+
name: t.name,
|
|
59
|
+
description: t.description,
|
|
60
|
+
inputSchema: t.inputSchema,
|
|
61
|
+
})),
|
|
62
|
+
}));
|
|
74
63
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
75
64
|
const name = request.params.name;
|
|
76
65
|
const args = (request.params.arguments ?? {});
|
|
@@ -152,6 +141,11 @@ export function createSharkcraftServer(config) {
|
|
|
152
141
|
export async function startMcpServer(options = {}) {
|
|
153
142
|
const cwd = resolveTargetRoot(options.cwd, options.projectRoot);
|
|
154
143
|
const verbose = options.verbose ?? false;
|
|
144
|
+
// Best-effort upgrade from the estimator to gpt-tokenizer. Failure leaves
|
|
145
|
+
// the estimator active — the server still serves requests.
|
|
146
|
+
if (process.env.SHARKCRAFT_NO_REAL_TOKENIZER !== '1') {
|
|
147
|
+
initTokenizer().catch(() => undefined);
|
|
148
|
+
}
|
|
155
149
|
const { server, state } = createSharkcraftServer({
|
|
156
150
|
name: MCP_SERVER_NAME,
|
|
157
151
|
version: MCP_SERVER_VERSION,
|
|
@@ -204,8 +198,8 @@ export async function startMcpServer(options = {}) {
|
|
|
204
198
|
};
|
|
205
199
|
process.on('SIGINT', () => void stop());
|
|
206
200
|
process.on('SIGTERM', () => void stop());
|
|
207
|
-
|
|
208
|
-
|
|
201
|
+
// Always announce the URL on stderr so the user knows where to connect.
|
|
202
|
+
process.stderr.write(`[mcp] http endpoint: ${handle.url}\n`);
|
|
209
203
|
return;
|
|
210
204
|
}
|
|
211
205
|
const transport = new StdioServerTransport();
|
|
@@ -2,9 +2,9 @@ import type { Server as McpServer } from '@modelcontextprotocol/sdk/server/index
|
|
|
2
2
|
export interface StartHttpServerOptions {
|
|
3
3
|
/** McpServer (NOT yet connected). */
|
|
4
4
|
server: McpServer;
|
|
5
|
-
/** Bind host. Default '
|
|
5
|
+
/** Bind host. Default '127.0.0.1' (loopback only). */
|
|
6
6
|
host?: string;
|
|
7
|
-
/** Bind port. Default
|
|
7
|
+
/** Bind port. Default 0 (kernel-assigned ephemeral). */
|
|
8
8
|
port?: number;
|
|
9
9
|
/** Path that accepts MCP traffic. Default '/mcp'. */
|
|
10
10
|
path?: string;
|
|
@@ -17,7 +17,12 @@ export interface StartHttpServerOptions {
|
|
|
17
17
|
log?: (line: string) => void;
|
|
18
18
|
}
|
|
19
19
|
export interface HttpServerHandle {
|
|
20
|
+
/** Full URL clients should POST to (host:port + path). */
|
|
20
21
|
url: string;
|
|
22
|
+
/** The host the server is actually bound to. */
|
|
23
|
+
host: string;
|
|
24
|
+
/** The port the server is actually bound to (resolved when port=0). */
|
|
25
|
+
port: number;
|
|
21
26
|
close: () => Promise<void>;
|
|
22
27
|
}
|
|
23
28
|
export declare function startHttpServer(options: StartHttpServerOptions): Promise<HttpServerHandle>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../../src/server/http-transport.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http-transport.d.ts","sourceRoot":"","sources":["../../src/server/http-transport.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,IAAI,SAAS,EAAE,MAAM,2CAA2C,CAAC;AAErF,MAAM,WAAW,sBAAsB;IACrC,qCAAqC;IACrC,MAAM,EAAE,SAAS,CAAC;IAClB,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,0DAA0D;IAC1D,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAsBD,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAuDhG"}
|
|
@@ -22,8 +22,8 @@ function writeJson(res, status, body) {
|
|
|
22
22
|
res.end(JSON.stringify(body));
|
|
23
23
|
}
|
|
24
24
|
export async function startHttpServer(options) {
|
|
25
|
-
const host = options.host ?? '
|
|
26
|
-
const
|
|
25
|
+
const host = options.host ?? '127.0.0.1';
|
|
26
|
+
const requestedPort = options.port ?? 0;
|
|
27
27
|
const path = options.path ?? '/mcp';
|
|
28
28
|
const stateful = options.stateful ?? true;
|
|
29
29
|
const log = options.log ?? (() => undefined);
|
|
@@ -40,7 +40,7 @@ export async function startHttpServer(options) {
|
|
|
40
40
|
writeJson(res, 200, { ok: true, transport: 'streamable-http', stateful });
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
const url = new URL(req.url, `http://${host}:${
|
|
43
|
+
const url = new URL(req.url, `http://${host}:${requestedPort || 80}`);
|
|
44
44
|
if (url.pathname !== path) {
|
|
45
45
|
writeJson(res, 404, { error: `not found: ${url.pathname}` });
|
|
46
46
|
return;
|
|
@@ -59,10 +59,14 @@ export async function startHttpServer(options) {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
|
-
await new Promise((resolve) => http.listen(
|
|
63
|
-
|
|
62
|
+
await new Promise((resolve) => http.listen(requestedPort, host, resolve));
|
|
63
|
+
const address = http.address();
|
|
64
|
+
const actualPort = address?.port ?? requestedPort;
|
|
65
|
+
log(`[mcp:http] listening on http://${host}:${actualPort}${path} (stateful=${stateful})`);
|
|
64
66
|
return {
|
|
65
|
-
url: `http://${host}:${
|
|
67
|
+
url: `http://${host}:${actualPort}${path}`,
|
|
68
|
+
host,
|
|
69
|
+
port: actualPort,
|
|
66
70
|
close: async () => {
|
|
67
71
|
await transport.close().catch(() => undefined);
|
|
68
72
|
await new Promise((resolve, reject) => http.close((err) => (err ? reject(err) : resolve())));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"all-tools.d.ts","sourceRoot":"","sources":["../../src/tools/all-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"all-tools.d.ts","sourceRoot":"","sources":["../../src/tools/all-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAmTpE,eAAO,MAAM,SAAS,EAAE,SAAS,eAAe,EA6P9C,CAAC"}
|
package/dist/tools/all-tools.js
CHANGED
|
@@ -116,8 +116,7 @@ import { getContractTemplateTool, getLanguageCommandsTool, getLanguageProfilesTo
|
|
|
116
116
|
import { getTaskContextTool, understandTaskTool, validateChangeContextTool, } from "./r26-task-context.tool.js";
|
|
117
117
|
import { getLanguageCacheStatusTool, getLanguageProfilesLiveTool, getLanguageRunPlanTool, getPolyglotBoundaryReportTool, } from "./r27-polyglot.tool.js";
|
|
118
118
|
import { getChangedBoundaryReportTool } from "./r28-changed-boundary.tool.js";
|
|
119
|
-
import {
|
|
120
|
-
import { getFileAdviceTool } from "./file-advice.tool.js";
|
|
119
|
+
import { previewPluginRemoveTool, previewPluginRenameTool } from "./r28-plugin-lifecycle.tool.js";
|
|
121
120
|
import { getHelperTool, listHelpersTool, previewHelperPlanTool } from "./r28-helpers.tool.js";
|
|
122
121
|
import { getPackDevStatusTool, previewPackTestsTool } from "./r28-pack-author.tool.js";
|
|
123
122
|
import { getRegistryLifecycleReportTool } from "./r28-registry-lifecycle.tool.js";
|
|
@@ -136,7 +135,7 @@ import { suggestCommandsTool, searchCommandsTool, explainCommandTool, } from "./
|
|
|
136
135
|
import { previewFixTool, listFixKindsTool, } from "./r31-fix-preview.tool.js";
|
|
137
136
|
import { getScaffoldCoverageReportTool } from "./r31-scaffold-coverage.tool.js";
|
|
138
137
|
import { getChangesSummaryTool, getPrSummaryPreviewTool, getCiIntegrityReportTool, } from "./r31-changes-pr-ci.tool.js";
|
|
139
|
-
import { listProfilesTool, getProfileTool, getProfilesDoctorTool, } from "./r32-profiles.tool.js";
|
|
138
|
+
import { listProfilesTool, getProfileTool, getProfilesDoctorTool, listPluginLifecycleProfilesTool, getPluginLifecycleProfileTool, getPluginLifecycleProfileDoctorTool, } from "./r32-profiles.tool.js";
|
|
140
139
|
import { getProjectCouplingReportTool } from "./r32-project-coupling.tool.js";
|
|
141
140
|
import { getPackContributionsTool, getPackConflictsTool, } from "./r33-pack-contributions.tool.js";
|
|
142
141
|
import { listConventionsTool, getConventionTool, getConventionsDoctorTool, } from "./r33-conventions.tool.js";
|
|
@@ -335,8 +334,8 @@ export const ALL_TOOLS = Object.freeze([
|
|
|
335
334
|
getLanguageCacheStatusTool,
|
|
336
335
|
getLanguageProfilesLiveTool,
|
|
337
336
|
getChangedBoundaryReportTool,
|
|
338
|
-
|
|
339
|
-
|
|
337
|
+
previewPluginRenameTool,
|
|
338
|
+
previewPluginRemoveTool,
|
|
340
339
|
listHelpersTool,
|
|
341
340
|
getHelperTool,
|
|
342
341
|
previewHelperPlanTool,
|
|
@@ -371,6 +370,9 @@ export const ALL_TOOLS = Object.freeze([
|
|
|
371
370
|
listProfilesTool,
|
|
372
371
|
getProfileTool,
|
|
373
372
|
getProfilesDoctorTool,
|
|
373
|
+
listPluginLifecycleProfilesTool,
|
|
374
|
+
getPluginLifecycleProfileTool,
|
|
375
|
+
getPluginLifecycleProfileDoctorTool,
|
|
374
376
|
getProjectCouplingReportTool,
|
|
375
377
|
getPackContributionsTool,
|
|
376
378
|
getPackConflictsTool,
|
|
@@ -93,7 +93,7 @@ export const getCommandTaxonomyTool = {
|
|
|
93
93
|
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
94
94
|
async handler() {
|
|
95
95
|
// The CLI carries the canonical catalog; here we describe the surface
|
|
96
|
-
// without importing it (no inspector → CLI dependency).
|
|
96
|
+
// without importing it (no inspector → CLI dependency). Adopters can
|
|
97
97
|
// run `shrk commands taxonomy` for the full grouped output.
|
|
98
98
|
return {
|
|
99
99
|
data: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"r28-plugin-lifecycle.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r28-plugin-lifecycle.tool.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAMpE,eAAO,MAAM,uBAAuB,EAAE,eA0CrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAuCrC,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plan-only plugin lifecycle previews (profile-driven).
|
|
3
|
+
*
|
|
4
|
+
* preview_plugin_rename — returns the rename plan (replace ops + manual steps).
|
|
5
|
+
* preview_plugin_remove — returns the destructive remove plan.
|
|
6
|
+
*
|
|
7
|
+
* Both tools never write source — the human runs `shrk plugin rename|remove`
|
|
8
|
+
* to produce the saved plan and applies it via `shrk apply --verify-signature`.
|
|
9
|
+
*
|
|
10
|
+
* A `profile` input is required. If exactly one lifecycle profile is
|
|
11
|
+
* registered, the tool implicitly uses it; otherwise the caller must supply
|
|
12
|
+
* `profile`.
|
|
13
|
+
*/
|
|
14
|
+
import { buildPluginRemovePlan, buildPluginRenamePlan, resolvePluginLifecycleProfile, } from '@shrkcrft/inspector';
|
|
15
|
+
function nextHint(cmd) {
|
|
16
|
+
return `Next: \`${cmd}\` (CLI is the only write path).`;
|
|
17
|
+
}
|
|
18
|
+
export const previewPluginRenameTool = {
|
|
19
|
+
name: 'preview_plugin_rename',
|
|
20
|
+
description: 'Preview a plugin rename plan. Read-only — returns the structured plan only. Requires a registered plugin lifecycle profile (pass `profile` if more than one is registered).',
|
|
21
|
+
inputSchema: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
required: ['oldName', 'newName'],
|
|
25
|
+
properties: {
|
|
26
|
+
oldName: { type: 'string' },
|
|
27
|
+
newName: { type: 'string' },
|
|
28
|
+
profile: { type: 'string' },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
async handler(input, ctx) {
|
|
32
|
+
const oldName = String(input.oldName ?? '');
|
|
33
|
+
const newName = String(input.newName ?? '');
|
|
34
|
+
const profileId = typeof input.profile === 'string' ? input.profile : undefined;
|
|
35
|
+
const resolved = await resolvePluginLifecycleProfile(ctx.inspection, {
|
|
36
|
+
profileId,
|
|
37
|
+
allowSingleDefault: true,
|
|
38
|
+
});
|
|
39
|
+
if (!resolved.entry) {
|
|
40
|
+
return {
|
|
41
|
+
isError: true,
|
|
42
|
+
error: { code: 'profile-required', message: resolved.error ?? 'Profile resolution failed' },
|
|
43
|
+
data: { availableProfiles: resolved.availableIds },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const plan = buildPluginRenamePlan({
|
|
47
|
+
projectRoot: ctx.cwd,
|
|
48
|
+
profile: resolved.entry.profile,
|
|
49
|
+
oldName,
|
|
50
|
+
newName,
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
text: nextHint(`shrk plugin rename ${oldName} ${newName} --profile ${resolved.entry.profile.id} --output /tmp/plan.json`),
|
|
54
|
+
data: plan,
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const previewPluginRemoveTool = {
|
|
59
|
+
name: 'preview_plugin_remove',
|
|
60
|
+
description: 'Preview a destructive plugin remove plan. Read-only — returns the structured plan only. Requires a registered plugin lifecycle profile (pass `profile` if more than one is registered).',
|
|
61
|
+
inputSchema: {
|
|
62
|
+
type: 'object',
|
|
63
|
+
additionalProperties: false,
|
|
64
|
+
required: ['name'],
|
|
65
|
+
properties: {
|
|
66
|
+
name: { type: 'string' },
|
|
67
|
+
profile: { type: 'string' },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
async handler(input, ctx) {
|
|
71
|
+
const name = String(input.name ?? '');
|
|
72
|
+
const profileId = typeof input.profile === 'string' ? input.profile : undefined;
|
|
73
|
+
const resolved = await resolvePluginLifecycleProfile(ctx.inspection, {
|
|
74
|
+
profileId,
|
|
75
|
+
allowSingleDefault: true,
|
|
76
|
+
});
|
|
77
|
+
if (!resolved.entry) {
|
|
78
|
+
return {
|
|
79
|
+
isError: true,
|
|
80
|
+
error: { code: 'profile-required', message: resolved.error ?? 'Profile resolution failed' },
|
|
81
|
+
data: { availableProfiles: resolved.availableIds },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const plan = buildPluginRemovePlan({
|
|
85
|
+
projectRoot: ctx.cwd,
|
|
86
|
+
profile: resolved.entry.profile,
|
|
87
|
+
oldName: name,
|
|
88
|
+
});
|
|
89
|
+
return {
|
|
90
|
+
text: nextHint(`shrk plugin remove ${name} --profile ${resolved.entry.profile.id} --output /tmp/plan.json`),
|
|
91
|
+
data: plan,
|
|
92
|
+
};
|
|
93
|
+
},
|
|
94
|
+
};
|
|
@@ -2,4 +2,7 @@ import type { IToolDefinition } from '../server/tool-definition.js';
|
|
|
2
2
|
export declare const listProfilesTool: IToolDefinition;
|
|
3
3
|
export declare const getProfileTool: IToolDefinition;
|
|
4
4
|
export declare const getProfilesDoctorTool: IToolDefinition;
|
|
5
|
+
export declare const listPluginLifecycleProfilesTool: IToolDefinition;
|
|
6
|
+
export declare const getPluginLifecycleProfileTool: IToolDefinition;
|
|
7
|
+
export declare const getPluginLifecycleProfileDoctorTool: IToolDefinition;
|
|
5
8
|
//# sourceMappingURL=r32-profiles.tool.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"r32-profiles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r32-profiles.tool.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"r32-profiles.tool.d.ts","sourceRoot":"","sources":["../../src/tools/r32-profiles.tool.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAsB5B,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAOnC,CAAC;AAEF,eAAO,MAAM,+BAA+B,EAAE,eAO7C,CAAC;AAEF,eAAO,MAAM,6BAA6B,EAAE,eAmB3C,CAAC;AAEF,eAAO,MAAM,mCAAmC,EAAE,eAqBjD,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Read-only MCP tools for profiles.
|
|
2
|
+
* Read-only MCP tools for profiles + lifecycle profiles.
|
|
3
3
|
*
|
|
4
4
|
* Engine never writes from MCP. These tools surface pack-contributed and
|
|
5
5
|
* locally configured profiles so an agent can decide which one to pass to
|
|
6
|
-
*
|
|
6
|
+
* `shrk plugin rename|remove --profile <id>` (the human runs the CLI).
|
|
7
7
|
*/
|
|
8
|
-
import { findProfile, listProfileIssues, listProfiles, ProfileKind, } from '@shrkcrft/inspector';
|
|
8
|
+
import { checkPluginLifecycleProfileHealth, findProfile, listProfileIssues, listProfiles, listPluginLifecycleProfiles, ProfileKind, } from '@shrkcrft/inspector';
|
|
9
9
|
export const listProfilesTool = {
|
|
10
10
|
name: 'list_profiles',
|
|
11
11
|
description: 'List all pack-contributed and locally configured profiles. Read-only.',
|
|
@@ -54,3 +54,54 @@ export const getProfilesDoctorTool = {
|
|
|
54
54
|
return { data: { issues: await listProfileIssues(ctx.inspection) } };
|
|
55
55
|
},
|
|
56
56
|
};
|
|
57
|
+
export const listPluginLifecycleProfilesTool = {
|
|
58
|
+
name: 'list_plugin_lifecycle_profiles',
|
|
59
|
+
description: 'List registered plugin lifecycle profiles. Read-only.',
|
|
60
|
+
inputSchema: { type: 'object', additionalProperties: false, properties: {} },
|
|
61
|
+
async handler(_input, ctx) {
|
|
62
|
+
return { data: await listPluginLifecycleProfiles(ctx.inspection) };
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
export const getPluginLifecycleProfileTool = {
|
|
66
|
+
name: 'get_plugin_lifecycle_profile',
|
|
67
|
+
description: 'Get one plugin lifecycle profile by id. Read-only.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
additionalProperties: false,
|
|
71
|
+
required: ['id'],
|
|
72
|
+
properties: { id: { type: 'string' } },
|
|
73
|
+
},
|
|
74
|
+
async handler(input, ctx) {
|
|
75
|
+
const id = typeof input.id === 'string' ? input.id : '';
|
|
76
|
+
if (!id)
|
|
77
|
+
return { isError: true, error: { code: 'invalid-input', message: 'id is required.' } };
|
|
78
|
+
const entries = await listPluginLifecycleProfiles(ctx.inspection);
|
|
79
|
+
const entry = entries.find((e) => e.profile.id === id);
|
|
80
|
+
if (!entry) {
|
|
81
|
+
return { isError: true, error: { code: 'not-found', message: `Unknown lifecycle profile "${id}".` } };
|
|
82
|
+
}
|
|
83
|
+
return { data: entry };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
export const getPluginLifecycleProfileDoctorTool = {
|
|
87
|
+
name: 'get_plugin_lifecycle_profile_doctor',
|
|
88
|
+
description: 'Health check for plugin lifecycle profiles. Read-only.',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
type: 'object',
|
|
91
|
+
additionalProperties: false,
|
|
92
|
+
properties: { id: { type: 'string' } },
|
|
93
|
+
},
|
|
94
|
+
async handler(input, ctx) {
|
|
95
|
+
const id = typeof input.id === 'string' ? input.id : undefined;
|
|
96
|
+
const entries = await listPluginLifecycleProfiles(ctx.inspection);
|
|
97
|
+
const targets = id ? entries.filter((e) => e.profile.id === id) : entries;
|
|
98
|
+
if (id && targets.length === 0) {
|
|
99
|
+
return { isError: true, error: { code: 'not-found', message: `Unknown lifecycle profile "${id}".` } };
|
|
100
|
+
}
|
|
101
|
+
const health = {};
|
|
102
|
+
for (const e of targets) {
|
|
103
|
+
health[e.profile.id] = checkPluginLifecycleProfileHealth(ctx.cwd, e.profile);
|
|
104
|
+
}
|
|
105
|
+
return { data: { health } };
|
|
106
|
+
},
|
|
107
|
+
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/mcp-server",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
4
|
-
"description": "SharkCraft MCP server:
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
|
+
"description": "SharkCraft MCP server: read-only @modelcontextprotocol/sdk tools (stdio + streamable-HTTP) for AI coding agents.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
|
-
"types": "./dist/index.d.
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": {
|
|
12
12
|
"types": "./dist/index.d.ts",
|
|
13
|
-
"bun": "./src/index.ts",
|
|
14
13
|
"import": "./dist/index.js",
|
|
15
14
|
"default": "./dist/index.js"
|
|
16
15
|
}
|
|
@@ -22,12 +21,12 @@
|
|
|
22
21
|
],
|
|
23
22
|
"repository": {
|
|
24
23
|
"type": "git",
|
|
25
|
-
"url": "git+https://github.com/
|
|
24
|
+
"url": "git+https://github.com/sharkcraft/sharkcraft.git",
|
|
26
25
|
"directory": "packages/mcp-server"
|
|
27
26
|
},
|
|
28
|
-
"homepage": "https://github.com/
|
|
27
|
+
"homepage": "https://github.com/sharkcraft/sharkcraft",
|
|
29
28
|
"bugs": {
|
|
30
|
-
"url": "https://github.com/
|
|
29
|
+
"url": "https://github.com/sharkcraft/sharkcraft/issues"
|
|
31
30
|
},
|
|
32
31
|
"keywords": [
|
|
33
32
|
"sharkcraft",
|
|
@@ -44,20 +43,20 @@
|
|
|
44
43
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
45
44
|
},
|
|
46
45
|
"dependencies": {
|
|
47
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
48
|
-
"@shrkcrft/config": "^0.1.0-alpha.
|
|
49
|
-
"@shrkcrft/workspace": "^0.1.0-alpha.
|
|
50
|
-
"@shrkcrft/knowledge": "^0.1.0-alpha.
|
|
51
|
-
"@shrkcrft/context": "^0.1.0-alpha.
|
|
52
|
-
"@shrkcrft/rules": "^0.1.0-alpha.
|
|
53
|
-
"@shrkcrft/paths": "^0.1.0-alpha.
|
|
54
|
-
"@shrkcrft/templates": "^0.1.0-alpha.
|
|
55
|
-
"@shrkcrft/pipelines": "^0.1.0-alpha.
|
|
56
|
-
"@shrkcrft/presets": "^0.1.0-alpha.
|
|
57
|
-
"@shrkcrft/boundaries": "^0.1.0-alpha.
|
|
58
|
-
"@shrkcrft/packs": "^0.1.0-alpha.
|
|
59
|
-
"@shrkcrft/generator": "^0.1.0-alpha.
|
|
60
|
-
"@shrkcrft/inspector": "^0.1.0-alpha.
|
|
46
|
+
"@shrkcrft/core": "^0.1.0-alpha.9",
|
|
47
|
+
"@shrkcrft/config": "^0.1.0-alpha.9",
|
|
48
|
+
"@shrkcrft/workspace": "^0.1.0-alpha.9",
|
|
49
|
+
"@shrkcrft/knowledge": "^0.1.0-alpha.9",
|
|
50
|
+
"@shrkcrft/context": "^0.1.0-alpha.9",
|
|
51
|
+
"@shrkcrft/rules": "^0.1.0-alpha.9",
|
|
52
|
+
"@shrkcrft/paths": "^0.1.0-alpha.9",
|
|
53
|
+
"@shrkcrft/templates": "^0.1.0-alpha.9",
|
|
54
|
+
"@shrkcrft/pipelines": "^0.1.0-alpha.9",
|
|
55
|
+
"@shrkcrft/presets": "^0.1.0-alpha.9",
|
|
56
|
+
"@shrkcrft/boundaries": "^0.1.0-alpha.9",
|
|
57
|
+
"@shrkcrft/packs": "^0.1.0-alpha.9",
|
|
58
|
+
"@shrkcrft/generator": "^0.1.0-alpha.9",
|
|
59
|
+
"@shrkcrft/inspector": "^0.1.0-alpha.9",
|
|
61
60
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
62
61
|
"zod": "^3.25.0 || ^4.0.0"
|
|
63
62
|
},
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only MCP tool: get_diff_check_report.
|
|
3
|
-
*
|
|
4
|
-
* The MCP-side mirror of `shrk diff-check`. Same envelope, same
|
|
5
|
-
* verdict logic — the agent gets a single structured answer to "did
|
|
6
|
-
* my edits pass this project's boundary + import-hygiene gates?".
|
|
7
|
-
*
|
|
8
|
-
* Still read-only: this tool DOES NOT fix anything, even when it
|
|
9
|
-
* could trivially suggest the fix. The agent reads the envelope, then
|
|
10
|
-
* the human (or the agent, via a separate write-path tool) runs the
|
|
11
|
-
* fix on the CLI. Keeps the safety contract intact.
|
|
12
|
-
*/
|
|
13
|
-
import type { IToolDefinition } from '../server/tool-definition.js';
|
|
14
|
-
export declare const getDiffCheckReportTool: IToolDefinition;
|
|
15
|
-
//# sourceMappingURL=diff-check.tool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"diff-check.tool.d.ts","sourceRoot":"","sources":["../../src/tools/diff-check.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAyBpE,eAAO,MAAM,sBAAsB,EAAE,eAyHpC,CAAC"}
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only MCP tool: get_diff_check_report.
|
|
3
|
-
*
|
|
4
|
-
* The MCP-side mirror of `shrk diff-check`. Same envelope, same
|
|
5
|
-
* verdict logic — the agent gets a single structured answer to "did
|
|
6
|
-
* my edits pass this project's boundary + import-hygiene gates?".
|
|
7
|
-
*
|
|
8
|
-
* Still read-only: this tool DOES NOT fix anything, even when it
|
|
9
|
-
* could trivially suggest the fix. The agent reads the envelope, then
|
|
10
|
-
* the human (or the agent, via a separate write-path tool) runs the
|
|
11
|
-
* fix on the CLI. Keeps the safety contract intact.
|
|
12
|
-
*/
|
|
13
|
-
import { buildImportHygieneReport, filterViolationsToChangedScope, resolveChangedFiles, } from '@shrkcrft/inspector';
|
|
14
|
-
import { evaluateBoundaries, loadTsconfigPaths, scanImports, } from '@shrkcrft/boundaries';
|
|
15
|
-
const SCHEMA = 'sharkcraft.diff-check/v1';
|
|
16
|
-
function resolveScopeFromInput(input, cwd) {
|
|
17
|
-
const staged = input.staged === true;
|
|
18
|
-
const since = typeof input.since === 'string' ? input.since : undefined;
|
|
19
|
-
const files = Array.isArray(input.files)
|
|
20
|
-
? input.files.filter((f) => typeof f === 'string')
|
|
21
|
-
: [];
|
|
22
|
-
if (files.length > 0) {
|
|
23
|
-
return { mode: 'files', options: { projectRoot: cwd, files } };
|
|
24
|
-
}
|
|
25
|
-
if (staged) {
|
|
26
|
-
return { mode: 'staged', options: { projectRoot: cwd, staged: true } };
|
|
27
|
-
}
|
|
28
|
-
if (since) {
|
|
29
|
-
return { mode: 'since', options: { projectRoot: cwd, since } };
|
|
30
|
-
}
|
|
31
|
-
return { mode: 'worktree', options: { projectRoot: cwd, includeWorktree: true } };
|
|
32
|
-
}
|
|
33
|
-
export const getDiffCheckReportTool = {
|
|
34
|
-
name: 'get_diff_check_report',
|
|
35
|
-
description: 'Self-check the current git diff against this project\'s boundary + import-hygiene rules. Single-call composite of the boundary-check and import-hygiene engines, scoped to the changed files, with one verdict (ok | warnings | errors) and one nextAction line. Use after editing code so you can validate before declaring done. Read-only.',
|
|
36
|
-
inputSchema: {
|
|
37
|
-
type: 'object',
|
|
38
|
-
additionalProperties: false,
|
|
39
|
-
properties: {
|
|
40
|
-
staged: { type: 'boolean', description: 'Scope to staged changes only.' },
|
|
41
|
-
since: { type: 'string', description: 'Compare against ref (HEAD, origin/main, SHA).' },
|
|
42
|
-
files: {
|
|
43
|
-
type: 'array',
|
|
44
|
-
items: { type: 'string' },
|
|
45
|
-
description: 'Explicit file list (overrides --staged / --since).',
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
async handler(input, ctx) {
|
|
50
|
-
const cwd = ctx.cwd;
|
|
51
|
-
const { mode, options: scopeOptions } = resolveScopeFromInput(input, cwd);
|
|
52
|
-
const changed = resolveChangedFiles(scopeOptions);
|
|
53
|
-
const changedFiles = changed.files;
|
|
54
|
-
const rules = ctx.inspection.boundaryRegistry.list();
|
|
55
|
-
let boundaryBlock = {
|
|
56
|
-
ran: false,
|
|
57
|
-
rulesEvaluated: 0,
|
|
58
|
-
counts: { error: 0, warning: 0, info: 0 },
|
|
59
|
-
violations: [],
|
|
60
|
-
};
|
|
61
|
-
if (rules.length > 0 && changedFiles.length > 0) {
|
|
62
|
-
const scan = scanImports({ projectRoot: cwd });
|
|
63
|
-
const tsconfigPaths = loadTsconfigPaths(cwd);
|
|
64
|
-
const evalResult = evaluateBoundaries(scan, rules, {
|
|
65
|
-
...(tsconfigPaths.aliases.size > 0 ? { tsconfigPaths } : {}),
|
|
66
|
-
});
|
|
67
|
-
const filtered = filterViolationsToChangedScope(evalResult.violations, scopeOptions);
|
|
68
|
-
boundaryBlock = {
|
|
69
|
-
ran: true,
|
|
70
|
-
rulesEvaluated: evalResult.rulesEvaluated,
|
|
71
|
-
counts: {
|
|
72
|
-
error: filtered.includedViolations.filter((v) => v.severity === 'error').length,
|
|
73
|
-
warning: filtered.includedViolations.filter((v) => v.severity === 'warning').length,
|
|
74
|
-
info: filtered.includedViolations.filter((v) => v.severity === 'info').length,
|
|
75
|
-
},
|
|
76
|
-
violations: filtered.includedViolations,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
else if (rules.length > 0) {
|
|
80
|
-
boundaryBlock = { ...boundaryBlock, ran: true, rulesEvaluated: rules.length };
|
|
81
|
-
}
|
|
82
|
-
let importsBlock = {
|
|
83
|
-
ran: false,
|
|
84
|
-
verdict: 'skipped',
|
|
85
|
-
counts: {},
|
|
86
|
-
findings: [],
|
|
87
|
-
};
|
|
88
|
-
if (changedFiles.length > 0) {
|
|
89
|
-
const report = buildImportHygieneReport(cwd, { files: changedFiles });
|
|
90
|
-
importsBlock = {
|
|
91
|
-
ran: true,
|
|
92
|
-
verdict: report.verdict,
|
|
93
|
-
counts: report.counts ?? {},
|
|
94
|
-
findings: report.findings,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
// Derive verdict — same logic as the CLI command, duplicated
|
|
98
|
-
// intentionally to keep the MCP tool self-contained (no CLI
|
|
99
|
-
// import — preserves the package dependency direction).
|
|
100
|
-
const bErr = boundaryBlock.counts.error;
|
|
101
|
-
const bWarn = boundaryBlock.counts.warning;
|
|
102
|
-
const iErr = importsBlock.verdict === 'errors' ? (importsBlock.counts.error ?? importsBlock.findings.length) : 0;
|
|
103
|
-
const iWarn = importsBlock.verdict === 'warnings' ? (importsBlock.counts.warning ?? importsBlock.findings.length) : 0;
|
|
104
|
-
let verdict;
|
|
105
|
-
let summary;
|
|
106
|
-
let nextAction;
|
|
107
|
-
if (changedFiles.length === 0) {
|
|
108
|
-
verdict = 'ok';
|
|
109
|
-
summary = 'No files changed in the current diff scope.';
|
|
110
|
-
nextAction =
|
|
111
|
-
'Nothing to check. If you expected changes, verify the `staged` / `since` argument or save edits first.';
|
|
112
|
-
}
|
|
113
|
-
else if (bErr > 0 || iErr > 0) {
|
|
114
|
-
verdict = 'errors';
|
|
115
|
-
const parts = [];
|
|
116
|
-
if (bErr > 0)
|
|
117
|
-
parts.push(`${bErr} boundary violation${bErr === 1 ? '' : 's'}`);
|
|
118
|
-
if (iErr > 0)
|
|
119
|
-
parts.push(`${iErr} import-hygiene error${iErr === 1 ? '' : 's'}`);
|
|
120
|
-
summary = `Diff fails the gate: ${parts.join(', ')}.`;
|
|
121
|
-
nextAction =
|
|
122
|
-
'Fix every error in `boundaries.violations` and `imports.findings` (each entry\'s `suggestedFix` shows the fix), then re-run.';
|
|
123
|
-
}
|
|
124
|
-
else if (bWarn > 0 || iWarn > 0) {
|
|
125
|
-
verdict = 'warnings';
|
|
126
|
-
const parts = [];
|
|
127
|
-
if (bWarn > 0)
|
|
128
|
-
parts.push(`${bWarn} boundary warning${bWarn === 1 ? '' : 's'}`);
|
|
129
|
-
if (iWarn > 0)
|
|
130
|
-
parts.push(`${iWarn} import-hygiene warning${iWarn === 1 ? '' : 's'}`);
|
|
131
|
-
summary = `Diff passes the gate with ${parts.join(', ')}.`;
|
|
132
|
-
nextAction = 'Safe to declare done. Review warnings if the diff touches a sensitive area.';
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
verdict = 'ok';
|
|
136
|
-
summary = `Diff passes the gate (${changedFiles.length} file${changedFiles.length === 1 ? '' : 's'}, 0 violations).`;
|
|
137
|
-
nextAction = 'Safe to declare done.';
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
text: `verdict=${verdict}. ${summary} ${nextAction}`,
|
|
141
|
-
data: {
|
|
142
|
-
schema: SCHEMA,
|
|
143
|
-
generatedAt: new Date().toISOString(),
|
|
144
|
-
scope: {
|
|
145
|
-
mode,
|
|
146
|
-
files: changedFiles,
|
|
147
|
-
fileCount: changedFiles.length,
|
|
148
|
-
},
|
|
149
|
-
boundaries: boundaryBlock,
|
|
150
|
-
imports: importsBlock,
|
|
151
|
-
verdict,
|
|
152
|
-
summary,
|
|
153
|
-
nextAction,
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
},
|
|
157
|
-
};
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only MCP tool: get_file_advice.
|
|
3
|
-
*
|
|
4
|
-
* For a given file path, returns the rules, path conventions, boundary
|
|
5
|
-
* rules, and knowledge entries that apply to it. The agent equivalent
|
|
6
|
-
* of `shrk why <file>` — same engine (`buildWhyReport`), shaped for
|
|
7
|
-
* MCP consumption.
|
|
8
|
-
*
|
|
9
|
-
* Why this exists alongside `inspect_workspace` and the various
|
|
10
|
-
* `list_*` tools: agents tend to over-explore when given browsable
|
|
11
|
-
* catalogs. Asking "give me everything for this file" in one call
|
|
12
|
-
* keeps the prompt window tight and the answer focused — the agent
|
|
13
|
-
* doesn't have to discover which rule matches the file's path glob,
|
|
14
|
-
* which boundary rule constrains its imports, or which knowledge
|
|
15
|
-
* entry is the right one to read first.
|
|
16
|
-
*
|
|
17
|
-
* Use after a `shrk diff-check` flags a violation, or before editing
|
|
18
|
-
* an unfamiliar file. Read-only.
|
|
19
|
-
*/
|
|
20
|
-
import type { IToolDefinition } from '../server/tool-definition.js';
|
|
21
|
-
export declare const getFileAdviceTool: IToolDefinition;
|
|
22
|
-
//# sourceMappingURL=file-advice.tool.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file-advice.tool.d.ts","sourceRoot":"","sources":["../../src/tools/file-advice.tool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,eAAO,MAAM,iBAAiB,EAAE,eAyC/B,CAAC"}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read-only MCP tool: get_file_advice.
|
|
3
|
-
*
|
|
4
|
-
* For a given file path, returns the rules, path conventions, boundary
|
|
5
|
-
* rules, and knowledge entries that apply to it. The agent equivalent
|
|
6
|
-
* of `shrk why <file>` — same engine (`buildWhyReport`), shaped for
|
|
7
|
-
* MCP consumption.
|
|
8
|
-
*
|
|
9
|
-
* Why this exists alongside `inspect_workspace` and the various
|
|
10
|
-
* `list_*` tools: agents tend to over-explore when given browsable
|
|
11
|
-
* catalogs. Asking "give me everything for this file" in one call
|
|
12
|
-
* keeps the prompt window tight and the answer focused — the agent
|
|
13
|
-
* doesn't have to discover which rule matches the file's path glob,
|
|
14
|
-
* which boundary rule constrains its imports, or which knowledge
|
|
15
|
-
* entry is the right one to read first.
|
|
16
|
-
*
|
|
17
|
-
* Use after a `shrk diff-check` flags a violation, or before editing
|
|
18
|
-
* an unfamiliar file. Read-only.
|
|
19
|
-
*/
|
|
20
|
-
import { buildWhyReport } from '@shrkcrft/inspector';
|
|
21
|
-
export const getFileAdviceTool = {
|
|
22
|
-
name: 'get_file_advice',
|
|
23
|
-
description: 'For a given file path, return the rules, path conventions, boundary rules, and knowledge entries that apply to it. Single-call replacement for browsing the registry to figure out what constrains one file. Read-only.',
|
|
24
|
-
inputSchema: {
|
|
25
|
-
type: 'object',
|
|
26
|
-
additionalProperties: false,
|
|
27
|
-
required: ['file'],
|
|
28
|
-
properties: {
|
|
29
|
-
file: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
description: 'Path to the file (absolute, or relative to the project root). The file does not need to exist — path-string matching still works.',
|
|
32
|
-
},
|
|
33
|
-
limit: {
|
|
34
|
-
type: 'number',
|
|
35
|
-
description: 'Cap rules and knowledge entries returned (default 10).',
|
|
36
|
-
},
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
async handler(input, ctx) {
|
|
40
|
-
const file = typeof input.file === 'string' ? input.file : '';
|
|
41
|
-
if (!file) {
|
|
42
|
-
return {
|
|
43
|
-
text: 'Error: `file` argument is required.',
|
|
44
|
-
data: { error: 'missing-argument', argument: 'file' },
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
const limit = typeof input.limit === 'number' && input.limit > 0 ? Math.floor(input.limit) : 10;
|
|
48
|
-
const report = buildWhyReport({
|
|
49
|
-
inspection: ctx.inspection,
|
|
50
|
-
projectRoot: ctx.cwd,
|
|
51
|
-
target: file,
|
|
52
|
-
limit,
|
|
53
|
-
});
|
|
54
|
-
const summary = buildSummary(report);
|
|
55
|
-
return {
|
|
56
|
-
text: summary,
|
|
57
|
-
data: report,
|
|
58
|
-
};
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
function buildSummary(report) {
|
|
62
|
-
const lines = [];
|
|
63
|
-
lines.push(`File: ${report.target.relativePath} (${report.target.kind})`);
|
|
64
|
-
if (report.inferredPackage)
|
|
65
|
-
lines.push(`Package: ${report.inferredPackage}`);
|
|
66
|
-
if (report.inferredLayer)
|
|
67
|
-
lines.push(`Layer: ${report.inferredLayer}`);
|
|
68
|
-
const counts = {
|
|
69
|
-
rules: report.rules.length,
|
|
70
|
-
boundaries: report.boundaries.length,
|
|
71
|
-
paths: report.pathConventions.length,
|
|
72
|
-
knowledge: report.knowledge.length,
|
|
73
|
-
};
|
|
74
|
-
lines.push(`Matches: ${counts.rules} rule${counts.rules === 1 ? '' : 's'}, ` +
|
|
75
|
-
`${counts.boundaries} boundary rule${counts.boundaries === 1 ? '' : 's'}, ` +
|
|
76
|
-
`${counts.paths} path convention${counts.paths === 1 ? '' : 's'}, ` +
|
|
77
|
-
`${counts.knowledge} knowledge entr${counts.knowledge === 1 ? 'y' : 'ies'}.`);
|
|
78
|
-
if (counts.rules === 0 &&
|
|
79
|
-
counts.boundaries === 0 &&
|
|
80
|
-
counts.paths === 0 &&
|
|
81
|
-
counts.knowledge === 0) {
|
|
82
|
-
lines.push('No registry entries matched. The file may be outside the conventions, or the workspace may not have rules / paths defined yet.');
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
lines.push('See `data` for the full per-category list.');
|
|
86
|
-
}
|
|
87
|
-
return lines.join('\n');
|
|
88
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Primary MCP tools — the ~30 tools advertised to a connected agent
|
|
3
|
-
* by default. Every tool in {@link ALL_TOOLS} stays callable (so an
|
|
4
|
-
* agent that already knows the name can use it), but `tools/list`
|
|
5
|
-
* only advertises the primary set. Smaller surface = better
|
|
6
|
-
* tool-selection accuracy for the agent.
|
|
7
|
-
*
|
|
8
|
-
* Picked to match the CLI's allowlist semantics: anything an agent
|
|
9
|
-
* could realistically reach for during a normal task — discovery,
|
|
10
|
-
* context, planning, validation — is in. Internal introspection
|
|
11
|
-
* (catalog dumps, fix-preview internals, drift baselines) is out;
|
|
12
|
-
* still callable, just not in the default tool list.
|
|
13
|
-
*
|
|
14
|
-
* Escape hatch: set `SHRK_MCP_FULL_TOOLS=1` to advertise the full
|
|
15
|
-
* catalog (useful when debugging an agent's tool selection).
|
|
16
|
-
*/
|
|
17
|
-
export declare const PRIMARY_MCP_TOOLS: ReadonlySet<string>;
|
|
18
|
-
/**
|
|
19
|
-
* Should `tools/list` advertise the full catalog instead of the
|
|
20
|
-
* primary set? Driven by the env var so an agent operator can flip it
|
|
21
|
-
* without rebuilding.
|
|
22
|
-
*/
|
|
23
|
-
export declare function shouldAdvertiseFullToolset(): boolean;
|
|
24
|
-
//# sourceMappingURL=primary-tools.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"primary-tools.d.ts","sourceRoot":"","sources":["../../src/tools/primary-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CA4ChD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAGpD"}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Primary MCP tools — the ~30 tools advertised to a connected agent
|
|
3
|
-
* by default. Every tool in {@link ALL_TOOLS} stays callable (so an
|
|
4
|
-
* agent that already knows the name can use it), but `tools/list`
|
|
5
|
-
* only advertises the primary set. Smaller surface = better
|
|
6
|
-
* tool-selection accuracy for the agent.
|
|
7
|
-
*
|
|
8
|
-
* Picked to match the CLI's allowlist semantics: anything an agent
|
|
9
|
-
* could realistically reach for during a normal task — discovery,
|
|
10
|
-
* context, planning, validation — is in. Internal introspection
|
|
11
|
-
* (catalog dumps, fix-preview internals, drift baselines) is out;
|
|
12
|
-
* still callable, just not in the default tool list.
|
|
13
|
-
*
|
|
14
|
-
* Escape hatch: set `SHRK_MCP_FULL_TOOLS=1` to advertise the full
|
|
15
|
-
* catalog (useful when debugging an agent's tool selection).
|
|
16
|
-
*/
|
|
17
|
-
export const PRIMARY_MCP_TOOLS = new Set([
|
|
18
|
-
// Project orientation
|
|
19
|
-
'inspect_workspace',
|
|
20
|
-
'get_project_overview',
|
|
21
|
-
'get_agent_instructions',
|
|
22
|
-
'get_start_here',
|
|
23
|
-
// Context / task routing
|
|
24
|
-
'get_relevant_context',
|
|
25
|
-
'get_task_packet',
|
|
26
|
-
'get_action_hints',
|
|
27
|
-
'create_agent_brief',
|
|
28
|
-
'get_relevant_rules',
|
|
29
|
-
'explain_command',
|
|
30
|
-
// Browse the registries
|
|
31
|
-
'list_knowledge',
|
|
32
|
-
'get_knowledge',
|
|
33
|
-
'list_rules',
|
|
34
|
-
'get_rule',
|
|
35
|
-
'list_path_conventions',
|
|
36
|
-
'list_templates',
|
|
37
|
-
'get_template',
|
|
38
|
-
'list_pipelines',
|
|
39
|
-
'get_pipeline',
|
|
40
|
-
'list_presets',
|
|
41
|
-
'get_preset',
|
|
42
|
-
'list_packs',
|
|
43
|
-
'get_pack',
|
|
44
|
-
// Safe code generation (plan-first)
|
|
45
|
-
'create_generation_plan',
|
|
46
|
-
'render_template_preview',
|
|
47
|
-
'explain_generation_target',
|
|
48
|
-
// Validation gates (read-only)
|
|
49
|
-
'check_boundaries',
|
|
50
|
-
'get_diff_check_report',
|
|
51
|
-
'get_file_advice',
|
|
52
|
-
'get_architecture_constraints',
|
|
53
|
-
'get_architecture_violations',
|
|
54
|
-
// Doctor / readiness
|
|
55
|
-
'get_ai_readiness_report',
|
|
56
|
-
'doctor_packs',
|
|
57
|
-
// Search
|
|
58
|
-
'search_all',
|
|
59
|
-
'search_knowledge',
|
|
60
|
-
'search_commands',
|
|
61
|
-
]);
|
|
62
|
-
/**
|
|
63
|
-
* Should `tools/list` advertise the full catalog instead of the
|
|
64
|
-
* primary set? Driven by the env var so an agent operator can flip it
|
|
65
|
-
* without rebuilding.
|
|
66
|
-
*/
|
|
67
|
-
export function shouldAdvertiseFullToolset() {
|
|
68
|
-
const v = process.env.SHRK_MCP_FULL_TOOLS;
|
|
69
|
-
return v === '1' || v === 'true' || v === 'yes';
|
|
70
|
-
}
|