@frmwrkd/snag-mcp 0.1.0
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/LICENSE +19 -0
- package/README.md +187 -0
- package/dist/client.d.ts +544 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1074 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/resources.d.ts +19 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +281 -0
- package/dist/resources.js.map +1 -0
- package/dist/tokens.d.ts +69 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +90 -0
- package/dist/tokens.js.map +1 -0
- package/dist/tools.d.ts +115 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +868 -0
- package/dist/tools.js.map +1 -0
- package/package.json +44 -0
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token accounting — the single number that shapes this entire package.
|
|
3
|
+
*
|
|
4
|
+
* doc 03 §4.3 **[V]**:
|
|
5
|
+
*
|
|
6
|
+
* > **MCP tool output: warns at 10,000 tokens, hard-capped at 25,000**
|
|
7
|
+
* > (`MAX_MCP_OUTPUT_TOKENS`). This is the single most important number for
|
|
8
|
+
* > handoff design — it's why `.snag/` files, not MCP, carry the bulk.
|
|
9
|
+
*
|
|
10
|
+
* And doc 02 §9.2:
|
|
11
|
+
*
|
|
12
|
+
* > **Five tools. Not fifty.** Chrome DevTools MCP ships ~17,000 tokens of tool
|
|
13
|
+
* > definitions before a single call; that cost is paid on every turn.
|
|
14
|
+
*
|
|
15
|
+
* The estimator here is deliberately the crude `chars / 4` heuristic rather
|
|
16
|
+
* than a real tokenizer: no tokenizer is installed, and the point of these
|
|
17
|
+
* numbers is to fail a regression test when a response grows by an order of
|
|
18
|
+
* magnitude, not to bill anyone. It over-estimates on JSON (punctuation-heavy
|
|
19
|
+
* text tokenizes denser than 4 chars/token), which is the safe direction.
|
|
20
|
+
*/
|
|
21
|
+
/** Claude Code prints a warning above this many tokens of tool output. */
|
|
22
|
+
export declare const MCP_OUTPUT_WARN_TOKENS = 10000;
|
|
23
|
+
/** `MAX_MCP_OUTPUT_TOKENS` — Claude Code truncates above this. */
|
|
24
|
+
export declare const MCP_OUTPUT_HARD_CAP_TOKENS = 25000;
|
|
25
|
+
/**
|
|
26
|
+
* doc 02 §9.3: the `SessionStart` hook injects a **≤400-token** digest via
|
|
27
|
+
* `hookSpecificOutput.additionalContext`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const SESSION_START_DIGEST_MAX_TOKENS = 400;
|
|
30
|
+
/**
|
|
31
|
+
* doc 03 §4.3: "auto-compaction re-attaches only the first 5,000 tokens of each
|
|
32
|
+
* skill within a 25,000-token combined budget. Keep bodies under ~1,500 tokens."
|
|
33
|
+
*/
|
|
34
|
+
export declare const SKILL_BODY_MAX_TOKENS = 1500;
|
|
35
|
+
/**
|
|
36
|
+
* doc 03 §4.4 **[V]**: the standard tier downscales an image to a 1568px long
|
|
37
|
+
* edge, so a screenshot costs **at most** 1,568 visual tokens — roughly 1,200
|
|
38
|
+
* words of prose. A 1440×900 shot lands almost exactly on that ceiling.
|
|
39
|
+
*
|
|
40
|
+
* Used as the per-image charge when estimating a tool result, because the real
|
|
41
|
+
* charge depends on pixel dimensions this package never decodes.
|
|
42
|
+
*/
|
|
43
|
+
export declare const IMAGE_VISUAL_TOKENS_STANDARD = 1568;
|
|
44
|
+
/** Characters per token in the heuristic. */
|
|
45
|
+
export declare const CHARS_PER_TOKEN = 4;
|
|
46
|
+
export declare function estimateTokens(text: string): number;
|
|
47
|
+
export declare function estimateJsonTokens(value: unknown): number;
|
|
48
|
+
/** The minimum shape of a `CallToolResult` this estimator understands. */
|
|
49
|
+
export interface EstimableResult {
|
|
50
|
+
content?: readonly unknown[];
|
|
51
|
+
structuredContent?: unknown;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Approximate what a tool result costs the client's output budget.
|
|
55
|
+
*
|
|
56
|
+
* Image blocks are charged at `IMAGE_VISUAL_TOKENS_STANDARD` rather than at
|
|
57
|
+
* the length of their base64 payload: doc 03 §4.4 records that Claude
|
|
58
|
+
* tokenizes images in 28×28 patches after downscaling, so the base64 length is
|
|
59
|
+
* not the cost. **[U]** — the appendix also notes the MCP spec gives no
|
|
60
|
+
* explicit byte limit on image content and that the binding constraint is the
|
|
61
|
+
* client's own accounting, which is not published. If a client charged base64
|
|
62
|
+
* length instead, a single screenshot would blow the cap; that is exactly why
|
|
63
|
+
* `snag_get` never inlines one (D13) and `snag_screenshot` caps the bytes it
|
|
64
|
+
* will inline (see `tools.ts`).
|
|
65
|
+
*/
|
|
66
|
+
export declare function estimateResultTokens(result: EstimableResult): number;
|
|
67
|
+
/** Cut `text` to at most `maxTokens`, on a word boundary, with an ellipsis. */
|
|
68
|
+
export declare function truncateToTokens(text: string, maxTokens: number): string;
|
|
69
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,sBAAsB,QAAS,CAAC;AAE7C,kEAAkE;AAClE,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,OAAQ,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,OAAQ,CAAC;AAElD,6CAA6C;AAC7C,eAAO,MAAM,eAAe,IAAI,CAAC;AAEjC,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAiBpE;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAMxE"}
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token accounting — the single number that shapes this entire package.
|
|
3
|
+
*
|
|
4
|
+
* doc 03 §4.3 **[V]**:
|
|
5
|
+
*
|
|
6
|
+
* > **MCP tool output: warns at 10,000 tokens, hard-capped at 25,000**
|
|
7
|
+
* > (`MAX_MCP_OUTPUT_TOKENS`). This is the single most important number for
|
|
8
|
+
* > handoff design — it's why `.snag/` files, not MCP, carry the bulk.
|
|
9
|
+
*
|
|
10
|
+
* And doc 02 §9.2:
|
|
11
|
+
*
|
|
12
|
+
* > **Five tools. Not fifty.** Chrome DevTools MCP ships ~17,000 tokens of tool
|
|
13
|
+
* > definitions before a single call; that cost is paid on every turn.
|
|
14
|
+
*
|
|
15
|
+
* The estimator here is deliberately the crude `chars / 4` heuristic rather
|
|
16
|
+
* than a real tokenizer: no tokenizer is installed, and the point of these
|
|
17
|
+
* numbers is to fail a regression test when a response grows by an order of
|
|
18
|
+
* magnitude, not to bill anyone. It over-estimates on JSON (punctuation-heavy
|
|
19
|
+
* text tokenizes denser than 4 chars/token), which is the safe direction.
|
|
20
|
+
*/
|
|
21
|
+
/** Claude Code prints a warning above this many tokens of tool output. */
|
|
22
|
+
export const MCP_OUTPUT_WARN_TOKENS = 10_000;
|
|
23
|
+
/** `MAX_MCP_OUTPUT_TOKENS` — Claude Code truncates above this. */
|
|
24
|
+
export const MCP_OUTPUT_HARD_CAP_TOKENS = 25_000;
|
|
25
|
+
/**
|
|
26
|
+
* doc 02 §9.3: the `SessionStart` hook injects a **≤400-token** digest via
|
|
27
|
+
* `hookSpecificOutput.additionalContext`.
|
|
28
|
+
*/
|
|
29
|
+
export const SESSION_START_DIGEST_MAX_TOKENS = 400;
|
|
30
|
+
/**
|
|
31
|
+
* doc 03 §4.3: "auto-compaction re-attaches only the first 5,000 tokens of each
|
|
32
|
+
* skill within a 25,000-token combined budget. Keep bodies under ~1,500 tokens."
|
|
33
|
+
*/
|
|
34
|
+
export const SKILL_BODY_MAX_TOKENS = 1_500;
|
|
35
|
+
/**
|
|
36
|
+
* doc 03 §4.4 **[V]**: the standard tier downscales an image to a 1568px long
|
|
37
|
+
* edge, so a screenshot costs **at most** 1,568 visual tokens — roughly 1,200
|
|
38
|
+
* words of prose. A 1440×900 shot lands almost exactly on that ceiling.
|
|
39
|
+
*
|
|
40
|
+
* Used as the per-image charge when estimating a tool result, because the real
|
|
41
|
+
* charge depends on pixel dimensions this package never decodes.
|
|
42
|
+
*/
|
|
43
|
+
export const IMAGE_VISUAL_TOKENS_STANDARD = 1_568;
|
|
44
|
+
/** Characters per token in the heuristic. */
|
|
45
|
+
export const CHARS_PER_TOKEN = 4;
|
|
46
|
+
export function estimateTokens(text) {
|
|
47
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN);
|
|
48
|
+
}
|
|
49
|
+
export function estimateJsonTokens(value) {
|
|
50
|
+
return estimateTokens(JSON.stringify(value) ?? '');
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Approximate what a tool result costs the client's output budget.
|
|
54
|
+
*
|
|
55
|
+
* Image blocks are charged at `IMAGE_VISUAL_TOKENS_STANDARD` rather than at
|
|
56
|
+
* the length of their base64 payload: doc 03 §4.4 records that Claude
|
|
57
|
+
* tokenizes images in 28×28 patches after downscaling, so the base64 length is
|
|
58
|
+
* not the cost. **[U]** — the appendix also notes the MCP spec gives no
|
|
59
|
+
* explicit byte limit on image content and that the binding constraint is the
|
|
60
|
+
* client's own accounting, which is not published. If a client charged base64
|
|
61
|
+
* length instead, a single screenshot would blow the cap; that is exactly why
|
|
62
|
+
* `snag_get` never inlines one (D13) and `snag_screenshot` caps the bytes it
|
|
63
|
+
* will inline (see `tools.ts`).
|
|
64
|
+
*/
|
|
65
|
+
export function estimateResultTokens(result) {
|
|
66
|
+
let total = 0;
|
|
67
|
+
for (const block of result.content ?? []) {
|
|
68
|
+
if (typeof block === 'object' &&
|
|
69
|
+
block !== null &&
|
|
70
|
+
block.type === 'image') {
|
|
71
|
+
total += IMAGE_VISUAL_TOKENS_STANDARD;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
total += estimateJsonTokens(block);
|
|
75
|
+
}
|
|
76
|
+
if (result.structuredContent !== undefined) {
|
|
77
|
+
total += estimateJsonTokens(result.structuredContent);
|
|
78
|
+
}
|
|
79
|
+
return total;
|
|
80
|
+
}
|
|
81
|
+
/** Cut `text` to at most `maxTokens`, on a word boundary, with an ellipsis. */
|
|
82
|
+
export function truncateToTokens(text, maxTokens) {
|
|
83
|
+
const maxChars = maxTokens * CHARS_PER_TOKEN;
|
|
84
|
+
if (text.length <= maxChars)
|
|
85
|
+
return text;
|
|
86
|
+
const hard = text.slice(0, Math.max(0, maxChars - 1));
|
|
87
|
+
const lastSpace = hard.lastIndexOf(' ');
|
|
88
|
+
return `${lastSpace > maxChars / 2 ? hard.slice(0, lastSpace) : hard}…`;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../src/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,0EAA0E;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAE7C,kEAAkE;AAClE,MAAM,CAAC,MAAM,0BAA0B,GAAG,MAAM,CAAC;AAEjD;;;GAGG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAE3C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAElD,6CAA6C;AAC7C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,OAAO,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC;AAQD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAuB;IAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QACzC,IACE,OAAO,KAAK,KAAK,QAAQ;YACzB,KAAK,KAAK,IAAI;YACb,KAA4B,CAAC,IAAI,KAAK,OAAO,EAC9C,CAAC;YACD,KAAK,IAAI,4BAA4B,CAAC;YACtC,SAAS;QACX,CAAC;QACD,KAAK,IAAI,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,MAAM,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QAC3C,KAAK,IAAI,kBAAkB,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,SAAiB;IAC9D,MAAM,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC;IAC7C,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACxC,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC;AAC1E,CAAC"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { Task } from '@frmwrkd/snag-core';
|
|
2
|
+
import type { CallToolResult, Tool, ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import type { SnagBackend } from './client.js';
|
|
5
|
+
/** Belt-and-braces on the list size. 50 summaries is ~2,400 tokens. */
|
|
6
|
+
export declare const LIST_LIMIT_DEFAULT = 20;
|
|
7
|
+
export declare const LIST_LIMIT_MAX = 50;
|
|
8
|
+
/**
|
|
9
|
+
* Refuse to inline a screenshot larger than this. doc 03 §4.4 records that the
|
|
10
|
+
* MCP spec gives **no** explicit byte limit on image content and marks the real
|
|
11
|
+
* binding constraint **[U]**; the provider limit is 10 MB of base64. 3.5 MB of
|
|
12
|
+
* PNG is ~4.7 MB base64, comfortably inside that, and anything bigger is a
|
|
13
|
+
* full-page capture that the agent should `Read` from disk instead.
|
|
14
|
+
*/
|
|
15
|
+
export declare const SCREENSHOT_MAX_INLINE_BYTES = 3500000;
|
|
16
|
+
/**
|
|
17
|
+
* doc 02 §9.2: "compact task list — id, title, severity, wave, first suspect
|
|
18
|
+
* file". `uid` is deliberately absent: it is 30 characters × every row, and
|
|
19
|
+
* every tool here accepts the `SNAG-<n>` label instead. `kind` is one token and
|
|
20
|
+
* tells the agent whether `snag_screenshot` is worth calling at all.
|
|
21
|
+
*/
|
|
22
|
+
declare const TaskSummarySchema: z.ZodObject<{
|
|
23
|
+
id: z.ZodString;
|
|
24
|
+
title: z.ZodString;
|
|
25
|
+
severity: z.ZodEnum<["p0", "p1", "p2", "p3"]>;
|
|
26
|
+
kind: z.ZodString;
|
|
27
|
+
wave: z.ZodNumber;
|
|
28
|
+
status: z.ZodEnum<["open", "claimed", "in_progress", "verifying", "needs_review", "resolved", "wontfix"]>;
|
|
29
|
+
/** VERBATIM. The first `suspect_files` entry — never paraphrased. */
|
|
30
|
+
suspect_file: z.ZodNullable<z.ZodString>;
|
|
31
|
+
suspect_lines: z.ZodNullable<z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>>;
|
|
32
|
+
}, "strip", z.ZodTypeAny, {
|
|
33
|
+
id: string;
|
|
34
|
+
title: string;
|
|
35
|
+
status: "open" | "resolved" | "claimed" | "in_progress" | "verifying" | "needs_review" | "wontfix";
|
|
36
|
+
kind: string;
|
|
37
|
+
severity: "p0" | "p1" | "p2" | "p3";
|
|
38
|
+
wave: number;
|
|
39
|
+
suspect_file: string | null;
|
|
40
|
+
suspect_lines: [number, number] | null;
|
|
41
|
+
}, {
|
|
42
|
+
id: string;
|
|
43
|
+
title: string;
|
|
44
|
+
status: "open" | "resolved" | "claimed" | "in_progress" | "verifying" | "needs_review" | "wontfix";
|
|
45
|
+
kind: string;
|
|
46
|
+
severity: "p0" | "p1" | "p2" | "p3";
|
|
47
|
+
wave: number;
|
|
48
|
+
suspect_file: string | null;
|
|
49
|
+
suspect_lines: [number, number] | null;
|
|
50
|
+
}>;
|
|
51
|
+
export type TaskSummary = z.infer<typeof TaskSummarySchema>;
|
|
52
|
+
export declare function summarise(task: Task): TaskSummary;
|
|
53
|
+
export interface SnagToolDefinition {
|
|
54
|
+
readonly name: string;
|
|
55
|
+
readonly title: string;
|
|
56
|
+
readonly description: string;
|
|
57
|
+
readonly inputSchema: z.ZodTypeAny;
|
|
58
|
+
readonly outputSchema?: z.ZodTypeAny;
|
|
59
|
+
readonly annotations: ToolAnnotations;
|
|
60
|
+
handle(args: unknown, backend: SnagBackend): Promise<CallToolResult>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* ⚠️ EXACTLY FIVE. doc 02 §9.2 and doc 03 §1.3: Chrome DevTools MCP ships
|
|
64
|
+
* ~17,000 tokens of tool definitions before a single call and pays that on
|
|
65
|
+
* every turn; Vercel measured a 3.5× speed-up and 37% fewer tokens from cutting
|
|
66
|
+
* an agent's toolset by 80%. Adding a sixth tool taxes every turn of every
|
|
67
|
+
* session in every repo that installs this server. If you need one more verb,
|
|
68
|
+
* add a parameter to an existing tool or put the data in `.snag/`, which has no
|
|
69
|
+
* cap at all.
|
|
70
|
+
*/
|
|
71
|
+
export declare const TOOLS: readonly SnagToolDefinition[];
|
|
72
|
+
export declare const TOOL_COUNT = 5;
|
|
73
|
+
export declare function findTool(name: string): SnagToolDefinition | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Drop the redundant `items: false` from a length-pinned tuple.
|
|
76
|
+
*
|
|
77
|
+
* `@frmwrkd/snag-core`'s `toInlineJsonSchema` correctly rewrites a draft-7 tuple into
|
|
78
|
+
* the 2020-12 form:
|
|
79
|
+
*
|
|
80
|
+
* ```json
|
|
81
|
+
* { "type": "array", "minItems": 2, "maxItems": 2,
|
|
82
|
+
* "prefixItems": [{"type":"integer"},{"type":"integer"}], "items": false }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* That is exactly right per the spec — and the MCP TypeScript SDK v1.30.0
|
|
86
|
+
* rejects it. Its default validator is `AjvJsonSchemaValidator`, which
|
|
87
|
+
* constructs `new Ajv(...)` from `ajv`'s **draft-07** entry point. Draft-07 has
|
|
88
|
+
* no `prefixItems` keyword, so Ajv ignores it and applies `items: false` to
|
|
89
|
+
* every element, and a client validating `structuredContent` against the
|
|
90
|
+
* declared `outputSchema` fails a perfectly valid `[80, 96]` with:
|
|
91
|
+
*
|
|
92
|
+
* > MCP error -32602: Structured content does not match the tool's output
|
|
93
|
+
* > schema: data/tasks/0/suspect_lines/0 boolean schema is false
|
|
94
|
+
*
|
|
95
|
+
* Verified against `ajv@8.20.0` and reproduced end to end over stdio, which is
|
|
96
|
+
* the only place it shows up — the server-side path never sees it.
|
|
97
|
+
*
|
|
98
|
+
* When `maxItems === prefixItems.length`, `items` is vacuous in 2020-12 (there
|
|
99
|
+
* can be no element past the prefix), so deleting it changes nothing for a
|
|
100
|
+
* 2020-12 validator and makes a draft-07 validator fall back to `minItems` /
|
|
101
|
+
* `maxItems`, which still pin the arity. Where the arity is *not* pinned,
|
|
102
|
+
* `items: false` is load-bearing and is left alone.
|
|
103
|
+
*
|
|
104
|
+
* `Task.suspect_files[].lines` is the tuple that hits this, and it is the one
|
|
105
|
+
* field an agent uses to open a file at the right place. Losing it to a
|
|
106
|
+
* validation error would be a silent, total failure of `snag_get`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function openTupleTail(node: unknown): unknown;
|
|
109
|
+
export declare function toolDefinitions(): Tool[];
|
|
110
|
+
/** Dispatch a `tools/call`. Unknown names are an error result, not a throw. */
|
|
111
|
+
export declare function callTool(name: string, args: unknown, backend: SnagBackend): Promise<CallToolResult>;
|
|
112
|
+
/** Exposed for the regression test that pins the status vocabulary. */
|
|
113
|
+
export declare const KNOWN_TASK_STATUSES: readonly ["open", "claimed", "in_progress", "verifying", "needs_review", "resolved", "wontfix"];
|
|
114
|
+
export {};
|
|
115
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAsDA,OAAO,KAAK,EAAQ,IAAI,EAAc,MAAM,oBAAoB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAC;AAChG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EACV,WAAW,EAIZ,MAAM,aAAa,CAAC;AAqBrB,uEAAuE;AACvE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,cAAc,KAAK,CAAC;AAEjC;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,UAAY,CAAC;AAIrD;;;;;GAKG;AACH,QAAA,MAAM,iBAAiB;;;;;;;IAOrB,qEAAqE;;;;;;;;;;;;;;;;;;;;;EAGrE,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,CAYjD;AAyFD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,UAAU,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC;IACrC,QAAQ,CAAC,WAAW,EAAE,eAAe,CAAC;IACtC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CACtE;AA8pBD;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,EAAE,SAAS,kBAAkB,EAM9C,CAAC;AAEF,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAErE;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAkBpD;AAED,wBAAgB,eAAe,IAAI,IAAI,EAAE,CAiBxC;AAED,+EAA+E;AAC/E,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,cAAc,CAAC,CAgBzB;AAED,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,iGAAgB,CAAC"}
|