@kaminari-ad/mcp 0.12.0 → 0.13.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/CHANGELOG.md +14 -1
- package/dist/bin.js +3 -3
- package/dist/{chunk-T6GEYHLP.js → chunk-HOKDLKJN.js} +58 -26
- package/dist/chunk-HOKDLKJN.js.map +1 -0
- package/dist/{chunk-TMUGAKI2.js → chunk-VFDW3UL2.js} +3 -3
- package/dist/{chunk-TMUGAKI2.js.map → chunk-VFDW3UL2.js.map} +1 -1
- package/dist/{http-bootstrap-Y4YKT7KV.js → http-bootstrap-ROIMZVK2.js} +4 -4
- package/dist/{http-bootstrap-Y4YKT7KV.js.map → http-bootstrap-ROIMZVK2.js.map} +1 -1
- package/dist/{stdio-bootstrap-JW6I4H3U.js → stdio-bootstrap-VM6OOG2O.js} +4 -4
- package/dist/{stdio-bootstrap-JW6I4H3U.js.map → stdio-bootstrap-VM6OOG2O.js.map} +1 -1
- package/package.json +1 -1
- package/dist/chunk-T6GEYHLP.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.0] - 2026-08-20
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added `regexp_request_url` support to `create_custom_rule`,
|
|
15
|
+
`test_custom_rule`, and `update_custom_rule`. Create and preview inputs
|
|
16
|
+
validate the strict config (`pattern`, optional `flags: "" | "i"`) and fixed
|
|
17
|
+
`page` target before dispatch; update documents the API-enforced,
|
|
18
|
+
full-replacement contract because its immutable rule type is not part of the
|
|
19
|
+
update payload. Fresh scans evaluate up to 5,000 captured URLs; stored-scan
|
|
20
|
+
tests and rechecks reconstruct a reduced request tree and are best-effort.
|
|
21
|
+
|
|
10
22
|
## [0.12.0] - 2026-08-07
|
|
11
23
|
|
|
12
24
|
### Security
|
|
@@ -1025,7 +1037,8 @@ Initial public release. The first version that ships to npm under
|
|
|
1025
1037
|
need them.
|
|
1026
1038
|
- Invoice PDF fetcher — same reason.
|
|
1027
1039
|
|
|
1028
|
-
[Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.
|
|
1040
|
+
[Unreleased]: https://github.com/kaminari-ad/mcp/compare/v0.13.0...HEAD
|
|
1041
|
+
[0.13.0]: https://github.com/kaminari-ad/mcp/compare/v0.12.0...v0.13.0
|
|
1029
1042
|
[0.12.0]: https://github.com/kaminari-ad/mcp/compare/v0.11.0...v0.12.0
|
|
1030
1043
|
[0.11.0]: https://github.com/kaminari-ad/mcp/compare/v0.10.0...v0.11.0
|
|
1031
1044
|
[0.10.0]: https://github.com/kaminari-ad/mcp/compare/v0.8.0...v0.10.0
|
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { NAME, VERSION, err, ok } from './chunk-
|
|
2
|
+
import { NAME, VERSION, err, ok } from './chunk-VFDW3UL2.js';
|
|
3
3
|
import process from 'process';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
|
|
@@ -160,10 +160,10 @@ async function main() {
|
|
|
160
160
|
}
|
|
161
161
|
const config = configResult.value;
|
|
162
162
|
if (config.transport === "stdio") {
|
|
163
|
-
const { bootstrapStdio } = await import('./stdio-bootstrap-
|
|
163
|
+
const { bootstrapStdio } = await import('./stdio-bootstrap-VM6OOG2O.js');
|
|
164
164
|
return bootstrapStdio(config);
|
|
165
165
|
}
|
|
166
|
-
const { bootstrapHttp } = await import('./http-bootstrap-
|
|
166
|
+
const { bootstrapHttp } = await import('./http-bootstrap-ROIMZVK2.js');
|
|
167
167
|
return bootstrapHttp(config);
|
|
168
168
|
}
|
|
169
169
|
main().then(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { err, ok } from './chunk-
|
|
2
|
+
import { err, ok } from './chunk-VFDW3UL2.js';
|
|
3
3
|
import { createHash, randomUUID } from 'crypto';
|
|
4
4
|
import createClient from 'openapi-fetch';
|
|
5
5
|
import { fetch } from 'undici';
|
|
@@ -3944,19 +3944,47 @@ var updateCampaignTool = {
|
|
|
3944
3944
|
return ok(result.value);
|
|
3945
3945
|
}
|
|
3946
3946
|
};
|
|
3947
|
+
var requestUrlRuleConfigSchema = z.object({
|
|
3948
|
+
pattern: z.string().min(1).max(4096).describe("Non-empty request-URL regular expression, at most 4,096 characters."),
|
|
3949
|
+
flags: z.enum(["", "i"]).optional().describe("Omit or use '' for case-sensitive matching; use 'i' to ignore case.")
|
|
3950
|
+
}).strict();
|
|
3951
|
+
var REQUEST_URL_RULE_CONFIG_DOC = "For `rule_type='regexp_request_url'`, `config` must be exactly `{ pattern: string, flags?: '' | 'i' }`: pattern is non-empty and at most 4,096 characters; omit flags or use `''` for case-sensitive matching, or `'i'` for case-insensitive matching. `target` must be `'page'`. Fresh scans inspect up to 5,000 captured request URLs. Tests and rechecks of stored scans reconstruct main-frame hops plus up to 200 persisted subrequests with selected resource types omitted, so historical matching is best-effort.";
|
|
3952
|
+
function requestUrlRuleInputError(input) {
|
|
3953
|
+
if (input.rule_type !== "regexp_request_url") return null;
|
|
3954
|
+
if (input.target !== void 0 && input.target !== "page") {
|
|
3955
|
+
return {
|
|
3956
|
+
kind: "invalid-input",
|
|
3957
|
+
message: "regexp_request_url requires target='page'.",
|
|
3958
|
+
fieldErrors: { target: ["Must be 'page' for regexp_request_url."] }
|
|
3959
|
+
};
|
|
3960
|
+
}
|
|
3961
|
+
const parsed = requestUrlRuleConfigSchema.safeParse(input.config);
|
|
3962
|
+
if (parsed.success) return null;
|
|
3963
|
+
const issue = parsed.error.issues[0];
|
|
3964
|
+
const field = issue?.path.length ? `config.${issue.path.join(".")}` : "config";
|
|
3965
|
+
const message = issue?.message ?? "Invalid request-URL rule config.";
|
|
3966
|
+
return {
|
|
3967
|
+
kind: "invalid-input",
|
|
3968
|
+
message: `Invalid regexp_request_url config: ${message}`,
|
|
3969
|
+
fieldErrors: { [field]: [message] }
|
|
3970
|
+
};
|
|
3971
|
+
}
|
|
3947
3972
|
var COMBO_MATCH_SCOPE_DOC = 'For `rule_type=\'combo\'` the optional rule-level key `match_scope` decides where conditions are counted. `"scan"` (the default, and the behaviour when the key is absent) checks them against the union of all tags on the scan. With `"url"`, every condition must be satisfied by tags on the same link, and the tag is assigned to that link \u2014 so a rule built only from link-less tags (AI verdicts, crawler behaviour) has no link to attach to and will never match; keep those on the whole scan. Mixing the two kinds does not help either: AI verdicts and per-link detections reach the engine in different scan parts and are not evaluated together today, so such a combo fires under neither scope. Give a `"url"` rule at least one positive condition (`all_of` / `any_of` / `tag_category` + `count_gte`) \u2014 a config carrying only `none_of` matches every link that merely lacks those tags, and the API does not reject that on this path. Thresholds stay "N or more" (`count_gte` / `any_of_min`) in both scopes; no other `match_scope` value is accepted.';
|
|
3948
3973
|
var COMBO_MATCH_SCOPE_READ_DOC = 'For `rule_type=\'combo\'` the returned `config` may carry the rule-level key `match_scope` ("scan" = count conditions across the whole scan, "url" = every condition must be satisfied by tags on the same link). Resend it verbatim when updating: `update_custom_rule` replaces `config` wholesale, so dropping the key reverts the rule to whole-scan matching.';
|
|
3949
3974
|
var ruleConfigField = z.record(z.unknown()).superRefine((config, ctx) => {
|
|
3950
3975
|
const scope = config["match_scope"];
|
|
3951
|
-
if (scope
|
|
3952
|
-
|
|
3976
|
+
if (scope !== void 0 && scope !== "scan" && scope !== "url") {
|
|
3977
|
+
ctx.addIssue({
|
|
3978
|
+
code: z.ZodIssueCode.custom,
|
|
3979
|
+
path: ["match_scope"],
|
|
3980
|
+
message: 'config.match_scope must be "scan" or "url". Omit the key for scan-wide matching (the default).'
|
|
3981
|
+
});
|
|
3953
3982
|
}
|
|
3954
|
-
ctx.addIssue({
|
|
3955
|
-
code: z.ZodIssueCode.custom,
|
|
3956
|
-
path: ["match_scope"],
|
|
3957
|
-
message: 'config.match_scope must be "scan" or "url". Omit the key for scan-wide matching (the default).'
|
|
3958
|
-
});
|
|
3959
3983
|
});
|
|
3984
|
+
var requestUrlAwareRuleConfigField = z.union([
|
|
3985
|
+
requestUrlRuleConfigSchema,
|
|
3986
|
+
ruleConfigField
|
|
3987
|
+
]);
|
|
3960
3988
|
|
|
3961
3989
|
// src/application/tools/custom-rules/create-custom-rule.tool.ts
|
|
3962
3990
|
var CreateCustomRuleInputShape = {
|
|
@@ -3967,18 +3995,18 @@ var CreateCustomRuleInputShape = {
|
|
|
3967
3995
|
"Tag slug to assign on match. Empty = create-only (advanced). The API auto-registers a custom tag definition for this slug with `display_name = name`. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `scope=system`); colliding requests return 422 with code `checking.system_slug_reserved`. For `rule_type='llm'` use `config.tags` keys instead and leave `tag_slug` empty."
|
|
3968
3996
|
),
|
|
3969
3997
|
rule_type: z.string().max(50).describe(
|
|
3970
|
-
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`. The API validates."
|
|
3998
|
+
"Rule engine. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs. The API validates."
|
|
3971
3999
|
),
|
|
3972
|
-
config:
|
|
3973
|
-
"Rule-type-specific configuration object. Shape depends on `rule_type`. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`). " + COMBO_MATCH_SCOPE_DOC
|
|
4000
|
+
config: requestUrlAwareRuleConfigField.describe(
|
|
4001
|
+
"Rule-type-specific configuration object. Shape depends on `rule_type`. " + REQUEST_URL_RULE_CONFIG_DOC + " `regexp_url` remains redirect-chain-only. For `rule_type='llm'` the shape is `{ prompt: string, tags: { <tag_slug>: <description>, ... } }`; each key in `config.tags` is auto-registered as a custom tag definition AND must not collide with a system slug (same 422 contract as `tag_slug`). " + COMBO_MATCH_SCOPE_DOC
|
|
3974
4002
|
),
|
|
3975
4003
|
target: z.string().max(30).optional().describe(
|
|
3976
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). Default: page. See API docs for the full set of valid values."
|
|
4004
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` requires `target='page'`. Default: page. See API docs for the full set of valid values."
|
|
3977
4005
|
)
|
|
3978
4006
|
};
|
|
3979
4007
|
var createCustomRuleTool = {
|
|
3980
4008
|
name: "create_custom_rule",
|
|
3981
|
-
description: "Define a custom tag-detection rule.
|
|
4009
|
+
description: "Define a custom tag-detection rule. Use `rule_type='regexp_request_url'` to match captured network and subresource URLs on the fixed `page` target; fresh scans carry up to 5,000 URLs, while later tests/rechecks use a reduced persisted request tree and are best-effort. `rule_type='regexp_url'` remains redirect-chain-only. The API auto-registers a tag definition for each emitted slug and rejects built-in system-slug collisions with HTTP 422 / `checking.system_slug_reserved`. Matches tag future scans; existing scans are untouched until `recheck_scans`.",
|
|
3982
4010
|
annotations: {
|
|
3983
4011
|
title: "Create Custom Rule",
|
|
3984
4012
|
readOnlyHint: false,
|
|
@@ -3988,6 +4016,8 @@ var createCustomRuleTool = {
|
|
|
3988
4016
|
},
|
|
3989
4017
|
inputSchema: z.object(CreateCustomRuleInputShape),
|
|
3990
4018
|
handler: async (input, ctx) => {
|
|
4019
|
+
const inputError = requestUrlRuleInputError(input);
|
|
4020
|
+
if (inputError) return err(inputError);
|
|
3991
4021
|
const body = {
|
|
3992
4022
|
name: input.name,
|
|
3993
4023
|
rule_type: input.rule_type,
|
|
@@ -4061,19 +4091,19 @@ var listCustomRulesTool = {
|
|
|
4061
4091
|
};
|
|
4062
4092
|
var TestCustomRuleInputShape = {
|
|
4063
4093
|
rule_type: z.string().max(50).describe(
|
|
4064
|
-
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `blacklist_domain`, `combo`, `llm`."
|
|
4094
|
+
"Rule engine type. One of: `stopword_content`, `stopword_url`, `regexp_content`, `regexp_url`, `regexp_request_url`, `blacklist_domain`, `combo`, `llm`. `regexp_url` checks redirect-chain URLs only; `regexp_request_url` checks captured network and subresource URLs."
|
|
4065
4095
|
),
|
|
4066
|
-
config:
|
|
4067
|
-
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`. " + COMBO_MATCH_SCOPE_DOC
|
|
4096
|
+
config: requestUrlAwareRuleConfigField.describe(
|
|
4097
|
+
"Rule-type-specific config to test. Same shape as `create_custom_rule`'s `config`. " + REQUEST_URL_RULE_CONFIG_DOC + " `regexp_url` remains redirect-chain-only. NOTE: `test_custom_rule` evaluates the rule against a scan WITHOUT persisting it, so slug-collision validation does NOT run here \u2014 verify slugs against `list_tags` (`scope=system`) before promoting to `create_custom_rule`. " + COMBO_MATCH_SCOPE_DOC
|
|
4068
4098
|
),
|
|
4069
4099
|
target: z.string().max(30).describe(
|
|
4070
|
-
"Where to apply the rule (e.g. 'page' for landing HTML). See API docs for the full set of valid values."
|
|
4100
|
+
"Where to apply the rule (e.g. 'page' for landing HTML). `regexp_request_url` requires `target='page'`. See API docs for the full set of valid values."
|
|
4071
4101
|
),
|
|
4072
4102
|
scan_id: z.string().uuid().describe("Existing scan UUID to evaluate the rule against.")
|
|
4073
4103
|
};
|
|
4074
4104
|
var testCustomRuleTool = {
|
|
4075
4105
|
name: "test_custom_rule",
|
|
4076
|
-
description: "Preview-test a rule definition against
|
|
4106
|
+
description: "Preview-test a rule definition against a stored scan without persisting it. For `regexp_request_url`, the historical snapshot contains main-frame hops plus at most 200 persisted subrequests with selected resource types omitted, so a no-match does not prove the original fresh scan lacked the request. Returns match state, elapsed time, and per-tag detail; the preview response does not expose the matched request URL separately. Slug-collision validation does not run in preview mode.",
|
|
4077
4107
|
annotations: {
|
|
4078
4108
|
title: "Test Custom Rule",
|
|
4079
4109
|
readOnlyHint: true,
|
|
@@ -4083,6 +4113,8 @@ var testCustomRuleTool = {
|
|
|
4083
4113
|
},
|
|
4084
4114
|
inputSchema: z.object(TestCustomRuleInputShape),
|
|
4085
4115
|
handler: async (input, ctx) => {
|
|
4116
|
+
const inputError = requestUrlRuleInputError(input);
|
|
4117
|
+
if (inputError) return err(inputError);
|
|
4086
4118
|
const result = await ctx.api.testCustomRule({
|
|
4087
4119
|
rule_type: input.rule_type,
|
|
4088
4120
|
config: input.config,
|
|
@@ -4096,22 +4128,22 @@ var testCustomRuleTool = {
|
|
|
4096
4128
|
var UpdateCustomRuleInputShape = {
|
|
4097
4129
|
rule_id: z.string().uuid().describe("Rule UUID to update."),
|
|
4098
4130
|
name: z.string().min(1).max(200).optional().describe(
|
|
4099
|
-
"New display name. For non-LLM rules this
|
|
4131
|
+
"New rule display name. For PERSONAL non-LLM rules this refreshes the auto-registered tag's display name. A same-slug GLOBAL rule keeps its separately managed tag metadata unchanged; use `update_tag_definition` for that tag."
|
|
4100
4132
|
),
|
|
4101
4133
|
tag_slug: z.string().max(100).optional().describe(
|
|
4102
|
-
"New tag slug to assign on match. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `scope=system`); colliding requests return 422 with code `checking.system_slug_reserved`.
|
|
4134
|
+
"New tag slug to assign on match. **MUST NOT collide with a built-in system tag slug** (see `list_tags` where `scope=system`); colliding requests return 422 with code `checking.system_slug_reserved`. Leaving a GLOBAL rule on the same slug preserves its admin-managed tag metadata."
|
|
4103
4135
|
),
|
|
4104
|
-
config:
|
|
4105
|
-
"New rule-type-specific config object. Replaces the stored config wholesale \u2014 resend every key you want to keep, including a combo rule's `match_scope`. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract. " + COMBO_MATCH_SCOPE_DOC
|
|
4136
|
+
config: requestUrlAwareRuleConfigField.optional().describe(
|
|
4137
|
+
"New rule-type-specific config object. Replaces the stored config wholesale \u2014 resend every key you want to keep, including a combo rule's `match_scope`. For `rule_type='llm'` the keys of `config.tags` are auto-registered as tag definitions; any key that collides with a system slug returns the same 422 contract. " + REQUEST_URL_RULE_CONFIG_DOC + " Read the rule first because `rule_type` is immutable and is not repeated in this update input. " + COMBO_MATCH_SCOPE_DOC
|
|
4106
4138
|
),
|
|
4107
4139
|
target: z.string().max(30).optional().describe(
|
|
4108
|
-
"Where to apply the rule
|
|
4140
|
+
"Where to apply the rule. `regexp_request_url` is fixed to `page`; do not change it. See API docs for the valid targets of other rule types."
|
|
4109
4141
|
),
|
|
4110
4142
|
is_active: z.boolean().optional().describe("Enable/disable the rule.")
|
|
4111
4143
|
};
|
|
4112
4144
|
var updateCustomRuleTool = {
|
|
4113
4145
|
name: "update_custom_rule",
|
|
4114
|
-
description: "Update a custom tag-detection rule. Only supplied fields are sent, but
|
|
4146
|
+
description: "Update a custom tag-detection rule. Only supplied fields are sent, but `config` replaces the stored object wholesale; read the rule first and resend every required key. `regexp_request_url` needs a non-empty pattern (max 4,096), flags `''`/`'i'`, and the fixed `page` target. Same-slug GLOBAL rule edits preserve separately managed tag metadata; use `update_tag_definition` to change it. Existing scans are not re-evaluated until `recheck_scans`.",
|
|
4115
4147
|
annotations: {
|
|
4116
4148
|
title: "Update Custom Rule",
|
|
4117
4149
|
readOnlyHint: false,
|
|
@@ -5659,5 +5691,5 @@ function formatToolError(error) {
|
|
|
5659
5691
|
}
|
|
5660
5692
|
|
|
5661
5693
|
export { BearerToken, SERVER_INSTRUCTIONS, createHttpApiGateway, createPinoLogger, declareEmptyResourcesAndPrompts, newRequestId, wireToolsIntoMcpServer };
|
|
5662
|
-
//# sourceMappingURL=chunk-
|
|
5663
|
-
//# sourceMappingURL=chunk-
|
|
5694
|
+
//# sourceMappingURL=chunk-HOKDLKJN.js.map
|
|
5695
|
+
//# sourceMappingURL=chunk-HOKDLKJN.js.map
|