@matimo/core 0.1.0-alpha.12.1 → 0.1.0-alpha.13

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.
Files changed (111) hide show
  1. package/README.md +169 -8
  2. package/dist/approval/approval-handler.d.ts +5 -1
  3. package/dist/approval/approval-handler.d.ts.map +1 -1
  4. package/dist/approval/approval-handler.js +6 -0
  5. package/dist/approval/approval-handler.js.map +1 -1
  6. package/dist/core/schema.d.ts +29 -8
  7. package/dist/core/schema.d.ts.map +1 -1
  8. package/dist/core/schema.js +10 -3
  9. package/dist/core/schema.js.map +1 -1
  10. package/dist/core/skill-content-parser.d.ts +91 -0
  11. package/dist/core/skill-content-parser.d.ts.map +1 -0
  12. package/dist/core/skill-content-parser.js +248 -0
  13. package/dist/core/skill-content-parser.js.map +1 -0
  14. package/dist/core/skill-loader.d.ts +46 -0
  15. package/dist/core/skill-loader.d.ts.map +1 -0
  16. package/dist/core/skill-loader.js +310 -0
  17. package/dist/core/skill-loader.js.map +1 -0
  18. package/dist/core/skill-registry.d.ts +131 -0
  19. package/dist/core/skill-registry.d.ts.map +1 -0
  20. package/dist/core/skill-registry.js +316 -0
  21. package/dist/core/skill-registry.js.map +1 -0
  22. package/dist/core/tfidf-embedding.d.ts +45 -0
  23. package/dist/core/tfidf-embedding.d.ts.map +1 -0
  24. package/dist/core/tfidf-embedding.js +199 -0
  25. package/dist/core/tfidf-embedding.js.map +1 -0
  26. package/dist/core/types.d.ts +155 -6
  27. package/dist/core/types.d.ts.map +1 -1
  28. package/dist/errors/matimo-error.d.ts +3 -1
  29. package/dist/errors/matimo-error.d.ts.map +1 -1
  30. package/dist/errors/matimo-error.js +2 -0
  31. package/dist/errors/matimo-error.js.map +1 -1
  32. package/dist/index.d.ts +20 -3
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +14 -1
  35. package/dist/index.js.map +1 -1
  36. package/dist/integrations/langchain.d.ts +55 -0
  37. package/dist/integrations/langchain.d.ts.map +1 -1
  38. package/dist/integrations/langchain.js +66 -0
  39. package/dist/integrations/langchain.js.map +1 -1
  40. package/dist/logging/winston-logger.d.ts.map +1 -1
  41. package/dist/logging/winston-logger.js +9 -1
  42. package/dist/logging/winston-logger.js.map +1 -1
  43. package/dist/matimo-instance.d.ts +171 -6
  44. package/dist/matimo-instance.d.ts.map +1 -1
  45. package/dist/matimo-instance.js +602 -13
  46. package/dist/matimo-instance.js.map +1 -1
  47. package/dist/mcp/mcp-server.d.ts +23 -0
  48. package/dist/mcp/mcp-server.d.ts.map +1 -1
  49. package/dist/mcp/mcp-server.js +119 -8
  50. package/dist/mcp/mcp-server.js.map +1 -1
  51. package/dist/mcp/tool-converter.d.ts.map +1 -1
  52. package/dist/mcp/tool-converter.js +10 -1
  53. package/dist/mcp/tool-converter.js.map +1 -1
  54. package/dist/policy/approval-manifest.d.ts +74 -0
  55. package/dist/policy/approval-manifest.d.ts.map +1 -0
  56. package/dist/policy/approval-manifest.js +178 -0
  57. package/dist/policy/approval-manifest.js.map +1 -0
  58. package/dist/policy/content-validator.d.ts +19 -0
  59. package/dist/policy/content-validator.d.ts.map +1 -0
  60. package/dist/policy/content-validator.js +196 -0
  61. package/dist/policy/content-validator.js.map +1 -0
  62. package/dist/policy/default-policy.d.ts +46 -0
  63. package/dist/policy/default-policy.d.ts.map +1 -0
  64. package/dist/policy/default-policy.js +241 -0
  65. package/dist/policy/default-policy.js.map +1 -0
  66. package/dist/policy/events.d.ts +71 -0
  67. package/dist/policy/events.d.ts.map +1 -0
  68. package/dist/policy/events.js +8 -0
  69. package/dist/policy/events.js.map +1 -0
  70. package/dist/policy/index.d.ts +13 -0
  71. package/dist/policy/index.d.ts.map +1 -0
  72. package/dist/policy/index.js +9 -0
  73. package/dist/policy/index.js.map +1 -0
  74. package/dist/policy/integrity-tracker.d.ts +62 -0
  75. package/dist/policy/integrity-tracker.d.ts.map +1 -0
  76. package/dist/policy/integrity-tracker.js +79 -0
  77. package/dist/policy/integrity-tracker.js.map +1 -0
  78. package/dist/policy/policy-loader.d.ts +58 -0
  79. package/dist/policy/policy-loader.d.ts.map +1 -0
  80. package/dist/policy/policy-loader.js +153 -0
  81. package/dist/policy/policy-loader.js.map +1 -0
  82. package/dist/policy/risk-classifier.d.ts +18 -0
  83. package/dist/policy/risk-classifier.d.ts.map +1 -0
  84. package/dist/policy/risk-classifier.js +43 -0
  85. package/dist/policy/risk-classifier.js.map +1 -0
  86. package/dist/policy/types.d.ts +126 -0
  87. package/dist/policy/types.d.ts.map +1 -0
  88. package/dist/policy/types.js +8 -0
  89. package/dist/policy/types.js.map +1 -0
  90. package/package.json +2 -2
  91. package/tools/matimo_approve_tool/definition.yaml +36 -0
  92. package/tools/matimo_approve_tool/matimo_approve_tool.ts +90 -0
  93. package/tools/matimo_create_skill/definition.yaml +46 -0
  94. package/tools/matimo_create_skill/matimo_create_skill.ts +75 -0
  95. package/tools/matimo_create_tool/definition.yaml +48 -0
  96. package/tools/matimo_create_tool/matimo_create_tool.ts +137 -0
  97. package/tools/matimo_get_skill/definition.yaml +60 -0
  98. package/tools/matimo_get_skill/matimo_get_skill.ts +182 -0
  99. package/tools/matimo_get_tool_status/definition.yaml +42 -0
  100. package/tools/matimo_get_tool_status/matimo_get_tool_status.ts +101 -0
  101. package/tools/matimo_list_skills/definition.yaml +52 -0
  102. package/tools/matimo_list_skills/matimo_list_skills.ts +138 -0
  103. package/tools/matimo_list_user_tools/definition.yaml +32 -0
  104. package/tools/matimo_list_user_tools/matimo_list_user_tools.ts +74 -0
  105. package/tools/matimo_reload_tools/definition.yaml +35 -0
  106. package/tools/matimo_reload_tools/matimo_reload_tools.ts +29 -0
  107. package/tools/matimo_validate_skill/definition.yaml +43 -0
  108. package/tools/matimo_validate_skill/matimo_validate_skill.ts +137 -0
  109. package/tools/matimo_validate_tool/definition.yaml +34 -0
  110. package/tools/matimo_validate_tool/matimo_validate_tool.ts +168 -0
  111. package/tools/shared/skill-validation.ts +335 -0
