@unclick/mcp-server 0.3.16 → 0.3.18
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/dist/__tests__/legalpass-tool.test.d.ts +2 -0
- package/dist/__tests__/legalpass-tool.test.d.ts.map +1 -0
- package/dist/__tests__/legalpass-tool.test.js +44 -0
- package/dist/__tests__/legalpass-tool.test.js.map +1 -0
- package/dist/__tests__/tool-schema-validation.test.d.ts +2 -0
- package/dist/__tests__/tool-schema-validation.test.d.ts.map +1 -0
- package/dist/__tests__/tool-schema-validation.test.js +54 -0
- package/dist/__tests__/tool-schema-validation.test.js.map +1 -0
- package/dist/copypass-tool.d.ts +3 -0
- package/dist/copypass-tool.d.ts.map +1 -0
- package/dist/copypass-tool.js +123 -0
- package/dist/copypass-tool.js.map +1 -0
- package/dist/copypass-tool.test.d.ts +2 -0
- package/dist/copypass-tool.test.d.ts.map +1 -0
- package/dist/copypass-tool.test.js +41 -0
- package/dist/copypass-tool.test.js.map +1 -0
- package/dist/legalpass-tool.d.ts +15 -0
- package/dist/legalpass-tool.d.ts.map +1 -0
- package/dist/legalpass-tool.js +121 -0
- package/dist/legalpass-tool.js.map +1 -0
- package/dist/memory/__tests__/response-bounds.test.js +63 -1
- package/dist/memory/__tests__/response-bounds.test.js.map +1 -1
- package/dist/memory/handlers.d.ts +1 -0
- package/dist/memory/handlers.d.ts.map +1 -1
- package/dist/memory/handlers.js +91 -3
- package/dist/memory/handlers.js.map +1 -1
- package/dist/memory/local.d.ts.map +1 -1
- package/dist/memory/local.js +2 -0
- package/dist/memory/local.js.map +1 -1
- package/dist/memory/supabase.d.ts.map +1 -1
- package/dist/memory/supabase.js +2 -0
- package/dist/memory/supabase.js.map +1 -1
- package/dist/memory/types.d.ts +2 -0
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/server.d.ts +12 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +141 -3
- package/dist/server.js.map +1 -1
- package/dist/tool-wiring.d.ts +944 -0
- package/dist/tool-wiring.d.ts.map +1 -1
- package/dist/tool-wiring.js +903 -56
- package/dist/tool-wiring.js.map +1 -1
- package/package.json +2 -1
package/dist/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import * as AjvModule from "ajv";
|
|
3
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
5
|
import { CATALOG, TOOL_MAP, ENDPOINT_MAP } from "./catalog.js";
|
|
5
6
|
import { createClient } from "./client.js";
|
|
@@ -141,6 +142,7 @@ const INTERNAL_TOOLS = [
|
|
|
141
142
|
"Example: 'I need to resize an image' returns the image tool with its endpoints.",
|
|
142
143
|
inputSchema: {
|
|
143
144
|
type: "object",
|
|
145
|
+
additionalProperties: false,
|
|
144
146
|
properties: {
|
|
145
147
|
query: {
|
|
146
148
|
type: "string",
|
|
@@ -161,6 +163,7 @@ const INTERNAL_TOOLS = [
|
|
|
161
163
|
"Returns a list of tools with their slugs and descriptions.",
|
|
162
164
|
inputSchema: {
|
|
163
165
|
type: "object",
|
|
166
|
+
additionalProperties: false,
|
|
164
167
|
properties: {
|
|
165
168
|
category: {
|
|
166
169
|
type: "string",
|
|
@@ -177,6 +180,7 @@ const INTERNAL_TOOLS = [
|
|
|
177
180
|
"exactly how to call a tool.",
|
|
178
181
|
inputSchema: {
|
|
179
182
|
type: "object",
|
|
183
|
+
additionalProperties: false,
|
|
180
184
|
properties: {
|
|
181
185
|
slug: {
|
|
182
186
|
type: "string",
|
|
@@ -194,6 +198,7 @@ const INTERNAL_TOOLS = [
|
|
|
194
198
|
"Example: endpoint_id='image.resize', params={image: '<base64>', width: 800, height: 600}",
|
|
195
199
|
inputSchema: {
|
|
196
200
|
type: "object",
|
|
201
|
+
additionalProperties: false,
|
|
197
202
|
properties: {
|
|
198
203
|
endpoint_id: {
|
|
199
204
|
type: "string",
|
|
@@ -226,6 +231,7 @@ const VISIBLE_TOOLS = [
|
|
|
226
231
|
"Do NOT trigger for pure factual lookups (capitals, math, definitions) that require no personal context.",
|
|
227
232
|
inputSchema: {
|
|
228
233
|
type: "object",
|
|
234
|
+
additionalProperties: false,
|
|
229
235
|
properties: {
|
|
230
236
|
num_sessions: {
|
|
231
237
|
type: "number",
|
|
@@ -251,18 +257,22 @@ const VISIBLE_TOOLS = [
|
|
|
251
257
|
description: "Saves a new persistent fact about the user that will be available in all future sessions across every AI tool. " +
|
|
252
258
|
"Use whenever the user shares anything worth keeping -- even if they don't explicitly ask: 'capture', 'noted', " +
|
|
253
259
|
"'remember this', 'log', 'store', 'don't forget', or any preference, decision, correction, contact, project detail, " +
|
|
254
|
-
"technical choice, or personal detail the user mentions. " +
|
|
260
|
+
"technical choice, troubleshooting fix, or personal detail the user mentions. " +
|
|
261
|
+
"When the user reports a solved client/tool issue, save it as category 'troubleshooting' using the shape " +
|
|
262
|
+
"'Issue: <symptom>. Solution: <fix>'. " +
|
|
255
263
|
"Also trigger proactively when the user corrects you (save the correction immediately), " +
|
|
256
264
|
"reveals a preference by rejecting something, or names a person/tool/project for the first time. " +
|
|
257
265
|
"Do NOT trigger for transient values (today's weather, one-off calculations, temporary state that won't matter next session). " +
|
|
258
266
|
"Do NOT trigger for facts already confirmed stored earlier in this session.",
|
|
259
267
|
inputSchema: {
|
|
260
268
|
type: "object",
|
|
269
|
+
additionalProperties: false,
|
|
261
270
|
properties: {
|
|
262
271
|
fact: { type: "string", description: "The fact -- a single atomic statement" },
|
|
263
272
|
category: {
|
|
264
273
|
type: "string",
|
|
265
|
-
|
|
274
|
+
enum: ["preference", "decision", "technical", "contact", "project", "troubleshooting", "general"],
|
|
275
|
+
description: "Category: preference, decision, technical, contact, project, troubleshooting, general",
|
|
266
276
|
default: "general",
|
|
267
277
|
},
|
|
268
278
|
confidence: { type: "number", minimum: 0, maximum: 1, default: 0.9 },
|
|
@@ -288,6 +298,7 @@ const VISIBLE_TOOLS = [
|
|
|
288
298
|
"Do NOT trigger if load_memory was just called and already returned the relevant context.",
|
|
289
299
|
inputSchema: {
|
|
290
300
|
type: "object",
|
|
301
|
+
additionalProperties: false,
|
|
291
302
|
properties: {
|
|
292
303
|
query: { type: "string", description: "Search query" },
|
|
293
304
|
max_results: { type: "number", minimum: 1, maximum: 50, default: 10 },
|
|
@@ -318,9 +329,11 @@ const VISIBLE_TOOLS = [
|
|
|
318
329
|
"Do NOT trigger for information the user explicitly says is temporary.",
|
|
319
330
|
inputSchema: {
|
|
320
331
|
type: "object",
|
|
332
|
+
additionalProperties: false,
|
|
321
333
|
properties: {
|
|
322
334
|
category: {
|
|
323
335
|
type: "string",
|
|
336
|
+
enum: ["identity", "preference", "client", "workflow", "technical", "standing_rule"],
|
|
324
337
|
description: "Category: identity, preference, client, workflow, technical, standing_rule",
|
|
325
338
|
},
|
|
326
339
|
key: { type: "string", description: "Unique key within category (e.g. 'timezone', 'preferred_stack')" },
|
|
@@ -343,6 +356,7 @@ const VISIBLE_TOOLS = [
|
|
|
343
356
|
"Do NOT trigger if the session has already been saved with no new work done since.",
|
|
344
357
|
inputSchema: {
|
|
345
358
|
type: "object",
|
|
359
|
+
additionalProperties: false,
|
|
346
360
|
properties: {
|
|
347
361
|
session_id: { type: "string", description: "Unique session identifier (timestamp or UUID)" },
|
|
348
362
|
summary: { type: "string", description: "Narrative of what happened: decisions, work completed, problems solved" },
|
|
@@ -364,6 +378,7 @@ const VISIBLE_TOOLS = [
|
|
|
364
378
|
"search_memory result. Do NOT use for new information -- use save_fact instead.",
|
|
365
379
|
inputSchema: {
|
|
366
380
|
type: "object",
|
|
381
|
+
additionalProperties: false,
|
|
367
382
|
properties: {
|
|
368
383
|
fact_id: { type: "string", description: "UUID of the fact to invalidate" },
|
|
369
384
|
reason: { type: "string", description: "Why the fact is no longer valid (optional but recommended)" },
|
|
@@ -381,6 +396,7 @@ const VISIBLE_TOOLS = [
|
|
|
381
396
|
"Automatically marks them as read so you do not re-narrate later.",
|
|
382
397
|
inputSchema: {
|
|
383
398
|
type: "object",
|
|
399
|
+
additionalProperties: false,
|
|
384
400
|
properties: {
|
|
385
401
|
agent_id: {
|
|
386
402
|
type: "string",
|
|
@@ -400,6 +416,7 @@ const VISIBLE_TOOLS = [
|
|
|
400
416
|
"Do NOT call this on every session, only the first time on a new device or after a reset.",
|
|
401
417
|
inputSchema: {
|
|
402
418
|
type: "object",
|
|
419
|
+
additionalProperties: false,
|
|
403
420
|
properties: {
|
|
404
421
|
agent_id: {
|
|
405
422
|
type: "string",
|
|
@@ -433,6 +450,7 @@ const VISIBLE_TOOLS = [
|
|
|
433
450
|
"If you're replying to a specific earlier message, set thread_id to that message's id. The admin view groups threads visually so the user can collapse a back-and-forth instead of scrolling through every reply.",
|
|
434
451
|
inputSchema: {
|
|
435
452
|
type: "object",
|
|
453
|
+
additionalProperties: false,
|
|
436
454
|
properties: {
|
|
437
455
|
agent_id: {
|
|
438
456
|
type: "string",
|
|
@@ -475,6 +493,7 @@ const VISIBLE_TOOLS = [
|
|
|
475
493
|
"Optional next_checkin_at acts as a dead-man's-switch. Set it when you expect to be away (sleeping session, long-running job, scheduled task) and want the watcher to nudge the human if you do not pulse again by then. Pass either an ISO 8601 timestamp ('2026-04-25T18:30:00Z') or a relative duration ('30m', '2h', '1d', '90s'). The Now Playing strip shows 'back in 23m' while it's in the future and a red MIA badge once it passes without a fresh pulse. Pass an empty string to clear it.",
|
|
476
494
|
inputSchema: {
|
|
477
495
|
type: "object",
|
|
496
|
+
additionalProperties: false,
|
|
478
497
|
properties: {
|
|
479
498
|
agent_id: {
|
|
480
499
|
type: "string",
|
|
@@ -501,6 +520,7 @@ const VISIBLE_TOOLS = [
|
|
|
501
520
|
"Posts a 'todo-created' Fishbowl event so other agents notice without polling.",
|
|
502
521
|
inputSchema: {
|
|
503
522
|
type: "object",
|
|
523
|
+
additionalProperties: false,
|
|
504
524
|
properties: {
|
|
505
525
|
agent_id: { type: "string", description: "Stable identifier for the calling agent. Same value as set_my_emoji." },
|
|
506
526
|
title: { type: "string", description: "Short title (max 200 chars)" },
|
|
@@ -517,6 +537,7 @@ const VISIBLE_TOOLS = [
|
|
|
517
537
|
description: "Update a todo's title, description, priority, status, or assignee. Use when scope changes, ownership shifts, or you move it between kanban columns ('open', 'in_progress', 'done', 'dropped'). agent_id required for attribution.",
|
|
518
538
|
inputSchema: {
|
|
519
539
|
type: "object",
|
|
540
|
+
additionalProperties: false,
|
|
520
541
|
properties: {
|
|
521
542
|
agent_id: { type: "string", description: "Stable identifier for the calling agent." },
|
|
522
543
|
todo_id: { type: "string", description: "UUID of the todo to update" },
|
|
@@ -535,6 +556,7 @@ const VISIBLE_TOOLS = [
|
|
|
535
556
|
description: "Shortcut for marking a todo as done. Sets status='done' and stamps completed_at. Posts a 'todo-completed' Fishbowl event. agent_id required.",
|
|
536
557
|
inputSchema: {
|
|
537
558
|
type: "object",
|
|
559
|
+
additionalProperties: false,
|
|
538
560
|
properties: {
|
|
539
561
|
agent_id: { type: "string" },
|
|
540
562
|
todo_id: { type: "string" },
|
|
@@ -548,6 +570,7 @@ const VISIBLE_TOOLS = [
|
|
|
548
570
|
description: "Marks a todo as dropped (decided not to do it). Soft state, not a delete. Use when a todo is obsolete but the history still matters. agent_id required.",
|
|
549
571
|
inputSchema: {
|
|
550
572
|
type: "object",
|
|
573
|
+
additionalProperties: false,
|
|
551
574
|
properties: {
|
|
552
575
|
agent_id: { type: "string" },
|
|
553
576
|
todo_id: { type: "string" },
|
|
@@ -561,6 +584,7 @@ const VISIBLE_TOOLS = [
|
|
|
561
584
|
description: "Hard-deletes a todo and any comments on it. Use sparingly: prefer drop_todo so history is preserved. agent_id required for the audit log.",
|
|
562
585
|
inputSchema: {
|
|
563
586
|
type: "object",
|
|
587
|
+
additionalProperties: false,
|
|
564
588
|
properties: {
|
|
565
589
|
agent_id: { type: "string" },
|
|
566
590
|
todo_id: { type: "string" },
|
|
@@ -574,6 +598,7 @@ const VISIBLE_TOOLS = [
|
|
|
574
598
|
description: "Returns todos for this tenant, optionally filtered by status. Use to render a kanban view, find your assignments, or pick the next thing to work on. agent_id required.",
|
|
575
599
|
inputSchema: {
|
|
576
600
|
type: "object",
|
|
601
|
+
additionalProperties: false,
|
|
577
602
|
properties: {
|
|
578
603
|
agent_id: { type: "string" },
|
|
579
604
|
status: { type: "string", enum: ["open", "in_progress", "done", "dropped"], description: "Optional filter" },
|
|
@@ -589,6 +614,7 @@ const VISIBLE_TOOLS = [
|
|
|
589
614
|
description: "Drops a new idea into the Fishbowl Ideas board so the pack can react and vote. Use when something is too speculative for a todo but worth capturing. agent_id required. Posts an 'idea-created' Fishbowl event.",
|
|
590
615
|
inputSchema: {
|
|
591
616
|
type: "object",
|
|
617
|
+
additionalProperties: false,
|
|
592
618
|
properties: {
|
|
593
619
|
agent_id: { type: "string" },
|
|
594
620
|
title: { type: "string", description: "Short title (max 200 chars)" },
|
|
@@ -603,6 +629,7 @@ const VISIBLE_TOOLS = [
|
|
|
603
629
|
description: "Edit an idea's title, description, or status ('proposed', 'voting', 'locked', 'parked', 'rejected'). agent_id required.",
|
|
604
630
|
inputSchema: {
|
|
605
631
|
type: "object",
|
|
632
|
+
additionalProperties: false,
|
|
606
633
|
properties: {
|
|
607
634
|
agent_id: { type: "string" },
|
|
608
635
|
idea_id: { type: "string" },
|
|
@@ -619,6 +646,7 @@ const VISIBLE_TOOLS = [
|
|
|
619
646
|
description: "Cast or change your vote on an idea ('up' or 'down'). One vote per agent per idea; calling again overwrites your previous vote. agent_id required.",
|
|
620
647
|
inputSchema: {
|
|
621
648
|
type: "object",
|
|
649
|
+
additionalProperties: false,
|
|
622
650
|
properties: {
|
|
623
651
|
agent_id: { type: "string" },
|
|
624
652
|
idea_id: { type: "string" },
|
|
@@ -633,6 +661,7 @@ const VISIBLE_TOOLS = [
|
|
|
633
661
|
description: "Returns ideas for this tenant sorted by score (upvotes minus downvotes) desc, optionally filtered by status. agent_id required.",
|
|
634
662
|
inputSchema: {
|
|
635
663
|
type: "object",
|
|
664
|
+
additionalProperties: false,
|
|
636
665
|
properties: {
|
|
637
666
|
agent_id: { type: "string" },
|
|
638
667
|
status: { type: "string", enum: ["proposed", "voting", "locked", "parked", "rejected"] },
|
|
@@ -647,6 +676,7 @@ const VISIBLE_TOOLS = [
|
|
|
647
676
|
description: "Converts an idea into a tracked todo and locks the idea. Requires either net upvotes >= 1, or admin caller. Sets idea.status='locked' and idea.promoted_to_todo_id. Posts an 'idea-promoted' Fishbowl event. agent_id required.",
|
|
648
677
|
inputSchema: {
|
|
649
678
|
type: "object",
|
|
679
|
+
additionalProperties: false,
|
|
650
680
|
properties: {
|
|
651
681
|
agent_id: { type: "string" },
|
|
652
682
|
idea_id: { type: "string" },
|
|
@@ -662,6 +692,7 @@ const VISIBLE_TOOLS = [
|
|
|
662
692
|
description: "Adds a comment to a Fishbowl todo or idea. Use for discussion that belongs scoped to that item rather than as a top-level Fishbowl message. target_kind is 'todo' or 'idea'. agent_id required.",
|
|
663
693
|
inputSchema: {
|
|
664
694
|
type: "object",
|
|
695
|
+
additionalProperties: false,
|
|
665
696
|
properties: {
|
|
666
697
|
agent_id: { type: "string" },
|
|
667
698
|
target_kind: { type: "string", enum: ["todo", "idea"] },
|
|
@@ -677,6 +708,7 @@ const VISIBLE_TOOLS = [
|
|
|
677
708
|
description: "Returns comments on a specific Fishbowl todo or idea, in chronological order. agent_id required.",
|
|
678
709
|
inputSchema: {
|
|
679
710
|
type: "object",
|
|
711
|
+
additionalProperties: false,
|
|
680
712
|
properties: {
|
|
681
713
|
agent_id: { type: "string" },
|
|
682
714
|
target_kind: { type: "string", enum: ["todo", "idea"] },
|
|
@@ -702,6 +734,7 @@ const VISIBLE_TOOLS = [
|
|
|
702
734
|
"Recommended start-of-session loop: (1) call read_messages to catch up on Fishbowl (you're doing this now), (2) check mentions[] for anything addressed to you, (3) call set_my_status to declare you're back online and set next_checkin_at if you expect to be away again.",
|
|
703
735
|
inputSchema: {
|
|
704
736
|
type: "object",
|
|
737
|
+
additionalProperties: false,
|
|
705
738
|
properties: {
|
|
706
739
|
agent_id: {
|
|
707
740
|
type: "string",
|
|
@@ -738,6 +771,7 @@ const DIRECT_TOOLS = [
|
|
|
738
771
|
description: "Shorten a URL using UnClick. Returns a short URL and its code.",
|
|
739
772
|
inputSchema: {
|
|
740
773
|
type: "object",
|
|
774
|
+
additionalProperties: false,
|
|
741
775
|
properties: {
|
|
742
776
|
url: { type: "string", description: "The URL to shorten" },
|
|
743
777
|
},
|
|
@@ -749,6 +783,7 @@ const DIRECT_TOOLS = [
|
|
|
749
783
|
description: "Generate a QR code from text or a URL. Returns base64-encoded PNG or SVG.",
|
|
750
784
|
inputSchema: {
|
|
751
785
|
type: "object",
|
|
786
|
+
additionalProperties: false,
|
|
752
787
|
properties: {
|
|
753
788
|
text: { type: "string", description: "Text or URL to encode in the QR code" },
|
|
754
789
|
format: { type: "string", enum: ["png", "svg"], default: "png" },
|
|
@@ -762,6 +797,7 @@ const DIRECT_TOOLS = [
|
|
|
762
797
|
description: "Compute a cryptographic hash (MD5, SHA1, SHA256, SHA512) of text.",
|
|
763
798
|
inputSchema: {
|
|
764
799
|
type: "object",
|
|
800
|
+
additionalProperties: false,
|
|
765
801
|
properties: {
|
|
766
802
|
text: { type: "string" },
|
|
767
803
|
algorithm: {
|
|
@@ -778,6 +814,7 @@ const DIRECT_TOOLS = [
|
|
|
778
814
|
description: "Transform text case: upper, lower, title, sentence, camelCase, snake_case, kebab-case, PascalCase.",
|
|
779
815
|
inputSchema: {
|
|
780
816
|
type: "object",
|
|
817
|
+
additionalProperties: false,
|
|
781
818
|
properties: {
|
|
782
819
|
text: { type: "string" },
|
|
783
820
|
to: {
|
|
@@ -793,6 +830,7 @@ const DIRECT_TOOLS = [
|
|
|
793
830
|
description: "Validate an email address format.",
|
|
794
831
|
inputSchema: {
|
|
795
832
|
type: "object",
|
|
833
|
+
additionalProperties: false,
|
|
796
834
|
properties: {
|
|
797
835
|
email: { type: "string" },
|
|
798
836
|
},
|
|
@@ -804,6 +842,7 @@ const DIRECT_TOOLS = [
|
|
|
804
842
|
description: "Validate a URL format, optionally check if it's reachable.",
|
|
805
843
|
inputSchema: {
|
|
806
844
|
type: "object",
|
|
845
|
+
additionalProperties: false,
|
|
807
846
|
properties: {
|
|
808
847
|
url: { type: "string" },
|
|
809
848
|
check_reachable: { type: "boolean", default: false },
|
|
@@ -816,6 +855,7 @@ const DIRECT_TOOLS = [
|
|
|
816
855
|
description: "Resize an image (provided as base64) to specified dimensions.",
|
|
817
856
|
inputSchema: {
|
|
818
857
|
type: "object",
|
|
858
|
+
additionalProperties: false,
|
|
819
859
|
properties: {
|
|
820
860
|
image: { type: "string", description: "Base64-encoded image (with or without data: prefix)" },
|
|
821
861
|
width: { type: "number" },
|
|
@@ -834,6 +874,7 @@ const DIRECT_TOOLS = [
|
|
|
834
874
|
description: "Parse a CSV string into a JSON array of rows.",
|
|
835
875
|
inputSchema: {
|
|
836
876
|
type: "object",
|
|
877
|
+
additionalProperties: false,
|
|
837
878
|
properties: {
|
|
838
879
|
csv: { type: "string" },
|
|
839
880
|
header: { type: "boolean", default: true },
|
|
@@ -847,9 +888,17 @@ const DIRECT_TOOLS = [
|
|
|
847
888
|
description: "Format / pretty-print a JSON string.",
|
|
848
889
|
inputSchema: {
|
|
849
890
|
type: "object",
|
|
891
|
+
additionalProperties: false,
|
|
850
892
|
properties: {
|
|
851
893
|
json: { type: "string" },
|
|
852
|
-
indent: {
|
|
894
|
+
indent: {
|
|
895
|
+
oneOf: [
|
|
896
|
+
{ type: "number", enum: [2, 4] },
|
|
897
|
+
{ type: "string", enum: ["tab"] },
|
|
898
|
+
],
|
|
899
|
+
description: "2, 4, or 'tab'",
|
|
900
|
+
default: 2,
|
|
901
|
+
},
|
|
853
902
|
},
|
|
854
903
|
required: ["json"],
|
|
855
904
|
},
|
|
@@ -859,6 +908,7 @@ const DIRECT_TOOLS = [
|
|
|
859
908
|
description: "Encode or decode text. Supports base64, URL, HTML, and hex.",
|
|
860
909
|
inputSchema: {
|
|
861
910
|
type: "object",
|
|
911
|
+
additionalProperties: false,
|
|
862
912
|
properties: {
|
|
863
913
|
text: { type: "string" },
|
|
864
914
|
operation: {
|
|
@@ -879,6 +929,7 @@ const DIRECT_TOOLS = [
|
|
|
879
929
|
description: "Generate one or more random UUIDs (v4).",
|
|
880
930
|
inputSchema: {
|
|
881
931
|
type: "object",
|
|
932
|
+
additionalProperties: false,
|
|
882
933
|
properties: {
|
|
883
934
|
count: { type: "number", minimum: 1, maximum: 100, default: 1 },
|
|
884
935
|
},
|
|
@@ -889,6 +940,7 @@ const DIRECT_TOOLS = [
|
|
|
889
940
|
description: "Generate a secure random password.",
|
|
890
941
|
inputSchema: {
|
|
891
942
|
type: "object",
|
|
943
|
+
additionalProperties: false,
|
|
892
944
|
properties: {
|
|
893
945
|
length: { type: "number", minimum: 4, maximum: 512, default: 16 },
|
|
894
946
|
uppercase: { type: "boolean", default: true },
|
|
@@ -903,6 +955,7 @@ const DIRECT_TOOLS = [
|
|
|
903
955
|
description: "Convert a cron expression to a human-readable description and get next occurrences.",
|
|
904
956
|
inputSchema: {
|
|
905
957
|
type: "object",
|
|
958
|
+
additionalProperties: false,
|
|
906
959
|
properties: {
|
|
907
960
|
expression: { type: "string", description: "e.g. '0 9 * * 1-5' (weekdays at 9am)" },
|
|
908
961
|
next_count: { type: "number", minimum: 1, maximum: 10, default: 5 },
|
|
@@ -915,6 +968,7 @@ const DIRECT_TOOLS = [
|
|
|
915
968
|
description: "Parse an IP address -- get decimal, binary, hex, and type (private/loopback/multicast).",
|
|
916
969
|
inputSchema: {
|
|
917
970
|
type: "object",
|
|
971
|
+
additionalProperties: false,
|
|
918
972
|
properties: {
|
|
919
973
|
ip: { type: "string" },
|
|
920
974
|
},
|
|
@@ -926,8 +980,24 @@ const DIRECT_TOOLS = [
|
|
|
926
980
|
description: "Convert a color between hex, RGB, HSL, and HSV formats.",
|
|
927
981
|
inputSchema: {
|
|
928
982
|
type: "object",
|
|
983
|
+
additionalProperties: false,
|
|
929
984
|
properties: {
|
|
930
985
|
color: {
|
|
986
|
+
oneOf: [
|
|
987
|
+
{ type: "string" },
|
|
988
|
+
{
|
|
989
|
+
type: "object",
|
|
990
|
+
additionalProperties: false,
|
|
991
|
+
properties: { r: { type: "number" }, g: { type: "number" }, b: { type: "number" } },
|
|
992
|
+
required: ["r", "g", "b"],
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
type: "object",
|
|
996
|
+
additionalProperties: false,
|
|
997
|
+
properties: { h: { type: "number" }, s: { type: "number" }, l: { type: "number" } },
|
|
998
|
+
required: ["h", "s", "l"],
|
|
999
|
+
},
|
|
1000
|
+
],
|
|
931
1001
|
description: "Color as hex string (e.g. '#ff6b6b'), RGB object {r,g,b}, or HSL object {h,s,l}",
|
|
932
1002
|
},
|
|
933
1003
|
},
|
|
@@ -939,6 +1009,7 @@ const DIRECT_TOOLS = [
|
|
|
939
1009
|
description: "Test a regex pattern against text and get all matches with groups.",
|
|
940
1010
|
inputSchema: {
|
|
941
1011
|
type: "object",
|
|
1012
|
+
additionalProperties: false,
|
|
942
1013
|
properties: {
|
|
943
1014
|
pattern: { type: "string", description: "Regex pattern (no surrounding slashes)" },
|
|
944
1015
|
flags: { type: "string", description: "Flags like 'gi'", default: "" },
|
|
@@ -952,8 +1023,10 @@ const DIRECT_TOOLS = [
|
|
|
952
1023
|
description: "Convert a timestamp (ISO, Unix seconds, or Unix ms) to all common formats.",
|
|
953
1024
|
inputSchema: {
|
|
954
1025
|
type: "object",
|
|
1026
|
+
additionalProperties: false,
|
|
955
1027
|
properties: {
|
|
956
1028
|
timestamp: {
|
|
1029
|
+
oneOf: [{ type: "string" }, { type: "number" }],
|
|
957
1030
|
description: "ISO string, Unix seconds (e.g. 1700000000), or Unix ms (e.g. 1700000000000)",
|
|
958
1031
|
},
|
|
959
1032
|
},
|
|
@@ -965,6 +1038,7 @@ const DIRECT_TOOLS = [
|
|
|
965
1038
|
description: "Compare two strings and return a unified diff showing what changed.",
|
|
966
1039
|
inputSchema: {
|
|
967
1040
|
type: "object",
|
|
1041
|
+
additionalProperties: false,
|
|
968
1042
|
properties: {
|
|
969
1043
|
a: { type: "string", description: "Original text" },
|
|
970
1044
|
b: { type: "string", description: "New text" },
|
|
@@ -977,6 +1051,7 @@ const DIRECT_TOOLS = [
|
|
|
977
1051
|
description: "Store a value in the UnClick key-value store with optional TTL.",
|
|
978
1052
|
inputSchema: {
|
|
979
1053
|
type: "object",
|
|
1054
|
+
additionalProperties: false,
|
|
980
1055
|
properties: {
|
|
981
1056
|
key: { type: "string" },
|
|
982
1057
|
value: { description: "Any JSON-serializable value" },
|
|
@@ -990,6 +1065,7 @@ const DIRECT_TOOLS = [
|
|
|
990
1065
|
description: "Retrieve a value from the UnClick key-value store.",
|
|
991
1066
|
inputSchema: {
|
|
992
1067
|
type: "object",
|
|
1068
|
+
additionalProperties: false,
|
|
993
1069
|
properties: {
|
|
994
1070
|
key: { type: "string" },
|
|
995
1071
|
},
|
|
@@ -1004,6 +1080,7 @@ const DIRECT_TOOLS = [
|
|
|
1004
1080
|
"timeout/503 → high, 4xx/invalid → low, everything else → medium.",
|
|
1005
1081
|
inputSchema: {
|
|
1006
1082
|
type: "object",
|
|
1083
|
+
additionalProperties: false,
|
|
1007
1084
|
properties: {
|
|
1008
1085
|
tool_name: {
|
|
1009
1086
|
type: "string",
|
|
@@ -1030,6 +1107,58 @@ const DIRECT_TOOLS = [
|
|
|
1030
1107
|
},
|
|
1031
1108
|
},
|
|
1032
1109
|
];
|
|
1110
|
+
const AjvCtor = (AjvModule.default ?? AjvModule);
|
|
1111
|
+
const ajv = new AjvCtor({
|
|
1112
|
+
allErrors: true,
|
|
1113
|
+
});
|
|
1114
|
+
const TOOL_INPUT_SCHEMAS = new Map();
|
|
1115
|
+
const TOOL_VALIDATORS = new Map();
|
|
1116
|
+
function registerToolInputSchema(tool) {
|
|
1117
|
+
if (tool.inputSchema)
|
|
1118
|
+
TOOL_INPUT_SCHEMAS.set(tool.name, tool.inputSchema);
|
|
1119
|
+
}
|
|
1120
|
+
for (const tool of [...INTERNAL_TOOLS, ...VISIBLE_TOOLS, ...DIRECT_TOOLS, ...ADDITIONAL_TOOLS]) {
|
|
1121
|
+
registerToolInputSchema(tool);
|
|
1122
|
+
}
|
|
1123
|
+
// Backwards-compatible memory tool names still dispatch directly, so they need
|
|
1124
|
+
// the same runtime guard as the newer visible names.
|
|
1125
|
+
for (const [alias, canonical] of Object.entries(MEMORY_TOOL_ALIASES)) {
|
|
1126
|
+
const schema = TOOL_INPUT_SCHEMAS.get(alias);
|
|
1127
|
+
if (schema)
|
|
1128
|
+
TOOL_INPUT_SCHEMAS.set(canonical, schema);
|
|
1129
|
+
}
|
|
1130
|
+
function formatAjvError(error) {
|
|
1131
|
+
const additionalProperty = error.keyword === "additionalProperties" &&
|
|
1132
|
+
error.params &&
|
|
1133
|
+
typeof error.params.additionalProperty === "string"
|
|
1134
|
+
? ` '${error.params.additionalProperty}'`
|
|
1135
|
+
: "";
|
|
1136
|
+
return {
|
|
1137
|
+
path: error.instancePath || error.dataPath || "/",
|
|
1138
|
+
message: `${error.message ?? "invalid input"}${additionalProperty}`,
|
|
1139
|
+
keyword: error.keyword,
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
export function validateToolArgumentsForRuntime(toolName, args) {
|
|
1143
|
+
const schema = TOOL_INPUT_SCHEMAS.get(toolName);
|
|
1144
|
+
if (!schema)
|
|
1145
|
+
return null;
|
|
1146
|
+
let validate = TOOL_VALIDATORS.get(toolName);
|
|
1147
|
+
if (!validate) {
|
|
1148
|
+
const compiled = ajv.compile(schema);
|
|
1149
|
+
TOOL_VALIDATORS.set(toolName, compiled);
|
|
1150
|
+
validate = compiled;
|
|
1151
|
+
}
|
|
1152
|
+
const validator = validate;
|
|
1153
|
+
if (validator(args))
|
|
1154
|
+
return null;
|
|
1155
|
+
return {
|
|
1156
|
+
code: "validation_error",
|
|
1157
|
+
tool: toolName,
|
|
1158
|
+
message: `Invalid arguments for ${toolName}`,
|
|
1159
|
+
details: (validator.errors ?? []).map(formatAjvError),
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1033
1162
|
const DIRECT_HANDLERS = {
|
|
1034
1163
|
unclick_shorten_url: (c, a) => c.call("POST", "/v1/shorten", a),
|
|
1035
1164
|
unclick_generate_qr: (c, a) => c.call("POST", "/v1/qr", a),
|
|
@@ -1105,6 +1234,8 @@ export function createServer() {
|
|
|
1105
1234
|
" 'facts about me', 'who am I', or references any past work.",
|
|
1106
1235
|
" 3. SAVE -- call `save_fact` the moment the user shares anything",
|
|
1107
1236
|
" worth keeping: name, preferences, decisions, corrections.",
|
|
1237
|
+
" For solved client/tool issues, use category `troubleshooting`",
|
|
1238
|
+
" and write `Issue: <symptom>. Solution: <fix>`.",
|
|
1108
1239
|
" Use `save_identity` for standing rules that apply every",
|
|
1109
1240
|
" session (role, timezone, stack, workflow).",
|
|
1110
1241
|
" 4. END -- call `save_session` before closing. Record decisions made,",
|
|
@@ -1128,6 +1259,13 @@ export function createServer() {
|
|
|
1128
1259
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1129
1260
|
const { name, arguments: rawArgs } = request.params;
|
|
1130
1261
|
const args = (rawArgs ?? {});
|
|
1262
|
+
const validationError = validateToolArgumentsForRuntime(name, args);
|
|
1263
|
+
if (validationError) {
|
|
1264
|
+
return {
|
|
1265
|
+
content: [{ type: "text", text: JSON.stringify(validationError, null, 2) }],
|
|
1266
|
+
isError: true,
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1131
1269
|
// Fire-and-forget Umami event for tool-usage stats. Never awaited.
|
|
1132
1270
|
trackToolCall(name);
|
|
1133
1271
|
try {
|