@@ -0,0 +1,48 @@
1
+ name: matimo_create_tool
2
+ version: '1.0.0'
3
+ description: Create a new tool definition on disk. Validates the YAML, forces draft status and requires_approval, and writes the tool to the target directory.
4
+ requires_approval: true
5
+ tags:
6
+ - matimo
7
+ - meta
8
+ - creation
9
+
10
+ parameters:
11
+ name:
12
+ type: string
13
+ required: true
14
+ description: Name for the new tool (snake_case, no path traversal)
15
+ yaml_content:
16
+ type: string
17
+ required: true
18
+ description: The YAML content of the tool definition
19
+ target_dir:
20
+ type: string
21
+ required: false
22
+ description: Directory to create the tool in (default ./matimo-tools)
23
+ proposed_by:
24
+ type: string
25
+ required: false
26
+ description: Identifier of who proposed this tool
27
+ justification:
28
+ type: string
29
+ required: false
30
+ description: Reason for creating this tool
31
+
32
+ execution:
33
+ type: function
34
+ code: './matimo_create_tool.ts'
35
+
36
+ output_schema:
37
+ type: object
38
+ properties:
39
+ success:
40
+ type: boolean
41
+ path:
42
+ type: string
43
+ riskLevel:
44
+ type: string
45
+ status:
46
+ type: string
47
+ message:
48
+ type: string
@@ -0,0 +1,137 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import {
5
+ validateToolDefinition,
6
+ validateToolContent,
7
+ classifyRisk,
8
+ getTierForTool,
9
+ getGlobalMatimoLogger,
10
+ } from '@matimo/core';
11
+ import type { Violation } from '@matimo/core';
12
+
13
+ interface CreateParams {
14
+ name: string;
15
+ yaml_content: string;
16
+ target_dir?: string;
17
+ proposed_by?: string;
18
+ justification?: string;
19
+ }
20
+
21
+ interface CreateResult {
22
+ success: boolean;
23
+ path?: string;
24
+ riskLevel?: string;
25
+ status?: string;
26
+ /** Signals what approval state the tool is in after creation.
27
+ * - `pending`: requires human approval before execution (untrusted source, non-trivial risk)
28
+ * - `auto-approved`: low-risk read-only GET tool, can be used immediately
29
+ * - `approved`: manually approved (set externally by matimo_approve_tool)
30
+ * - `rejected`: policy blocked the tool
31
+ */
32
+ approvalState?: 'pending' | 'auto-approved' | 'approved' | 'rejected';
33
+ message: string;
34
+ errors?: string[];
35
+ }
36
+
37
+ const UNSAFE_NAME_PATTERN = /[/\\]|\.\.|[\x00-\x1f]/;
38
+
39
+ export default async function matimoCreateTool(
40
+ params: CreateParams,
41
+ ): Promise<CreateResult> {
42
+ const logger = getGlobalMatimoLogger();
43
+ const targetDir = params.target_dir || './matimo-tools';
44
+
45
+ // Step 1: Sanitize name
46
+ if (!params.name || params.name.trim().length === 0) {
47
+ return { success: false, message: 'Tool name is required' };
48
+ }
49
+ if (UNSAFE_NAME_PATTERN.test(params.name)) {
50
+ return { success: false, message: 'Tool name contains invalid characters (path traversal, backslash, or control characters)' };
51
+ }
52
+ if (params.name.startsWith('matimo_')) {
53
+ return { success: false, message: 'Tool name cannot start with reserved namespace "matimo_"' };
54
+ }
55
+
56
+ // Step 2: Parse YAML
57
+ let parsed: Record<string, unknown>;
58
+ try {
59
+ parsed = yaml.load(params.yaml_content) as Record<string, unknown>;
60
+ if (!parsed || typeof parsed !== 'object') {
61
+ return { success: false, message: 'YAML must parse to an object' };
62
+ }
63
+ } catch (err) {
64
+ return { success: false, message: `YAML parse error: ${(err as Error).message}` };
65
+ }
66
+
67
+ // Step 3: Force safety fields
68
+ parsed.name = params.name;
69
+ parsed.requires_approval = true;
70
+ parsed.status = 'draft';
71
+
72
+ // Step 4: Validate against schema
73
+ const yamlStr = yaml.dump(parsed);
74
+ try {
75
+ validateToolDefinition(yaml.load(yamlStr));
76
+ } catch (err) {
77
+ return { success: false, message: `Schema validation failed: ${(err as Error).message}` };
78
+ }
79
+
80
+ // Step 5: Run content validator
81
+ const tool = validateToolDefinition(yaml.load(yamlStr));
82
+ const validation = validateToolContent(tool, { source: 'untrusted' });
83
+ const criticalOrHigh = validation.violations.filter(
84
+ (v: Violation) => v.severity === 'critical' || v.severity === 'high',
85
+ );
86
+ if (criticalOrHigh.length > 0) {
87
+ return {
88
+ success: false,
89
+ message: 'Tool failed policy validation',
90
+ errors: criticalOrHigh.map((v: Violation) => `[${v.severity}] ${v.rule}: ${v.message}`),
91
+ };
92
+ }
93
+
94
+ // Step 6: Classify risk + tier
95
+ const riskLevel = classifyRisk(tool);
96
+ const tier = getTierForTool(tool);
97
+ const approvalState: CreateResult['approvalState'] = tier === 'auto' ? 'auto-approved' : 'pending';
98
+
99
+ // Step 7: Write to disk
100
+ const toolDirPath = path.resolve(targetDir, params.name);
101
+ fs.mkdirSync(toolDirPath, { recursive: true });
102
+
103
+ let header = '';
104
+ if (params.proposed_by) {
105
+ header += `# Proposed by: ${params.proposed_by}\n`;
106
+ }
107
+ if (params.justification) {
108
+ header += `# Justification: ${params.justification}\n`;
109
+ }
110
+ if (header) {
111
+ header += '\n';
112
+ }
113
+
114
+ const filePath = path.join(toolDirPath, 'definition.yaml');
115
+ fs.writeFileSync(filePath, header + yamlStr, 'utf-8');
116
+
117
+ logger.info('matimo_create_tool: tool created', {
118
+ name: params.name,
119
+ path: filePath,
120
+ riskLevel,
121
+ approvalState,
122
+ });
123
+
124
+ const message =
125
+ approvalState === 'auto-approved'
126
+ ? 'Tool created and auto-approved (low-risk read-only). Ready for use.'
127
+ : 'Tool created as draft. Requires approval before execution. Use matimo_approve_tool to promote.';
128
+
129
+ return {
130
+ success: true,
131
+ path: filePath,
132
+ riskLevel,
133
+ status: 'draft',
134
+ approvalState,
135
+ message,
136
+ };
137
+ }
@@ -0,0 +1,60 @@
1
+ name: matimo_get_skill
2
+ version: '1.0.0'
3
+ description: >-
4
+ Level 2 activation / Level 3 resource access — read a skill's SKILL.md
5
+ content and metadata, or read a specific bundled resource file (scripts,
6
+ references, assets). Returns frontmatter fields, markdown instructions, and
7
+ a listing of bundled resources per the Agent Skills specification.
8
+ requires_approval: false
9
+ tags:
10
+ - matimo
11
+ - meta
12
+ - skill
13
+ parameters:
14
+ name:
15
+ type: string
16
+ required: true
17
+ description: Name of the skill to retrieve (must match the skill's directory name)
18
+ skills_dir:
19
+ type: string
20
+ required: false
21
+ description: Directory containing skills (default ./matimo-tools/skills)
22
+ file:
23
+ type: string
24
+ required: false
25
+ description: >-
26
+ Optional path to a bundled resource file within the skill directory
27
+ (e.g. "scripts/extract.py" or "references/FORMS.md"). If provided,
28
+ returns that file's content instead of SKILL.md (Level 3 access).
29
+
30
+ execution:
31
+ type: function
32
+ code: './matimo_get_skill.ts'
33
+
34
+ output_schema:
35
+ type: object
36
+ properties:
37
+ success:
38
+ type: boolean
39
+ name:
40
+ type: string
41
+ description:
42
+ type: string
43
+ content:
44
+ type: string
45
+ description: Full content of SKILL.md or the requested resource file
46
+ path:
47
+ type: string
48
+ license:
49
+ type: string
50
+ compatibility:
51
+ type: string
52
+ metadata:
53
+ type: object
54
+ resources:
55
+ type: object
56
+ description: >-
57
+ Bundled resources listing (scripts, references, assets, other) —
58
+ only included when reading SKILL.md, not when reading a specific file
59
+ message:
60
+ type: string
@@ -0,0 +1,182 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { getGlobalMatimoLogger, getGlobalMatimoInstance, ToolLoader, SkillSummary } from '@matimo/core';
4
+ import { parseSkillContent, listBundledResources, type BundledResources } from '../shared/skill-validation';
5
+
6
+ interface GetSkillParams {
7
+ name: string;
8
+ skills_dir?: string;
9
+ file?: string;
10
+ }
11
+
12
+ interface GetSkillResult {
13
+ success: boolean;
14
+ name?: string;
15
+ description?: string;
16
+ content?: string;
17
+ path?: string;
18
+ message: string;
19
+ license?: string;
20
+ compatibility?: string;
21
+ metadata?: Record<string, string>;
22
+ resources?: BundledResources;
23
+ }
24
+
25
+ /** Path traversal detection — defense-in-depth. */
26
+ const UNSAFE_NAME_PATTERN = /[/\\]|\.\.|[\x00-\x1f]/;
27
+
28
+ /**
29
+ * Helper: Find skill directory using auto-discovery (like matimo_list_skills)
30
+ */
31
+ function findSkillDir(skillName: string, explicitSkillsDir?: string): string | null {
32
+ // Try explicit skills_dir first
33
+ if (explicitSkillsDir) {
34
+ const skillPath = path.join(explicitSkillsDir, skillName, 'SKILL.md');
35
+ if (fs.existsSync(skillPath)) {
36
+ return path.join(explicitSkillsDir, skillName);
37
+ }
38
+ }
39
+
40
+ // Try MatimoInstance
41
+ try {
42
+ const matimo = getGlobalMatimoInstance();
43
+ if (matimo) {
44
+ const skills = matimo.listSkills();
45
+ const found = skills?.find((s) => s.name === skillName);
46
+ if (found) {
47
+ const skillPath = (found as SkillSummary & { _path?: string })._path;
48
+ if (skillPath && fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
49
+ return skillPath;
50
+ }
51
+ }
52
+ }
53
+ } catch {
54
+ // Fall through
55
+ }
56
+
57
+ // Auto-discover from @matimo/* packages
58
+ try {
59
+ const toolLoader = new ToolLoader();
60
+ const discoveredPaths = toolLoader.autoDiscoverPackages();
61
+ for (const toolPath of discoveredPaths) {
62
+ const pkgDir = path.dirname(toolPath);
63
+ const skillPath = path.join(pkgDir, 'skills', skillName, 'SKILL.md');
64
+ if (fs.existsSync(skillPath)) {
65
+ return path.join(pkgDir, 'skills', skillName);
66
+ }
67
+ }
68
+ } catch {
69
+ // Fall through
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Read a skill's content by name — Level 2 activation (SKILL.md) or
77
+ * Level 3 resource access (bundled file).
78
+ *
79
+ * When called without `file`, returns SKILL.md content + metadata + resource listing.
80
+ * When called with `file`, returns the contents of that bundled resource file.
81
+ *
82
+ * Skills are discovered in this order (priority):
83
+ * 1. Explicit skills_dir if provided
84
+ * 2. Global MatimoInstance (if initialized)
85
+ * 3. Auto-discovered @matimo/* packages
86
+ *
87
+ * @see https://agentskills.io/specification
88
+ */
89
+ export default async function matimoGetSkill(
90
+ params: GetSkillParams,
91
+ ): Promise<GetSkillResult> {
92
+ const logger = getGlobalMatimoLogger();
93
+
94
+ if (!params.name || params.name.trim().length === 0) {
95
+ return { success: false, message: 'Skill name is required' };
96
+ }
97
+
98
+ if (UNSAFE_NAME_PATTERN.test(params.name)) {
99
+ return { success: false, message: 'Skill name contains invalid characters' };
100
+ }
101
+
102
+ // Find skill directory using auto-discovery
103
+ const skillDir = findSkillDir(params.name, params.skills_dir);
104
+ if (!skillDir) {
105
+ return { success: false, message: `Skill "${params.name}" not found` };
106
+ }
107
+
108
+ const skillPath = path.join(skillDir, 'SKILL.md');
109
+
110
+ // Level 3: Read a specific bundled resource file
111
+ if (params.file) {
112
+ // For file paths, allow forward slashes but reject path traversal
113
+ if (/\.\.|\\/u.test(params.file) || /[\x00-\x1f]/.test(params.file)) {
114
+ return { success: false, message: 'File path contains invalid characters' };
115
+ }
116
+
117
+ const resourcePath = path.join(skillDir, params.file);
118
+ // Verify the resolved path stays within the skill directory
119
+ const resolvedPath = path.resolve(resourcePath);
120
+ const resolvedSkillDir = path.resolve(skillDir);
121
+ if (!resolvedPath.startsWith(resolvedSkillDir + path.sep) && resolvedPath !== resolvedSkillDir) {
122
+ return { success: false, message: 'File path escapes the skill directory' };
123
+ }
124
+
125
+ if (!fs.existsSync(resourcePath)) {
126
+ return { success: false, message: `Resource file "${params.file}" not found in skill "${params.name}"` };
127
+ }
128
+
129
+ try {
130
+ const fileContent = fs.readFileSync(resourcePath, 'utf-8');
131
+ return {
132
+ success: true,
133
+ name: params.name,
134
+ content: fileContent,
135
+ path: resourcePath,
136
+ message: `Resource file "${params.file}" retrieved successfully.`,
137
+ };
138
+ } catch (err) {
139
+ return { success: false, message: `Failed to read resource file: ${(err as Error).message}` };
140
+ }
141
+ }
142
+
143
+ // Level 2: Read SKILL.md + metadata + resource listing
144
+ try {
145
+ const rawContent = fs.readFileSync(skillPath, 'utf-8');
146
+ const parseResult = parseSkillContent(rawContent);
147
+
148
+ const result: GetSkillResult = {
149
+ success: true,
150
+ name: params.name,
151
+ content: rawContent,
152
+ path: skillPath,
153
+ message: 'Skill retrieved successfully.',
154
+ };
155
+
156
+ if (parseResult.success && parseResult.parsed) {
157
+ const { frontmatter } = parseResult.parsed;
158
+ result.name = frontmatter.name || params.name;
159
+ result.description = frontmatter.description || '';
160
+ if (frontmatter.license) result.license = frontmatter.license;
161
+ if (frontmatter.compatibility) result.compatibility = frontmatter.compatibility;
162
+ if (frontmatter.metadata) result.metadata = frontmatter.metadata;
163
+ }
164
+
165
+ // List bundled resources (Level 3 discovery)
166
+ result.resources = listBundledResources(skillDir);
167
+
168
+ logger.info('matimo_get_skill: skill retrieved', {
169
+ name: params.name,
170
+ path: skillPath,
171
+ });
172
+
173
+ return result;
174
+ } catch (err) {
175
+ const errorMsg = (err as Error).message;
176
+ logger.error('matimo_get_skill: failed to read skill', {
177
+ name: params.name,
178
+ error: errorMsg,
179
+ });
180
+ return { success: false, message: `Failed to read skill: ${errorMsg}` };
181
+ }
182
+ }
@@ -0,0 +1,42 @@
1
+ name: matimo_get_tool_status
2
+ version: '1.0.0'
3
+ description: Get the current status, risk level, and approval state of a tool. Works for both pending (agent-created) and approved tools.
4
+ requires_approval: true
5
+ tags:
6
+ - matimo
7
+ - meta
8
+ - approval
9
+
10
+ parameters:
11
+ name:
12
+ type: string
13
+ required: true
14
+ description: Name of the tool to check
15
+ tool_dir:
16
+ type: string
17
+ required: false
18
+ description: Directory containing the tool (default ./matimo-tools)
19
+
20
+ execution:
21
+ type: function
22
+ code: './matimo_get_tool_status.ts'
23
+
24
+ output_schema:
25
+ type: object
26
+ properties:
27
+ found:
28
+ type: boolean
29
+ name:
30
+ type: string
31
+ status:
32
+ type: string
33
+ riskLevel:
34
+ type: string
35
+ approvalState:
36
+ type: string
37
+ approvedAt:
38
+ type: string
39
+ approvedBy:
40
+ type: string
41
+ message:
42
+ type: string
@@ -0,0 +1,101 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import {
5
+ validateToolDefinition,
6
+ classifyRisk,
7
+ getTierForTool,
8
+ ApprovalManifest,
9
+ getGlobalMatimoLogger,
10
+ } from '@matimo/core';
11
+
12
+ interface StatusParams {
13
+ name: string;
14
+ tool_dir?: string;
15
+ }
16
+
17
+ interface StatusResult {
18
+ found: boolean;
19
+ name?: string;
20
+ status?: string;
21
+ riskLevel?: string;
22
+ approvalState?: 'pending' | 'auto-approved' | 'approved' | 'rejected';
23
+ approvedAt?: string;
24
+ approvedBy?: string;
25
+ message: string;
26
+ }
27
+
28
+ export default async function matimoGetToolStatus(
29
+ params: StatusParams,
30
+ context?: { credentials?: Record<string, string> },
31
+ ): Promise<StatusResult> {
32
+ const logger = getGlobalMatimoLogger();
33
+ const toolDir = params.tool_dir || './matimo-tools';
34
+
35
+ const defPath = path.join(toolDir, params.name, 'definition.yaml');
36
+ if (!fs.existsSync(defPath)) {
37
+ logger.warn('matimo_get_tool_status: tool not found', { name: params.name, path: defPath });
38
+ return { found: false, message: `Tool "${params.name}" not found at ${defPath}` };
39
+ }
40
+
41
+ const yamlContent = fs.readFileSync(defPath, 'utf-8');
42
+
43
+ let tool;
44
+ try {
45
+ const parsed = yaml.load(yamlContent);
46
+ tool = validateToolDefinition(parsed);
47
+ } catch (err) {
48
+ return {
49
+ found: true,
50
+ name: params.name,
51
+ message: `Tool YAML is invalid: ${(err as Error).message}`,
52
+ };
53
+ }
54
+
55
+ const riskLevel = classifyRisk(tool);
56
+ const tier = getTierForTool(tool);
57
+
58
+ // Determine approval state from manifest
59
+ const approvalDir = path.resolve(toolDir);
60
+ const manifest = new ApprovalManifest(
61
+ approvalDir,
62
+ context?.credentials?.MATIMO_APPROVAL_SECRET,
63
+ );
64
+
65
+ const hash = manifest.computeHash(yamlContent);
66
+ const approvalRecord = manifest.getApproval(params.name);
67
+ const isApproved = approvalRecord ? manifest.isApproved(params.name, hash) : false;
68
+ const pendingTools = manifest.getPendingTools();
69
+
70
+ let approvalState: StatusResult['approvalState'];
71
+ if (tool.status === 'deprecated') {
72
+ approvalState = 'rejected';
73
+ } else if (isApproved) {
74
+ approvalState = 'approved';
75
+ } else if (tier === 'auto') {
76
+ approvalState = 'auto-approved';
77
+ } else if (pendingTools.includes(params.name)) {
78
+ approvalState = 'pending';
79
+ } else {
80
+ // Tool exists on disk but no pending record and not approved — treat as pending
81
+ approvalState = 'pending';
82
+ }
83
+
84
+ logger.debug('matimo_get_tool_status: status retrieved', {
85
+ name: params.name,
86
+ status: tool.status,
87
+ riskLevel,
88
+ approvalState,
89
+ });
90
+
91
+ return {
92
+ found: true,
93
+ name: params.name,
94
+ status: tool.status ?? 'draft',
95
+ riskLevel,
96
+ approvalState,
97
+ approvedAt: approvalRecord?.approvedAt,
98
+ approvedBy: approvalRecord?.approvedBy,
99
+ message: `Tool "${params.name}" is ${approvalState} (${riskLevel} risk)`,
100
+ };
101
+ }
@@ -0,0 +1,52 @@
1
+ name: matimo_list_skills
2
+ version: '1.0.0'
3
+ description: >-
4
+ Level 1 metadata discovery — list all skills available to the system.
5
+
6
+ Skills are auto-discovered in this order (priority):
7
+ 1. From global Matimo instance (if initialized)
8
+ 2. From @matimo/* packages in node_modules (auto-discovery like tools)
9
+ 3. From core skills bundled with @matimo/core
10
+ 4. From optional explicit skills_dir (if provided)
11
+
12
+ Returns each skill's name, description, and optional frontmatter fields
13
+ (license, compatibility, metadata) per the Agent Skills specification.
14
+
15
+ requires_approval: false
16
+ tags:
17
+ - matimo
18
+ - meta
19
+ - skill
20
+ - discovery
21
+
22
+ parameters:
23
+ skills_dir:
24
+ type: string
25
+ required: false
26
+ description: >-
27
+ Optional directory to scan for skills. If provided, loads skills directly
28
+ from this path (each skill is a subdirectory containing a SKILL.md file
29
+ with YAML frontmatter).
30
+
31
+ Paths are resolved relative to the current working directory where the tool
32
+ is executed. For reliability, use absolute paths or verify relative paths
33
+ are correct from the execution context.
34
+
35
+ If not provided, returns skills from the global Matimo instance with
36
+ auto-discovered @matimo/*/skills and user-created skills.
37
+
38
+ execution:
39
+ type: function
40
+ code: './matimo_list_skills.ts'
41
+
42
+ output_schema:
43
+ type: object
44
+ properties:
45
+ skills:
46
+ type: array
47
+ description: >-
48
+ List of discovered skills with name, description, path, and optional
49
+ fields (license, compatibility, metadata)
50
+ total:
51
+ type: number
52
+ description: Total number of skills found